merged ub-launcher3-burnaby-nyc, and resolved conflicts
b/28269144
Change-Id: Ie884be65901b8d9c6a912944094b8c45796162ee
diff --git a/Android.mk b/Android.mk
index 1cf15be..d3823c3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -33,7 +33,7 @@
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res \
$(LOCAL_PATH)/res \
- $(LOCAL_PATH)/../../../frameworks/support/v7/recyclerview/res
+ frameworks/support/v7/recyclerview/res
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
@@ -54,40 +54,20 @@
#
-# Protocol Buffer Debug Utility in Java
+# Launcher proto buffer jar used for development
#
include $(CLEAR_VARS)
-LOCAL_SRC_FILES := $(call all-java-files-under, util) \
- $(call all-proto-files-under, protos)
+LOCAL_SRC_FILES := $(call all-proto-files-under, protos)
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := launcher_protoutil_lib
+LOCAL_MODULE := launcher_proto_lib
LOCAL_IS_HOST_MODULE := true
-LOCAL_JAR_MANIFEST := util/etc/manifest.txt
+LOCAL_STATIC_JAVA_LIBRARIES := host-libprotobuf-java-nano
include $(BUILD_HOST_JAVA_LIBRARY)
-#
-# Protocol Buffer Debug Utility Wrapper Script
-#
-include $(CLEAR_VARS)
-LOCAL_IS_HOST_MODULE := true
-LOCAL_MODULE_CLASS := EXECUTABLES
-LOCAL_MODULE_TAGS := optional
-LOCAL_MODULE := launcher_protoutil
-
-include $(BUILD_SYSTEM)/base_rules.mk
-
-$(LOCAL_BUILT_MODULE): | $(HOST_OUT_JAVA_LIBRARIES)/launcher_protoutil_lib.jar
-$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/util/etc/launcher_protoutil | $(ACP)
- @echo "Copy: $(PRIVATE_MODULE) ($@)"
- $(copy-file-to-new-target)
- $(hide) chmod 755 $@
-
-INTERNAL_DALVIK_MODULES += $(LOCAL_INSTALLED_MODULE)
-
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 48dcf4e..918ae52 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -81,6 +81,7 @@
android:theme="@style/Theme"
android:windowSoftInputMode="adjustPan"
android:screenOrientation="nosensor"
+ android:configChanges="keyboard|keyboardHidden|navigation"
android:resumeWhilePausing="true"
android:taskAffinity=""
android:enabled="true">
@@ -92,39 +93,6 @@
</intent-filter>
</activity>
- <!-- ENABLE_FOR_TESTING
-
- <activity
- android:name="com.android.launcher3.testing.LauncherExtension"
- android:launchMode="singleTask"
- android:clearTaskOnLaunch="true"
- android:stateNotNeeded="true"
- android:theme="@style/Theme"
- android:windowSoftInputMode="adjustPan"
- android:screenOrientation="nosensor"
- android:enabled="false">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.HOME" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.MONKEY"/>
- </intent-filter>
- </activity>
-
- -->
-
- <activity
- android:name="com.android.launcher3.ToggleWeightWatcher"
- android:label="@string/toggle_weight_watcher"
- android:enabled="@bool/debug_memory_enabled"
- android:icon="@mipmap/ic_launcher_home">
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
<activity
android:name="com.android.launcher3.WallpaperPickerActivity"
android:theme="@style/Theme.WallpaperPicker"
@@ -159,34 +127,6 @@
android:process=":settings_process">
</activity>
- <!-- Debugging tools -->
- <activity
- android:name="com.android.launcher3.MemoryDumpActivity"
- android:theme="@android:style/Theme.NoDisplay"
- android:label="@string/debug_memory_activity"
- android:enabled="@bool/debug_memory_enabled"
- android:excludeFromRecents="true"
- android:icon="@mipmap/ic_launcher_home"
- >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.DEFAULT" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
-
- <service android:name="com.android.launcher3.MemoryTracker"
- android:enabled="@bool/debug_memory_enabled"
- >
- </service>
-
- <receiver
- android:name="com.android.launcher3.WallpaperChangedReceiver">
- <intent-filter>
- <action android:name="android.intent.action.WALLPAPER_CHANGED" />
- </intent-filter>
- </receiver>
-
<!-- Intent received used to install shortcuts from other applications -->
<receiver
android:name="com.android.launcher3.InstallShortcutReceiver"
@@ -219,5 +159,54 @@
<meta-data android:name="android.nfc.disable_beam_default"
android:value="true" />
+
+ <!-- ENABLE_FOR_TESTING
+
+ <activity
+ android:name="com.android.launcher3.testing.LauncherExtension"
+ android:launchMode="singleTask"
+ android:clearTaskOnLaunch="true"
+ android:stateNotNeeded="true"
+ android:theme="@style/Theme"
+ android:windowSoftInputMode="adjustPan"
+ android:screenOrientation="nosensor"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.HOME" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.MONKEY"/>
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="com.android.launcher3.testing.MemoryDumpActivity"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:label="* HPROF"
+ android:excludeFromRecents="true"
+ android:icon="@mipmap/ic_launcher_home"
+ >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <activity
+ android:name="com.android.launcher3.testing.ToggleWeightWatcher"
+ android:label="Show Mem"
+ android:icon="@mipmap/ic_launcher_home">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ <service android:name="com.android.launcher3.testing.MemoryTracker" />
+
+ -->
+
</application>
</manifest>
diff --git a/WallpaperPicker/res/anim/fade_out.xml b/WallpaperPicker/res/anim/fade_out.xml
new file mode 100644
index 0000000..2749d92
--- /dev/null
+++ b/WallpaperPicker/res/anim/fade_out.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- startOffset is the same as the duration of the wallpaper_enter animation. We have this delay so
+ that we don't see the wallpaper changing before fading back to the home screen. -->
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:startOffset="@android:integer/config_longAnimTime"
+ android:duration="@android:integer/config_mediumAnimTime"
+ android:fromAlpha="1"
+ android:toAlpha="0"/>
+
diff --git a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml b/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
index 8e349b7..2b39b09 100644
--- a/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
+++ b/WallpaperPicker/res/layout/actionbar_set_wallpaper.xml
@@ -28,5 +28,6 @@
android:drawableLeft="@drawable/ic_actionbar_accept"
android:drawablePadding="8dp"
android:gravity="start|center_vertical"
- android:text="@string/wallpaper_instructions">
+ android:text="@string/wallpaper_instructions"
+ android:enabled="false">
</com.android.launcher3.AlphaDisableableButton>
diff --git a/WallpaperPicker/res/values-af/strings.xml b/WallpaperPicker/res/values-af/strings.xml
index eb81640..bc87fe1 100644
--- a/WallpaperPicker/res/values-af/strings.xml
+++ b/WallpaperPicker/res/values-af/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Stel muurpapier"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Kon nie prent laai nie"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Kon nie prent as muurpapier laai nie"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kon nie prent as muurpapier stel nie"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d gekies"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d gekies"</item>
diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml
index 7e79385..23bf538 100644
--- a/WallpaperPicker/res/values-am/strings.xml
+++ b/WallpaperPicker/res/values-am/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ልጣፍ አዘጋጅ"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ምስሉን መጫን አልተቻለም"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ምስሉን እንደ ግድግዳ ወረቀት መጫን አልተቻለም"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ምስሉን እንደ ግድግዳ ወረቀት ማዘጋጀት አልተቻለም"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ተመርጧል"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ተመርጧል"</item>
diff --git a/WallpaperPicker/res/values-ar/strings.xml b/WallpaperPicker/res/values-ar/strings.xml
index 84fa104..db6834c 100644
--- a/WallpaperPicker/res/values-ar/strings.xml
+++ b/WallpaperPicker/res/values-ar/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"تعيين الخلفية"</string>
<string name="image_load_fail" msgid="7538534580694411837">"تعذر تحميل الصورة"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"تعذر تحميل الصورة كخلفية"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"تعذر تعيين الصورة كخلفية"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"تم تحديد %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"تم تحديد %1$d"</item>
diff --git a/WallpaperPicker/res/values-bg/strings.xml b/WallpaperPicker/res/values-bg/strings.xml
index 60ae302..bf2a83b 100644
--- a/WallpaperPicker/res/values-bg/strings.xml
+++ b/WallpaperPicker/res/values-bg/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Задаване на тапета"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Изображението не можа да бъде заредено"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Изображението не можа да бъде заредено като тапет"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Изображението не можа да бъде зададено като тапет"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Избрахте %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Избрахте %1$d"</item>
diff --git a/WallpaperPicker/res/values-bn-rBD/strings.xml b/WallpaperPicker/res/values-bn-rBD/strings.xml
index 74d7e57..1c4d3d4 100644
--- a/WallpaperPicker/res/values-bn-rBD/strings.xml
+++ b/WallpaperPicker/res/values-bn-rBD/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ওয়ালপেপার সেট করুন"</string>
<string name="image_load_fail" msgid="7538534580694411837">"চিত্র লোড করা যায়নি"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ওয়ালপেপার হিসাবে চিত্র লোড করা যায়নি"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ওয়ালপেপার হিসাবে চিত্র সেট করা যায়নি"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$dটি নির্বাচন করা হয়েছে"</item>
<item quantity="one" msgid="8409622005831789373">"%1$dটি নির্বাচন করা হয়েছে"</item>
diff --git a/WallpaperPicker/res/values-ca/strings.xml b/WallpaperPicker/res/values-ca/strings.xml
index cbec762..9533ae9 100644
--- a/WallpaperPicker/res/values-ca/strings.xml
+++ b/WallpaperPicker/res/values-ca/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Estableix el fons de pantalla"</string>
<string name="image_load_fail" msgid="7538534580694411837">"No s\'ha pogut carregar la imatge."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"No s\'ha pogut carregar la imatge com a fons de pantalla."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"No s\'ha pogut definir la imatge com a fons de pantalla"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Seleccionats: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Seleccionats: %1$d"</item>
diff --git a/WallpaperPicker/res/values-cs/strings.xml b/WallpaperPicker/res/values-cs/strings.xml
index da0758e..aab8cc8 100644
--- a/WallpaperPicker/res/values-cs/strings.xml
+++ b/WallpaperPicker/res/values-cs/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Nastavit jako tapetu"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Obrázek nelze načíst."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Obrázek nelze načíst jako tapetu."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Obrázek nelze nastavit jako tapetu"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Vybráno: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Vybráno: %1$d"</item>
diff --git a/WallpaperPicker/res/values-da/strings.xml b/WallpaperPicker/res/values-da/strings.xml
index 12c0fe8..10ef5b3 100644
--- a/WallpaperPicker/res/values-da/strings.xml
+++ b/WallpaperPicker/res/values-da/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Angiv baggrund"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Billedet kunne ikke indlæses"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Billedet kunne ikke indlæses som baggrund"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Billedet kunne ikke indlæses som baggrund"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d er valgt"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d er valgt"</item>
diff --git a/WallpaperPicker/res/values-de/strings.xml b/WallpaperPicker/res/values-de/strings.xml
index 3171b3f..be35b73 100644
--- a/WallpaperPicker/res/values-de/strings.xml
+++ b/WallpaperPicker/res/values-de/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Hintergrund auswählen"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Bild konnte nicht geladen werden."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Bild konnte nicht als Hintergrund geladen werden."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Bild konnte nicht als Hintergrund festgelegt werden."</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ausgewählt"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ausgewählt"</item>
diff --git a/WallpaperPicker/res/values-el/strings.xml b/WallpaperPicker/res/values-el/strings.xml
index 2988bec..1d799a8 100644
--- a/WallpaperPicker/res/values-el/strings.xml
+++ b/WallpaperPicker/res/values-el/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Ορισμός ταπετσαρίας"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Δεν ήταν δυνατή η φόρτωση της εικόνας"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Δεν ήταν δυνατή η φόρτωση της εικόνας ως ταπετσαρία"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Δεν ήταν δυνατός ο ορισμός της εικόνας ως ταπετσαρία"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d επιλεγμένα"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d επιλεγμένα"</item>
diff --git a/WallpaperPicker/res/values-en-rAU/strings.xml b/WallpaperPicker/res/values-en-rAU/strings.xml
index 30450ee..a384ff6 100644
--- a/WallpaperPicker/res/values-en-rAU/strings.xml
+++ b/WallpaperPicker/res/values-en-rAU/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selected"</item>
diff --git a/WallpaperPicker/res/values-en-rGB/strings.xml b/WallpaperPicker/res/values-en-rGB/strings.xml
index 30450ee..a384ff6 100644
--- a/WallpaperPicker/res/values-en-rGB/strings.xml
+++ b/WallpaperPicker/res/values-en-rGB/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selected"</item>
diff --git a/WallpaperPicker/res/values-en-rIN/strings.xml b/WallpaperPicker/res/values-en-rIN/strings.xml
index 30450ee..a384ff6 100644
--- a/WallpaperPicker/res/values-en-rIN/strings.xml
+++ b/WallpaperPicker/res/values-en-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Set wallpaper"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Couldn\'t load image"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Couldn\'t load image as wallpaper"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Couldn\'t set image as wallpaper"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selected"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selected"</item>
diff --git a/WallpaperPicker/res/values-es-rUS/strings.xml b/WallpaperPicker/res/values-es-rUS/strings.xml
index c18ecb2..924ea56 100644
--- a/WallpaperPicker/res/values-es-rUS/strings.xml
+++ b/WallpaperPicker/res/values-es-rUS/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer como fondo de pantalla"</string>
<string name="image_load_fail" msgid="7538534580694411837">"No se pudo cargar la imagen."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"No se pudo cargar la imagen como fondo de pantalla."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"No se pudo establecer la imagen como fondo de pantalla"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d seleccionado"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d seleccionado"</item>
diff --git a/WallpaperPicker/res/values-es/strings.xml b/WallpaperPicker/res/values-es/strings.xml
index b7221af..8ecd3f4 100644
--- a/WallpaperPicker/res/values-es/strings.xml
+++ b/WallpaperPicker/res/values-es/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer fondo"</string>
<string name="image_load_fail" msgid="7538534580694411837">"No se ha podido cargar la imagen"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"No se ha podido cargar la imagen como fondo de pantalla"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"No se ha podido establecer la imagen como fondo de pantalla"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Seleccionados: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Seleccionados: %1$d"</item>
diff --git a/WallpaperPicker/res/values-et-rEE/strings.xml b/WallpaperPicker/res/values-et-rEE/strings.xml
index 571a0ff..59ca770 100644
--- a/WallpaperPicker/res/values-et-rEE/strings.xml
+++ b/WallpaperPicker/res/values-et-rEE/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Määra taustapilt"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Kujutist ei õnnestunud laadida"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Kujutist ei õnnestunud taustapildina laadida"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kujutist ei õnnestunud taustapildiks määrata"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Valitud on %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Valitud on %1$d"</item>
diff --git a/WallpaperPicker/res/values-eu-rES/strings.xml b/WallpaperPicker/res/values-eu-rES/strings.xml
index 45bf7a1..fb77b1a 100644
--- a/WallpaperPicker/res/values-eu-rES/strings.xml
+++ b/WallpaperPicker/res/values-eu-rES/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Ezarri horma-papera"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Ezin izan da irudia kargatu"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Ezin izan da irudia horma-paper gisa kargatu"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ezin izan da ezarri irudia horma-paper gisa"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d hautatuta"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d hautatuta"</item>
diff --git a/WallpaperPicker/res/values-fa/strings.xml b/WallpaperPicker/res/values-fa/strings.xml
index 469ec12..da4b7a1 100644
--- a/WallpaperPicker/res/values-fa/strings.xml
+++ b/WallpaperPicker/res/values-fa/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"تنظیم کاغذدیواری"</string>
<string name="image_load_fail" msgid="7538534580694411837">"تصویر بارگیری نشد"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"تصویر بهعنوان کاغذدیواری بارگیری نشد"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"تصویر بهعنوان کاغذدیواری تنظیم نشد"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d انتخاب شد"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d انتخاب شد"</item>
diff --git a/WallpaperPicker/res/values-fi/strings.xml b/WallpaperPicker/res/values-fi/strings.xml
index c82d3e0..3c8f1f5 100644
--- a/WallpaperPicker/res/values-fi/strings.xml
+++ b/WallpaperPicker/res/values-fi/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Aseta taustakuva"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Kuvan lataus epäonnistui"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Kuvaa ei voitu ladata taustakuvaksi"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kuvan asettaminen taustakuvaksi epäonnistui."</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d valittu"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d valittu"</item>
diff --git a/WallpaperPicker/res/values-fr-rCA/strings.xml b/WallpaperPicker/res/values-fr-rCA/strings.xml
index 9256896..ba1d430 100644
--- a/WallpaperPicker/res/values-fr-rCA/strings.xml
+++ b/WallpaperPicker/res/values-fr-rCA/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Définir le fond d\'écran"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Impossible de charger l\'image"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossible de charger l\'image comme fond d\'écran"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossible d\'utiliser l\'image comme fond d\'écran"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d sélectionné"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d sélectionné"</item>
diff --git a/WallpaperPicker/res/values-fr/strings.xml b/WallpaperPicker/res/values-fr/strings.xml
index 479a7e4..9f3b525 100644
--- a/WallpaperPicker/res/values-fr/strings.xml
+++ b/WallpaperPicker/res/values-fr/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Définir comme fond d\'écran"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Impossible de charger l\'image."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossible de charger l\'image comme fond d\'écran."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossible de définir l\'image comme fond d\'écran."</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d élément sélectionné"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d élément sélectionné"</item>
diff --git a/WallpaperPicker/res/values-gl-rES/strings.xml b/WallpaperPicker/res/values-gl-rES/strings.xml
index 0396b2f..5805489 100644
--- a/WallpaperPicker/res/values-gl-rES/strings.xml
+++ b/WallpaperPicker/res/values-gl-rES/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Establecer fondo de pantalla"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Non se puido cargar a imaxe"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Non se puido cargar a imaxe como fondo de pantalla"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Non se puido definir a imaxe como fondo de pantalla"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Seleccionaches %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Seleccionaches %1$d"</item>
diff --git a/WallpaperPicker/res/values-hi/strings.xml b/WallpaperPicker/res/values-hi/strings.xml
index 60834e1..6f610b5 100644
--- a/WallpaperPicker/res/values-hi/strings.xml
+++ b/WallpaperPicker/res/values-hi/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"वॉलपेपर सेट करें"</string>
<string name="image_load_fail" msgid="7538534580694411837">"चित्र लोड नहीं किया जा सका"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"चित्र को वॉलपेपर के रूप में लोड नहीं किया जा सका"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"चित्र को वॉलपेपर के रूप में सेट नहीं किया जा सका"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d चयनित"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d चयनित"</item>
diff --git a/WallpaperPicker/res/values-hr/strings.xml b/WallpaperPicker/res/values-hr/strings.xml
index 9ed702c..aaf5e1b 100644
--- a/WallpaperPicker/res/values-hr/strings.xml
+++ b/WallpaperPicker/res/values-hr/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Postavi pozadinu"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Nije moguće učitati sliku"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Nije moguće učitati sliku kao pozadinu"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Postavljanje slike kao pozadine nije uspjelo"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Odabrano je %1$d stavki"</item>
<item quantity="one" msgid="8409622005831789373">"Odabrana je %1$d stavka"</item>
diff --git a/WallpaperPicker/res/values-hu/strings.xml b/WallpaperPicker/res/values-hu/strings.xml
index d8b08fd..06c0952 100644
--- a/WallpaperPicker/res/values-hu/strings.xml
+++ b/WallpaperPicker/res/values-hu/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Háttérkép beállítása"</string>
<string name="image_load_fail" msgid="7538534580694411837">"A kép betöltése nem sikerült"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"A kép betöltése háttérképként nem sikerült"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"A kép beállítása háttérképként nem sikerült"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d kiválasztva"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d kiválasztva"</item>
diff --git a/WallpaperPicker/res/values-hy-rAM/strings.xml b/WallpaperPicker/res/values-hy-rAM/strings.xml
index 027b841..f68d49f 100644
--- a/WallpaperPicker/res/values-hy-rAM/strings.xml
+++ b/WallpaperPicker/res/values-hy-rAM/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Սահմանել պաստառը"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Չհաջողվեց բեռնել նկարը"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Չհաջողվեց նկարը սահմանել որպես պաստառ"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Չհաջողվեց նկարը դնել որպես պաստառ"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ընտրված"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ընտրված"</item>
diff --git a/WallpaperPicker/res/values-in/strings.xml b/WallpaperPicker/res/values-in/strings.xml
index 9155452..634eb1f 100644
--- a/WallpaperPicker/res/values-in/strings.xml
+++ b/WallpaperPicker/res/values-in/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Setel wallpaper"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Tidak dapat memuat gambar"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Tidak dapat memuat gambar sebagai wallpaper"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Tidak dapat menyetel gambar sebagai wallpaper"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d dipilih"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d dipilih"</item>
diff --git a/WallpaperPicker/res/values-is-rIS/strings.xml b/WallpaperPicker/res/values-is-rIS/strings.xml
index 69e6cd4..eac44ec 100644
--- a/WallpaperPicker/res/values-is-rIS/strings.xml
+++ b/WallpaperPicker/res/values-is-rIS/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Velja veggfóður"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Ekki var hægt að hlaða mynd"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Ekki var hægt að hlaða mynd sem veggfóður"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ekki var hægt að nota mynd sem veggfóður"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d valin"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d valið"</item>
diff --git a/WallpaperPicker/res/values-it/strings.xml b/WallpaperPicker/res/values-it/strings.xml
index f4d38d1..fdb0ce8 100644
--- a/WallpaperPicker/res/values-it/strings.xml
+++ b/WallpaperPicker/res/values-it/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Imposta sfondo"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Impossibile caricare l\'immagine"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Impossibile caricare l\'immagine come sfondo"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Impossibile impostare l\'immagine come sfondo"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selezionati"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selezionato"</item>
diff --git a/WallpaperPicker/res/values-iw/strings.xml b/WallpaperPicker/res/values-iw/strings.xml
index e29f237..c6a583d 100644
--- a/WallpaperPicker/res/values-iw/strings.xml
+++ b/WallpaperPicker/res/values-iw/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"הגדר טפט"</string>
<string name="image_load_fail" msgid="7538534580694411837">"לא ניתן היה לטעון את התמונה"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"לא ניתן היה לטעון את התמונה כטפט"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"לא ניתן היה להגדיר את התמונה כטפט"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d נבחרו"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d נבחרו"</item>
diff --git a/WallpaperPicker/res/values-ja/strings.xml b/WallpaperPicker/res/values-ja/strings.xml
index e86026b..f18da5d 100644
--- a/WallpaperPicker/res/values-ja/strings.xml
+++ b/WallpaperPicker/res/values-ja/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"壁紙を設定"</string>
<string name="image_load_fail" msgid="7538534580694411837">"画像を読み込めませんでした"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"画像を壁紙として読み込めませんでした"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"画像を壁紙として設定できませんでした"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d個選択済み"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d個選択済み"</item>
diff --git a/WallpaperPicker/res/values-ka-rGE/strings.xml b/WallpaperPicker/res/values-ka-rGE/strings.xml
index 1ae1021..1f65282 100644
--- a/WallpaperPicker/res/values-ka-rGE/strings.xml
+++ b/WallpaperPicker/res/values-ka-rGE/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ფონის დაყენება"</string>
<string name="image_load_fail" msgid="7538534580694411837">"სურათი ვერ ჩაიტვირთა."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"სურათი ფონად ვერ ჩაიტვირთა."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"სურათი ფონად ვერ დაყენდა"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"არჩეულია %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"არჩეულია %1$d"</item>
diff --git a/WallpaperPicker/res/values-kk-rKZ/strings.xml b/WallpaperPicker/res/values-kk-rKZ/strings.xml
index a10e273..6f4ee7c 100644
--- a/WallpaperPicker/res/values-kk-rKZ/strings.xml
+++ b/WallpaperPicker/res/values-kk-rKZ/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Тұсқағаз орнату"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Суретті жүктей алмады"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Суретті артқы фон ретінде жүктей алмады"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Кескінді тұсқағаз ретінде орнату мүмкін болмады"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d таңдалған"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d таңдалған"</item>
diff --git a/WallpaperPicker/res/values-km-rKH/strings.xml b/WallpaperPicker/res/values-km-rKH/strings.xml
index 2a605e8..801260d 100644
--- a/WallpaperPicker/res/values-km-rKH/strings.xml
+++ b/WallpaperPicker/res/values-km-rKH/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"កំណត់ផ្ទាំងរូបភាព"</string>
<string name="image_load_fail" msgid="7538534580694411837">"មិនអាចផ្ទុករូបភាព"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"មិនអាចផ្ទុករូបភាពជាផ្ទាំងរូបភាព"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"មិនអាចកំណត់រូបភាពជាផ្ទាំងរូបភាពទេ"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"បានជ្រើស %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"បានជ្រើស %1$d"</item>
diff --git a/WallpaperPicker/res/values-kn-rIN/strings.xml b/WallpaperPicker/res/values-kn-rIN/strings.xml
index b69bda5..7d4d7e7 100644
--- a/WallpaperPicker/res/values-kn-rIN/strings.xml
+++ b/WallpaperPicker/res/values-kn-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ವಾಲ್ಪೇಪರ್ ಹೊಂದಿಸಿ"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ಚಿತ್ರವನ್ನು ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ಚಿತ್ರವನ್ನು ವಾಲ್ಪೇಪರ್ ರೂಪದಲ್ಲಿ ಲೋಡ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ಚಿತ್ರವನ್ನು ವಾಲ್ಪೇಪರ್ ರೂಪದಲ್ಲಿ ಹೊಂದಿಸಲಾಗಲಿಲ್ಲ"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</item>
diff --git a/WallpaperPicker/res/values-ko/strings.xml b/WallpaperPicker/res/values-ko/strings.xml
index 30e7cfa..f407294 100644
--- a/WallpaperPicker/res/values-ko/strings.xml
+++ b/WallpaperPicker/res/values-ko/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"배경화면 설정"</string>
<string name="image_load_fail" msgid="7538534580694411837">"이미지를 로드할 수 없습니다."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"이미지를 배경화면으로 로드할 수 없습니다."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"이미지를 배경화면으로 설정할 수 없습니다."</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d개 선택됨"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d개 선택됨"</item>
diff --git a/WallpaperPicker/res/values-ky-rKG/strings.xml b/WallpaperPicker/res/values-ky-rKG/strings.xml
index f1ebf09..f53f52b 100644
--- a/WallpaperPicker/res/values-ky-rKG/strings.xml
+++ b/WallpaperPicker/res/values-ky-rKG/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Тушкагаз орнотуу"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Сүрөт жүктөө мүмкүн болбоду"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Сүрөттү тушкагаз катары жүктөө кыйрады"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Сүрөт тушкагаз катары коюлбай койду"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d тандалды"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d тандалды"</item>
diff --git a/WallpaperPicker/res/values-lo-rLA/strings.xml b/WallpaperPicker/res/values-lo-rLA/strings.xml
index e32cafb..1da6d29 100644
--- a/WallpaperPicker/res/values-lo-rLA/strings.xml
+++ b/WallpaperPicker/res/values-lo-rLA/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ຕັ້ງເປັນພາບພື້ນຫຼັງ"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ບໍ່ສາມາດໂຫຼດຮູບໄດ້"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ບໍ່ສາມາດໂຫຼດຮູບເປັນພາບພື້ນຫຼັງໄດ້"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ບໍ່ສາມາດໂຫຼດຮູບເປັນພາບພື້ນຫຼັງໄດ້"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"ເລືອກ %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"ເລືອກ %1$d"</item>
diff --git a/WallpaperPicker/res/values-lt/strings.xml b/WallpaperPicker/res/values-lt/strings.xml
index c442e3a..98c9a36 100644
--- a/WallpaperPicker/res/values-lt/strings.xml
+++ b/WallpaperPicker/res/values-lt/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Nustatyti ekrano foną"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Nepavyko įkelti vaizdo"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Nepavyko įkelti vaizdo kaip ekrano fono"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nepavyko nustatyti vaizdo kaip ekrano fono"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Pasirinkta: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Pasirinkta: %1$d"</item>
diff --git a/WallpaperPicker/res/values-lv/strings.xml b/WallpaperPicker/res/values-lv/strings.xml
index d05a175..ff7876c 100644
--- a/WallpaperPicker/res/values-lv/strings.xml
+++ b/WallpaperPicker/res/values-lv/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Iestatīt fona tapeti"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Nevarēja ielādēt attēlu."</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Nevarēja ielādēt attēlu kā fona tapeti."</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nevarēja iestatīt attēlu kā fona tapeti."</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Atlasīts: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Atlasīta: %1$d"</item>
diff --git a/WallpaperPicker/res/values-mk-rMK/strings.xml b/WallpaperPicker/res/values-mk-rMK/strings.xml
index f9e0963..13b38cd 100644
--- a/WallpaperPicker/res/values-mk-rMK/strings.xml
+++ b/WallpaperPicker/res/values-mk-rMK/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Подеси тапет"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Сликата не можеше да се вчита"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Сликата не можеше да се вчита како тапет"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Сликата не може да се постави како тапет"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Избрано %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Избрано %1$d"</item>
diff --git a/WallpaperPicker/res/values-ml-rIN/strings.xml b/WallpaperPicker/res/values-ml-rIN/strings.xml
index 6be771c..5831b36 100644
--- a/WallpaperPicker/res/values-ml-rIN/strings.xml
+++ b/WallpaperPicker/res/values-ml-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"വാൾപേപ്പർ സജ്ജീകരിക്കുക"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ചിത്രം ലോഡുചെയ്യാനായില്ല"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"വാൾപേപ്പറായി ചിത്രം ലോഡുചെയ്യാനായില്ല"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"വാൾപേപ്പറായി ചിത്രം ലോഡുചെയ്യാനായില്ല"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d തിരഞ്ഞെടുത്തു"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d തിരഞ്ഞെടുത്തു"</item>
diff --git a/WallpaperPicker/res/values-mn-rMN/strings.xml b/WallpaperPicker/res/values-mn-rMN/strings.xml
index b88da5a..9995547 100644
--- a/WallpaperPicker/res/values-mn-rMN/strings.xml
+++ b/WallpaperPicker/res/values-mn-rMN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Ханын зургийг тохируулах"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Зургийг ачаалж чадсангүй"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Зургийг ханын зураг болгож ачаалж чадсангүй"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Зургийг ханын зураг болгож чадсангүй"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d сонгогдсон"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d сонгогдсон"</item>
diff --git a/WallpaperPicker/res/values-mr-rIN/strings.xml b/WallpaperPicker/res/values-mr-rIN/strings.xml
index 128b21c..d740fd2 100644
--- a/WallpaperPicker/res/values-mr-rIN/strings.xml
+++ b/WallpaperPicker/res/values-mr-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"वॉलपेपर सेट करा"</string>
<string name="image_load_fail" msgid="7538534580694411837">"प्रतिमा लोड करू शकलो नाही"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"वॉलपेपर म्हणून प्रतिमा लोड करू शकलो नाही"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"प्रतिमा वॉलपेपर म्हणून सेट करू शकलो नाही"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d निवडले"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d निवडले"</item>
diff --git a/WallpaperPicker/res/values-ms-rMY/strings.xml b/WallpaperPicker/res/values-ms-rMY/strings.xml
index 9d824e4..759e48c 100644
--- a/WallpaperPicker/res/values-ms-rMY/strings.xml
+++ b/WallpaperPicker/res/values-ms-rMY/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Tetapkan kertas dinding"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Tidak dapat memuatkan imej"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Tidak dapat memuatkan imej sebagai kertas dinding"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Tidak dapat menetapkan imej sebagai kertas dinding"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d dipilih"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d dipilih"</item>
diff --git a/WallpaperPicker/res/values-my-rMM/strings.xml b/WallpaperPicker/res/values-my-rMM/strings.xml
index 2336114..5197b98 100644
--- a/WallpaperPicker/res/values-my-rMM/strings.xml
+++ b/WallpaperPicker/res/values-my-rMM/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"နောက်ခံအား သတ်မှတ်ရန်"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ပုံရိပ် တင် မရပါ"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ပုံရိပ်အား နောက်ခံအဖြစ် တင် မရပါ"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ပုံရိပ်အား နောက်ခံအဖြစ် တင်၍မရပါ"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ရွေးချယ်ပြီး"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ရွေးချယ်ပြီး"</item>
diff --git a/WallpaperPicker/res/values-nb/strings.xml b/WallpaperPicker/res/values-nb/strings.xml
index 9ae0b98..8125b53 100644
--- a/WallpaperPicker/res/values-nb/strings.xml
+++ b/WallpaperPicker/res/values-nb/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Angi bakgrunn"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Kunne ikke laste inn bildet"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Kunne ikke laste inn bildet som bakgrunn"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kunne ikke angi bildet som bakgrunn"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d valgt"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d valgt"</item>
diff --git a/WallpaperPicker/res/values-ne-rNP/strings.xml b/WallpaperPicker/res/values-ne-rNP/strings.xml
index 5bca8d8..b77a1c5 100644
--- a/WallpaperPicker/res/values-ne-rNP/strings.xml
+++ b/WallpaperPicker/res/values-ne-rNP/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"वालपेपर मिलाउनुहोस्"</string>
<string name="image_load_fail" msgid="7538534580694411837">"तस्बिर लोड गर्न सकिएन"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"तस्बिरलाई वालपेपरका रूपमा लोड गर्न सकिएन"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"छविलाई वालपेपरको रूपमा सेट गर्न सकिएन"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d चयन भयो"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d चयन भयो"</item>
diff --git a/WallpaperPicker/res/values-nl/strings.xml b/WallpaperPicker/res/values-nl/strings.xml
index a86f0f6..dc78305 100644
--- a/WallpaperPicker/res/values-nl/strings.xml
+++ b/WallpaperPicker/res/values-nl/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Achtergrond instellen"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Kan afbeelding niet laden"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Kan afbeelding niet laden als achtergrond"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Kan afbeelding niet instellen als achtergrond"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d geselecteerd"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d geselecteerd"</item>
diff --git a/WallpaperPicker/res/values-pl/strings.xml b/WallpaperPicker/res/values-pl/strings.xml
index cd52082..9693de4 100644
--- a/WallpaperPicker/res/values-pl/strings.xml
+++ b/WallpaperPicker/res/values-pl/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Ustaw tapetę"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Nie udało się załadować obrazu"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Nie udało się załadować obrazu jako tapety"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nie udało się ustawić obrazu jako tapety"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Wybranych %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Wybrana %1$d"</item>
diff --git a/WallpaperPicker/res/values-pt-rPT/strings.xml b/WallpaperPicker/res/values-pt-rPT/strings.xml
index 82aa469..3c4fa9b 100644
--- a/WallpaperPicker/res/values-pt-rPT/strings.xml
+++ b/WallpaperPicker/res/values-pt-rPT/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Definir imagem fundo"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Não foi possível carregar a imagem"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Não foi possível carregar a imagem como imagem de fundo"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Não foi possível definir a imagem como imagem de fundo"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selecionadas"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selecionada"</item>
diff --git a/WallpaperPicker/res/values-pt/strings.xml b/WallpaperPicker/res/values-pt/strings.xml
index 74ff310..2520eed 100644
--- a/WallpaperPicker/res/values-pt/strings.xml
+++ b/WallpaperPicker/res/values-pt/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Definir plano de fundo"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Não foi possível carregar a imagem"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Não foi possível carregar a imagem como plano de fundo"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Não foi possível definir a imagem como plano de fundo"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selecionados"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selecionado"</item>
diff --git a/WallpaperPicker/res/values-ro/strings.xml b/WallpaperPicker/res/values-ro/strings.xml
index 6281b80..f5df3ee 100644
--- a/WallpaperPicker/res/values-ro/strings.xml
+++ b/WallpaperPicker/res/values-ro/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Setați imaginea de fundal"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Nu s-a putut încărca imaginea"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Nu s-a putut încărca imaginea ca fundal"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Nu s-a putut seta ca imagine de fundal"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d selectate"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d selectată"</item>
diff --git a/WallpaperPicker/res/values-ru/strings.xml b/WallpaperPicker/res/values-ru/strings.xml
index 959205a..f8c350a 100644
--- a/WallpaperPicker/res/values-ru/strings.xml
+++ b/WallpaperPicker/res/values-ru/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Установить как обои"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Не удалось загрузить изображение"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Не удалось загрузить изображение"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Не удалось сменить обои"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Выбрано: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Выбрано: %1$d"</item>
diff --git a/WallpaperPicker/res/values-si-rLK/strings.xml b/WallpaperPicker/res/values-si-rLK/strings.xml
index 37ce1fc..3945bdf 100644
--- a/WallpaperPicker/res/values-si-rLK/strings.xml
+++ b/WallpaperPicker/res/values-si-rLK/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"වෝල්පේපරය සකසන්න"</string>
<string name="image_load_fail" msgid="7538534580694411837">"පින්තූරය පූරණය කිරීමට නොහැකි විය"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"පින්තූරය වෝල්පේපරයක් ලෙස පූරණය කිරීමට නොහැකි විය"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"පින්තූරය බිතුපතක් ලෙස සැකසීමට නොහැකි විය"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d තෝරා ගන්නා ලදි"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d තෝරා ගන්නා ලදි"</item>
diff --git a/WallpaperPicker/res/values-sk/strings.xml b/WallpaperPicker/res/values-sk/strings.xml
index 9d38de4..fb2c819 100644
--- a/WallpaperPicker/res/values-sk/strings.xml
+++ b/WallpaperPicker/res/values-sk/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Nastaviť tapetu"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Obrázok nie je možné načítať"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Obrázok nie je možné načítať ako tapetu"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Obrázok nie je možné nastaviť ako tapetu"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Počet vybratých položiek: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Počet vybratých položiek: %1$d"</item>
diff --git a/WallpaperPicker/res/values-sl/strings.xml b/WallpaperPicker/res/values-sl/strings.xml
index e776cc7..a7ff089 100644
--- a/WallpaperPicker/res/values-sl/strings.xml
+++ b/WallpaperPicker/res/values-sl/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Nastavi ozadje"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Slike ni bilo mogoče naložiti"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Slike ni bilo mogoče naložiti kot ozadje"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Slike ni bilo mogoče nastaviti kot ozadje"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Št. izbranih: %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Št. izbranih: %1$d"</item>
diff --git a/WallpaperPicker/res/values-sr/strings.xml b/WallpaperPicker/res/values-sr/strings.xml
index c37bc06..6154526 100644
--- a/WallpaperPicker/res/values-sr/strings.xml
+++ b/WallpaperPicker/res/values-sr/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Подеси позадину"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Није могуће учитати слику"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Није могуће учитати слику као позадину"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Учитавање слике као позадине није успело"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Изабрано је %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Изабрана је %1$d"</item>
diff --git a/WallpaperPicker/res/values-sv/strings.xml b/WallpaperPicker/res/values-sv/strings.xml
index 044508b..38062b9 100644
--- a/WallpaperPicker/res/values-sv/strings.xml
+++ b/WallpaperPicker/res/values-sv/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Ange bakgrund"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Det gick inte att läsa in bilden"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Det gick inte att läsa in bilden som bakgrund"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Det gick inte att ange bilden som bakgrund"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d har valts"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d har valts"</item>
diff --git a/WallpaperPicker/res/values-sw/strings.xml b/WallpaperPicker/res/values-sw/strings.xml
index 367912a..729a79a 100644
--- a/WallpaperPicker/res/values-sw/strings.xml
+++ b/WallpaperPicker/res/values-sw/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Weka mandhari"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Haikuweza kupakia picha"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Haikuweza kupakia picha iwe mandhari"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Haikuweza kuweka picha kuwa mandhari"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d zimechaguliwa"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d zimechaguliwa"</item>
diff --git a/WallpaperPicker/res/values-sw720dp-v19/styles.xml b/WallpaperPicker/res/values-sw720dp-v19/styles.xml
deleted file mode 100644
index d8dab22..0000000
--- a/WallpaperPicker/res/values-sw720dp-v19/styles.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2013 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-* http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
- <item name="android:windowActionModeOverlay">true</item>
- <item name="android:windowTranslucentStatus">true</item>
- <item name="android:windowTranslucentNavigation">true</item>
- </style>
-</resources>
diff --git a/WallpaperPicker/res/values-sw720dp/styles.xml b/WallpaperPicker/res/values-sw720dp/styles.xml
index 12f8884..0058f7e 100644
--- a/WallpaperPicker/res/values-sw720dp/styles.xml
+++ b/WallpaperPicker/res/values-sw720dp/styles.xml
@@ -18,7 +18,11 @@
-->
<resources>
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
+ <style name="BaseWallpaperTheme" parent="@android:style/Theme.DeviceDefault.Light.NoActionBar">
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:colorBackgroundCacheHint">@null</item>
+ <item name="android:windowShowWallpaper">true</item>
+ <item name="android:windowNoTitle">true</item>
<item name="android:windowActionModeOverlay">true</item>
</style>
</resources>
diff --git a/WallpaperPicker/res/values-ta-rIN/strings.xml b/WallpaperPicker/res/values-ta-rIN/strings.xml
index 7284da8..69a9d38 100644
--- a/WallpaperPicker/res/values-ta-rIN/strings.xml
+++ b/WallpaperPicker/res/values-ta-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"வால்பேப்பரை அமை"</string>
<string name="image_load_fail" msgid="7538534580694411837">"படத்தை ஏற்ற முடியவில்லை"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"படத்தை வால்பேப்பராக ஏற்ற முடியவில்லை"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"படத்தை வால்பேப்பராக அமைக்க முடியவில்லை"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d தேர்ந்தெடுக்கப்பட்டன"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d தேர்ந்தெடுக்கப்பட்டது"</item>
diff --git a/WallpaperPicker/res/values-te-rIN/strings.xml b/WallpaperPicker/res/values-te-rIN/strings.xml
index 4c5ad2f..6fb5fa2 100644
--- a/WallpaperPicker/res/values-te-rIN/strings.xml
+++ b/WallpaperPicker/res/values-te-rIN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"వాల్పేపర్ను సెట్ చేయి"</string>
<string name="image_load_fail" msgid="7538534580694411837">"చిత్రాన్ని లోడ్ చేయడం సాధ్యపడలేదు"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"చిత్రాన్ని వాల్పేపర్గా లోడ్ చేయడం సాధ్యపడలేదు"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"చిత్రాన్ని వాల్పేపర్గా సెట్ చేయడం సాధ్యపడలేదు"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ఎంచుకోబడింది"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ఎంచుకోబడింది"</item>
diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml
index b3d7a8b..c689436 100644
--- a/WallpaperPicker/res/values-th/strings.xml
+++ b/WallpaperPicker/res/values-th/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"ตั้งวอลเปเปอร์"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ไม่สามารถโหลดรูปภาพ"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ไม่สามารถโหลดรูปภาพเป็นวอลเปเปอร์"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"ไม่สามารถตั้งรูปภาพเป็นวอลเปเปอร์"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"เลือกไว้ %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"เลือกไว้ %1$d"</item>
diff --git a/WallpaperPicker/res/values-tl/strings.xml b/WallpaperPicker/res/values-tl/strings.xml
index db8f4d5..c760d7f 100644
--- a/WallpaperPicker/res/values-tl/strings.xml
+++ b/WallpaperPicker/res/values-tl/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Itakda ang wallpaper"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Hindi ma-load ang larawan"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Hindi ma-load ang larawan bilang wallpaper"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Hindi maitakda ang larawan bilang wallpaper"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ang napili"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ang napili"</item>
diff --git a/WallpaperPicker/res/values-tr/strings.xml b/WallpaperPicker/res/values-tr/strings.xml
index d1e57ed..e9dc1d2 100644
--- a/WallpaperPicker/res/values-tr/strings.xml
+++ b/WallpaperPicker/res/values-tr/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Duvar kağıdını ayarla"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Resim yüklenemedi"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Resim duvar kağıdı olarak yüklenemedi"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Resim, duvar kağıdı olarak ayarlanamadı"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d tane seçildi"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d tane seçildi"</item>
diff --git a/WallpaperPicker/res/values-uk/strings.xml b/WallpaperPicker/res/values-uk/strings.xml
index 756e7cf..c6669c0 100644
--- a/WallpaperPicker/res/values-uk/strings.xml
+++ b/WallpaperPicker/res/values-uk/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Установити фон"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Не вдалося завантажити зображення"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Не вдалося завантажити зображення як фоновий малюнок"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Не вдалося зробити зображення фоновим малюнком"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Вибрано %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Вибрано %1$d"</item>
diff --git a/WallpaperPicker/res/values-ur-rPK/strings.xml b/WallpaperPicker/res/values-ur-rPK/strings.xml
index 86d8163..e240e1a 100644
--- a/WallpaperPicker/res/values-ur-rPK/strings.xml
+++ b/WallpaperPicker/res/values-ur-rPK/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"وال پیپر سیٹ کریں"</string>
<string name="image_load_fail" msgid="7538534580694411837">"تصویر کو لوڈ نہیں کیا جا سکا"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"تصویر کو وال پیپر کے بطور لوڈ نہیں کیا جا سکا"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"تصویر کو بطور وال پیپر سیٹ نہیں کیا جا سکا"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d کو منتخب کیا گیا"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d کو منتخب کیا گیا"</item>
diff --git a/WallpaperPicker/res/values-uz-rUZ/strings.xml b/WallpaperPicker/res/values-uz-rUZ/strings.xml
index 97d3938..5a79981 100644
--- a/WallpaperPicker/res/values-uz-rUZ/strings.xml
+++ b/WallpaperPicker/res/values-uz-rUZ/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Fonga rasm o‘rnatish"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Rasm yuklanmadi"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Fon rasmi sifatida rasm yuklanmadi"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Rasmni fon rasmi sifatida o‘rnatib bo‘lmadi"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d ta tanlandi"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d ta tanlandi"</item>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
index 70220ed..d2f7dd4 100644
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -34,8 +34,11 @@
</style>
<style name="Theme" parent="@style/BaseWallpaperTheme">
- <item name="android:windowTranslucentStatus">true</item>
- <item name="android:windowTranslucentNavigation">true</item>
+ <item name="android:windowTranslucentStatus">false</item>
+ <item name="android:windowTranslucentNavigation">false</item>
+ <item name="android:windowDrawsSystemBarBackgrounds">true</item>
+ <item name="android:statusBarColor">#00000000</item>
+ <item name="android:navigationBarColor">#00000000</item>
<item name="android:colorControlActivated">@color/launcher_accent_color</item>
<item name="android:colorAccent">@color/launcher_accent_color</item>
<item name="android:colorPrimary">@color/launcher_accent_color</item>
diff --git a/WallpaperPicker/res/values-vi/strings.xml b/WallpaperPicker/res/values-vi/strings.xml
index ae00d58..a7c636d 100644
--- a/WallpaperPicker/res/values-vi/strings.xml
+++ b/WallpaperPicker/res/values-vi/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Đặt hình nền"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Không thể tải hình ảnh"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Không thể tải hình ảnh làm hình nền"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Không thể đặt hình ảnh làm hình nền"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"Đã chọn %1$d"</item>
<item quantity="one" msgid="8409622005831789373">"Đã chọn %1$d"</item>
diff --git a/WallpaperPicker/res/values-zh-rCN/strings.xml b/WallpaperPicker/res/values-zh-rCN/strings.xml
index 8bd5342..4656ec6 100644
--- a/WallpaperPicker/res/values-zh-rCN/strings.xml
+++ b/WallpaperPicker/res/values-zh-rCN/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"设置壁纸"</string>
<string name="image_load_fail" msgid="7538534580694411837">"无法加载图片"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"无法加载要设为壁纸的图片"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"无法将图片设为壁纸"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"已选择%1$d项"</item>
<item quantity="one" msgid="8409622005831789373">"已选择%1$d项"</item>
diff --git a/WallpaperPicker/res/values-zh-rHK/strings.xml b/WallpaperPicker/res/values-zh-rHK/strings.xml
index e51d60a..eb9c327 100644
--- a/WallpaperPicker/res/values-zh-rHK/strings.xml
+++ b/WallpaperPicker/res/values-zh-rHK/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"設定桌布"</string>
<string name="image_load_fail" msgid="7538534580694411837">"無法載入圖片"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"無法載入圖片設為桌布"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"無法將圖片設為桌布"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"已選取 %1$d 張"</item>
<item quantity="one" msgid="8409622005831789373">"已選取 %1$d 張"</item>
diff --git a/WallpaperPicker/res/values-zh-rTW/strings.xml b/WallpaperPicker/res/values-zh-rTW/strings.xml
index c12350a..fda123c 100644
--- a/WallpaperPicker/res/values-zh-rTW/strings.xml
+++ b/WallpaperPicker/res/values-zh-rTW/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"設定桌布"</string>
<string name="image_load_fail" msgid="7538534580694411837">"無法載入圖片"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"無法載入您要設為桌布的圖片"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"無法將圖片設為桌布"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"已選取 %1$d 個"</item>
<item quantity="one" msgid="8409622005831789373">"已選取 %1$d 個"</item>
diff --git a/WallpaperPicker/res/values-zu/strings.xml b/WallpaperPicker/res/values-zu/strings.xml
index 537b2f3..1a5b95e 100644
--- a/WallpaperPicker/res/values-zu/strings.xml
+++ b/WallpaperPicker/res/values-zu/strings.xml
@@ -22,6 +22,7 @@
<string name="wallpaper_instructions" msgid="3524143401182707094">"Setha isithombe sangemuva"</string>
<string name="image_load_fail" msgid="7538534580694411837">"Ayikwazanga ukulayisha isithombe"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"Ayikwazanga ukulayisha isithombe njengesithombe sangemuva"</string>
+ <string name="wallpaper_set_fail" msgid="7023180794008631780">"Ayikwazanga ukusetha isithombe njengesithombe sangemuva"</string>
<plurals name="number_of_items_selected">
<item quantity="zero" msgid="9015111147509924344">"%1$d khethiwe"</item>
<item quantity="one" msgid="8409622005831789373">"%1$d khethiwe"</item>
diff --git a/WallpaperPicker/res/values/arrays.xml b/WallpaperPicker/res/values/arrays.xml
new file mode 100644
index 0000000..5c10b98
--- /dev/null
+++ b/WallpaperPicker/res/values/arrays.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+ <string-array name="which_wallpaper_options">
+ <item>@string/which_wallpaper_option_home_screen</item>
+ <item>@string/which_wallpaper_option_lock_screen</item>
+ <item>@string/which_wallpaper_option_home_screen_and_lock_screen</item>
+ </string-array>
+</resources>
\ No newline at end of file
diff --git a/WallpaperPicker/res/values/strings.xml b/WallpaperPicker/res/values/strings.xml
index 72b1e15..aef6a1a 100644
--- a/WallpaperPicker/res/values/strings.xml
+++ b/WallpaperPicker/res/values/strings.xml
@@ -28,6 +28,9 @@
usually see this when using another app and trying to set
an image as the wallpaper -->
<string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string>
+ <!-- Error message when an image is selected as a wallpaper,
+ but something goes wrong when the user clicks "Set wallpaper" -->
+ <string name="wallpaper_set_fail">Couldn\'t set image as wallpaper</string>
<!-- Shown when wallpapers are selected in Wallpaper picker -->
<!-- String indicating how many media item(s) is(are) selected
eg. 1 selected [CHAR LIMIT=30] -->
@@ -50,4 +53,11 @@
<string name="pick_wallpaper">Wallpapers</string>
<!-- Title of activity for cropping wallpapers -->
<string name="crop_wallpaper">Crop wallpaper</string>
+
+ <!-- Option for setting the wallpaper only on the home screen. -->
+ <string name="which_wallpaper_option_home_screen">Home screen</string>
+ <!-- Option for setting the wallpaper only on the lock screen. -->
+ <string name="which_wallpaper_option_lock_screen">Lock screen</string>
+ <!-- Option for setting the wallpaper on both the home screen and lock screen. -->
+ <string name="which_wallpaper_option_home_screen_and_lock_screen">Home screen and lock screen</string>
</resources>
diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
index 45118bf..153fa90 100644
--- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
+++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapCropTask.java
@@ -31,6 +31,11 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.util.Log;
+import android.widget.Toast;
+
+import com.android.launcher3.NycWallpaperUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@@ -39,10 +44,14 @@
import java.io.IOException;
import java.io.InputStream;
-public class BitmapCropTask extends AsyncTask<Void, Void, Boolean> {
+public class BitmapCropTask extends AsyncTask<Integer, Void, Boolean> {
public interface OnBitmapCroppedHandler {
- public void onBitmapCropped(byte[] imageBytes);
+ public void onBitmapCropped(byte[] imageBytes, Rect cropHint);
+ }
+
+ public interface OnEndCropHandler {
+ public void run(boolean cropSucceeded);
}
private static final int DEFAULT_COMPRESS_QUALITY = 90;
@@ -59,56 +68,47 @@
boolean mSetWallpaper;
boolean mSaveCroppedBitmap;
Bitmap mCroppedBitmap;
- Runnable mOnEndRunnable;
+ BitmapCropTask.OnEndCropHandler mOnEndCropHandler;
Resources mResources;
BitmapCropTask.OnBitmapCroppedHandler mOnBitmapCroppedHandler;
boolean mNoCrop;
- public BitmapCropTask(Context c, String filePath,
- RectF cropBounds, int rotation, int outWidth, int outHeight,
- boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
- mContext = c;
- mInFilePath = filePath;
- init(cropBounds, rotation,
- outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
- }
-
public BitmapCropTask(byte[] imageBytes,
RectF cropBounds, int rotation, int outWidth, int outHeight,
- boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+ boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
mInImageBytes = imageBytes;
init(cropBounds, rotation,
- outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+ outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
}
public BitmapCropTask(Context c, Uri inUri,
RectF cropBounds, int rotation, int outWidth, int outHeight,
- boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+ boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
mContext = c;
mInUri = inUri;
init(cropBounds, rotation,
- outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+ outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
}
public BitmapCropTask(Context c, Resources res, int inResId,
RectF cropBounds, int rotation, int outWidth, int outHeight,
- boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+ boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
mContext = c;
mInResId = inResId;
mResources = res;
init(cropBounds, rotation,
- outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndRunnable);
+ outWidth, outHeight, setWallpaper, saveCroppedBitmap, onEndCropHandler);
}
private void init(RectF cropBounds, int rotation, int outWidth, int outHeight,
- boolean setWallpaper, boolean saveCroppedBitmap, Runnable onEndRunnable) {
+ boolean setWallpaper, boolean saveCroppedBitmap, OnEndCropHandler onEndCropHandler) {
mCropBounds = cropBounds;
mRotation = rotation;
mOutWidth = outWidth;
mOutHeight = outHeight;
mSetWallpaper = setWallpaper;
mSaveCroppedBitmap = saveCroppedBitmap;
- mOnEndRunnable = onEndRunnable;
+ mOnEndCropHandler = onEndCropHandler;
}
public void setOnBitmapCropped(BitmapCropTask.OnBitmapCroppedHandler handler) {
@@ -119,8 +119,8 @@
mNoCrop = value;
}
- public void setOnEndRunnable(Runnable onEndRunnable) {
- mOnEndRunnable = onEndRunnable;
+ public void setOnEndRunnable(OnEndCropHandler onEndCropHandler) {
+ mOnEndCropHandler = onEndCropHandler;
}
// Helper to setup input stream
@@ -168,28 +168,45 @@
public Bitmap getCroppedBitmap() {
return mCroppedBitmap;
}
- public boolean cropBitmap() {
+ public boolean cropBitmap(int whichWallpaper) {
boolean failure = false;
-
- WallpaperManager wallpaperManager = null;
- if (mSetWallpaper) {
- wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
- }
-
-
if (mSetWallpaper && mNoCrop) {
try {
InputStream is = regenerateInputStream();
- if (is != null) {
- wallpaperManager.setStream(is);
- Utils.closeSilently(is);
- }
+ setWallpaper(is, null, whichWallpaper);
+ Utils.closeSilently(is);
} catch (IOException e) {
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
failure = true;
}
return !failure;
+ } else if (mSetWallpaper && Utilities.isNycOrAbove()
+ && mRotation == 0 && mOutWidth > 0 && mOutHeight > 0) {
+ Rect hint = new Rect();
+ mCropBounds.roundOut(hint);
+
+ InputStream is = null;
+ try {
+ is = regenerateInputStream();
+ if (is == null) {
+ Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
+ failure = true;
+ return false;
+ }
+ WallpaperManager.getInstance(mContext).suggestDesiredDimensions(mOutWidth, mOutHeight);
+ setWallpaper(is, hint, whichWallpaper);
+
+ if (mOnBitmapCroppedHandler != null) {
+ mOnBitmapCroppedHandler.onBitmapCropped(null, hint);
+ }
+
+ failure = false;
+ } catch (IOException e) {
+ Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+ } finally {
+ Utils.closeSilently(is);
+ }
} else {
// Find crop bounds (scaled to original image size)
Rect roundedTrueCrop = new Rect();
@@ -218,7 +235,6 @@
mCropBounds.offset(-rotatedBounds[0]/2, -rotatedBounds[1]/2);
inverseRotateMatrix.mapRect(mCropBounds);
mCropBounds.offset(bounds.x/2, bounds.y/2);
-
}
mCropBounds.roundOut(roundedTrueCrop);
@@ -289,22 +305,16 @@
roundedTrueCrop.right = roundedTrueCrop.left + fullSize.getWidth();
}
if (roundedTrueCrop.right > fullSize.getWidth()) {
- // Adjust the left value
- int adjustment = roundedTrueCrop.left -
- Math.max(0, roundedTrueCrop.right - roundedTrueCrop.width());
- roundedTrueCrop.left -= adjustment;
- roundedTrueCrop.right -= adjustment;
+ // Adjust the left and right values.
+ roundedTrueCrop.offset(-(roundedTrueCrop.right - fullSize.getWidth()), 0);
}
if (roundedTrueCrop.height() > fullSize.getHeight()) {
// Adjust the height
roundedTrueCrop.bottom = roundedTrueCrop.top + fullSize.getHeight();
}
if (roundedTrueCrop.bottom > fullSize.getHeight()) {
- // Adjust the top value
- int adjustment = roundedTrueCrop.top -
- Math.max(0, roundedTrueCrop.bottom - roundedTrueCrop.height());
- roundedTrueCrop.top -= adjustment;
- roundedTrueCrop.bottom -= adjustment;
+ // Adjust the top and bottom values.
+ roundedTrueCrop.offset(0, -(roundedTrueCrop.bottom - fullSize.getHeight()));
}
crop = Bitmap.createBitmap(fullSize, roundedTrueCrop.left,
@@ -371,12 +381,13 @@
ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
if (crop.compress(CompressFormat.JPEG, DEFAULT_COMPRESS_QUALITY, tmpOut)) {
// If we need to set to the wallpaper, set it
- if (mSetWallpaper && wallpaperManager != null) {
+ if (mSetWallpaper) {
try {
byte[] outByteArray = tmpOut.toByteArray();
- wallpaperManager.setStream(new ByteArrayInputStream(outByteArray));
+ setWallpaper(new ByteArrayInputStream(outByteArray), null, whichWallpaper);
if (mOnBitmapCroppedHandler != null) {
- mOnBitmapCroppedHandler.onBitmapCropped(outByteArray);
+ mOnBitmapCroppedHandler.onBitmapCropped(outByteArray,
+ new Rect(0, 0, crop.getWidth(), crop.getHeight()));
}
} catch (IOException e) {
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
@@ -392,14 +403,25 @@
}
@Override
- protected Boolean doInBackground(Void... params) {
- return cropBitmap();
+ protected Boolean doInBackground(Integer... params) {
+ return cropBitmap(params.length == 0 ? NycWallpaperUtils.FLAG_SET_SYSTEM : params[0]);
}
@Override
- protected void onPostExecute(Boolean result) {
- if (mOnEndRunnable != null) {
- mOnEndRunnable.run();
+ protected void onPostExecute(Boolean cropSucceeded) {
+ if (!cropSucceeded) {
+ Toast.makeText(mContext, R.string.wallpaper_set_fail, Toast.LENGTH_SHORT).show();
+ }
+ if (mOnEndCropHandler != null) {
+ mOnEndCropHandler.run(cropSucceeded);
+ }
+ }
+
+ private void setWallpaper(InputStream in, Rect crop, int whichWallpaper) throws IOException {
+ if (!Utilities.isNycOrAbove()) {
+ WallpaperManager.getInstance(mContext.getApplicationContext()).setStream(in);
+ } else {
+ NycWallpaperUtils.setStream(mContext, in, crop, true, whichWallpaper);
}
}
}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java
index 50f779a..c1578c1 100644
--- a/WallpaperPicker/src/com/android/launcher3/CropView.java
+++ b/WallpaperPicker/src/com/android/launcher3/CropView.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
@@ -31,7 +32,7 @@
import com.android.photos.views.TiledImageRenderer.TileSource;
import com.android.photos.views.TiledImageView;
-public class CropView extends TiledImageView implements OnScaleGestureListener {
+public class CropView extends TiledImageView implements OnScaleGestureListener {
private ScaleGestureDetector mScaleGestureDetector;
private long mTouchDownTime;
@@ -148,12 +149,19 @@
updateMinScale(w, h, mRenderer.source, false);
}
- public void setScale(float scale) {
+ public void setScaleAndCenter(float scale, float x, float y) {
synchronized (mLock) {
mRenderer.scale = scale;
+ mCenterX = x;
+ mCenterY = y;
+ updateCenter();
}
}
+ public float getScale() {
+ return mRenderer.scale;
+ }
+
private void updateMinScale(int w, int h, TileSource source, boolean resetScale) {
synchronized (mLock) {
if (resetScale) {
@@ -211,6 +219,10 @@
mRenderer.centerY = Math.round(mCenterY);
}
+ public PointF getCenter() {
+ return new PointF(mCenterX, mCenterY);
+ }
+
public void setTouchEnabled(boolean enabled) {
mTouchEnabled = enabled;
}
diff --git a/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java b/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java
new file mode 100644
index 0000000..abac830
--- /dev/null
+++ b/WallpaperPicker/src/com/android/launcher3/NycWallpaperUtils.java
@@ -0,0 +1,79 @@
+package com.android.launcher3;
+
+import android.app.AlertDialog;
+import android.app.WallpaperManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Rect;
+import android.os.AsyncTask;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Utility class used to help set lockscreen wallpapers on N+.
+ */
+public class NycWallpaperUtils {
+ public static final int FLAG_SET_SYSTEM = 1 << 0; // TODO: use WallpaperManager.FLAG_SET_SYSTEM
+ public static final int FLAG_SET_LOCK = 1 << 1; // TODO: use WallpaperManager.FLAG_SET_LOCK
+
+ /**
+ * Calls cropTask.execute(), once the user has selected which wallpaper to set. On pre-N
+ * devices, the prompt is not displayed since there is no API to set the lockscreen wallpaper.
+ */
+ public static void executeCropTaskAfterPrompt(
+ Context context, final AsyncTask<Integer, ?, ?> cropTask,
+ DialogInterface.OnCancelListener onCancelListener) {
+ if (Utilities.isNycOrAbove()) {
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.wallpaper_instructions)
+ .setItems(R.array.which_wallpaper_options, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int selectedItemIndex) {
+ int whichWallpaper;
+ if (selectedItemIndex == 0) {
+ whichWallpaper = FLAG_SET_SYSTEM;
+ } else if (selectedItemIndex == 1) {
+ whichWallpaper = FLAG_SET_LOCK;
+ } else {
+ whichWallpaper = FLAG_SET_SYSTEM | FLAG_SET_LOCK;
+ }
+ cropTask.execute(whichWallpaper);
+ }
+ })
+ .setOnCancelListener(onCancelListener)
+ .show();
+ } else {
+ cropTask.execute(FLAG_SET_SYSTEM);
+ }
+ }
+
+ public static void setStream(Context context, final InputStream data, Rect visibleCropHint,
+ boolean allowBackup, int whichWallpaper) throws IOException {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
+ try {
+ // TODO: use mWallpaperManager.setStream(data, visibleCropHint, allowBackup, which)
+ // without needing reflection.
+ Method setStream = WallpaperManager.class.getMethod("setStream", InputStream.class,
+ Rect.class, boolean.class, int.class);
+ setStream.invoke(wallpaperManager, data, visibleCropHint, allowBackup, whichWallpaper);
+ } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
+ // Fall back to previous implementation (set system)
+ wallpaperManager.setStream(data);
+ }
+ }
+
+ public static void clear(Context context, int whichWallpaper) throws IOException {
+ WallpaperManager wallpaperManager = WallpaperManager.getInstance(context);
+ try {
+ // TODO: use mWallpaperManager.clear(whichWallpaper) without needing reflection.
+ Method clear = WallpaperManager.class.getMethod("clear", int.class);
+ clear.invoke(wallpaperManager, whichWallpaper);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ // Fall back to previous implementation (clear system)
+ wallpaperManager.clear();
+ }
+ }
+}
\ No newline at end of file
diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
index 64b0ac4..34226f7 100644
--- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
@@ -25,6 +25,8 @@
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.LayoutInflater;
@@ -33,9 +35,14 @@
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
+import com.android.launcher3.WallpaperCropActivity.CropViewScaleAndOffsetProvider;
+import com.android.photos.views.TiledImageRenderer.TileSource;
+
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
@@ -48,15 +55,42 @@
public static class SavedWallpaperTile extends WallpaperPickerActivity.FileWallpaperInfo {
private int mDbId;
- public SavedWallpaperTile(int dbId, File target, Drawable thumb) {
+
+ // three floats representing scale, centerX and centerY of the crop view in order.
+ private Float[] mExtras;
+ public SavedWallpaperTile(int dbId, File target, Drawable thumb, Float[] extras) {
super(target, thumb);
mDbId = dbId;
+ mExtras = extras != null && extras.length == 3 ? extras : null;
}
@Override
public void onDelete(WallpaperPickerActivity a) {
a.getSavedImages().deleteImage(mDbId);
}
+
+ @Override
+ protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
+ if (mExtras != null) {
+ return new CropViewScaleAndOffsetProvider() {
+ @Override
+ public void updateCropView(WallpaperCropActivity a, TileSource src) {
+ a.mCropView.setScaleAndCenter(mExtras[0], mExtras[1], mExtras[2]);
+ }
+ };
+ }
+ return null;
+ }
+
+ @Override
+ public void onSave(final WallpaperPickerActivity a) {
+ if (mExtras == null) {
+ super.onSave(a);
+ } else {
+ boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
+ a.cropImageAndSetWallpaper(Uri.fromFile(mFile), null, true, shouldFadeOutOnFinish);
+ }
+ }
}
public SavedWallpaperImages(Context context) {
@@ -74,7 +108,8 @@
Cursor result = db.query(ImageDb.TABLE_NAME,
new String[] { ImageDb.COLUMN_ID,
ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
- ImageDb.COLUMN_IMAGE_FILENAME}, // cols to return
+ ImageDb.COLUMN_IMAGE_FILENAME,
+ ImageDb.COLUMN_EXTRAS}, // cols to return
null, // select query
null, // args to select query
null,
@@ -88,9 +123,25 @@
Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
if (thumb != null) {
+
+ Float[] extras = null;
+ String extraStr = result.getString(3);
+ if (extraStr != null) {
+ String[] parts = extraStr.split(",");
+ extras = new Float[parts.length];
+ for (int i = 0; i < parts.length; i++) {
+ try {
+ extras[i] = Float.parseFloat(parts[i]);
+ } catch (Exception e) {
+ extras = null;
+ break;
+ }
+ }
+ }
+
mImages.add(new SavedWallpaperTile(result.getInt(0),
new File(mContext.getFilesDir(), result.getString(2)),
- new BitmapDrawable(thumb)));
+ new BitmapDrawable(thumb), extras));
}
}
result.close();
@@ -155,34 +206,55 @@
public void writeImage(Bitmap thumbnail, byte[] imageBytes) {
try {
- File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
- FileOutputStream imageFileStream =
- mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
- imageFileStream.write(imageBytes);
- imageFileStream.close();
-
- File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
- FileOutputStream thumbFileStream =
- mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
- thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
- thumbFileStream.close();
-
- SQLiteDatabase db = mDb.getWritableDatabase();
- ContentValues values = new ContentValues();
- values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
- values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
- db.insert(ImageDb.TABLE_NAME, null, values);
+ writeImage(thumbnail, new ByteArrayInputStream(imageBytes), null);
} catch (IOException e) {
Log.e(TAG, "Failed writing images to storage " + e);
}
}
+ public void writeImage(Bitmap thumbnail, Uri uri, Float[] extras) {
+ try {
+ writeImage(thumbnail, mContext.getContentResolver().openInputStream(uri), extras);
+ } catch (IOException e) {
+ Log.e(TAG, "Failed writing images to storage " + e);
+ }
+ }
+
+ private void writeImage(Bitmap thumbnail, InputStream in, Float[] extras) throws IOException {
+ File imageFile = File.createTempFile("wallpaper", "", mContext.getFilesDir());
+ FileOutputStream imageFileStream =
+ mContext.openFileOutput(imageFile.getName(), Context.MODE_PRIVATE);
+ byte[] buf = new byte[4096]; // 4k
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ imageFileStream.write(buf, 0, len);
+ }
+ imageFileStream.close();
+ in.close();
+
+ File thumbFile = File.createTempFile("wallpaperthumb", "", mContext.getFilesDir());
+ FileOutputStream thumbFileStream =
+ mContext.openFileOutput(thumbFile.getName(), Context.MODE_PRIVATE);
+ thumbnail.compress(Bitmap.CompressFormat.JPEG, 95, thumbFileStream);
+ thumbFileStream.close();
+
+ SQLiteDatabase db = mDb.getWritableDatabase();
+ ContentValues values = new ContentValues();
+ values.put(ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME, thumbFile.getName());
+ values.put(ImageDb.COLUMN_IMAGE_FILENAME, imageFile.getName());
+ if (extras != null) {
+ values.put(ImageDb.COLUMN_EXTRAS, TextUtils.join(",", extras));
+ }
+ db.insert(ImageDb.TABLE_NAME, null, values);
+ }
+
static class ImageDb extends SQLiteOpenHelper {
- final static int DB_VERSION = 1;
+ final static int DB_VERSION = 2;
final static String TABLE_NAME = "saved_wallpaper_images";
final static String COLUMN_ID = "id";
final static String COLUMN_IMAGE_THUMBNAIL_FILENAME = "image_thumbnail";
final static String COLUMN_IMAGE_FILENAME = "image";
+ final static String COLUMN_EXTRAS = "extras";
Context mContext;
@@ -209,15 +281,20 @@
COLUMN_ID + " INTEGER NOT NULL, " +
COLUMN_IMAGE_THUMBNAIL_FILENAME + " TEXT NOT NULL, " +
COLUMN_IMAGE_FILENAME + " TEXT NOT NULL, " +
+ COLUMN_EXTRAS + " TEXT, " +
"PRIMARY KEY (" + COLUMN_ID + " ASC) " +
");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion != newVersion) {
+ if (oldVersion == 1) {
+ // Add extras column
+ db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD COLUMN " + COLUMN_EXTRAS + " TEXT;");
+ } else if (oldVersion != newVersion) {
// Delete all the records; they'll be repopulated as this is a cache
db.execSQL("DELETE FROM " + TABLE_NAME);
+ onCreate(db);
}
}
}
diff --git a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
index f46da53..099bbda 100644
--- a/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
+++ b/WallpaperPicker/src/com/android/launcher3/ThirdPartyWallpaperPickerListAdapter.java
@@ -54,7 +54,9 @@
final ComponentName itemComponentName = new ComponentName(
mResolveInfo.activityInfo.packageName, mResolveInfo.activityInfo.name);
Intent launchIntent = new Intent(Intent.ACTION_SET_WALLPAPER);
- launchIntent.setComponent(itemComponentName);
+ launchIntent.setComponent(itemComponentName)
+ .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET,
+ a.getWallpaperParallaxOffset());
a.startActivityForResultSafely(
launchIntent, WallpaperPickerActivity.PICK_WALLPAPER_THIRD_PARTY_ACTIVITY);
}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index f2459dd..f6c53ec 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -21,6 +21,7 @@
import android.app.Activity;
import android.app.WallpaperManager;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -28,6 +29,7 @@
import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.RectF;
import android.net.Uri;
import android.os.Build;
@@ -86,6 +88,18 @@
@Thunk Set<Bitmap> mReusableBitmaps =
Collections.newSetFromMap(new WeakHashMap<Bitmap, Boolean>());
+ private final DialogInterface.OnCancelListener mOnDialogCancelListener =
+ new DialogInterface.OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActionBar().show();
+ View wallpaperStrip = findViewById(R.id.wallpaper_strip);
+ if (wallpaperStrip != null) {
+ wallpaperStrip.setVisibility(View.VISIBLE);
+ }
+ }
+ };
+
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -123,8 +137,13 @@
new View.OnClickListener() {
@Override
public void onClick(View v) {
+ actionBar.hide();
boolean finishActivityWhenDone = true;
- cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
+ // Never fade on finish because we return to the app that started us (e.g.
+ // Photos), not the home screen.
+ boolean shouldFadeOutOnFinish = false;
+ cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone,
+ shouldFadeOutOnFinish);
}
});
mSetWallpaperButton = findViewById(R.id.set_wallpaper_button);
@@ -237,6 +256,10 @@
}
}
+ public DialogInterface.OnCancelListener getOnDialogCancelListener() {
+ return mOnDialogCancelListener;
+ }
+
protected void onLoadRequestComplete(LoadRequest req, boolean success) {
mCurrentLoadRequest = null;
if (success) {
@@ -246,8 +269,8 @@
if (req.moveToLeft) {
mCropView.moveToLeft();
}
- if (req.scaleProvider != null) {
- mCropView.setScale(req.scaleProvider.getScale(req.result));
+ if (req.scaleAndOffsetProvider != null) {
+ req.scaleAndOffsetProvider.updateCropView(this, req.result);
}
// Free last image
@@ -265,13 +288,13 @@
}
public final void setCropViewTileSource(BitmapSource bitmapSource, boolean touchEnabled,
- boolean moveToLeft, CropViewScaleProvider scaleProvider, Runnable postExecute) {
+ boolean moveToLeft, CropViewScaleAndOffsetProvider scaleProvider, Runnable postExecute) {
final LoadRequest req = new LoadRequest();
req.moveToLeft = moveToLeft;
req.src = bitmapSource;
req.touchEnabled = touchEnabled;
req.postExecute = postExecute;
- req.scaleProvider = scaleProvider;
+ req.scaleAndOffsetProvider = scaleProvider;
mCurrentLoadRequest = req;
// Remove any pending requests
@@ -295,54 +318,64 @@
return getResources().getBoolean(R.bool.allow_rotation);
}
- protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) {
+ protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone,
+ final boolean shouldFadeOutOnFinish) {
int rotation = BitmapUtils.getRotationFromExif(getContext(), uri);
BitmapCropTask cropTask = new BitmapCropTask(
getContext(), uri, null, rotation, 0, 0, true, false, null);
final Point bounds = cropTask.getImageBounds();
- Runnable onEndCrop = new Runnable() {
- public void run() {
+ BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
+ public void run(boolean cropSucceeded) {
updateWallpaperDimensions(bounds.x, bounds.y);
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
+ if (cropSucceeded && shouldFadeOutOnFinish) {
+ overridePendingTransition(0, R.anim.fade_out);
+ }
}
}
};
cropTask.setOnEndRunnable(onEndCrop);
cropTask.setNoCrop(true);
- cropTask.execute();
+ NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
}
- protected void cropImageAndSetWallpaper(
- Resources res, int resId, final boolean finishActivityWhenDone) {
+ protected void cropImageAndSetWallpaper(Resources res, int resId,
+ final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) {
// crop this image and scale it down to the default wallpaper size for
// this device
int rotation = BitmapUtils.getRotationFromExif(res, resId);
Point inSize = mCropView.getSourceDimensions();
- Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(),
- getWindowManager());
+ Point outSize = WallpaperUtils.getDefaultWallpaperSize(getResources(), getWindowManager());
RectF crop = Utils.getMaxCropRect(
inSize.x, inSize.y, outSize.x, outSize.y, false);
- Runnable onEndCrop = new Runnable() {
- public void run() {
+ BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
+ public void run(boolean cropSucceeded) {
// Passing 0, 0 will cause launcher to revert to using the
// default wallpaper size
updateWallpaperDimensions(0, 0);
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
+ if (cropSucceeded && shouldFadeOutOnFinish) {
+ overridePendingTransition(0, R.anim.fade_out);
+ }
}
}
};
BitmapCropTask cropTask = new BitmapCropTask(getContext(), res, resId,
crop, rotation, outSize.x, outSize.y, true, false, onEndCrop);
- cropTask.execute();
+ NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
protected void cropImageAndSetWallpaper(Uri uri,
- BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler, final boolean finishActivityWhenDone) {
+ BitmapCropTask.OnBitmapCroppedHandler onBitmapCroppedHandler,
+ final boolean finishActivityWhenDone, final boolean shouldFadeOutOnFinish) {
+ // Give some feedback so user knows something is happening.
+ mProgressView.setVisibility(View.VISIBLE);
+
boolean centerCrop = getResources().getBoolean(R.bool.center_crop);
// Get the crop
boolean ltr = mCropView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
@@ -418,13 +451,16 @@
final int outWidth = (int) Math.round(cropRect.width() * cropScale);
final int outHeight = (int) Math.round(cropRect.height() * cropScale);
- Runnable onEndCrop = new Runnable() {
- public void run() {
+ BitmapCropTask.OnEndCropHandler onEndCrop = new BitmapCropTask.OnEndCropHandler() {
+ public void run(boolean cropSucceeded) {
updateWallpaperDimensions(outWidth, outHeight);
if (finishActivityWhenDone) {
setResult(Activity.RESULT_OK);
finish();
}
+ if (cropSucceeded && shouldFadeOutOnFinish) {
+ overridePendingTransition(0, R.anim.fade_out);
+ }
}
};
BitmapCropTask cropTask = new BitmapCropTask(getContext(), uri,
@@ -432,7 +468,7 @@
if (onBitmapCroppedHandler != null) {
cropTask.setOnBitmapCropped(onBitmapCroppedHandler);
}
- cropTask.execute();
+ NycWallpaperUtils.executeCropTaskAfterPrompt(this, cropTask, getOnDialogCancelListener());
}
protected void updateWallpaperDimensions(int width, int height) {
@@ -446,7 +482,7 @@
editor.remove(WALLPAPER_WIDTH_KEY);
editor.remove(WALLPAPER_HEIGHT_KEY);
}
- editor.commit();
+ editor.apply();
WallpaperUtils.suggestWallpaperDimension(getResources(),
sp, getWindowManager(), WallpaperManager.getInstance(getContext()), true);
}
@@ -456,12 +492,37 @@
boolean touchEnabled;
boolean moveToLeft;
Runnable postExecute;
- CropViewScaleProvider scaleProvider;
+ CropViewScaleAndOffsetProvider scaleAndOffsetProvider;
TileSource result;
}
- interface CropViewScaleProvider {
- float getScale(TileSource src);
+ public static class CropViewScaleAndOffsetProvider {
+ public float getScale(Point wallpaperSize, RectF crop) {
+ return 1f;
+ }
+
+ public float getParallaxOffset() {
+ return 0.5f;
+ }
+
+ public void updateCropView(WallpaperCropActivity a, TileSource src) {
+ Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
+ a.getResources(), a.getWindowManager());
+ RectF crop = Utils.getMaxCropRect(src.getImageWidth(), src.getImageHeight(),
+ wallpaperSize.x, wallpaperSize.y, false /* leftAligned */);
+
+ float scale = getScale(wallpaperSize, crop);
+ PointF center = a.mCropView.getCenter();
+
+ // Offsets wallpaper preview according to the state it will be displayed in upon
+ // returning home. Offset ranges from 0 to 1, where 0 is the leftmost parallax and
+ // 1 is the rightmost.
+ // Make sure the offset is in the correct range.
+ float offset = Math.max(0, Math.min(getParallaxOffset(), 1));
+ float screenWidth = a.mCropView.getWidth() / scale;
+ center.x = screenWidth / 2 + offset * (crop.width() - screenWidth) + crop.left;
+ a.mCropView.setScaleAndCenter(scale, center.x, center.y);
+ }
}
}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 5985850..40f0544 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -16,7 +16,7 @@
package com.android.launcher3;
-import android.Manifest.permission;
+import android.Manifest;
import android.animation.LayoutTransition;
import android.annotation.TargetApi;
import android.app.ActionBar;
@@ -31,20 +31,23 @@
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Point;
+import android.graphics.PointF;
import android.graphics.PorterDuff;
+import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.Manifest;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
import android.provider.MediaStore;
+import android.support.annotation.NonNull;
import android.util.Log;
import android.util.Pair;
import android.view.ActionMode;
@@ -75,21 +78,24 @@
import com.android.gallery3d.common.BitmapUtils;
import com.android.gallery3d.common.Utils;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.util.WallpaperUtils;
import com.android.photos.BitmapRegionTileSource;
import com.android.photos.BitmapRegionTileSource.BitmapSource;
-import com.android.photos.views.TiledImageRenderer.TileSource;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.util.ArrayList;
public class WallpaperPickerActivity extends WallpaperCropActivity {
- static final String TAG = "Launcher.WallpaperPickerActivity";
+ static final String TAG = "WallpaperPickerActivity";
public static final int IMAGE_PICK = 5;
public static final int PICK_WALLPAPER_THIRD_PARTY_ACTIVITY = 6;
+ /** An Intent extra used when opening the wallpaper picker from the workspace overlay. */
+ public static final String EXTRA_WALLPAPER_OFFSET = "com.android.launcher3.WALLPAPER_OFFSET";
private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
private static final String SELECTED_INDEX = "SELECTED_INDEX";
private static final int FLAG_POST_DELAY_MILLIS = 200;
@@ -110,6 +116,7 @@
ArrayList<Uri> mTempWallpaperTiles = new ArrayList<Uri>();
private SavedWallpaperImages mSavedImages;
@Thunk int mSelectedIndex = -1;
+ private float mWallpaperParallaxOffset;
public static abstract class WallpaperTileInfo {
protected View mView;
@@ -171,15 +178,49 @@
public void onSave(final WallpaperPickerActivity a) {
boolean finishActivityWhenDone = true;
BitmapCropTask.OnBitmapCroppedHandler h = new BitmapCropTask.OnBitmapCroppedHandler() {
- public void onBitmapCropped(byte[] imageBytes) {
+ @Override
+ public void onBitmapCropped(byte[] imageBytes, Rect hint) {
+ Bitmap thumb = null;
Point thumbSize = getDefaultThumbnailSize(a.getResources());
- // rotation is set to 0 since imageBytes has already been correctly rotated
- Bitmap thumb = createThumbnail(
- thumbSize, null, null, imageBytes, null, 0, 0, true);
- a.getSavedImages().writeImage(thumb, imageBytes);
+ if (imageBytes != null) {
+ // rotation is set to 0 since imageBytes has already been correctly rotated
+ thumb = createThumbnail(
+ thumbSize, null, null, imageBytes, null, 0, 0, true);
+ a.getSavedImages().writeImage(thumb, imageBytes);
+ } else {
+ try {
+ // Generate thumb
+ Point size = getDefaultThumbnailSize(a.getResources());
+ Rect finalCropped = new Rect();
+ Utils.getMaxCropRect(hint.width(), hint.height(), size.x, size.y, false)
+ .roundOut(finalCropped);
+ finalCropped.offset(hint.left, hint.top);
+
+ InputStream in = a.getContentResolver().openInputStream(mUri);
+ BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(in, true);
+
+ BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inSampleSize = finalCropped.width() / size.x;
+ thumb = decoder.decodeRegion(finalCropped, options);
+ decoder.recycle();
+ Utils.closeSilently(in);
+ if (thumb != null) {
+ thumb = Bitmap.createScaledBitmap(thumb, size.x, size.y, true);
+ }
+ } catch (IOException e) { }
+ PointF center = a.mCropView.getCenter();
+
+ Float[] extras = new Float[] {
+ a.mCropView.getScale(),
+ center.x,
+ center.y
+ };
+ a.getSavedImages().writeImage(thumb, mUri, extras);
+ }
}
};
- a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone);
+ boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
+ a.cropImageAndSetWallpaper(mUri, h, finishActivityWhenDone, shouldFadeOutOnFinish);
}
@Override
public boolean isSelectable() {
@@ -192,7 +233,7 @@
}
public static class FileWallpaperInfo extends WallpaperTileInfo {
- private File mFile;
+ protected File mFile;
public FileWallpaperInfo(File target, Drawable thumb) {
mFile = target;
@@ -203,19 +244,26 @@
a.setWallpaperButtonEnabled(false);
final BitmapRegionTileSource.UriBitmapSource bitmapSource =
new BitmapRegionTileSource.UriBitmapSource(a.getContext(), Uri.fromFile(mFile));
- a.setCropViewTileSource(bitmapSource, false, true, null, new Runnable() {
+ a.setCropViewTileSource(bitmapSource, false, true, getCropViewScaleAndOffsetProvider(),
+ new Runnable() {
@Override
public void run() {
- if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
a.setWallpaperButtonEnabled(true);
}
}
});
}
+
+ protected CropViewScaleAndOffsetProvider getCropViewScaleAndOffsetProvider() {
+ return null;
+ }
+
@Override
public void onSave(WallpaperPickerActivity a) {
- a.setWallpaper(Uri.fromFile(mFile), true);
+ boolean shouldFadeOutOnFinish = a.getWallpaperParallaxOffset() == 0f;
+ a.setWallpaper(Uri.fromFile(mFile), true, shouldFadeOutOnFinish);
}
@Override
public boolean isSelectable() {
@@ -241,16 +289,16 @@
a.setWallpaperButtonEnabled(false);
final BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
new BitmapRegionTileSource.ResourceBitmapSource(mResources, mResId);
- a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleProvider() {
+ a.setCropViewTileSource(bitmapSource, false, false, new CropViewScaleAndOffsetProvider() {
@Override
- public float getScale(TileSource src) {
- Point wallpaperSize = WallpaperUtils.getDefaultWallpaperSize(
- a.getResources(), a.getWindowManager());
- RectF crop = Utils.getMaxCropRect(
- src.getImageWidth(), src.getImageHeight(),
- wallpaperSize.x, wallpaperSize.y, false);
- return wallpaperSize.x / crop.width();
+ public float getScale(Point wallpaperSize, RectF crop) {
+ return wallpaperSize.x /crop.width();
+ }
+
+ @Override
+ public float getParallaxOffset() {
+ return a.getWallpaperParallaxOffset();
}
}, new Runnable() {
@@ -265,7 +313,9 @@
@Override
public void onSave(WallpaperPickerActivity a) {
boolean finishActivityWhenDone = true;
- a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone);
+ boolean shouldFadeOutOnFinish = true;
+ a.cropImageAndSetWallpaper(mResources, mResId, finishActivityWhenDone,
+ shouldFadeOutOnFinish);
}
@Override
public boolean isSelectable() {
@@ -296,27 +346,81 @@
LoadRequest req = new LoadRequest();
req.moveToLeft = false;
req.touchEnabled = false;
- req.scaleProvider = new CropViewScaleProvider() {
-
- @Override
- public float getScale(TileSource src) {
- return 1f;
- }
- };
+ req.scaleAndOffsetProvider = new CropViewScaleAndOffsetProvider();
req.result = new DrawableTileSource(a.getContext(),
defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE);
a.onLoadRequestComplete(req, true);
}
@Override
- public void onSave(WallpaperPickerActivity a) {
- try {
- WallpaperManager.getInstance(a.getContext()).clear();
- a.setResult(Activity.RESULT_OK);
- } catch (IOException e) {
- Log.w("Setting wallpaper to default threw exception", e);
+ public void onSave(final WallpaperPickerActivity a) {
+ if (!Utilities.isNycOrAbove()) {
+ try {
+ WallpaperManager.getInstance(a.getContext()).clear();
+ a.setResult(Activity.RESULT_OK);
+ } catch (IOException e) {
+ Log.e(TAG, "Setting wallpaper to default threw exception", e);
+ } catch (SecurityException e) {
+ Log.w(TAG, "Setting wallpaper to default threw exception", e);
+ // In this case, clearing worked; the exception was thrown afterwards.
+ a.setResult(Activity.RESULT_OK);
+ }
+ a.finish();
+ } else {
+ BitmapCropTask.OnEndCropHandler onEndCropHandler
+ = new BitmapCropTask.OnEndCropHandler() {
+ @Override
+ public void run(boolean cropSucceeded) {
+ if (cropSucceeded) {
+ a.setResult(Activity.RESULT_OK);
+ }
+ a.finish();
+ }
+ };
+ BitmapCropTask setWallpaperTask = getDefaultWallpaperCropTask(a, onEndCropHandler);
+
+ NycWallpaperUtils.executeCropTaskAfterPrompt(a, setWallpaperTask,
+ a.getOnDialogCancelListener());
}
- a.finish();
}
+
+ @NonNull
+ private BitmapCropTask getDefaultWallpaperCropTask(final WallpaperPickerActivity a,
+ final BitmapCropTask.OnEndCropHandler onEndCropHandler) {
+ return new BitmapCropTask(a, null, null, -1, -1, -1,
+ true, false, onEndCropHandler) {
+ @Override
+ protected Boolean doInBackground(Integer... params) {
+ int whichWallpaper = params[0];
+ boolean succeeded = true;
+ try {
+ if (whichWallpaper == NycWallpaperUtils.FLAG_SET_LOCK) {
+ Bitmap defaultWallpaper = ((BitmapDrawable) WallpaperManager
+ .getInstance(a.getApplicationContext()).getBuiltInDrawable())
+ .getBitmap();
+ ByteArrayOutputStream tmpOut = new ByteArrayOutputStream(2048);
+ if (defaultWallpaper.compress(Bitmap.CompressFormat.PNG, 100,
+ tmpOut)) {
+ byte[] outByteArray = tmpOut.toByteArray();
+ NycWallpaperUtils.setStream(a.getApplicationContext(),
+ new ByteArrayInputStream(outByteArray), null, true,
+ NycWallpaperUtils.FLAG_SET_LOCK);
+ }
+ } else {
+ NycWallpaperUtils.clear(a, whichWallpaper);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Setting wallpaper to default threw exception", e);
+ succeeded = false;
+ } catch (SecurityException e) {
+ Log.w(TAG, "Setting wallpaper to default threw exception", e);
+ // In this case, clearing worked; the exception was thrown afterwards.
+ succeeded = true;
+ }
+ return succeeded;
+ }
+ };
+ }
+
@Override
public boolean isSelectable() {
return true;
@@ -433,10 +537,10 @@
}
return;
}
- setWallpaperButtonEnabled(true);
WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
selectTile(v);
+ setWallpaperButtonEnabled(true);
}
info.onClick(WallpaperPickerActivity.this);
}
@@ -461,6 +565,8 @@
}
};
+ mWallpaperParallaxOffset = getIntent().getFloatExtra(EXTRA_WALLPAPER_OFFSET, 0);
+
// Populate the built-in wallpapers
ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
@@ -550,7 +656,7 @@
new View.OnClickListener() {
@Override
public void onClick(View v) {
- // Ensure that a tile is slelected and loaded.
+ // Ensure that a tile is selected and loaded.
if (mSelectedTile != null && mCropView.getTileSource() != null) {
// Prevent user from selecting any new tile.
mWallpaperStrip.setVisibility(View.GONE);
@@ -559,9 +665,9 @@
WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
info.onSave(WallpaperPickerActivity.this);
} else {
- // no tile was selected, so we just finish the activity and go back
- setResult(Activity.RESULT_OK);
- finish();
+ // This case shouldn't be possible, since "Set wallpaper" is disabled
+ // until user clicks on a title.
+ Log.w(TAG, "\"Set wallpaper\" was clicked when no tile was selected");
}
}
});
@@ -661,6 +767,10 @@
mSetWallpaperButton.setEnabled(enabled);
}
+ public float getWallpaperParallaxOffset() {
+ return mWallpaperParallaxOffset;
+ }
+
@Thunk void selectTile(View v) {
if (mSelectedTile != null) {
mSelectedTile.setSelected(false);
@@ -834,7 +944,7 @@
(int) rotatedBounds[0], (int) rotatedBounds[1], width, height, leftAligned);
cropTask.setCropBounds(cropRect);
- if (cropTask.cropBitmap()) {
+ if (cropTask.cropBitmap(NycWallpaperUtils.FLAG_SET_SYSTEM)) {
return cropTask.getCroppedBitmap();
} else {
return null;
@@ -842,12 +952,31 @@
}
private void addTemporaryWallpaperTile(final Uri uri, boolean fromRestore) {
- mTempWallpaperTiles.add(uri);
- // Add a tile for the image picked from Gallery
- final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
- inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
- pickedImageThumbnail.setVisibility(View.GONE);
- mWallpapersView.addView(pickedImageThumbnail, 0);
+ // Add a tile for the image picked from Gallery, reusing the existing tile if there is one.
+ FrameLayout existingImageThumbnail = null;
+ int indexOfExistingTile = 0;
+ for (; indexOfExistingTile < mWallpapersView.getChildCount(); indexOfExistingTile++) {
+ FrameLayout thumbnail = (FrameLayout) mWallpapersView.getChildAt(indexOfExistingTile);
+ Object tag = thumbnail.getTag();
+ if (tag instanceof UriWallpaperInfo && ((UriWallpaperInfo) tag).mUri.equals(uri)) {
+ existingImageThumbnail = thumbnail;
+ break;
+ }
+ }
+ final FrameLayout pickedImageThumbnail;
+ if (existingImageThumbnail != null) {
+ pickedImageThumbnail = existingImageThumbnail;
+ // Always move the existing wallpaper to the front so user can see it without scrolling.
+ mWallpapersView.removeViewAt(indexOfExistingTile);
+ mWallpapersView.addView(existingImageThumbnail, 0);
+ } else {
+ // This is the first time this temporary wallpaper has been added
+ pickedImageThumbnail = (FrameLayout) getLayoutInflater()
+ .inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
+ pickedImageThumbnail.setVisibility(View.GONE);
+ mWallpapersView.addView(pickedImageThumbnail, 0);
+ mTempWallpaperTiles.add(uri);
+ }
// Load the thumbnail
final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
@@ -857,7 +986,8 @@
protected Bitmap doInBackground(Void...args) {
try {
int rotation = BitmapUtils.getRotationFromExif(context, uri);
- return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
+ return createThumbnail(defaultSize, context, uri, null, null, 0, rotation,
+ false);
} catch (SecurityException securityException) {
if (isActivityDestroyed()) {
// Temporarily granted permissions are revoked when the activity
@@ -1160,11 +1290,6 @@
@Override
public boolean enableRotation() {
- // Check if rotation is enabled for this device.
- if (Utilities.isRotationAllowedForDevice(getContext()))
- return true;
-
- // Check if the user has specifically enabled rotation via preferences.
- return Utilities.isAllowRotationPrefEnabled(getApplicationContext(), true);
+ return true;
}
}
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 2d496a5..6baac6a 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -159,6 +159,7 @@
public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
private State mState = State.NOT_LOADED;
+ /** Returns whether loading was successful. */
public boolean loadInBackground(InBitmapProvider bitmapProvider) {
ExifInterface ei = new ExifInterface();
if (readExif(ei)) {
@@ -193,7 +194,7 @@
try {
mPreview = loadPreviewBitmap(opts);
} catch (IllegalArgumentException e) {
- Log.d(TAG, "Unable to reusage bitmap", e);
+ Log.d(TAG, "Unable to reuse bitmap", e);
opts.inBitmap = null;
mPreview = null;
}
@@ -202,6 +203,10 @@
if (mPreview == null) {
mPreview = loadPreviewBitmap(opts);
}
+ if (mPreview == null) {
+ mState = State.ERROR_LOADING;
+ return false;
+ }
// Verify that the bitmap can be used on GL surface
try {
@@ -212,7 +217,7 @@
Log.d(TAG, "Image cannot be rendered on a GL surface", e);
mState = State.ERROR_LOADING;
}
- return true;
+ return mState == State.LOADED;
}
}
@@ -310,7 +315,7 @@
Bitmap b = BitmapFactory.decodeStream(is, null, options);
Utils.closeSilently(is);
return b;
- } catch (FileNotFoundException e) {
+ } catch (FileNotFoundException | OutOfMemoryError e) {
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
return null;
}
@@ -412,7 +417,8 @@
"Failed to create preview of apropriate size! "
+ " in: %dx%d, out: %dx%d",
mWidth, mHeight,
- preview.getWidth(), preview.getHeight()));
+ preview == null ? -1 : preview.getWidth(),
+ preview == null ? -1 : preview.getHeight()));
}
}
}
diff --git a/build.gradle b/build.gradle
index d971755..b9a990f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,7 +3,7 @@
mavenCentral()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.3.0'
+ classpath 'com.android.tools.build:gradle:1.5.0'
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
}
}
@@ -21,6 +21,9 @@
targetSdkVersion 23
versionCode 1
versionName "1.0"
+
+ testApplicationId "com.android.launcher3.tests"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
@@ -30,10 +33,16 @@
sourceSets {
main {
res.srcDirs = ['res', 'WallpaperPicker/res']
- main.java.srcDirs = ['src', 'WallpaperPicker/src']
+ java.srcDirs = ['src', 'WallpaperPicker/src']
manifest.srcFile 'AndroidManifest.xml'
proto.srcDirs 'protos/'
}
+
+ androidTest {
+ java.srcDirs = ['tests/src']
+ res.srcDirs = ['tests/res']
+ manifest.srcFile "tests/AndroidManifest.xml"
+ }
}
}
@@ -42,9 +51,13 @@
}
dependencies {
- compile 'com.android.support:support-v4:+'
- compile 'com.android.support:recyclerview-v7:+'
+ compile 'com.android.support:support-v4:23.1.1'
+ compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
+
+ testCompile 'junit:junit:4.12'
+ androidTestCompile 'com.android.support.test:runner:0.5'
+ androidTestCompile 'com.android.support.test.uiautomator:uiautomator-v18:2.1.2'
}
protobuf {
diff --git a/print_db.py b/print_db.py
index 05237d0..7257a12 100755
--- a/print_db.py
+++ b/print_db.py
@@ -4,6 +4,7 @@
import codecs
import os
import pprint
+import re
import shutil
import sys
import sqlite3
@@ -22,9 +23,10 @@
INDEX_FILE = DIR + "/index.html"
def usage():
- print "usage: print_db.py launcher.db <sw600|sw720> -- prints a launcher.db"
- print "usage: print_db.py <sw600|sw720> -- adb pulls a launcher.db from a device"
- print " and prints it"
+ print "usage: print_db.py launcher.db <4x4|5x5|5x6|...> -- prints a launcher.db with"
+ print " the specified grid size (rows x cols)"
+ print "usage: print_db.py <4x4|5x5|5x6|...> -- adb pulls a launcher.db from a device"
+ print " and prints it with the specified grid size (rows x cols)"
print
print "The dump will be created in a directory called db_files in cwd."
print "This script will delete any db_files directory you have now"
@@ -41,7 +43,7 @@
def pull_file(fn):
print "pull_file: " + fn
rv = os.system("adb pull"
- + " /data/data/com.google.android.googlequicksearchbox/databases/launcher.db"
+ + " /data/data/com.android.launcher3/databases/launcher.db"
+ " " + fn);
if rv != 0:
print "adb pull failed"
@@ -287,16 +289,11 @@
def updateDeviceClassConstants(str):
global SCREENS, COLUMNS, ROWS, HOTSEAT_SIZE
- devClass = str.lower()
- if devClass == "sw600":
- COLUMNS = 6
- ROWS = 6
- HOTSEAT_SIZE = 6
- return True
- elif devClass == "sw720":
- COLUMNS = 8
- ROWS = 6
- HOTSEAT_SIZE = 8
+ match = re.search(r"(\d+)x(\d+)", str)
+ if match:
+ COLUMNS = int(match.group(1))
+ ROWS = int(match.group(2))
+ HOTSEAT_SIZE = 2 * int(COLUMNS / 2)
return True
return False
diff --git a/proguard.flags b/proguard.flags
index d6b9ba3..15faed9 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -15,11 +15,6 @@
public float getAlpha();
}
--keep class com.android.launcher3.BubbleTextView {
- public void setFastScrollFocus(float);
- public float getFastScrollFocus();
-}
-
-keep class com.android.launcher3.ButtonDropTarget {
public int getTextColor();
}
@@ -52,8 +47,10 @@
}
-keep class com.android.launcher3.FastBitmapDrawable {
- public int getBrightness();
- public void setBrightness(int);
+ public void setDesaturation(float);
+ public float getDesaturation();
+ public void setBrightness(float);
+ public float getBrightness();
}
-keep class com.android.launcher3.MemoryDumpActivity {
@@ -69,3 +66,10 @@
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
}
+
+# Proguard will strip new callbacks in LauncherApps.Callback from
+# WrappedCallback if compiled against an older SDK. Don't let this happen.
+-keep class com.android.launcher3.compat.** {
+ *;
+}
+
diff --git a/protos/backup.proto b/protos/backup.proto
index b7ad5ef..c3f27e1 100644
--- a/protos/backup.proto
+++ b/protos/backup.proto
@@ -18,8 +18,7 @@
package launcher_backup;
-option javanano_use_deprecated_package = true;
-option java_package = "com.android.launcher3.backup";
+option java_package = "com.android.launcher3.backup.nano";
option java_outer_classname = "BackupProtos";
message Key {
diff --git a/res/drawable-hdpi/cling_bg.9.png b/res/drawable-hdpi/cling_bg.9.png
index e173ba5..fb101f4 100644
--- a/res/drawable-hdpi/cling_bg.9.png
+++ b/res/drawable-hdpi/cling_bg.9.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
index 64f50df..43b1bed 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_1.png b/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
index df3e2de..d2c4cc1 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_icon_1.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_2.png b/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
index 7138ee8..57b7456 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_icon_2.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_3.png b/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
index ed88199..54fe70b 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_icon_3.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_all_apps_bg_icon_4.png b/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
index 0ff9453..9c0f777 100644
--- a/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
+++ b/res/drawable-hdpi/ic_all_apps_bg_icon_4.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_allapps.png b/res/drawable-hdpi/ic_allapps.png
index b98e65f..253755f 100644
--- a/res/drawable-hdpi/ic_allapps.png
+++ b/res/drawable-hdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_allapps_pressed.png b/res/drawable-hdpi/ic_allapps_pressed.png
index b7eaa67..1e644c5 100644
--- a/res/drawable-hdpi/ic_allapps_pressed.png
+++ b/res/drawable-hdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_arrow_back_grey.png b/res/drawable-hdpi/ic_arrow_back_grey.png
index c7c0088..7d7bfb1 100755
--- a/res/drawable-hdpi/ic_arrow_back_grey.png
+++ b/res/drawable-hdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_info_launcher.png b/res/drawable-hdpi/ic_info_launcher.png
index ec0cdd1..11162e1 100644
--- a/res/drawable-hdpi/ic_info_launcher.png
+++ b/res/drawable-hdpi/ic_info_launcher.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
index ab0e5db..6e3f5af 100644
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ b/res/drawable-hdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
index 423ca2b..6dbc4f9 100644
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ b/res/drawable-hdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current_folder.png b/res/drawable-hdpi/ic_pageindicator_current_folder.png
index 43fbb0e..c6c4228 100644
--- a/res/drawable-hdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-hdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png
index 83fa73f..19945a5 100644
--- a/res/drawable-hdpi/ic_pageindicator_default.png
+++ b/res/drawable-hdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default_folder.png b/res/drawable-hdpi/ic_pageindicator_default_folder.png
index 55cab1c..4710374 100644
--- a/res/drawable-hdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-hdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_remove_launcher.png b/res/drawable-hdpi/ic_remove_launcher.png
index c081457..ad2b9af 100644
--- a/res/drawable-hdpi/ic_remove_launcher.png
+++ b/res/drawable-hdpi/ic_remove_launcher.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_search_grey.png b/res/drawable-hdpi/ic_search_grey.png
index bd20ba0..bc50a47 100755
--- a/res/drawable-hdpi/ic_search_grey.png
+++ b/res/drawable-hdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting.png b/res/drawable-hdpi/ic_setting.png
index 1c12a5b..72a9745 100644
--- a/res/drawable-hdpi/ic_setting.png
+++ b/res/drawable-hdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting_pressed.png b/res/drawable-hdpi/ic_setting_pressed.png
index d5b5ca2..b86fce1 100644
--- a/res/drawable-hdpi/ic_setting_pressed.png
+++ b/res/drawable-hdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_uninstall_launcher.png b/res/drawable-hdpi/ic_uninstall_launcher.png
index 3d8f726..426683c 100644
--- a/res/drawable-hdpi/ic_uninstall_launcher.png
+++ b/res/drawable-hdpi/ic_uninstall_launcher.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_wallpaper.png b/res/drawable-hdpi/ic_wallpaper.png
index 34d5943..5936059 100644
--- a/res/drawable-hdpi/ic_wallpaper.png
+++ b/res/drawable-hdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_wallpaper_pressed.png b/res/drawable-hdpi/ic_wallpaper_pressed.png
index 1588ce7..4bb1958 100644
--- a/res/drawable-hdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-hdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget.png b/res/drawable-hdpi/ic_widget.png
index ed7e1ca..172664b 100644
--- a/res/drawable-hdpi/ic_widget.png
+++ b/res/drawable-hdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget_pressed.png b/res/drawable-hdpi/ic_widget_pressed.png
index 19d6fed..7f31ab3 100644
--- a/res/drawable-hdpi/ic_widget_pressed.png
+++ b/res/drawable-hdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget_resize_handle.png b/res/drawable-hdpi/ic_widget_resize_handle.png
index 844f3cf..b243aec 100644
--- a/res/drawable-hdpi/ic_widget_resize_handle.png
+++ b/res/drawable-hdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left.9.png b/res/drawable-hdpi/page_hover_left.9.png
index cc029d8..3f11d0b 100644
--- a/res/drawable-hdpi/page_hover_left.9.png
+++ b/res/drawable-hdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_active.9.png b/res/drawable-hdpi/page_hover_left_active.9.png
index 20c91a0..abe4c31 100644
--- a/res/drawable-hdpi/page_hover_left_active.9.png
+++ b/res/drawable-hdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right.9.png b/res/drawable-hdpi/page_hover_right.9.png
index a42822a..3bcf191 100644
--- a/res/drawable-hdpi/page_hover_right.9.png
+++ b/res/drawable-hdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_active.9.png b/res/drawable-hdpi/page_hover_right_active.9.png
index 523fafd..101e4dc 100644
--- a/res/drawable-hdpi/page_hover_right_active.9.png
+++ b/res/drawable-hdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_inner.png b/res/drawable-hdpi/portal_ring_inner.png
index c29b4aa..65f5af2 100644
--- a/res/drawable-hdpi/portal_ring_inner.png
+++ b/res/drawable-hdpi/portal_ring_inner.png
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_inner_nolip.png b/res/drawable-hdpi/portal_ring_inner_nolip.png
index e2f06fe..5be25fc 100644
--- a/res/drawable-hdpi/portal_ring_inner_nolip.png
+++ b/res/drawable-hdpi/portal_ring_inner_nolip.png
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_outer.png b/res/drawable-hdpi/portal_ring_outer.png
index e7b436b..712eeb2 100644
--- a/res/drawable-hdpi/portal_ring_outer.png
+++ b/res/drawable-hdpi/portal_ring_outer.png
Binary files differ
diff --git a/res/drawable-hdpi/portal_ring_rest.png b/res/drawable-hdpi/portal_ring_rest.png
index e3b1339..33cec32 100644
--- a/res/drawable-hdpi/portal_ring_rest.png
+++ b/res/drawable-hdpi/portal_ring_rest.png
Binary files differ
diff --git a/res/drawable-hdpi/quantum_panel_bitmap.9.png b/res/drawable-hdpi/quantum_panel_bitmap.9.png
index c331929..d2aee73 100644
--- a/res/drawable-hdpi/quantum_panel_bitmap.9.png
+++ b/res/drawable-hdpi/quantum_panel_bitmap.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quantum_panel_dark_bitmap.9.png b/res/drawable-hdpi/quantum_panel_dark_bitmap.9.png
index 0145d36..78345b8 100644
--- a/res/drawable-hdpi/quantum_panel_dark_bitmap.9.png
+++ b/res/drawable-hdpi/quantum_panel_dark_bitmap.9.png
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel.9.png b/res/drawable-hdpi/screenpanel.9.png
index f7ae011..5bccd33 100644
--- a/res/drawable-hdpi/screenpanel.9.png
+++ b/res/drawable-hdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel_hover.9.png b/res/drawable-hdpi/screenpanel_hover.9.png
index ac8e83d..f6b8c62 100644
--- a/res/drawable-hdpi/screenpanel_hover.9.png
+++ b/res/drawable-hdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-hdpi/virtual_preload.9.png b/res/drawable-hdpi/virtual_preload.9.png
index 71e5326..670088f 100644
--- a/res/drawable-hdpi/virtual_preload.9.png
+++ b/res/drawable-hdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-hdpi/virtual_preload_folder.9.png b/res/drawable-hdpi/virtual_preload_folder.9.png
index ece3226..68e2afe 100644
--- a/res/drawable-hdpi/virtual_preload_folder.9.png
+++ b/res/drawable-hdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_frame.9.png b/res/drawable-hdpi/widget_resize_frame.9.png
index b0a7403..a710932 100644
--- a/res/drawable-hdpi/widget_resize_frame.9.png
+++ b/res/drawable-hdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_resize_shadow.9.png b/res/drawable-hdpi/widget_resize_shadow.9.png
index 6e2932d..7cb5214 100644
--- a/res/drawable-hdpi/widget_resize_shadow.9.png
+++ b/res/drawable-hdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_tile.png b/res/drawable-hdpi/widget_tile.png
index 310ff8b..572bf6f 100644
--- a/res/drawable-hdpi/widget_tile.png
+++ b/res/drawable-hdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-hdpi/workspace_bg.9.png b/res/drawable-hdpi/workspace_bg.9.png
index 5bbfa4f..ff75186 100644
--- a/res/drawable-hdpi/workspace_bg.9.png
+++ b/res/drawable-hdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-land-hdpi/workspace_bg.9.png b/res/drawable-land-hdpi/workspace_bg.9.png
index 1a58144..eecd6de 100644
--- a/res/drawable-land-hdpi/workspace_bg.9.png
+++ b/res/drawable-land-hdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-land-mdpi/workspace_bg.9.png b/res/drawable-land-mdpi/workspace_bg.9.png
index a12519e..626f4a4 100644
--- a/res/drawable-land-mdpi/workspace_bg.9.png
+++ b/res/drawable-land-mdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-land-xhdpi/workspace_bg.9.png b/res/drawable-land-xhdpi/workspace_bg.9.png
index ce41454..60f7d73 100644
--- a/res/drawable-land-xhdpi/workspace_bg.9.png
+++ b/res/drawable-land-xhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-land-xxhdpi/workspace_bg.9.png b/res/drawable-land-xxhdpi/workspace_bg.9.png
index b0b4561..fc71a0f 100644
--- a/res/drawable-land-xxhdpi/workspace_bg.9.png
+++ b/res/drawable-land-xxhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/cling_bg.9.png b/res/drawable-mdpi/cling_bg.9.png
index fc49c89..6384f29 100644
--- a/res/drawable-mdpi/cling_bg.9.png
+++ b/res/drawable-mdpi/cling_bg.9.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
index d94bb7a..8868d6b 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_1.png b/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
index 76d973f..4c78288 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_icon_1.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_2.png b/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
index 0257f8c..0ed311b 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_icon_2.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_3.png b/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
index 67545f5..2aa3d4e 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_icon_3.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_all_apps_bg_icon_4.png b/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
index 3e36e27b..2cdea9c 100644
--- a/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
+++ b/res/drawable-mdpi/ic_all_apps_bg_icon_4.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps.png b/res/drawable-mdpi/ic_allapps.png
index f410673..6936b20 100644
--- a/res/drawable-mdpi/ic_allapps.png
+++ b/res/drawable-mdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps_pressed.png b/res/drawable-mdpi/ic_allapps_pressed.png
index aa4f913..850ded6 100644
--- a/res/drawable-mdpi/ic_allapps_pressed.png
+++ b/res/drawable-mdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_arrow_back_grey.png b/res/drawable-mdpi/ic_arrow_back_grey.png
index 5892c77..97999af 100755
--- a/res/drawable-mdpi/ic_arrow_back_grey.png
+++ b/res/drawable-mdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_info_launcher.png b/res/drawable-mdpi/ic_info_launcher.png
index c72d0c2..6fbe5e3 100644
--- a/res/drawable-mdpi/ic_info_launcher.png
+++ b/res/drawable-mdpi/ic_info_launcher.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
index 11659a3..d9939b4 100644
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ b/res/drawable-mdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
index ca889c4..832f8ef 100644
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ b/res/drawable-mdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current_folder.png b/res/drawable-mdpi/ic_pageindicator_current_folder.png
index 5bbba91..b6c4d7f 100644
--- a/res/drawable-mdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-mdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png
index 34493b1..9c44afc 100644
--- a/res/drawable-mdpi/ic_pageindicator_default.png
+++ b/res/drawable-mdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default_folder.png b/res/drawable-mdpi/ic_pageindicator_default_folder.png
index 0a987a4..f462558 100644
--- a/res/drawable-mdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-mdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_remove_launcher.png b/res/drawable-mdpi/ic_remove_launcher.png
index 4a2c319..2bb281d 100644
--- a/res/drawable-mdpi/ic_remove_launcher.png
+++ b/res/drawable-mdpi/ic_remove_launcher.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting.png b/res/drawable-mdpi/ic_setting.png
index c614e91..60c4fa5 100644
--- a/res/drawable-mdpi/ic_setting.png
+++ b/res/drawable-mdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting_pressed.png b/res/drawable-mdpi/ic_setting_pressed.png
index 61e574a..018bea3 100644
--- a/res/drawable-mdpi/ic_setting_pressed.png
+++ b/res/drawable-mdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_uninstall_launcher.png b/res/drawable-mdpi/ic_uninstall_launcher.png
index af45669..bfcbc6df 100644
--- a/res/drawable-mdpi/ic_uninstall_launcher.png
+++ b/res/drawable-mdpi/ic_uninstall_launcher.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_wallpaper.png b/res/drawable-mdpi/ic_wallpaper.png
index 8f2a00a..a934783 100644
--- a/res/drawable-mdpi/ic_wallpaper.png
+++ b/res/drawable-mdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_wallpaper_pressed.png b/res/drawable-mdpi/ic_wallpaper_pressed.png
index aa598c3..08794d9 100644
--- a/res/drawable-mdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-mdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget.png b/res/drawable-mdpi/ic_widget.png
index 1bd3935..5545350 100644
--- a/res/drawable-mdpi/ic_widget.png
+++ b/res/drawable-mdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget_pressed.png b/res/drawable-mdpi/ic_widget_pressed.png
index 9b690d9..634b415 100644
--- a/res/drawable-mdpi/ic_widget_pressed.png
+++ b/res/drawable-mdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget_resize_handle.png b/res/drawable-mdpi/ic_widget_resize_handle.png
index c3b287f..656e96c 100644
--- a/res/drawable-mdpi/ic_widget_resize_handle.png
+++ b/res/drawable-mdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left.9.png b/res/drawable-mdpi/page_hover_left.9.png
index 2bbf428..2b6094c 100644
--- a/res/drawable-mdpi/page_hover_left.9.png
+++ b/res/drawable-mdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_active.9.png b/res/drawable-mdpi/page_hover_left_active.9.png
index bf70f36..9eb00a2 100644
--- a/res/drawable-mdpi/page_hover_left_active.9.png
+++ b/res/drawable-mdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right.9.png b/res/drawable-mdpi/page_hover_right.9.png
index 4bafd0f..c2e59835 100644
--- a/res/drawable-mdpi/page_hover_right.9.png
+++ b/res/drawable-mdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_active.9.png b/res/drawable-mdpi/page_hover_right_active.9.png
index 4aaa014..d2771a1 100644
--- a/res/drawable-mdpi/page_hover_right_active.9.png
+++ b/res/drawable-mdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_inner.png b/res/drawable-mdpi/portal_ring_inner.png
index 99aaa60..7c5e2b7 100644
--- a/res/drawable-mdpi/portal_ring_inner.png
+++ b/res/drawable-mdpi/portal_ring_inner.png
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_inner_nolip.png b/res/drawable-mdpi/portal_ring_inner_nolip.png
index f981778..6ccdebb 100644
--- a/res/drawable-mdpi/portal_ring_inner_nolip.png
+++ b/res/drawable-mdpi/portal_ring_inner_nolip.png
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_outer.png b/res/drawable-mdpi/portal_ring_outer.png
index c98f64b..40a73ab 100644
--- a/res/drawable-mdpi/portal_ring_outer.png
+++ b/res/drawable-mdpi/portal_ring_outer.png
Binary files differ
diff --git a/res/drawable-mdpi/portal_ring_rest.png b/res/drawable-mdpi/portal_ring_rest.png
index 5c33b42..b2c733b 100644
--- a/res/drawable-mdpi/portal_ring_rest.png
+++ b/res/drawable-mdpi/portal_ring_rest.png
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel_bitmap.9.png b/res/drawable-mdpi/quantum_panel_bitmap.9.png
index 86be568..9325d49 100644
--- a/res/drawable-mdpi/quantum_panel_bitmap.9.png
+++ b/res/drawable-mdpi/quantum_panel_bitmap.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel_dark_bitmap.9.png b/res/drawable-mdpi/quantum_panel_dark_bitmap.9.png
index 70429b9..bf74fa0 100644
--- a/res/drawable-mdpi/quantum_panel_dark_bitmap.9.png
+++ b/res/drawable-mdpi/quantum_panel_dark_bitmap.9.png
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel.9.png b/res/drawable-mdpi/screenpanel.9.png
index c2779fc..9603c12 100644
--- a/res/drawable-mdpi/screenpanel.9.png
+++ b/res/drawable-mdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel_hover.9.png b/res/drawable-mdpi/screenpanel_hover.9.png
index 70b3078..7f28ce0 100644
--- a/res/drawable-mdpi/screenpanel_hover.9.png
+++ b/res/drawable-mdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-mdpi/virtual_preload.9.png b/res/drawable-mdpi/virtual_preload.9.png
index a3c7519..c4a01fe 100644
--- a/res/drawable-mdpi/virtual_preload.9.png
+++ b/res/drawable-mdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-mdpi/virtual_preload_folder.9.png b/res/drawable-mdpi/virtual_preload_folder.9.png
index fa2f131..2f3e420 100644
--- a/res/drawable-mdpi/virtual_preload_folder.9.png
+++ b/res/drawable-mdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_frame.9.png b/res/drawable-mdpi/widget_resize_frame.9.png
index 856cec6..252482f 100644
--- a/res/drawable-mdpi/widget_resize_frame.9.png
+++ b/res/drawable-mdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_resize_shadow.9.png b/res/drawable-mdpi/widget_resize_shadow.9.png
index 02522f3..a2010e2 100644
--- a/res/drawable-mdpi/widget_resize_shadow.9.png
+++ b/res/drawable-mdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_tile.png b/res/drawable-mdpi/widget_tile.png
index 1ba559d..9652ace 100644
--- a/res/drawable-mdpi/widget_tile.png
+++ b/res/drawable-mdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-mdpi/workspace_bg.9.png b/res/drawable-mdpi/workspace_bg.9.png
index 2856e09..c67c432 100644
--- a/res/drawable-mdpi/workspace_bg.9.png
+++ b/res/drawable-mdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-nodpi/ic_migration.png b/res/drawable-nodpi/ic_migration.png
index c282cd2..14f8721 100644
--- a/res/drawable-nodpi/ic_migration.png
+++ b/res/drawable-nodpi/ic_migration.png
Binary files differ
diff --git a/res/drawable-sw720dp-hdpi/workspace_bg.9.png b/res/drawable-sw720dp-hdpi/workspace_bg.9.png
index 5bbfa4f..ff75186 100644
--- a/res/drawable-sw720dp-hdpi/workspace_bg.9.png
+++ b/res/drawable-sw720dp-hdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-sw720dp-mdpi/workspace_bg.9.png b/res/drawable-sw720dp-mdpi/workspace_bg.9.png
index 2856e09..c67c432 100644
--- a/res/drawable-sw720dp-mdpi/workspace_bg.9.png
+++ b/res/drawable-sw720dp-mdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
index 72269f2..0b80cbf 100644
--- a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
+++ b/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png
index efc9b04..0d180c2 100644
--- a/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png
+++ b/res/drawable-sw720dp-xxhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/cling_bg.9.png b/res/drawable-xhdpi/cling_bg.9.png
index 4db356f..7d8b1f0 100644
--- a/res/drawable-xhdpi/cling_bg.9.png
+++ b/res/drawable-xhdpi/cling_bg.9.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
index 5dde7f3..8a67245 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
index f5bd32a..c0ebaed 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_icon_1.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
index fb07956..71cf250 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_icon_2.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
index c7d687e..3c69fc5 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_icon_3.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
index e22b962..5f6ca38 100644
--- a/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
+++ b/res/drawable-xhdpi/ic_all_apps_bg_icon_4.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps.png b/res/drawable-xhdpi/ic_allapps.png
index ff3d823..c11c103 100644
--- a/res/drawable-xhdpi/ic_allapps.png
+++ b/res/drawable-xhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps_pressed.png b/res/drawable-xhdpi/ic_allapps_pressed.png
index 5f188f6..f319bf1 100644
--- a/res/drawable-xhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_arrow_back_grey.png b/res/drawable-xhdpi/ic_arrow_back_grey.png
index 11996ef..22854bf 100755
--- a/res/drawable-xhdpi/ic_arrow_back_grey.png
+++ b/res/drawable-xhdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_info_launcher.png b/res/drawable-xhdpi/ic_info_launcher.png
index 076b59b..041f2b3 100644
--- a/res/drawable-xhdpi/ic_info_launcher.png
+++ b/res/drawable-xhdpi/ic_info_launcher.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
index af1da2d..7e18c05 100644
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
index 3054f2f..866725f 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current_folder.png b/res/drawable-xhdpi/ic_pageindicator_current_folder.png
index cd92e9f..ec19b7c 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png
index 38538dc..0cde8f4 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default_folder.png b/res/drawable-xhdpi/ic_pageindicator_default_folder.png
index e7c46e3..7c22d41 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_remove_launcher.png b/res/drawable-xhdpi/ic_remove_launcher.png
index de05889..ff94eb8 100644
--- a/res/drawable-xhdpi/ic_remove_launcher.png
+++ b/res/drawable-xhdpi/ic_remove_launcher.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png
index e83891c..e63182d 100755
--- a/res/drawable-xhdpi/ic_search_grey.png
+++ b/res/drawable-xhdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting.png b/res/drawable-xhdpi/ic_setting.png
index 3a7310b..bb90789 100644
--- a/res/drawable-xhdpi/ic_setting.png
+++ b/res/drawable-xhdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting_pressed.png b/res/drawable-xhdpi/ic_setting_pressed.png
index 005d49c..949373f 100644
--- a/res/drawable-xhdpi/ic_setting_pressed.png
+++ b/res/drawable-xhdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_uninstall_launcher.png b/res/drawable-xhdpi/ic_uninstall_launcher.png
index dd50e88..2c7ab56 100644
--- a/res/drawable-xhdpi/ic_uninstall_launcher.png
+++ b/res/drawable-xhdpi/ic_uninstall_launcher.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wallpaper.png b/res/drawable-xhdpi/ic_wallpaper.png
index d2bf246..0acf773 100644
--- a/res/drawable-xhdpi/ic_wallpaper.png
+++ b/res/drawable-xhdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wallpaper_pressed.png b/res/drawable-xhdpi/ic_wallpaper_pressed.png
index 5a9b84d..e1e291d 100644
--- a/res/drawable-xhdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-xhdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget.png b/res/drawable-xhdpi/ic_widget.png
index cf6be81..94bb79f 100644
--- a/res/drawable-xhdpi/ic_widget.png
+++ b/res/drawable-xhdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget_pressed.png b/res/drawable-xhdpi/ic_widget_pressed.png
index 633c9c6..1dcaf37 100644
--- a/res/drawable-xhdpi/ic_widget_pressed.png
+++ b/res/drawable-xhdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget_resize_handle.png b/res/drawable-xhdpi/ic_widget_resize_handle.png
index f445a1c..0e8dd68 100644
--- a/res/drawable-xhdpi/ic_widget_resize_handle.png
+++ b/res/drawable-xhdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left.9.png b/res/drawable-xhdpi/page_hover_left.9.png
index a2b9b65..dbcc0ab 100644
--- a/res/drawable-xhdpi/page_hover_left.9.png
+++ b/res/drawable-xhdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_active.9.png b/res/drawable-xhdpi/page_hover_left_active.9.png
index ba9478e..3233efe 100644
--- a/res/drawable-xhdpi/page_hover_left_active.9.png
+++ b/res/drawable-xhdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right.9.png b/res/drawable-xhdpi/page_hover_right.9.png
index 1243ea9..d82f809 100644
--- a/res/drawable-xhdpi/page_hover_right.9.png
+++ b/res/drawable-xhdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_active.9.png b/res/drawable-xhdpi/page_hover_right_active.9.png
index 582261c..819ea19 100644
--- a/res/drawable-xhdpi/page_hover_right_active.9.png
+++ b/res/drawable-xhdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_inner.png b/res/drawable-xhdpi/portal_ring_inner.png
index 7b6a8a0..b088042 100644
--- a/res/drawable-xhdpi/portal_ring_inner.png
+++ b/res/drawable-xhdpi/portal_ring_inner.png
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_inner_nolip.png b/res/drawable-xhdpi/portal_ring_inner_nolip.png
index 4b84436..decf766 100644
--- a/res/drawable-xhdpi/portal_ring_inner_nolip.png
+++ b/res/drawable-xhdpi/portal_ring_inner_nolip.png
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_outer.png b/res/drawable-xhdpi/portal_ring_outer.png
index 79c1888..5ab9a21 100644
--- a/res/drawable-xhdpi/portal_ring_outer.png
+++ b/res/drawable-xhdpi/portal_ring_outer.png
Binary files differ
diff --git a/res/drawable-xhdpi/portal_ring_rest.png b/res/drawable-xhdpi/portal_ring_rest.png
index 544a74f..7d1c842 100644
--- a/res/drawable-xhdpi/portal_ring_rest.png
+++ b/res/drawable-xhdpi/portal_ring_rest.png
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel_bitmap.9.png b/res/drawable-xhdpi/quantum_panel_bitmap.9.png
index 13bdf09..b89e8b4 100644
--- a/res/drawable-xhdpi/quantum_panel_bitmap.9.png
+++ b/res/drawable-xhdpi/quantum_panel_bitmap.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel_dark_bitmap.9.png b/res/drawable-xhdpi/quantum_panel_dark_bitmap.9.png
index ac2e423..1d17136 100644
--- a/res/drawable-xhdpi/quantum_panel_dark_bitmap.9.png
+++ b/res/drawable-xhdpi/quantum_panel_dark_bitmap.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel.9.png b/res/drawable-xhdpi/screenpanel.9.png
index 53a7812..75343f7 100644
--- a/res/drawable-xhdpi/screenpanel.9.png
+++ b/res/drawable-xhdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel_hover.9.png b/res/drawable-xhdpi/screenpanel_hover.9.png
index a2e200f..55b4d6e 100644
--- a/res/drawable-xhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/virtual_preload.9.png b/res/drawable-xhdpi/virtual_preload.9.png
index d2c3fea..2afade1 100644
--- a/res/drawable-xhdpi/virtual_preload.9.png
+++ b/res/drawable-xhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/virtual_preload_folder.9.png b/res/drawable-xhdpi/virtual_preload_folder.9.png
index 1f9202b..cb3fdca 100644
--- a/res/drawable-xhdpi/virtual_preload_folder.9.png
+++ b/res/drawable-xhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_frame.9.png b/res/drawable-xhdpi/widget_resize_frame.9.png
index bf7cc6b..563c75d 100644
--- a/res/drawable-xhdpi/widget_resize_frame.9.png
+++ b/res/drawable-xhdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_resize_shadow.9.png b/res/drawable-xhdpi/widget_resize_shadow.9.png
index 96dee7f..2b1ac05 100644
--- a/res/drawable-xhdpi/widget_resize_shadow.9.png
+++ b/res/drawable-xhdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_tile.png b/res/drawable-xhdpi/widget_tile.png
index 9730f35..be1748d 100644
--- a/res/drawable-xhdpi/widget_tile.png
+++ b/res/drawable-xhdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-xhdpi/workspace_bg.9.png b/res/drawable-xhdpi/workspace_bg.9.png
index 72269f2..0b80cbf 100644
--- a/res/drawable-xhdpi/workspace_bg.9.png
+++ b/res/drawable-xhdpi/workspace_bg.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_bg.9.png b/res/drawable-xxhdpi/cling_bg.9.png
index dc9f69a..d31ea70 100644
--- a/res/drawable-xxhdpi/cling_bg.9.png
+++ b/res/drawable-xxhdpi/cling_bg.9.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
index e107c2e..ed694f8 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
index 7482830..5cb0427 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_icon_1.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
index 028c7f4..cd0322b 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_icon_2.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
index dce7b67..19ffc2d 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_icon_3.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
index 811a6b3..311c3df 100644
--- a/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps.png b/res/drawable-xxhdpi/ic_allapps.png
index 5dbfe4c..cf6a2cb 100644
--- a/res/drawable-xxhdpi/ic_allapps.png
+++ b/res/drawable-xxhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps_pressed.png b/res/drawable-xxhdpi/ic_allapps_pressed.png
index e761723..379389a 100644
--- a/res/drawable-xxhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xxhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_arrow_back_grey.png b/res/drawable-xxhdpi/ic_arrow_back_grey.png
index ccd3900..a3ed052 100755
--- a/res/drawable-xxhdpi/ic_arrow_back_grey.png
+++ b/res/drawable-xxhdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_info_launcher.png b/res/drawable-xxhdpi/ic_info_launcher.png
index 386d091..8e602da 100644
--- a/res/drawable-xxhdpi/ic_info_launcher.png
+++ b/res/drawable-xxhdpi/ic_info_launcher.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
index c288952..d790e86 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
index 5941c8e..9550c61 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
index 602b89a..987c882 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current_folder.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png
index 3fa9e5f..3bee96f 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
index bbcd7f9..46ff473 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default_folder.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_remove_launcher.png b/res/drawable-xxhdpi/ic_remove_launcher.png
index 7c28bb0..78ca080 100644
--- a/res/drawable-xxhdpi/ic_remove_launcher.png
+++ b/res/drawable-xxhdpi/ic_remove_launcher.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png
index f4c5e27..33b4ea9 100755
--- a/res/drawable-xxhdpi/ic_search_grey.png
+++ b/res/drawable-xxhdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting.png b/res/drawable-xxhdpi/ic_setting.png
index 01bdcd5..3effb50 100644
--- a/res/drawable-xxhdpi/ic_setting.png
+++ b/res/drawable-xxhdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting_pressed.png b/res/drawable-xxhdpi/ic_setting_pressed.png
index d0cad5e..d78cad6 100644
--- a/res/drawable-xxhdpi/ic_setting_pressed.png
+++ b/res/drawable-xxhdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_uninstall_launcher.png b/res/drawable-xxhdpi/ic_uninstall_launcher.png
index 872e829..43aba6e 100644
--- a/res/drawable-xxhdpi/ic_uninstall_launcher.png
+++ b/res/drawable-xxhdpi/ic_uninstall_launcher.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wallpaper.png b/res/drawable-xxhdpi/ic_wallpaper.png
index 490c45a..218fd1d 100644
--- a/res/drawable-xxhdpi/ic_wallpaper.png
+++ b/res/drawable-xxhdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wallpaper_pressed.png b/res/drawable-xxhdpi/ic_wallpaper_pressed.png
index e5d200b..52c92cb 100644
--- a/res/drawable-xxhdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-xxhdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget.png b/res/drawable-xxhdpi/ic_widget.png
index d4b8324..cc5002e 100644
--- a/res/drawable-xxhdpi/ic_widget.png
+++ b/res/drawable-xxhdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget_pressed.png b/res/drawable-xxhdpi/ic_widget_pressed.png
index b8dd35d..0c9b02a 100644
--- a/res/drawable-xxhdpi/ic_widget_pressed.png
+++ b/res/drawable-xxhdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget_resize_handle.png b/res/drawable-xxhdpi/ic_widget_resize_handle.png
index 144cac9..736a157 100644
--- a/res/drawable-xxhdpi/ic_widget_resize_handle.png
+++ b/res/drawable-xxhdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left.9.png b/res/drawable-xxhdpi/page_hover_left.9.png
index 63869dd..c81f86c 100644
--- a/res/drawable-xxhdpi/page_hover_left.9.png
+++ b/res/drawable-xxhdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_active.9.png b/res/drawable-xxhdpi/page_hover_left_active.9.png
index 9a418ce..858a3b2 100644
--- a/res/drawable-xxhdpi/page_hover_left_active.9.png
+++ b/res/drawable-xxhdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right.9.png b/res/drawable-xxhdpi/page_hover_right.9.png
index c6fd398..c529770 100644
--- a/res/drawable-xxhdpi/page_hover_right.9.png
+++ b/res/drawable-xxhdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_active.9.png b/res/drawable-xxhdpi/page_hover_right_active.9.png
index 7aef373..9900553 100644
--- a/res/drawable-xxhdpi/page_hover_right_active.9.png
+++ b/res/drawable-xxhdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_inner.png b/res/drawable-xxhdpi/portal_ring_inner.png
index d088a17..cd23cf7 100644
--- a/res/drawable-xxhdpi/portal_ring_inner.png
+++ b/res/drawable-xxhdpi/portal_ring_inner.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_inner_nolip.png b/res/drawable-xxhdpi/portal_ring_inner_nolip.png
index 0fad656..d82b910 100644
--- a/res/drawable-xxhdpi/portal_ring_inner_nolip.png
+++ b/res/drawable-xxhdpi/portal_ring_inner_nolip.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_outer.png b/res/drawable-xxhdpi/portal_ring_outer.png
index 45ac040..e5d33b2 100644
--- a/res/drawable-xxhdpi/portal_ring_outer.png
+++ b/res/drawable-xxhdpi/portal_ring_outer.png
Binary files differ
diff --git a/res/drawable-xxhdpi/portal_ring_rest.png b/res/drawable-xxhdpi/portal_ring_rest.png
index 6fa6a53..d52825c 100644
--- a/res/drawable-xxhdpi/portal_ring_rest.png
+++ b/res/drawable-xxhdpi/portal_ring_rest.png
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel_bitmap.9.png b/res/drawable-xxhdpi/quantum_panel_bitmap.9.png
index b44269e..1dd1f6d 100644
--- a/res/drawable-xxhdpi/quantum_panel_bitmap.9.png
+++ b/res/drawable-xxhdpi/quantum_panel_bitmap.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel_dark_bitmap.9.png b/res/drawable-xxhdpi/quantum_panel_dark_bitmap.9.png
index 7979cf7..48d584b 100644
--- a/res/drawable-xxhdpi/quantum_panel_dark_bitmap.9.png
+++ b/res/drawable-xxhdpi/quantum_panel_dark_bitmap.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel.9.png b/res/drawable-xxhdpi/screenpanel.9.png
index 2d13954..b221b37 100644
--- a/res/drawable-xxhdpi/screenpanel.9.png
+++ b/res/drawable-xxhdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel_hover.9.png b/res/drawable-xxhdpi/screenpanel_hover.9.png
index 369fc44..418cf0a 100644
--- a/res/drawable-xxhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xxhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload.9.png b/res/drawable-xxhdpi/virtual_preload.9.png
index 93e3b33..03c6e7f 100644
--- a/res/drawable-xxhdpi/virtual_preload.9.png
+++ b/res/drawable-xxhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload_folder.9.png b/res/drawable-xxhdpi/virtual_preload_folder.9.png
index fae19b3..052a72e 100644
--- a/res/drawable-xxhdpi/virtual_preload_folder.9.png
+++ b/res/drawable-xxhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_frame.9.png b/res/drawable-xxhdpi/widget_resize_frame.9.png
index 7e189d4..ea527f4 100644
--- a/res/drawable-xxhdpi/widget_resize_frame.9.png
+++ b/res/drawable-xxhdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_resize_shadow.9.png b/res/drawable-xxhdpi/widget_resize_shadow.9.png
index 41c448b..5412168 100644
--- a/res/drawable-xxhdpi/widget_resize_shadow.9.png
+++ b/res/drawable-xxhdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_tile.png b/res/drawable-xxhdpi/widget_tile.png
index 3a3790d..c6237db 100644
--- a/res/drawable-xxhdpi/widget_tile.png
+++ b/res/drawable-xxhdpi/widget_tile.png
Binary files differ
diff --git a/res/drawable-xxhdpi/workspace_bg.9.png b/res/drawable-xxhdpi/workspace_bg.9.png
index efc9b04..0d180c2 100644
--- a/res/drawable-xxhdpi/workspace_bg.9.png
+++ b/res/drawable-xxhdpi/workspace_bg.9.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
index c638456..615374a 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
index 511a02a..10f8c41 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
index 2cc18f8..102d925 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
index c32f8ff..9be5b7a 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
index 7bead8a..d7fb29b 100644
--- a/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
+++ b/res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_arrow_back_grey.png b/res/drawable-xxxhdpi/ic_arrow_back_grey.png
index 79b9b48..6b42051 100755
--- a/res/drawable-xxxhdpi/ic_arrow_back_grey.png
+++ b/res/drawable-xxxhdpi/ic_arrow_back_grey.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_info_launcher.png b/res/drawable-xxxhdpi/ic_info_launcher.png
index bf39e5b..3540de1 100644
--- a/res/drawable-xxxhdpi/ic_info_launcher.png
+++ b/res/drawable-xxxhdpi/ic_info_launcher.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_remove_launcher.png b/res/drawable-xxxhdpi/ic_remove_launcher.png
index 7043be0..418d81a 100644
--- a/res/drawable-xxxhdpi/ic_remove_launcher.png
+++ b/res/drawable-xxxhdpi/ic_remove_launcher.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_search_grey.png b/res/drawable-xxxhdpi/ic_search_grey.png
index bd5fdf4..d957186 100755
--- a/res/drawable-xxxhdpi/ic_search_grey.png
+++ b/res/drawable-xxxhdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_uninstall_launcher.png b/res/drawable-xxxhdpi/ic_uninstall_launcher.png
index 77a3302..724437a 100644
--- a/res/drawable-xxxhdpi/ic_uninstall_launcher.png
+++ b/res/drawable-xxxhdpi/ic_uninstall_launcher.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/ic_widget_resize_handle.png b/res/drawable-xxxhdpi/ic_widget_resize_handle.png
index 4bde6b9..e3c45ad 100644
--- a/res/drawable-xxxhdpi/ic_widget_resize_handle.png
+++ b/res/drawable-xxxhdpi/ic_widget_resize_handle.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_inner.png b/res/drawable-xxxhdpi/portal_ring_inner.png
index 34a3599..59e811d 100644
--- a/res/drawable-xxxhdpi/portal_ring_inner.png
+++ b/res/drawable-xxxhdpi/portal_ring_inner.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_inner_nolip.png b/res/drawable-xxxhdpi/portal_ring_inner_nolip.png
index 8cebb35..c1e7585 100644
--- a/res/drawable-xxxhdpi/portal_ring_inner_nolip.png
+++ b/res/drawable-xxxhdpi/portal_ring_inner_nolip.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_outer.png b/res/drawable-xxxhdpi/portal_ring_outer.png
index d2df322..f2f818b 100644
--- a/res/drawable-xxxhdpi/portal_ring_outer.png
+++ b/res/drawable-xxxhdpi/portal_ring_outer.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/portal_ring_rest.png b/res/drawable-xxxhdpi/portal_ring_rest.png
index 11e92ee..2af67b8 100644
--- a/res/drawable-xxxhdpi/portal_ring_rest.png
+++ b/res/drawable-xxxhdpi/portal_ring_rest.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/quantum_panel_bitmap.9.png b/res/drawable-xxxhdpi/quantum_panel_bitmap.9.png
index bc887fe..915177d 100644
--- a/res/drawable-xxxhdpi/quantum_panel_bitmap.9.png
+++ b/res/drawable-xxxhdpi/quantum_panel_bitmap.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/quantum_panel_dark_bitmap.9.png b/res/drawable-xxxhdpi/quantum_panel_dark_bitmap.9.png
index 7cfd6e4..27b8466 100644
--- a/res/drawable-xxxhdpi/quantum_panel_dark_bitmap.9.png
+++ b/res/drawable-xxxhdpi/quantum_panel_dark_bitmap.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_frame.9.png b/res/drawable-xxxhdpi/widget_resize_frame.9.png
index cb609ce..4644e9a 100644
--- a/res/drawable-xxxhdpi/widget_resize_frame.9.png
+++ b/res/drawable-xxxhdpi/widget_resize_frame.9.png
Binary files differ
diff --git a/res/drawable-xxxhdpi/widget_resize_shadow.9.png b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
index 82c8b9c..63cea84 100644
--- a/res/drawable-xxxhdpi/widget_resize_shadow.9.png
+++ b/res/drawable-xxxhdpi/widget_resize_shadow.9.png
Binary files differ
diff --git a/res/drawable/all_apps_search_bg.xml b/res/drawable/all_apps_search_bg.xml
index a09f88f..5a2c9e8 100644
--- a/res/drawable/all_apps_search_bg.xml
+++ b/res/drawable/all_apps_search_bg.xml
@@ -14,7 +14,25 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<inset xmlns:android="http://schemas.android.com/apk/res/android"
- android:drawable="@drawable/quantum_panel"
- android:insetTop="@dimen/container_bounds_minus_quantum_panel_padding_inset"
- android:insetBottom="@dimen/container_bounds_minus_quantum_panel_padding_inset" />
\ No newline at end of file
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="rectangle">
+ <solid android:color="@color/quantum_panel_bg_color" />
+ <corners
+ android:topLeftRadius="2dp"
+ android:topRightRadius="2dp" />
+ </shape>
+ </item>
+ <item
+ android:top="@dimen/all_apps_search_bar_bg_overflow"
+ android:left="@dimen/all_apps_search_bar_bg_overflow"
+ android:right="@dimen/all_apps_search_bar_bg_overflow"
+ android:bottom="0dp">
+
+ <shape android:shape="rectangle">
+ <stroke
+ android:width="@dimen/all_apps_search_bar_divider_width"
+ android:color="#1E000000"/>
+ </shape>
+ </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/widget_internal_focus_bg.xml b/res/drawable/widget_internal_focus_bg.xml
new file mode 100644
index 0000000..4d4bea6
--- /dev/null
+++ b/res/drawable/widget_internal_focus_bg.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2015, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- Used as the widget host view background when giving focus to a child via keyboard. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_selected="true">
+ <shape android:shape="rectangle">
+ <stroke android:color="#fff" android:width="2dp" />
+ </shape>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 6500ebc..1a951f1 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -25,6 +25,8 @@
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index d0772ee..8bf9d64 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -26,6 +26,8 @@
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 802922e..2fc62c5 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -25,6 +25,8 @@
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/res/layout/all_apps.xml b/res/layout/all_apps.xml
index 1bf54ee..a677fff 100644
--- a/res/layout/all_apps.xml
+++ b/res/layout/all_apps.xml
@@ -18,40 +18,74 @@
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"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/apps_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:orientation="vertical"
+ launcher:revealBackground="@drawable/quantum_panel_shape">
- <!-- Both android:focusable and android:focusableInTouchMode are needed for
- the view to get focus change events. -->
- <FrameLayout
- android:id="@+id/search_box_container"
+ <View
+ android:id="@+id/reveal_view"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:focusable="false"
+ android:elevation="2dp"
+ android:visibility="invisible" />
+
+
+ <com.android.launcher3.allapps.AllAppsRecyclerViewContainerView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/main_content"
+ android:saveEnabled="false"
+ android:visibility="gone"
+ android:layout_gravity="center"
android:focusable="true"
android:focusableInTouchMode="true"
- android:visibility="gone" />
+ android:elevation="15dp" >
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_weight="1">
- <FrameLayout
- android:id="@+id/all_apps_reveal"
+ <!-- DO NOT CHANGE THE ID -->
+ <com.android.launcher3.allapps.AllAppsRecyclerView
+ android:id="@+id/apps_list_view"
+ android:theme="@style/Theme.Light.CustomOverscroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="false"
- android:elevation="2dp"
- android:visibility="invisible" />
- <include
- layout="@layout/all_apps_container"
- android:id="@+id/all_apps_container"
+ android:layout_gravity="center_horizontal|top"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:layout_marginTop="@dimen/all_apps_search_bar_height"
+ android:descendantFocusability="afterDescendants" />
+
+ <LinearLayout
+ android:id="@+id/search_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:visibility="gone" />
- </FrameLayout>
+ android:saveEnabled="false"
+ android:layout_height="@dimen/all_apps_search_bar_height"
+ android:layout_gravity="start|top"
+ android:orientation="horizontal"
+ android:background="@drawable/all_apps_search_bg" >
+
+ <com.android.launcher3.ExtendedEditText
+ android:id="@+id/search_box_input"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/transparent"
+ android:focusableInTouchMode="true"
+ android:gravity="fill_horizontal|center_vertical"
+ android:hint="@string/all_apps_search_bar_hint"
+ android:inputType="text|textNoSuggestions|textCapWords"
+ android:imeOptions="actionSearch|flagNoExtractUi"
+ android:maxLines="1"
+ android:scrollHorizontally="true"
+ android:layout_marginLeft="@dimen/container_fastscroll_thumb_max_width"
+ android:layout_marginRight="@dimen/container_fastscroll_thumb_max_width"
+ android:singleLine="true"
+ android:textColor="#4c4c4c"
+ android:textColorHint="#9c9c9c"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ </com.android.launcher3.allapps.AllAppsRecyclerViewContainerView>
</com.android.launcher3.allapps.AllAppsContainerView>
\ No newline at end of file
diff --git a/res/layout/all_apps_empty_search.xml b/res/layout/all_apps_empty_search.xml
index 5439111..e1635d6 100644
--- a/res/layout/all_apps_empty_search.xml
+++ b/res/layout/all_apps_empty_search.xml
@@ -16,7 +16,7 @@
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/empty_text"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:paddingTop="@dimen/all_apps_empty_search_message_top_offset"
diff --git a/res/layout/all_apps_icon.xml b/res/layout/all_apps_icon.xml
index 0985e95..bb95c5f 100644
--- a/res/layout/all_apps_icon.xml
+++ b/res/layout/all_apps_icon.xml
@@ -18,7 +18,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
style="@style/Icon.AllApps"
android:id="@+id/icon"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml
index 341d8ef..f15aeaf 100644
--- a/res/layout/all_apps_prediction_bar_icon.xml
+++ b/res/layout/all_apps_prediction_bar_icon.xml
@@ -18,7 +18,7 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
style="@style/Icon.AllApps"
android:id="@+id/icon"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
index 1ed5088..2e38ea0 100644
--- a/res/layout/all_apps_search_market.xml
+++ b/res/layout/all_apps_search_market.xml
@@ -16,7 +16,7 @@
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search_market_text"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="start|center_vertical"
android:paddingLeft="16dp"
@@ -25,5 +25,5 @@
android:textSize="14sp"
android:textColor="@color/launcher_accent_color"
android:textAllCaps="true"
- android:focusable="false"
+ android:focusable="true"
android:background="@drawable/all_apps_search_market_bg" />
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 1f02dce..4f54f1d 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -33,7 +33,8 @@
android:text="@string/wallpaper_button_text"
android:textAllCaps="true"
android:textColor="@android:color/white"
- android:textSize="12sp" />
+ android:textSize="12sp"
+ android:focusable="true" />
<TextView
android:id="@+id/widget_button"
@@ -47,7 +48,8 @@
android:text="@string/widget_button_text"
android:textAllCaps="true"
android:textColor="@android:color/white"
- android:textSize="12sp" />
+ android:textSize="12sp"
+ android:focusable="true" />
<TextView
android:id="@+id/settings_button"
@@ -61,6 +63,7 @@
android:text="@string/settings_button_text"
android:textAllCaps="true"
android:textColor="@android:color/white"
- android:textSize="12sp" />
+ android:textSize="12sp"
+ android:focusable="true" />
</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/user_folder_icon_normalized.xml b/res/layout/user_folder_icon_normalized.xml
new file mode 100644
index 0000000..75b5c48
--- /dev/null
+++ b/res/layout/user_folder_icon_normalized.xml
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.Folder xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/quantum_panel"
+ android:elevation="5dp"
+ android:orientation="vertical" >
+
+ <FrameLayout
+ android:id="@+id/folder_content_wrapper"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
+
+ <com.android.launcher3.FocusIndicatorView
+ android:id="@+id/focus_indicator"
+ android:layout_width="20dp"
+ android:layout_height="20dp" />
+
+ <com.android.launcher3.FolderPagedView
+ android:id="@+id/folder_content"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp"
+ android:paddingTop="8dp"
+ launcher:pageIndicator="@+id/folder_page_indicator" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:id="@+id/folder_footer"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipChildren="false"
+ android:orientation="horizontal"
+ android:paddingLeft="8dp"
+ android:paddingRight="8dp" >
+
+ <com.android.launcher3.ExtendedEditText
+ android:id="@+id/folder_name"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:background="#00000000"
+ android:fontFamily="sans-serif-condensed"
+ android:textStyle="bold"
+ android:gravity="center_horizontal"
+ android:hint="@string/folder_hint_text"
+ android:imeOptions="flagNoExtractUi"
+ android:paddingBottom="12dp"
+ android:paddingTop="4dp"
+ android:singleLine="true"
+ android:textColor="#EE777777"
+ android:textColorHighlight="#ffCCCCCC"
+ android:textColorHint="#ff808080"
+ android:textSize="14sp" />
+
+ <include
+ android:id="@+id/folder_page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="12dp"
+ android:layout_gravity="center_vertical"
+ layout="@layout/page_indicator" />
+
+ </LinearLayout>
+
+</com.android.launcher3.Folder>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 755634f..c51ec80 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -18,34 +18,36 @@
will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.widget.WidgetsContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:descendantFocusability="afterDescendants">
+ android:descendantFocusability="afterDescendants"
+ launcher:revealBackground="@drawable/quantum_panel_shape_dark">
+
+ <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:visibility="invisible" />
<FrameLayout
- android:id="@+id/content"
+ android:id="@+id/main_content"
+ android:layout_gravity="center"
+ android:elevation="15dp"
+ android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <FrameLayout
- android:id="@+id/widgets_reveal_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:focusable="false"
- android:elevation="2dp"
- android:visibility="invisible" />
- <!-- DO NOT CHANGE THE ID -->
<com.android.launcher3.widget.WidgetsRecyclerView
android:id="@+id/widgets_list_view"
android:theme="@style/Theme.Dark.CustomOverscroll"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- android:elevation="15dp"
- android:visibility="gone" />
+ android:layout_height="match_parent" />
</FrameLayout>
+
</com.android.launcher3.widget.WidgetsContainerView>
\ No newline at end of file
diff --git a/res/layout/dummy_widget.xml b/res/layout/zzz_dummy_widget.xml
similarity index 100%
rename from res/layout/dummy_widget.xml
rename to res/layout/zzz_dummy_widget.xml
diff --git a/res/layout/zzz_weight_watcher.xml b/res/layout/zzz_weight_watcher.xml
new file mode 100644
index 0000000..07fd39e
--- /dev/null
+++ b/res/layout/zzz_weight_watcher.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.android.launcher3.testing.WeightWatcher xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/res/mipmap-hdpi/ic_launcher_home.png b/res/mipmap-hdpi/ic_launcher_home.png
index b556d7a..d068d92 100644
--- a/res/mipmap-hdpi/ic_launcher_home.png
+++ b/res/mipmap-hdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_home.png b/res/mipmap-mdpi/ic_launcher_home.png
index 961bb7d..16c8ec2 100644
--- a/res/mipmap-mdpi/ic_launcher_home.png
+++ b/res/mipmap-mdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_home.png b/res/mipmap-xhdpi/ic_launcher_home.png
index 46ec2b7..8b2671b 100644
--- a/res/mipmap-xhdpi/ic_launcher_home.png
+++ b/res/mipmap-xhdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_home.png b/res/mipmap-xxhdpi/ic_launcher_home.png
index d2975a3..43d8b7d 100644
--- a/res/mipmap-xxhdpi/ic_launcher_home.png
+++ b/res/mipmap-xxhdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 78be3f4..6ea6f4a 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Werk"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Program is nie geïnstalleer nie."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Program is nie beskikbaar nie"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Afgelaaide program in veiligmodus gedeaktiveer"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Legstukke gedeaktiveer in Veiligmodus"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Wys Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Raak en hou om \'n legstuk op te tel."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en hou om \'n legstuk op te tel of gebruik gepasmaakte handelinge."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Deursoek programme"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Deursoek programme …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Laai tans programme …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Geen programme gevind wat met \"<xliff:g id="QUERY">%1$s</xliff:g>\" ooreenstem nie"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Gaan na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Laat die program toe om die instellings en kortpaaie in Tuis te lees."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"skryf Tuis-instellings en -kortpaaie"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Laat die program toe om die instellings en kortpaaie in Tuis te verander."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> word nie toegelaat om foonoproepe te maak nie"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Kon nie legstuk laai nie"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Stel op"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Naamlose vouer"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Bladsy %1$d van %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tuisskerm %1$d van %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nuwe tuisskermbladsy"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Welkom"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopieer jou program-ikone"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Voer ikone en vouers vanaf jou ou tuisskerms in?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Soek"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Hierdie program is nie geïnstalleer nie"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laai tans af, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wag tans om te installeer"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Voeg by tuisskerm"</string>
<string name="action_move_here" msgid="2170188780612570250">"Skuif item hierheen"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item is by tuisskerm gevoeg"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index b5d982e..ad9c7f9 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"ማስጀመሪያ3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"ማህደረ ማስታወሻ አሳይ"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"መተግበሪያዎችን ይፈልጉ"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"መተግበሪያዎችን ይፈልጉ…"</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="5470761048755751471">"ወደ <xliff:g id="QUERY">%1$s</xliff:g> ሂድ"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"እንኳን በደህና መጡ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"የመተግበሪያ አዶዎችዎን ይቅዱ"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"አዶዎች እና አቃፊዎች ከድሮው የመነሻ ማያ ገጾችዎ ይምጡ?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"ወደ መነሻ ማያ ገጽ ያክሉ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ንጥልን ወደዚህ ውሰድ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ወደ መነሻ ማያ ገጽ ንጥል ታክሏል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 619e11d..9d2c42b 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"عرض الذاكرة"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"البحث في التطبيقات"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"البحث في التطبيقات…"</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="5470761048755751471">"الانتقال إلى <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"مرحبًا"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"نسخ رموز التطبيقات"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"هل تريد استيراد رموز ومجلدات من الشاشات الرئيسية القديمة؟"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"إضافة إلى الشاشة الرئيسية"</string>
<string name="action_move_here" msgid="2170188780612570250">"نقل العنصر إلى هنا"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"تمت إضافة العنصر إلى الشاشة الرئيسية"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index b9ea83f..5256115 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Suvenirləri göstər"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Tətbiq Axtarın"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tətbiqləri 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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> daxil olun"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Xoş gəlmisiniz"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Tətbiq ikonalarınızı kopyalayın"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Köhnə Əsas ekranınızda olan ikonalar və qovluqlar import edilsin?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 214c64b..9c96cd8 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Показване на паметта"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Търсене в приложенията"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Търсене в приложенията…"</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="5470761048755751471">"Отваряне на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Добре дошли"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Икони на прилож. ви: Копиране"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Да се импортират ли иконите и папките от старите ви начални екрани?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Добавяне към началния екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Преместване на елемента тук"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Елементът е добавен към началния екран"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 129d250..5a81ed4 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"লঞ্চার৩"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"মেম দেখান"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> এ যান"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"স্বাগতম"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"আপনার অ্যাপ্লিকেশান আইকনগুলি অনুলিপি করুন"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"আপনার পুরানো হোম স্ক্রীন থেকে আইকন এবং ফোল্ডারগুলি আমদানি করবেন?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"হোম স্ক্রীনে যোগ করুন"</string>
<string name="action_move_here" msgid="2170188780612570250">"এখানে আইটেম সরান"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"হোম স্ক্রীনে আইটেম যোগ করা হয়েছে"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index cf874a4..05932a9 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Feina"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'aplicació no s\'ha instal·lat."</string>
<string name="activity_not_available" msgid="7456344436509528827">"L\'aplicació no està disponible."</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'aplicació que has baixat està desactivada al mode segur."</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"En Mode segur, els widgets estan desactivats."</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostra la memòria"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premut un widget per triar-lo."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Fes doble toc i mantén premut per seleccionar un widget o per utilitzar les accions personalitzades."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca a les aplicacions"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Cerca a les aplicacions…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"S\'estan carregant les aplicacions..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No s\'ha trobat cap aplicació que coincideixi amb <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Vés a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permet que l\'aplicació llegeixi la configuració i les dreceres de la pantalla d\'inici."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"escriu la configuració i les dreceres de la pantalla d\'inici"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permet que l\'aplicació canviï la configuració i les dreceres de la pantalla d\'inici."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> no té permís per fer trucades telefòniques"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"S\'ha produït un problema en carregar el widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuració"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sense nom"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Pàgina %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla d\'inici %1$d de %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Pàgina de la pantalla d\'inici nova"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Us donem la benvinguda"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiar les icones d\'aplicació"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importar icones i carpetes de pantalles d\'inici anteriors?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Aquesta aplicació no està instal·lada"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"S\'està baixant <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> completat"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"S\'està esperant per instal·lar <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Afegeix a la pantalla d\'inici"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mou l\'element aquí"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"S\'ha afegit l\'element a la pantalla d\'inici"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index c2f9f42..373ae83 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Práce"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikace není nainstalována."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikace není k dispozici."</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Stažená aplikace je v nouzovém režimu zakázána"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"V nouzovém režimu jsou widgety zakázány."</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Zobrazit Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget vyberete dotykem a podržením."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dvojitým klepnutím a podržením vyberte widget, případně použijte vlastní akce."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hledat aplikace"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hledat v aplikacích…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Načítání aplikací…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Dotazu „<xliff:g id="QUERY">%1$s</xliff:g>“ neodpovídají žádné aplikace"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Přejít na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Umožňuje aplikaci číst nastavení a odkazy na ploše."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"zápis nastavení a odkazů plochy"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Umožňuje aplikaci změnit nastavení a odkazy na ploše."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> nemá oprávnění telefonovat"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problém s načtením widgetu"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Nastavení"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Složka bez názvu"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Strana %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Vítejte"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Zkopírování ikon aplikací"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Chcete importovat ikony a složky ze svých starých ploch?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Hledat"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Tato aplikace není nainstalována"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Stahování aplikace <xliff:g id="NAME">%1$s</xliff:g> (dokončeno <xliff:g id="PROGRESS">%2$s</xliff:g>)"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Instalace aplikace <xliff:g id="NAME">%1$s</xliff:g> čeká na zahájení"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Přidat na plochu"</string>
<string name="action_move_here" msgid="2170188780612570250">"Přesunout položku sem"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Položka byla přidána na plochu"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index a948705..3654b72 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Arbejde"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installeret."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgængelig"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloadet app er deaktiveret i sikker tilstand"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets er deaktiveret i Beskyttet tilstand"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Vis Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Tryk på en widget, og hold den nede for at vælge."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryk to gange, og hold fingeren nede for at vælge en widget eller bruge tilpassede handlinger."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søg i Apps"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Søg i Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Indlæser apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Der blev ikke fundet nogen apps, som matcher \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå til <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Tillader, at appen læser indstillingerne og genvejene på startskærmen."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"skrive indstillinger og genveje for startskærmen"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Tillader, at appen ændrer indstillingerne og genvejene på startskærmen."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> har ikke tilladelse til at foretage telefonopkald"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Der er problemer med indlæsning af widgetten"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurer"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unavngiven mappe"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d ud af %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskærm %1$d ud af %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Ny startskærm"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Velkommen"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiér dine appikoner"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Vil du importere ikoner og mapper fra gamle startskærme?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Søg"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Denne app er ikke installeret"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloades. <xliff:g id="PROGRESS">%2$s</xliff:g> er gennemført"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> venter på at installere"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Føj til startskærm"</string>
<string name="action_move_here" msgid="2170188780612570250">"Flyt elementet hertil"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Elementet er føjet til startskærmen"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 96ae74b..a375ecd 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Arbeit"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App ist nicht installiert."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App nicht verfügbar"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Heruntergeladene App im abgesicherten Modus deaktiviert"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets im abgesicherten Modus deaktiviert"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Speicher anzeigen"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Zum Hinzufügen Widget berühren und halten"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Zum Hinzufügen auf Widget doppeltippen und gedrückt halten oder benutzerdefinierte Aktionen verwenden."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"In Apps suchen"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Apps suchen…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Apps werden geladen..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Keine Apps für \"<xliff:g id="QUERY">%1$s</xliff:g>\" gefunden"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Gehe zu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,19 +46,21 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu lesen"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"Einstellungen und Verknüpfungen für den Startbildschirm schreiben"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu ändern"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> darf keine Telefonanrufe tätigen."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem beim Laden des Widgets"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Einrichten"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unbenannter Ordner"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Seite %1$d von %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startbildschirm %1$d von %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Neue Startbildschirmseite"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Hallo!"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"App-Symbole kopieren"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Symbole und Ordner alter Startbildschirme importieren?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"Symbole kopieren"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"Standardübersicht verwenden"</string>
<string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Hintergründe, Widgets & Einstellungen"</string>
- <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Berühren und halten Sie den Hintergrund, um ihn anzupassen."</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Berühre und halte den Hintergrund, um ihn anzupassen."</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_opened" msgid="94695026776264709">"Ordner geöffnet, <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="1884479294466410023">"Ordner durch Berühren schließen"</string>
@@ -74,7 +76,9 @@
<string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
<string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Diese App ist nicht installiert"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die App für dieses Symbol ist nicht installiert. Sie können das Symbol entfernen oder nach der App suchen und sie manuell installieren."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die App für dieses Symbol ist nicht installiert. Du kannst das Symbol entfernen oder die App lokalisieren und dann manuell installieren."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wird heruntergeladen, <xliff:g id="PROGRESS">%2$s</xliff:g> abgeschlossen"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Warten auf Installation von <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Zum Startbildschirm hinzufügen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Element hierhin verschieben"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Element zum Startbildschirm hinzugefügt"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index 81b43e2..12a3157 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Εμφάνιση Mem"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Αναζήτηση εφαρμογών"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Αναζήτηση εφαρμογών…"</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="5470761048755751471">"Μετάβαση σε <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Καλώς ορίσατε"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Αντιγραφή εικονιδίων εφαρμογών"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Εισαγωγή εικονιδίων και φακέλων από παλιές αρχικές οθόνες;"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Προσθήκη στην αρχική οθόνη"</string>
<string name="action_move_here" msgid="2170188780612570250">"Μετακίνηση στοιχείου εδώ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Το στοιχείο προστέθηκε στην αρχική οθόνη"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index f2a6eab..f8873a5 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Show Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised actions."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not allowed to make phone calls"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Welcome"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copy your app icons"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Search"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index f2a6eab..f8873a5 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Show Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised actions."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not allowed to make phone calls"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Welcome"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copy your app icons"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Search"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index f2a6eab..f8873a5 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App isn\'t available"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets disabled in Safe mode"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Show Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Touch & hold to pick up a widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Double-tap & hold to pick up a widget or use customised actions."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Search Apps"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Search Apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Loading Apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No Apps found matching \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Go to <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Allows the app to read the settings and shortcuts in Home."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> is not allowed to make phone calls"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d of %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d of %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"New home screen page"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Welcome"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copy your app icons"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Import icons and folders from your old Home screens?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Search"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> downloading, <xliff:g id="PROGRESS">%2$s</xliff:g> complete"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> waiting to install"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Add to Home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Move item here"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item added to home screen"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index c754493..cc58b46 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabajo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"No se instaló la aplicación."</string>
<string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible."</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén presionado el widget que desees elegir."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Presiona dos veces y mantén presionado para elegir un widget o usa una acción personalizada."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Buscar aplicaciones"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar apps…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No hay aplicaciones que coincidan con <xliff:g id="QUERY">%1$s</xliff:g>."</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permite que la aplicación lea la configuración y los accesos directos de la pantalla principal."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"escribir configuración y accesos directos de la pantalla principal"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que la aplicación cambie la configuración y los accesos directos de la pantalla principal."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> no puede realizar llamadas telefónicas"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema al cargar el widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta es una aplicación del sistema y no se puede desinstalar."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla principal %1$d de %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nueva página en la pantalla principal"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bienvenido"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiar íconos de aplicaciones"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"¿Importar íconos y carpetas de pant. principales antiguas?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Se completó el <xliff:g id="PROGRESS">%2$s</xliff:g> de la descarga de <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Instalación de <xliff:g id="NAME">%1$s</xliff:g> en espera"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Agregar a la pantalla principal"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Se agregó el elemento a la pantalla principal."</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 16cd84f..c9c6854 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabajo"</string>
<string name="activity_not_found" msgid="8071924732094499514">"La aplicación no está instalada."</string>
<string name="activity_not_available" msgid="7456344436509528827">"La aplicación no está disponible"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets inhabilitados en modo seguro"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén pulsado el widget que quieras seleccionar."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toca dos veces y mantén pulsado el widget que quieras seleccionar o utiliza acciones personalizadas."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Busca aplicaciones"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Buscar aplicaciones…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicaciones…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"No se han encontrado aplicaciones que contengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permite que la aplicación consulte los ajustes y los accesos directos de la pantalla de inicio."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"escribir información de accesos directos y de ajustes de la pantalla de inicio"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que las aplicaciones cambien los ajustes y los accesos directos de la pantalla de inicio."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> no puede hacer llamadas"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema al cargar el widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación es del sistema y no se puede desinstalar."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nueva página de pantalla de inicio"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Te damos la bienvenida"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiar iconos de aplicaciones"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"¿Importar iconos y carpetas de pantallas de inicio antiguas?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla 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="action_add_to_workspace" msgid="8902165848117513641">"Añadir a la pantalla de inicio"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover elemento aquí"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento añadido a la pantalla de inicio"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 84ca45a..053791d 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</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="toggle_weight_watcher" msgid="5645299835184636119">"Mälu kuvamine"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Otsige rakendustest"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Otsimine 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="5470761048755751471">"Mine: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Tere tulemast"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopeerige rakenduste ikoonid"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Kas importida vanade avaekraanide ikoonid ja kaustad?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 096a4f9..c85466c 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Abiarazlea3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Erakutsi memoria"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Eduki ukituta 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="all_apps_search_bar_hint" msgid="7084713969757597256">"Bilatu aplikazioetan"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"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="5470761048755751471">"Joan hona: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Ongi etorri"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiatu aplikazioen ikonoak"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Ikonoak eta karpetak aurreko hasierako pantailatik inportatu?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 3b6d01a..85a379b 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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_shortcut_error" msgid="9160126848219158407">"برنامه بارگیری شده در حالت ایمن غیرفعال شد"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"ابزارکها در حالت ایمن غیرفعال هستند"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"نمایش Mem"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"جستجوی برنامهها"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"جستجوی برنامهها…"</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="5470761048755751471">"رفتن به <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"خوش آمدید"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"کپی کردن نمادهای برنامه شما"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"نمادها و پوشهها از صفحههای اصلی قدیمی شما وارد شوند؟"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"افزودن به صفحه اصلی"</string>
<string name="action_move_here" msgid="2170188780612570250">"انتقال مورد به اینجا"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"مورد به صفحه اصلی اضافه شد"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 47b580e..c27310b 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Työ"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Sovellusta ei ole asennettu."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Sovellus ei ole käytettävissä"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Ladattu sovellus poistettiin käytöstä suojatussa tilassa"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgetit poistettu käytöstä vikasietotilassa"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Näytä muisti"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Valitse widget painamalla sitä pitkään."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Valitse widget tai käytä muokattuja toimintoja kaksoisnapauttamalla ja painamalla kohdetta pitkään."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sovellushaku"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Hae sovelluksia…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Ladataan sovelluksia…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"”<xliff:g id="QUERY">%1$s</xliff:g>” ei palauttanut sovelluksia."</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Siirry: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -37,8 +37,8 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Sovellukset"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Aloitusruutu"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Poista"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Poista"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Poista kuvake"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Poista asennus"</string>
<string name="info_target_label" msgid="8053346143994679532">"Sovelluksen tiedot"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"asenna pikakuvakkeita"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Antaa sovelluksen lisätä pikakuvakkeita itsenäisesti ilman käyttäjän valintaa."</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Antaa sovelluksen lukea aloitusruudun asetukset ja pikakuvakkeet."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"kirjoita aloitusruudun asetuksia ja pikakuvakkeita"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Antaa sovelluksen muuttaa aloitusruudun asetuksia ja pikakuvakkeita."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ei saa soittaa puheluita."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Ongelma ladattaessa widgetiä"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Asetus"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tämä on järjestelmäsovellus, eikä sitä voi poistaa."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Nimetön kansio"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Sivu %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Aloitusruutu %1$d/%2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Uusi aloitusnäytön sivu"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Tervetuloa"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopioi sovelluskuvakkeet"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Tuodaanko kuvakkeet ja kansiot vanhoista aloitusruuduista?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Haku"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Sovellusta ei ole asennettu"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> latautuu, valmiina <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> odottaa asennusta"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Lisää aloitusnäytölle"</string>
<string name="action_move_here" msgid="2170188780612570250">"Siirrä kohde tänne"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Kohde lisättiin aloitusnäytölle."</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 91271f7..bb0f1a0 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Lanceur3"</string>
+ <string name="app_name" msgid="649227358658669779">"Lanceur3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Travail"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets désactivés en mode sans échec"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Afficher la mémoire"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Maintenez un doigt sur le widget pour l\'ajouter."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Touchez 2x un widget et maintenez doigt dessus pour l’ajouter ou utiliser des actions personnalisées"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher des applications"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Rechercher des applications..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application trouvée correspondant à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Aller à « <xliff:g id="QUERY">%1$s</xliff:g> »"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permet à l\'application de lire les paramètres et les raccourcis de l\'écran d\'accueil."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"enregistrer les paramètres de la page d\'accueil et des raccourcis"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permet à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas autorisée à faire des appels téléphoniques"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problème lors du chargement du widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bienvenue"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copier les icônes de vos applis"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importer les icônes et dossiers des anciens écrans d\'accueil?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Cette application n\'est pas installée"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Téléchargement de <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> en attente d\'installation"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Ajouter à l\'écran d\'accueil"</string>
<string name="action_move_here" msgid="2170188780612570250">"Déplacer l\'élément ici"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Élément ajouté à l\'écran d\'accueil"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 84e1594..f189c63 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Android Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Application indisponible"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Les widgets sont désactivés en mode sécurisé."</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Afficher la mémoire"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"App. de manière prolongée pour sélectionner widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Appuyez 2 fois et maintenez la pression pour sélectionner widget ou utilisez actions personnalisées."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d x %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher dans les applications"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Rechercher des applications…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Chargement des applications en cours…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Aucune application ne correspond à la requête \"<xliff:g id="QUERY">%1$s</xliff:g>\"."</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Accéder à <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permettre à l\'application de lire les paramètres et les raccourcis de l\'écran d\'accueil"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"modifier les paramètres et les raccourcis de l\'écran d\'accueil"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permettre à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil"</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"L\'application <xliff:g id="APP_NAME">%1$s</xliff:g> n\'est pas autorisée à passer des appels téléphoniques."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problème lors du chargement du widget."</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Page %1$d sur %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Écran d\'accueil %1$d sur %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nouvelle page d\'écran d\'accueil"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bienvenue"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copier les icônes de vos applis"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importer les icônes et dossiers des anciens écrans d\'accueil ?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Cette application n\'est pas installée"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> en cours de téléchargement, <xliff:g id="PROGRESS">%2$s</xliff:g> effectué(s)"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> en attente d\'installation"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Ajouter à l\'écran d\'accueil"</string>
<string name="action_move_here" msgid="2170188780612570250">"Déplacer l\'élément ici"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"L\'élément a bien été ajouté à l\'écran d\'accueil."</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index ccfe21e..ad25e84 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Aplicacións de busca"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"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="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Dámosche a benvida"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiar iconas das aplicacións"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Queres importar as iconas e os cartafoles doutras pantallas de inicio?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index db056a0..1cd6768 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mem બતાવો"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"શોધ એપ્લિકેશનો"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ઍપ્લિકેશનોમાં શોધો…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> પર જાઓ"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"સ્વાગત છે"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"તમારા એપ્લિકેશન આયકન્સને કૉપિ કરો"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"તમારી જૂની હોમ સ્ક્રીન્સથી આયકન્સ અને ફોલ્ડર્સને આયાત કરીએ?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"હોમ સ્ક્રીન પર ઉમેરો"</string>
<string name="action_move_here" msgid="2170188780612570250">"આઇટમ અહીં ખસેડો"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"હોમ સ્ક્રીનમાં આઇટમ ઉમેરી"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 7cf4f33..d2de54c 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"मेमोरी दिखाएं"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ऐप्स खोजें"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ऐप्स खोजें…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> पर जाएं"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"स्वागत है"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ऐप्स आइकन की प्रतिलिपि बनाएं"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"अपनी पुरानी होम स्क्रीन से आइकन और फ़ोल्डर आयात करें?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"होम स्क्रीन में जोड़ें"</string>
<string name="action_move_here" msgid="2170188780612570250">"आइटम यहां ले जाएं"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"होम स्क्रीन में आइटम जोड़ा गया"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 2679570..427e5c8 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Pokretač3"</string>
+ <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 onemogućena je u Sigurnom načinu rada"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgeti su onemogućeni u Sigurnom načinu rada"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Prikaži mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Dodirnite i držite kako biste podigli widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dodirnite dvaput i držite kako biste podigli widget ili pokušajte prilagođenim radnjama."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pretraživanje aplikacija…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Učitavanje aplikacija…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nema aplikacija podudarnih s upitom \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Idite na <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Aplikaciji omogućuje čitanje postavki i prečaca na početnom zaslonu."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"pisanje postavki početnog zaslona i prečaca"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Aplikaciji omogućuje promjenu postavki i prečaca na početnom zaslonu."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nema dopuštenje za telefonske pozive"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju widgeta"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je aplikacija sustava i ne može se ukloniti."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Stranica %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Početni zaslon %1$d od %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nova stranica početnog zaslona"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Dobro došli"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiranje ikona aplikacija"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Želite li uvesti ikone i mape sa starih početnih zaslona?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacija nije instalirana"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Preuzimanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>, dovršeno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Čekanje na instaliranje aplikacije <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Dodavanje na početni zaslon"</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 zaslon"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 41c38b2..b9a5b61 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Munka"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Az alkalmazás nincs telepítve."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Az alkalmazás nem érhető el"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"A letöltött alkalmazás Csökkentett módban ki van kapcsolva"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"A modulok ki vannak kapcsolva Csökkentett módban"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem. megjelenítése"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Modul felvételéhez érintse meg, és tartsa lenyomva"</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Modul mozgatásához koppintson rá duplán és tartsa lenyomva, vagy használjon egyéni műveleteket."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Alkalmazások keresése"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Alkalmazások keresése…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Alkalmazások betöltése…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Egy alkalmazás sem található a(z) „<xliff:g id="QUERY">%1$s</xliff:g>” lekérdezésre."</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Keresse fel ezt: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -38,7 +38,7 @@
<string name="all_apps_button_label" msgid="9110807029020582876">"Alkalmazások"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Főoldal"</string>
<string name="delete_target_label" msgid="1822697352535677073">"Eltávolítás"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Eltávolítás"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Törlés"</string>
<string name="info_target_label" msgid="8053346143994679532">"Alkalmazásinformáció"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"parancsikonok telepítése"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Lehetővé teszi egy alkalmazás számára, hogy felhasználói beavatkozás nélkül adjon hozzá parancsikonokat."</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Lehetővé teszi az alkalmazás számára, hogy beolvassa a kezdőképernyő beállításait és parancsikonjait."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"Főoldal beállításainak és parancsikonjainak írása"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a kezdőképernyő beállításait és parancsikonjait."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> nem kezdeményezhet telefonhívásokat"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Probléma történt a modul betöltésekor"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Beállítás"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ez egy rendszeralkalmazás, és nem lehet eltávolítani."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Névtelen mappa"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"%2$d/%1$d. oldal"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d/%1$d. kezdőképernyő"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Új kezdőképernyő oldal"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Üdvözöljük!"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Alkalmazásikonok másolása"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importálja ikonjait és mappáit régi kezdőképernyőiről?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Keresés"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Az alkalmazás nincs telepítve"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"A(z) <xliff:g id="NAME">%1$s</xliff:g> letöltése, <xliff:g id="PROGRESS">%2$s</xliff:g> kész"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"A(z) <xliff:g id="NAME">%1$s</xliff:g> telepítésre vár"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Hozzáadás a kezdőképernyőhöz"</string>
<string name="action_move_here" msgid="2170188780612570250">"Elem áthelyezése ide"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Elem hozzáadva a kezdőképernyőhöz"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 49a9d18..74d98d6 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Ցուցադրել մեմը"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Հավելվածների որոնում"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Հավելվածների որոնում…"</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="5470761048755751471">"Գնալ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Բարի գալուստ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Պատճենել ձեր ծրագրի պատկերակները"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Ներմուծե՞լ պատկերակները և պանակները ձեր նախկին Հիմնական էկրանից"</string>
@@ -65,7 +67,7 @@
<string name="folder_tap_to_rename" msgid="9191075570492871147">"Հպեք՝ վերանվանումը պահելու համար"</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="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>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Ավելացնել Հիմնական էկրանին"</string>
<string name="action_move_here" msgid="2170188780612570250">"Տեղափոխել տարրն այստեղ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Տարրն ավելացվեց հիմնական էկրանին"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 21c2080..1dda24b 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kantor"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikasi tidak dipasang."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikasi tidak tersedia"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikasi yang diunduh dinonaktifkan dalam mode Aman"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widget dinonaktifkan dalam mode Aman"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Tampilkan Memori"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Sentuh lama untuk memilih widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Ketuk dua kalip & tahan untuk mengambil widget atau menggunakan tindakan khusus."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Telusuri Apps"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Telusuri Aplikasi..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Memuat Aplikasi..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Tidak ditemukan Aplikasi yang cocok dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Buka <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Mengizinkan aplikasi membaca setelan dan pintasan di layar Utama."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"menulis setelan dan pintasan layar Utama"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Mengizinkan aplikasi mengubah setelan dan pintasan di layar Utama."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> tidak diizinkan untuk melakukan panggilan telepon"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuat widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Siapkan"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Laman %1$d dari %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Layar utama %1$d dari %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Laman layar utama baru"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Selamat Datang"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Salin ikon aplikasi Anda"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Impor ikon dan folder dari layar Utama lama Anda?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Telusuri"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikasi ini belum terpasang"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> sedang diunduh, <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 dipasang"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Tambahkan ke layar Utama"</string>
<string name="action_move_here" msgid="2170188780612570250">"Pindahkan item ke sini"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item ditambahkan ke layar utama"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 7422eaf..fd6977c 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Sýna minni"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Leita í forritum"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"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="5470761048755751471">"Fara í <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Komdu fagnandi"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Afritaðu forritatáknin þín"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Flytja inn tákn og möppur af eldri heimaskjáum?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index e1e75b3..a01bc9d 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Lavoro"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App non installata."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App non disponibile"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'app scaricata è stata disattivata in modalità provvisoria"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widget disabilitati in modalità provvisoria"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostra Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Tocca e tieni premuto per scegliere un widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tocca due volte e tieni premuto per scegliere un widget o per utilizzare azioni personalizzate."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca app"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ricerca app…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Caricamento di app…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nessuna app trovata corrispondente a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Vai a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Consente all\'app di leggere le impostazioni e le scorciatoie in Home."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"creazione di impostazioni e scorciatoie in Home"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Consente all\'app di modificare le impostazioni e le scorciatoie in Home."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"L\'app <xliff:g id="APP_NAME">%1$s</xliff:g> non è autorizzata a effettuare telefonate"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Errore durante il caricamento del widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configurazione"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Cartella senza nome"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d di %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Schermata Home %1$d di %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nuova pagina Schermata Home"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Benvenuto"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copia le icone delle tue app"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importare icone e cartelle dalle schermate Home precedenti?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"L\'app non è installata"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Download di <xliff:g id="NAME">%1$s</xliff:g> in corso, <xliff:g id="PROGRESS">%2$s</xliff:g> completato"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> in attesa di installazione"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Aggiungi a schermata Home"</string>
<string name="action_move_here" msgid="2170188780612570250">"Sposta elemento qui"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Elemento aggiunto alla schermata Home"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 3686abd..137800e 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"הצג זכרון"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"חפש אפליקציות"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"חיפוש אפליקציות..."</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="5470761048755751471">"עבור אל <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"ברוכים הבאים"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"העתקת סמלי האפליקציות שלך"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"האם לייבא סמלים ותיקיות ממסכי דף הבית הישנים שלך?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"הוסף למסך דף הבית"</string>
<string name="action_move_here" msgid="2170188780612570250">"העבר את הפריט לכאן"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"הפריט הועבר אל מסך דף הבית"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 87b3fa4..e3c3194 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"メモリーを表示"</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$dx%2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"アプリを検索"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"アプリを検索…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>にアクセス"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"ようこそ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"アプリのアイコンをコピー"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"古いホーム画面からアイコンとフォルダをインポートしますか?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"ホーム画面に追加"</string>
<string name="action_move_here" msgid="2170188780612570250">"アイテムをここに移動"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"アイテムをホーム画面に追加しました"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 2900330..288f0ba 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mem-ის ჩვენება"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"აპების ძიება"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"აპების ძიება…"</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="5470761048755751471">"გადადი <xliff:g id="QUERY">%1$s</xliff:g>-ში"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"მოგესალმებით"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"თქვენი აპის ხატულების კოპირება"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"გსურთ, ძველი მთავარი ეკრანიდან ხატულების და საქაღ. იმპორტი?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"მთავარ ეკრანზე დამატება"</string>
<string name="action_move_here" msgid="2170188780612570250">"ერთეულის გადაადგილება აქ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ერთეული დაემატა მთავარ ეკრანს"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index cae5e5e..b633790 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Жадты көрсету"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Қолданбаларды іздеу"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Қолданбаларды іздеу…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сұрауына өту"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Қош келдіңіз"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Қолданба таңбаларын көшіру"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Бұрынғы негізгі экрандарыңыздағы таңбалар мен қалталар импортталсын ба?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Негізгі экранға қосу"</string>
<string name="action_move_here" msgid="2170188780612570250">"Элементті мұнда жылжыту"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент негізгі экранға қосылды"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 5ffbf76..3cf2599 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"បង្ហាញ Mem"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ស្វែងរកកម្មវិធី"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ស្វែងរកកម្មវិធី…"</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="5470761048755751471">"ចូលទៅកាន់ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"សូមស្វាគមន៍"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ចម្លងរូបតំណាងកម្មវិធីរបស់អ្នក"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"នាំចូលរូបតំណាង និងថតពីអេក្រង់ដើមចាស់របស់អ្នក?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"បន្ថែមទៅអេក្រង់ដើម"</string>
<string name="action_move_here" msgid="2170188780612570250">"ផ្លាស់ធាតុមកទីនេះ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ធាតុដែលត្រូវបានបន្ថែមទៅអេក្រង់ដើម"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index a1aab7b..8766d71 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"ಲಾಂಚರ್3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"ಸ್ಮರಣೆ ತೋರಿಸು"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ಅಪ್ಲಿಕೇಷನ್ಗಳನ್ನು ಹುಡುಕಿ"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ಅಪ್ಲಿಕೇಷನ್ಗಳನ್ನು ಹುಡುಕಿ..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ಗೆ ಹೋಗಿ"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"ಸುಸ್ವಾಗತ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ಅಪ್ಲಿಕೇಶನ್ ಐಕಾನ್ ನಕಲಿಸು"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"ನಿಮ್ಮ ಹಳೆಯ ಮುಖಪುಟದ ಪರದೆಗಳಿಂದ ಐಕಾನ್ಗಳು ಮತ್ತು ಫೋಲ್ಡರ್ಗಳನ್ನು ಆಮದು ಮಾಡಿಕೊಳ್ಳುವುದೇ?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು"</string>
<string name="action_move_here" msgid="2170188780612570250">"ಐಟಂ ಇಲ್ಲಿಗೆ ಸರಿಸಿ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ಮುಖಪುಟ ಪರದೆಗೆ ಐಟಂ ಸೇರಿಸಲಾಗಿದೆ"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e069712..be5b506 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"메모리 표시"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"앱 검색"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"앱 검색..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>(으)로 이동"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"환영합니다."</string>
<string name="migration_cling_title" msgid="9181776667882933767">"앱 아이콘 복사"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"이전 메인 스크린에서 아이콘과 폴더를 가져오시겠습니까?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"메인 스크린에 추가"</string>
<string name="action_move_here" msgid="2170188780612570250">"여기에 항목을 이동"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"메인 스크린에 항목 추가됨"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index c530b5c..0d7cb48 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Мемди көргөзүү"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Колдонмолорду издөө"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Колдонмолорду издөө…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сурамына өтүңүз"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Кош келиңиз"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Колдонмоңуздун сүрөтчөлөрүн көчүрүү"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Эски үй экрандарыңыздан сүрөтчөлөр жана фолдерлер импорттолсунбу?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Башкы экранга кошуу"</string>
<string name="action_move_here" msgid="2170188780612570250">"Бул нерсени бул жерге жылдыруу"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Башкы экранга кошулду"</string>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 06a9984..fa36231 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -18,4 +18,6 @@
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">8dip</dimen>
<dimen name="toolbar_button_horizontal_padding">0dip</dimen>
+ <!-- Container -->
+ <item name="container_margin" format="fraction" type="fraction">12%</item>
</resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 005f026..8ed9c26 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"ສະແດງຄວາມຈຳ"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ຊອກຫາແອັບ"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ຄົ້ນຫາແອັບ"</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="5470761048755751471">"ໄປທີ່ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"ຍິນດີຕ້ອນຮັບ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ສຳເນົາໄອຄອນແອັບຯຂອງທ່ານ"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"ນຳເຂົ້າໄອຄອນ ແລະ ໂຟນເດີຈາກໂຮມສະກຣີນອັນເກົ່າຂອງທ່ານບໍ່?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 442874d..f20751a 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Darbas"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Programa neįdiegta."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Programa nepasiekiama"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Atsisiųsta programa išjungta Saugos režimu"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Valdikliai išjungti Saugiame režime"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Rodyti atmintinę"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Palieskite ir laikykite, kad pasirinkt. valdiklį."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dukart palieskite ir laikykite, kad pasirinktumėte valdiklį ar naudotumėte tinkintus veiksmus."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Ieškoti programų"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ieškoti programų..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Įkeliamos programos..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nerasta jokių užklausą „<xliff:g id="QUERY">%1$s</xliff:g>“ atitinkančių programų"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Eiti į <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -37,7 +37,7 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programos"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Pagrindinis"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Pašalinti"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Ištrinti"</string>
<string name="delete_target_uninstall_label" msgid="5100785476250872595">"Pašalinti"</string>
<string name="info_target_label" msgid="8053346143994679532">"Programos informacija"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"įdiegti sparčiuosius klavišus"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Programai leidžiama skaityti pagrindinio puslapio nustatymus ir sparčiuosius klavišus."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"rašyti pagrindinio puslapio nustatymus ir sparčiuosius klavišus"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Programai leidžiama keisti pagrindinio puslapio nustatymus ir sparčiuosius klavišus."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ neleidžiama skambinti"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema įkeliant valdiklį"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Sąranka"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Aplankas be pavadinimo"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d psl. iš %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d pagrindinis ekranas iš %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Naujas pagrindinio ekrano puslapis"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Sveiki"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Programų piktogramų kopij."</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importuoti piktogramas ir aplankus iš senų pagr. ekranų?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Ieškoti"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Ši programa neįdiegta"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Atsisiunčiama programa „<xliff:g id="NAME">%1$s</xliff:g>“, <xliff:g id="PROGRESS">%2$s</xliff:g> baigta"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Laukiama, kol bus įdiegta programa „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Pridėti prie pagrind. ekrano"</string>
<string name="action_move_here" msgid="2170188780612570250">"Perkelti elementą čia"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Elementas pridėtas prie pagrindinio ekrano"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 1bef22e..7e76374 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Darbs"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Lietotne nav instalēta."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Lietotne nav pieejama."</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Lejupielādētā lietotne ir atspējota drošajā režīmā."</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Logrīki atspējoti drošajā režīmā"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Rādīt atmiņu"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Lai izvēlētos logrīku, pieskarieties un turiet to."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Lai atlasītu logrīku, veiciet dubultskārienu uz tā un turiet to vai arī veiciet pielāgotas darbības."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Meklēt lietotnes"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Meklēt lietotnes…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Notiek lietotņu ielāde…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Vaicājumam “<xliff:g id="QUERY">%1$s</xliff:g>” neatbilda neviena lietotne."</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Doties uz: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Ļauj lietotnei lasīt iestatījumus un saīsnes sākuma ekrānā."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"rakstīt sākuma ekrāna iestatījumus un saīsnes"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ļauj lietotnei mainīt iestatījumus un saīsnes sākuma ekrānā."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"Lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g> nav atļauts veikt tālruņa zvanus."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Ielādējot logrīku, radās problēma."</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Notiek iestatīšana"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Mape bez nosaukuma"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"%1$d. lapa no %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Sākuma ekrāns: %1$d no %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Jauna sākuma ekrāna lapa"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Laipni lūdzam!"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Lietotņu ikonu kopēšana"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Vai importēt ikonas, mapes no iepriekšējiem sākuma ekrāniem?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Meklēt"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Šī lietotne nav instalēta"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Lietotnes <xliff:g id="NAME">%1$s</xliff:g> lejupielāde (<xliff:g id="PROGRESS">%2$s</xliff:g> pabeigti)"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Notiek <xliff:g id="NAME">%1$s</xliff:g> instalēšana"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Pievienot sākuma ekrānam"</string>
<string name="action_move_here" msgid="2170188780612570250">"Pārvietot vienumu šeit"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Vienums pievienots sākuma ekrānam"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 0a6704b..73a9d95 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Прикажи „Мени“"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Пребарување апликации"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пребарувај апликации…"</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="5470761048755751471">"Оди на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Добредојдовте"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Копирај икони за апликација"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Зачувај икони и папки од твоите стари почетни страни?"</string>
@@ -68,13 +70,15 @@
<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="8119458837558863227">"Поставки"</string>
<string name="allow_rotation_title" msgid="2118706734511831751">"Дозволи ротација"</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="action_add_to_workspace" msgid="8902165848117513641">"Додај на Почетен екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Премести ја ставката овде"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Ставката е додадена на почетниот екран"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 2bd80d3..1169be2 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"മെമ്മറി കാണിക്കുക"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പ്സ് തിരയുക"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ആപ്പ്സ് തിരയുക…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> എന്നതിലേക്ക് പോവുക"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"സ്വാഗതം"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"നിങ്ങളുടെ അപ്ലിക്കേഷൻ ഐക്കണുകൾ പകർത്തുക"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"നിങ്ങളുടെ പഴയ ഹോം സ്ക്രീനുകളിൽ നിന്ന് ഐക്കണുകളും ഫോൾഡറുകളും ഇമ്പോർട്ടുചെയ്യണോ?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"ഹോം സ്ക്രീനിൽ ചേർക്കുക"</string>
<string name="action_move_here" msgid="2170188780612570250">"ഇനം ഇവിടേക്ക് നീക്കുക"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ഹോം സ്ക്രീനിൽ ഇനം ചേർത്തു"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 19ba66f..46f46fa 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Мем харуулах"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Апп хайх"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Апп хайх..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> руу очих"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Тавтай морилно уу"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Таны апп дүрсүүдийг хуулах"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Таны хуучин Үндсэн дэлгэц дээрх дүрсүүдийг импорт хийх үү?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Нүүр дэлгэц нэмэх"</string>
<string name="action_move_here" msgid="2170188780612570250">"Энд байршуулах"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Нүүр дэлгэцэнд нэмсэн зүйл"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index dc29b54..5d29197 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mem दर्शवा"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"अॅप्स शोधा"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"अॅप्स शोधा..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> वर जा"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"सुस्वागतम"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"आपली अॅप चिन्हे कॉपी करा"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"आपल्या जुन्या मुख्य स्क्रीनवरून चिन्हे आणि फोल्डर आयात करायची?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"मुख्य स्क्रीनवर जोडा"</string>
<string name="action_move_here" msgid="2170188780612570250">"आयटम येथे हलवा"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"आयटम मुख्य स्क्रीनवर जोडला"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 26f2748..5da4331 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</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="toggle_weight_watcher" msgid="5645299835184636119">"Papar Mem"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Cari Apl"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"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="5470761048755751471">"Pergi ke <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Selamat datang"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Salin ikon apl anda"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Import ikon dan folder dari skrin Laman Utama lama anda?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index c13ff7e..0264c98 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher၃"</string>
+ <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">"App လက်လှမ်း မမှီပါ"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"လုံခြုံရေး မုဒ်ထဲမှာ ဝီဂျက်များကို ပိတ်ထား"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem ကိုပြရန်"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ရှာဖွေမှု Appများ"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"အက်ပ်များကို ရှာဖွေပါ…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"App များ ရယူနေစဉ်..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>ကို သွားပါ"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"မင်္ဂလာပါ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"အပ်ပလီကေးရှင်းပုံညွှန်းများကို ကူးယူပါ"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"ပင်မစာမျက်နှာအဟောင်းမှ ပုံညွှန်းများ နှင့် အကန့်များကို ယူလာပါမလား"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"ရှာဖွေရန်"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"App မတပ်ဆင်ရသေးပါ"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> ဒေါင်းလုဒ်လုပ်နေသည်၊ <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="action_add_to_workspace" msgid="8902165848117513641">"ပင်မမျက်နှာစာသို့ ထည့်ပါ"</string>
<string name="action_move_here" msgid="2170188780612570250">"၎င်းအား ဤသို့ ရွှေ့ပါ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ပင်မ ဖန်မျက်နှာပြင်သို့ ထည့်ပြီး၏"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 198d56c..7b5f3b6 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Jobb"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installert."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen er ikke tilgjengelig"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"En nedlastet app er deaktivert i sikker modus"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Moduler er deaktivert i sikker modus"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Vis minne"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Trykk og hold inne for å plukke opp en modul."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dobbelttrykk og hold inne for å velge en modul eller bruke tilpassede handlinger."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Søk i apper"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Søk i apper"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Laster inn apper …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Fant ingen apper som samsvarer med «<xliff:g id="QUERY">%1$s</xliff:g>»"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå til <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Lar appen lese innstillingene og snarveiene på startsiden."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"angi startsideinnstillinger og -snarveier"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Lar appen endre innstillingene og snarveiene på startsiden."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> har ikke tillatelse til å ringe"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem ved innlasting av modul"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurering"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Mappe uten navn"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Side %1$d av %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startside %1$d av %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Ny side på startskjermen"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Velkommen"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiér appikonene dine"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Vil du importere ikoner og mapper fra dine gamle startsider?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Søk"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Denne appen er ikke installert"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Laster ned <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> er fullført"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Venter på å installere <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Legg til på startskjermen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Flytt elementet hit"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Elementet er lagt til på startskjermen"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index 472bd4f..2abf005 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mem देखाउनुहोस्"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"अनुप्रयोगहरू खोज्नुहोस्"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"अनुप्रयोगहरू खोज्नुहोस्..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> मा जानुहोस्"</string>
@@ -38,7 +38,7 @@
<string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</string>
<string name="delete_target_label" msgid="1822697352535677073">"हटाउनुहोस्"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"हटाउनुहोस्"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"स्थापना हटाउनुहोस्"</string>
<string name="info_target_label" msgid="8053346143994679532">"अनुप्रयोग जानकारी"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"स्वागतम"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"तपाईँको अनुप्रयोग आईकनको प्रतिलिप गर्नुहोस्"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"आफ्नो पुरानो गृह स्क्रीनबाट अाईकन र फोल्डरहरू आयात गर्नुहोस्?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"गृह स्क्रिनमा थप्नुहोस्"</string>
<string name="action_move_here" msgid="2170188780612570250">"वस्तु यहाँ सार्नुहोस्"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"वस्तु गृह स्क्रिनमा थपियो"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 6f49810..a55aebf 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Werk"</string>
<string name="activity_not_found" msgid="8071924732094499514">"App is niet geïnstalleerd."</string>
<string name="activity_not_available" msgid="7456344436509528827">"App is niet beschikbaar"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Gedownloade app uitgeschakeld in veilige modus"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets uitgeschakeld in Veilige modus"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Geheugen weergeven"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Blijf aanraken om een widget toe te voegen."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Dubbeltik en blijf aanraken om een widget toe te voegen of aangepaste acties te gebruiken."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Apps zoeken"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Apps zoeken…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Apps laden…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Er zijn geen apps gevonden die overeenkomen met \'<xliff:g id="QUERY">%1$s</xliff:g>\'"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Ga naar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -38,7 +38,7 @@
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Startpagina"</string>
<string name="delete_target_label" msgid="1822697352535677073">"Verwijderen"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Verwijderen"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Deïnstalleren"</string>
<string name="info_target_label" msgid="8053346143994679532">"App-info"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Snelkoppelingen instellen"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Een app toestaan snelkoppelingen toe te voegen zonder tussenkomst van de gebruiker."</string>
@@ -46,15 +46,17 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"De app toestaan de instellingen en snelkoppelingen op de startpagina te lezen."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"instellingen en snelkoppelingen op de startpagina schrijven"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"De app toestaan de instellingen en snelkoppelingen op de startpagina te wijzigen."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> mag niet bellen"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Probleem bij het laden van widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuratie"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Naamloze map"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d van %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startscherm %1$d van %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nieuwe startschermpagina"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Welkom"</string>
- <string name="migration_cling_title" msgid="9181776667882933767">"Uw app-pictogrammen kopiëren"</string>
- <string name="migration_cling_description" msgid="2752413805582227644">"Pictogrammen en mappen importeren uit uw oude startschermen?"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Je app-pictogrammen kopiëren"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Pictogrammen en mappen importeren uit je oude startschermen?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"PICTOGRAMMEN KOPIËREN"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"OPNIEUW BEGINNEN"</string>
<string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Achtergronden, widgets en instellingen"</string>
@@ -74,7 +76,9 @@
<string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
<string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Deze app is niet geïnstalleerd"</string>
- <string name="abandoned_promise_explanation" msgid="3990027586878167529">"De app voor dit pictogram is niet geïnstalleerd. U kunt het pictogram verwijderen of de app zoeken en handmatig installeren."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"De app voor dit pictogram is niet geïnstalleerd. Je kunt het pictogram verwijderen of de app zoeken en handmatig installeren."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> wordt gedownload, <xliff:g id="PROGRESS">%2$s</xliff:g> voltooid"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> wacht op installatie"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Toevoegen aan startpagina"</string>
<string name="action_move_here" msgid="2170188780612570250">"Item hier naartoe verplaatsen"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item toegevoegd aan startscherm"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 30c4428..6f5acff 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"ਲਾਂਚਰ3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"ਮੈਮ ਦਿਖਾਓ"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ਐਪਾਂ ਖੋਜੋ…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ਤੇ ਜਾਓ"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"ਸੁਆਗਤ ਹੈ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ਆਪਣੇ ਐਪ ਆਈਕਨਾਂ ਨੂੰ ਕਾਪੀ ਕਰੋ"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"ਕੀ ਆਪਣੀਆਂ ਪੁਰਾਣੀਆਂ ਹੋਮ ਸਕ੍ਰੀਨਾਂ ਤੋਂ ਆਈਕਨਾਂ ਅਤੇ ਫੋਲਡਰ ਆਯਾਤ ਕਰਨੇ ਹਨ?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜੋ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਮੁੱਖ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index a1fd29f..ab70265 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Praca"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacja nie jest zainstalowana."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacja niedostępna"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Pobrana aplikacja została wyłączona w trybie awaryjnym"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widżety są wyłączone w trybie bezpiecznym"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Pokaż pamięć"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Aby dodać widżet, kliknij go i przytrzymaj."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Kliknij dwukrotnie i przytrzymaj, by wybrać widżet lub użyć działań niestandardowych."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Szukaj w aplikacjach"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Szukaj w aplikacjach…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Wczytuję aplikacje…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nie znaleziono aplikacji pasujących do zapytania „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Otwórz <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Pozwala aplikacji na odczytywanie ustawień i skrótów na ekranie głównym."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"zapisywanie ustawień i skrótów na ekranie głównym"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Umożliwia aplikacji zmianę ustawień i skrótów na ekranie głównym."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nie może wykonywać połączeń telefonicznych"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem podczas ładowania widżetu"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguracja"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Folder bez nazwy"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Strona %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ekran główny %1$d z %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nowa strona ekranu głównego"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Witamy"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiuj ikony aplikacji"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Zaimportować ikony i foldery ze starych ekranów głównych?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Szukaj"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacja nie jest zainstalowana"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Pobieranie elementu <xliff:g id="NAME">%1$s</xliff:g>, ukończono: <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> oczekuje na instalację"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Dodaj do strony głównej"</string>
<string name="action_move_here" msgid="2170188780612570250">"Przenieś element tutaj"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Element został dodany do ekranu głównego"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 0d8a431..8a469ac 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Iniciador3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabalho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"A aplicação não está instalada."</string>
<string name="activity_not_available" msgid="7456344436509528827">"A aplicação não está disponível"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicação transferida desativada no Modo de segurança"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no Modo de segurança"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Prima sem soltar para escolher um widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes sem soltar para escolher um widget ou utilize ações personalizadas."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar aplicações"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pesquisar aplicações..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"A carregar aplicações..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Não foram encontradas aplic. que correspondam a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Aceder a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permite à aplicação ler as definições e os atalhos no Ecrã Principal."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"escrever definições e atalhos do Ecrã principal"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite à aplicação alterar as definições e os atalhos no Ecrã Principal."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"O <xliff:g id="APP_NAME">%1$s</xliff:g> não tem autorização para efetuar chamadas telefónicas"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma aplicação de sistema e não pode ser desinstalada."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ecrã principal %1$d de %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nova página do ecrã principal"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bem-vindo(a)"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiar ícones das aplicações"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importar ícones e pastas dos ecrãs principais antigos?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicação não está instalada"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"A transferir o <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"A aguardar a instalação do <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar ao Ecrã principal"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover o item para aqui"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado ao ecrã principal"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 41e5c02..d9fbdef 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Tela de início 3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabalho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"O app não está instalado."</string>
<string name="activity_not_available" msgid="7456344436509528827">"O app não está disponível"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"App transferido por download desativado no modo de segurança"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets desativados no modo de segurança"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memória"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Toque e pressione para selecionar um widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Toque duas vezes e segure para selecionar um widget ou usar ações personalizadas."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pesquisar apps"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Pesquisar apps..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Carregando apps…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenhum app encontrado que corresponda a \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir para <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permite que o app leia as configurações e os atalhos na tela inicial."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"gravar configurações e atalhos da tela inicial"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que o app altere as configurações e os atalhos na tela inicial."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> não tem permissão para fazer chamadas"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um app do sistema e não pode ser desinstalado."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nova página na tela inicial"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bem-vindo"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiar ícones de apps"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importar ícones e pastas de suas telas iniciais antigas?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Este app não está instalado"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Fazendo download de <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> concluído"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Aguardando instalação de <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Adicionar à tela inicial"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mover item para cá"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Item adicionado à tela inicial"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index ffaca78..8c715f8 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplicația nu este instalată."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplicația nu este disponibilă"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicația descărcată este dezactivată în modul de siguranță"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgeturile sunt dezactivate în modul de siguranță"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Afișați memoria"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Atingeți lung un widget pentru a-l alege."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Atingeți de două ori și mențineți apăsat ca să alegeți un widget sau folosiți acțiuni personalizate."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Căutați aplicații"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Căutați aplicații…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Se încarcă aplicațiile..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nu s-a găsit nicio aplicație pentru „<xliff:g id="QUERY">%1$s</xliff:g>”"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Accesați <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Permite aplicației să citească setările și comenzile rapide din ecranul de pornire."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"scrie setări și comenzi rapide pentru ecranul de pornire"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite aplicației să modifice setările și comenzile rapide din ecranul de pornire."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> nu are permisiunea de a apela"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problemă la încărcarea widgetului"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Configurați"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Aceasta este o aplicație de sistem și nu poate fi dezinstalată."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dosar fără nume"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Pagina %1$d din %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ecranul de pornire %1$d din %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Pagină nouă pe ecranul de pornire"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bun venit"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Copiați pictogr. aplicațiilor"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Import. pictogr. și dosare de pe ecranele de pornire anter.?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Căutați"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Aplicația nu este instalată"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> se descarcă (finalizat <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> așteaptă instalarea"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Adăugați pe ecranul de pornire"</string>
<string name="action_move_here" msgid="2170188780612570250">"Mutați elementul aici"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Element adăugat pe ecranul de pornire"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 6e69e4d..b4ddae7 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Сведения о памяти"</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 x %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Поиск приложений"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Поиск приложений"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -37,7 +37,7 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Главный экран"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"Удалить"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Убрать"</string>
<string name="delete_target_uninstall_label" msgid="5100785476250872595">"Удалить"</string>
<string name="info_target_label" msgid="8053346143994679532">"О приложении"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"Создание ярлыков"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Добро пожаловать!"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Копировать значки приложений"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Импортировать значки и папки со старого главного экрана?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Добавить на главный экран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Переместить элемент сюда"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Элемент добавлен на главный экран"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 476fff8..1fd7df0 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mem පෙන්වන්න"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"යෙදුම් සෙවීම"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"යෙදුම් සොයන්න..."</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> වෙත යන්න"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"සාදරයෙන් පිළිගනිමු"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"ඔබේ යෙදුම් නිරූපක පිටපත් කිරීම"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"ඔබගේ පැරණි මුල් තිර වල නිරූපක සහ ෆෝල්ඩර ආයාත කරන්නද?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"මුල් තිරය වෙත එක් කරන්න"</string>
<string name="action_move_here" msgid="2170188780612570250">"මෙතනට අයිතමය ගෙන එන්න"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"අයිතමය මුල් තිරය වෙත එකතු කරන ලදි"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 703b46c..66772c7 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Pracovné"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikácia nie je nainštalovaná."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikácia nie je k dispozícii"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Stiahnutá aplikácia je v núdzovom režime zakázaná"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Miniaplikácie sú v núdzovom režime zakázané"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Zobraziť pamäť"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Miniaplikáciu pridáte stlačením a podržaním."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Miniaplikáciu pridáte dvojitým klepnutím a pridržaním alebo pomocou vlastných akcií."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Vyhľadávanie v aplikáciách"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Vyhľadávanie v aplikáciách…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Načítavajú sa aplikácie..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nenašli sa žiadne aplikácie zodpovedajúce dopytu <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Prejsť na dopyt <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Povoľuje aplikácii čítať nastavenia a odkazy na ploche."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"zápis nastavení a odkazov plochy"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Povoľuje aplikácii zmeniť nastavenia a odkazy na ploche."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> nemá povolenie uskutočňovať telefonické hovory"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problém s načítaním miniaplikácií"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Nastavenie"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikácia a nedá sa odinštalovať."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Nepomenovaný priečinok"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Stránka %1$d z %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Plocha %1$d z %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nová stránka plochy"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Vitajte!"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopírovanie ikon aplikácií"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Chcete importovať ikony a priečinky zo starých plôch?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Vyhľadať"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Táto aplikácia nie je nainštalovaná"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Sťahuje sa aplikácia <xliff:g id="NAME">%1$s</xliff:g>. Stiahnuté: <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikácia <xliff:g id="NAME">%1$s</xliff:g> čaká na inštaláciu"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Pridať na plochu"</string>
<string name="action_move_here" msgid="2170188780612570250">"Presunúť položku sem"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Položka bola pridaná na plochu"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 497378b..d5e1e4f 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Zaganjalnik3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Služba"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija ni nameščena."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Aplikacija ni na voljo"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Prenesena aplikacija je onemogočena v Varnem načinu"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Pripomočki so onemogočeni v varnem načinu"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Pokaži pomnilnik"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Za izbiro pripomočka se ga dotaknite in pridržite."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Če želite izbrati pripomoček ali uporabiti dejanja po meri, se ga dvakrat dotaknite in ga pridržite."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Iskanje po aplikacijah"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Iskanje po aplikacijah …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Nalaganje aplikacij …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Ni aplikacij, ki bi ustrezale poizvedbi »<xliff:g id="QUERY">%1$s</xliff:g>«"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Odpri storitev <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -38,7 +38,7 @@
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Začetni zaslon"</string>
<string name="delete_target_label" msgid="1822697352535677073">"Odstrani"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odstrani"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Odstrani namestitev"</string>
<string name="info_target_label" msgid="8053346143994679532">"Podatki o aplikaciji"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"namestitev bližnjic"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Aplikaciji dovoli dodajanje bližnjic brez posredovanja uporabnika."</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Aplikaciji dovoli branje nastavitev in bližnjic na začetnem zaslonu."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"zapis nastavitev in bližnjic na začetnem zaslonu"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Aplikaciji dovoli spreminjanje nastavitev in bližnjic na začetnem zaslonu."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"Aplikaciji <xliff:g id="APP_NAME">%1$s</xliff:g> ni dovoljeno opravljanje klicev"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Težava pri nalaganju pripomočka"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Nastavitev"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To je sistemska aplikacija in je ni mogoče odstraniti."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Stran %1$d od %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Začetni zaslon %1$d od %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Nova stran na začetnem zaslonu"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Pozdravljeni"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiranje ikon aplikacij"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Želite uvoziti ikone in mape iz starih začetnih zaslonov?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Iskanje"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacija ni nameščena."</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Prenašanje aplikacije <xliff:g id="NAME">%1$s</xliff:g>; preneseno <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Aplikacija <xliff:g id="NAME">%1$s</xliff:g> čaka na namestitev"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Dodajanje na začetni zaslon"</string>
<string name="action_move_here" msgid="2170188780612570250">"Premik elementa sem"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Element je bil dodan na začetni zaslon"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 2b535a3..6262170 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Nisësi3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Trego memorandumin"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Kërko për aplikacione"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"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="5470761048755751471">"Shko te <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Mirë se erdhe"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopjo ikonat e aplikacionit"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Të importohen ikona dhe dosje nga ekranet e vjetra bazë?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index f21aae0..f193909 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Апликација није инсталирана."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Апликација није доступна"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Преузета апликација је онемогућена у Безбедном режиму"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Виџети су онемогућени у Безбедном режиму"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Прикажи меморију"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Претражите апликације"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Претражите апликације..."</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="5470761048755751471">"Иди на апликацију <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Добро дошли"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Копирајте иконе апликација"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Увести иконе и директоријуме са старих почетних екрана?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Додај на почетни екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Премести ставку овде"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Ставка је додата на почетни екран"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 385d7a6..10f5ce4 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Arbete"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen är inte installerad."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Appen är inte tillgänglig"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Den hämtade appen inaktiverades i säkert läge"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Widgets är inaktiverade i felsäkert läge"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Visa Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Tryck länge om du vill flytta en widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryck två gånger och håll kvar om du vill ta upp en widget eller använda anpassade åtgärder."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sök efter appar"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Sök efter appar …"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Läser in appar …"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Det gick inte att hitta några appar som matchar <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Gå till <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Tillåter att appen läser inställningar och genvägar på startsidan."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"skriva inställningar och genvägar för startsidan"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Tillåter att appen ändrar inställningar och genvägar på startsidan."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inte behörighet att ringa samtal"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Det gick inte att läsa in widgeten"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Det här är en systemapp som inte kan avinstalleras."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Namnlös mapp"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Sidan %1$d av %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Startskärmen %1$d av %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Ny sida på startskärmen"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Välkommen"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopiera appikoner"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Vill du importera ikoner och mappar från gamla startskärmar?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Sök"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Appen är inte installerad"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> laddas ned, <xliff:g id="PROGRESS">%2$s</xliff:g> klart"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> väntar på installation"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Lägg till på startskärmen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Flytta objekt hit"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Objektet har lagts till på startskärmen"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index b86fb73..2abe824 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Kizindua3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Kazini"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Programu haijasakinishwa."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Programu haipatikani"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Programu iliyopakuliwa imezimwa katika Hali Salama"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Wijeti zimezimwa katika hali ya Usalama"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Onyesha Kumbukumbu"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Gusa na ushikilie ili kuteua wijeti."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Gonga mara mbili na ushikilie ile uchague wijeti au utumie vitendo maalum."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tafuta Programu"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tafuta Programu..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Inapakia Programu..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Haikupata programu zinazolingana na \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Nenda kwenye <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -38,7 +38,7 @@
<string name="all_apps_button_label" msgid="9110807029020582876">"Programu"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Mwanzo"</string>
<string name="delete_target_label" msgid="1822697352535677073">"Ondoa"</string>
- <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Ondoa"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Sakinusha"</string>
<string name="info_target_label" msgid="8053346143994679532">"Maelezo ya programu"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"kuweka njia za mkato"</string>
<string name="permdesc_install_shortcut" msgid="923466509822011139">"Huruhusu programu kuongeza njia za mkato bila mtumiaji kuingilia kati."</string>
@@ -46,6 +46,7 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Huruhusu programu kusoma mipangilio na njia za mikato zilizo katika skirini ya Mwanzo."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"andika mipangilio ya skrini ya Mwanzo na njia za mkato"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Huruhusu programu kubadilisha mipangilio na njia za mkato katika skrini ya Mwanzo."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> hairuhusiwi kupiga simu"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Tatizo la kupakia wijeti"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Sanidi"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</string>
@@ -54,6 +55,7 @@
<!-- String.format failed for translation -->
<!-- no translation found for workspace_scroll_format (8458889198184077399) -->
<skip />
+ <string name="workspace_new_page" msgid="257366611030256142">"Ukurasa mpya wa skrini ya kwanza"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Karibu"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Nakili ikoni za programu yako"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Je, ungependa kuingiza ikoni na folda kutoka kwenye skrini zako za Mwanzo za zamani?"</string>
@@ -77,6 +79,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Tafuta"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Programu hii haijasakinishwa"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> inapakuliwa, <xliff:g id="PROGRESS">%2$s</xliff:g> imekamilika"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> inasubiri kusakinisha"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Ongeza kwenye skrini ya Kwanza"</string>
<string name="action_move_here" msgid="2170188780612570250">"Hamishia kipengee hapa"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Kipengee kimeongezwa kwenye skrini ya kwanza"</string>
diff --git a/res/values-sw600dp-land/dimens.xml b/res/values-sw600dp-land/dimens.xml
index f9ca01b..644c891 100644
--- a/res/values-sw600dp-land/dimens.xml
+++ b/res/values-sw600dp-land/dimens.xml
@@ -18,4 +18,8 @@
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">12dip</dimen>
<dimen name="toolbar_button_horizontal_padding">20dip</dimen>
+
+<!-- Container -->
+ <dimen name="container_max_width">736dp</dimen>
+
</resources>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index fb54f12..0756dc9 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -15,12 +15,16 @@
-->
<resources>
-<!-- All Apps -->
+ <!-- Container -->
+ <dimen name="container_min_margin">16dp</dimen>
+
+ <!-- All Apps -->
<dimen name="all_apps_grid_view_start_margin">0dp</dimen>
<dimen name="all_apps_grid_section_text_size">26sp</dimen>
<dimen name="all_apps_icon_top_bottom_padding">12dp</dimen>
<dimen name="all_apps_background_canvas_width">850dp</dimen>
<dimen name="all_apps_background_canvas_height">525dp</dimen>
+ <dimen name="all_apps_icon_width_gap">36dp</dimen>
<!-- Cling -->
<dimen name="cling_migration_logo_height">400dp</dimen>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 807fab9..b9e28a9 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -16,6 +16,7 @@
<resources>
<!-- All Apps -->
+ <dimen name="all_apps_button_scale_down">8dp</dimen>
<dimen name="all_apps_search_bar_height">54dp</dimen>
<dimen name="all_apps_icon_top_bottom_padding">14dp</dimen>
<dimen name="all_apps_empty_search_message_top_offset">64dp</dimen>
diff --git a/res/values-sw768dp-port/dimens.xml b/res/values-sw768dp-port/dimens.xml
new file mode 100644
index 0000000..4df1725
--- /dev/null
+++ b/res/values-sw768dp-port/dimens.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+<!-- Container -->
+ <dimen name="container_max_width">736dp</dimen>
+
+</resources>
\ No newline at end of file
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 9dac6d0..3990286 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"லாஞ்சர்3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"நினைவகத்தைக் காட்டு"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"பயன்பாடுகளில் தேடுக"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"பயன்பாடுகளில் தேடுக…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>க்குச் செல்லவும்"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"நல்வரவு"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"பயன்பாட்டின் ஐகான்களை நகலெடுக்கவும்"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"பழைய முகப்புத் திரைகளிலிருந்து ஐகான்களையும் கோப்புறைகளையும் இறக்குமதி செய்யவா?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"முகப்புத் திரையில் சேர்"</string>
<string name="action_move_here" msgid="2170188780612570250">"இங்கு நகர்த்து"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"முகப்புத் திரையில் சேர்க்கப்பட்டது"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 32cb292..997e198 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"లాంచర్3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"మెమరీ చూపు"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"అనువర్తనాలను శోధించండి"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"అనువర్తనాల్లో శోధించండి…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>కి వెళ్లు"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"స్వాగతం"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"మీ అనువర్తన చిహ్నాలను కాపీ చేయండి"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"మీ పాత హోమ్ స్క్రీన్ల నుండి చిహ్నాలు మరియు ఫోల్డర్లను దిగుమతి చేయాలా?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"హోమ్ స్క్రీన్కు జోడించు"</string>
<string name="action_move_here" msgid="2170188780612570250">"అంశాన్ని ఇక్కడికి తరలించు"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"అంశం హోమ్స్క్రీన్కి జోడించబడింది"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 53ae589..b90599e 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"แสดง Mem"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ค้นหาแอป"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ค้นหาแอป…"</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="5470761048755751471">"ไปที่ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"ยินดีต้อนรับ"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"คัดลอกไอคอนแอปของคุณ"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"นำเข้าไอคอนและโฟลเดอร์จากหน้าจอหลักเก่าของคุณ"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"เพิ่มลงในหน้าแรก"</string>
<string name="action_move_here" msgid="2170188780612570250">"ย้ายรายการมาที่นี่"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"เพิ่มรายการไปยังหน้าจอหลักแล้ว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 43afeab..7da7b13 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Trabaho"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Hindi naka-install ang app."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Hindi available ang app"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Naka-disable ang na-download na app sa Safe mode"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Naka-disable ang mga widget sa Safe mode"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Ipakita ang Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Pindutin nang matagal upang kumuha ng widget."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"I-double tap nang matagal upang pumili ng widget o gumamit ng mga custom na pagkilos."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Mga App sa Paghahanap"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Maghanap ng Mga App…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Nilo-load ang Mga App…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Walang nakitang Mga App na tumutugma sa \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Pumunta sa <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Pinapayagan ang app na basahin ang mga setting at shortcut sa Home."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"magsulat ng mga setting at shortcut ng Home"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Pinapayagan ang app na baguhin ang mga setting at shortcut sa Home."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"Hindi pinahihintulutang tumawag ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema sa pag-load ng widget"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"I-setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Isa itong app ng system at hindi maaaring i-uninstall."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Walang Pangalang Folder"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Pahina %1$d ng %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Home screen %1$d ng %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Bagong page ng home screen"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Maligayang Pagdating"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopyahin ang mga icon ng app"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"I-import ang icon at folder mula sa luma mong Home screen?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Maghanap"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Hindi naka-install ang app na ito"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Hindi naka-install ang app para sa icon na ito. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Dina-download na ang <xliff:g id="NAME">%1$s</xliff:g>, tapos na ang <xliff:g id="PROGRESS">%2$s</xliff:g>"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Hinihintay nang mag-install ang <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Idagdag sa Home screen"</string>
<string name="action_move_here" msgid="2170188780612570250">"Ilipat ang item dito"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Naidagdag sa home screen ang item"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 064d049..07db876 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"İş"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Uygulama yüklü değil."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Uygulama kullanılamıyor"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"İndirilen uygulama Güvenli modda devre dışı bırakıldı"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Güvenli modda widget\'lar devre dışı"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Belleği Göster"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget seçmek için dokunun ve basılı tutun."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Bir widget\'ı seçmek veya özel işlemleri kullanmak için iki kez hafifçe dokunun ve basılı tutun."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Uygulamalarda Ara"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Uygulamalarda Ara…"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Uygulamalar Yükleniyor…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ile eşleşen uygulama bulunamadı"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> uygulamasına git"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Uygulamaya, Ana ekrandaki ayarları ve kısayolları okuma izni verir."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"Ana ekran ayarlarını ve kısayollarını yaz"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Uygulamaya, Ana ekrandaki ayarları ve kısayolları değiştirme izni verir."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasının telefon etmesine izin verilmiyor"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Widget yüklenirken sorun oluştu"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Kurulum"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Adsız Klasör"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Sayfa %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Ana ekran %1$d / %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Yeni ana ekran sayfası"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Hoş geldiniz"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Uygulama simgelerini kopyala"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Eski Ana ekranlarınızdaki simgeler ve klasörler içe aktarılsın mı?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Ara"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Bu uygulama yüklü değil"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"<xliff:g id="NAME">%1$s</xliff:g> indiriliyor, <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> uygulaması yüklenmek için bekliyor"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Ana ekrana ekle"</string>
<string name="action_move_here" msgid="2170188780612570250">"Öğeyi buraya taşı"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Öğe ana ekrana eklendi"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index ff9364e..66f2542 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Показати пам’ять"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук додатків"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Пошук додатків…"</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="5470761048755751471">"Перейти в додаток <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Вітаємо"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Копіювати значки програм"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Імпортувати значки та папки зі старих головних екранів?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"Додати на головний екран"</string>
<string name="action_move_here" msgid="2170188780612570250">"Перемістити елемент сюди"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Елемент додано на головний екран"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index f3d6311..b3d4c29 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Mem دکھائیں"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"ایپس تلاش کریں"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"ایپس تلاش کریں…"</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="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> پر جائیں"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"خوش آمدید"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"اپنے ایپ آئیکنز کو کاپی کریں"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"اپنی پرانی ہوم اسکرینوں سے آئیکنز اور فولڈرز درآمد کریں؟"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"ہوم اسکرین میں شامل کریں"</string>
<string name="action_move_here" msgid="2170188780612570250">"آئٹم یہاں منتقل کریں"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"آئٹم کو ہوم اسکرین میں شامل کر دیا گیا"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 24e4c4d..23f82d8 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Ishga tushirgich3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"Xotirani ko‘rsatish"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"Ilovalarni qidirish"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Ilovalarni 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>” bilan mos hech qanday ilova topilmadi"</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"O‘tish: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -37,7 +37,7 @@
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Uy"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"O‘chirish"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Olib tashlash"</string>
<string name="delete_target_uninstall_label" msgid="5100785476250872595">"O‘chirish"</string>
<string name="info_target_label" msgid="8053346143994679532">"Ilova haqida"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar yaratish"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"Xush kelibsiz"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Ilovangiz nishonchalaridan nusxa olish"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Eski \"Uy\" ekranlaringizdan jildlar va nishonchalar import qilinsinmi?"</string>
@@ -75,6 +77,8 @@
<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="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>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 6eff507..1ecc92e 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Trình chạy 3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Ứng dụng chưa được cài đặt."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Ứng dụng không có sẵn"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Ứng dụng đã tải xuống bị tắt ở chế độ An toàn"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Tiện ích con bị vô hiệu hóa ở chế độ an toàn"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Hiển thị bộ nhớ"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Chạm và giữ để chọn tiện ích con."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Nhấn đúp và giữ để chọn tiện ích hoặc sử dụng tác vụ tùy chỉnh."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tìm kiếm ứng dụng"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Tìm kiếm ứng dụng..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Đang tải ứng dụng..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Không tìm thấy ứng dụng nào phù hợp với \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Chuyển tới <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Cho phép ứng dụng đọc cài đặt và lối tắt trên Màn hình chính."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"ghi cài đặt và lối tắt trên Màn hình chính"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Cho phép ứng dụng thay đổi cài đặt và lối tắt trên Màn hình chính."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> không được phép thực hiện cuộc gọi điện thoại"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Sự cố khi tải tiện ích con"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Thiết lập"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Đây là ứng dụng hệ thống và không thể gỡ cài đặt."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Thư mục chưa đặt tên"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Trang %1$d / %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Màn hình chính %1$d / %2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Trang màn hình chính mới"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Chào mừng"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Sao chép biểu tượng ứng dụng của bạn"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Nhập biểu tượng và thư mục từ Màn hình chính cũ của bạn?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Tìm kiếm"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Ứng dụng này chưa được cài đặt"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"Đang tải xuống <xliff:g id="NAME">%1$s</xliff:g>, <xliff:g id="PROGRESS">%2$s</xliff:g> hoàn tất"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"Đang chờ cài đặt <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Thêm vào màn hình chính"</string>
<string name="action_move_here" msgid="2170188780612570250">"Di chuyển mục vào đây"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Đã thêm mục vào màn hình chính"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 77f07c9..93a7fa7 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <string name="app_name" msgid="649227358658669779">"Launcher3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Work"</string>
<string name="activity_not_found" msgid="8071924732094499514">"未安装该应用。"</string>
<string name="activity_not_available" msgid="7456344436509528827">"应用不可用"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"安全模式下不允许使用下载的此应用"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"安全模式下不允许使用小部件"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"显示内存空间"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"搜索应用"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜索应用…"</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="5470761048755751471">"转到 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"欢迎使用"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"复制应用图标"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"要导入旧的主屏幕中的图标和文件夹吗?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"添加到主屏幕"</string>
<string name="action_move_here" msgid="2170188780612570250">"将项目移至此处"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"已将项目添加到主屏幕"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 62032ec..41679b5 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"顯示記憶體"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜尋應用程式…"</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="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"歡迎"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"複製您的應用程式圖示"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"要從舊主畫面匯入圖示和資料夾嗎?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"新增至主畫面"</string>
<string name="action_move_here" msgid="2170188780612570250">"移動項目至這裡"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"已將項目加入至主畫面"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 0b3f4d6..bc3be0c 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Launcher3"</string>
+ <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="toggle_weight_watcher" msgid="5645299835184636119">"顯示記憶體"</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="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"搜尋應用程式…"</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="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<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="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="first_run_cling_title" msgid="2459738000155917941">"歡迎使用"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"複製您的應用程式圖示"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"要從舊的主螢幕匯入圖示和資料夾嗎?"</string>
@@ -75,6 +77,8 @@
<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="action_add_to_workspace" msgid="8902165848117513641">"新增至主畫面"</string>
<string name="action_move_here" msgid="2170188780612570250">"將項目移至這裡"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"已將項目新增到主畫面"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index bb49f1f..a1dec74 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -19,17 +19,17 @@
<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="5181331383435256801">"Isiqalisi3"</string>
+ <string name="app_name" msgid="649227358658669779">"Isiqalisi3"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="work_folder_name" msgid="3753320833950115786">"Umsebenzi"</string>
<string name="activity_not_found" msgid="8071924732094499514">"Uhlelo lokusebenza alufakiwe."</string>
<string name="activity_not_available" msgid="7456344436509528827">"Uhlelo lokusebenza alutholakali"</string>
<string name="safemode_shortcut_error" msgid="9160126848219158407">"Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile"</string>
<string name="safemode_widget_error" msgid="4863470563535682004">"Amawijethi akhutshaziwe kwimodi yokuphepha"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"Bonisa i-Mem"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Thinta uphinde ubambe ukuze uphakamise iwijethi."</string>
+ <string name="long_accessible_way_to_add" msgid="4289502106628154155">"Thepha kabili bese uyabamba ukuze uthathe iwijethi noma sebenzisa izenzo ezingokwezifiso."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
- <string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sesha Izinhlelo Zokusebenza"</string>
+ <string name="all_apps_search_bar_hint" msgid="6705987535534678581">"Sesha izinhlelo zokusebenza..."</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Ilayisha izinhlelo zokusebenza..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
<string name="all_apps_search_market_message" msgid="5470761048755751471">"Hamba ku-<xliff:g id="QUERY">%1$s</xliff:g>"</string>
@@ -46,12 +46,14 @@
<string name="permdesc_read_settings" msgid="5833423719057558387">"Ivumela uhlelo lokusebenza ukuthi lifunde izilungiselelo nezinqamuleli ekhaya."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"bhala izilungiselelo zokuthi Ikhaya nezinqamuleli"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ivumela uhlelo lokusebenza ukuthi lushintshe izilungiselelo nezinqamuleli Ekhaya."</string>
+ <string name="msg_no_phone_permission" msgid="9208659281529857371">"<xliff:g id="APP_NAME">%1$s</xliff:g> ayivunyelwe ukwenza amakholi wefoni"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Inkinga yokulayisha iwijethi"</string>
<string name="gadget_setup_text" msgid="8274003207686040488">"Ukumisa"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Ifolda engenagama"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Ikhasi elingu-%1$d kwangu-%2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Isikrini sasekhaya esingu-%1$d se-%2$d"</string>
+ <string name="workspace_new_page" msgid="257366611030256142">"Ikhasi elisha lesikrini sasekhaya"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Siyakwamukela"</string>
<string name="migration_cling_title" msgid="9181776667882933767">"Kopisha izithonjana zakho zohlelo lokusebenza"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Ngenisa izithonjana namafolda kusukela kuzikrini zakho ezindala zasekhaya?"</string>
@@ -75,6 +77,8 @@
<string name="abandoned_search" msgid="891119232568284442">"Sesha"</string>
<string name="abandoned_promises_title" msgid="7096178467971716750">"Lolu hlelo lokusebenza alifakiwe"</string>
<string name="abandoned_promise_explanation" msgid="3990027586878167529">"Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela."</string>
+ <string name="app_downloading_title" msgid="8336702962104482644">"I-<xliff:g id="NAME">%1$s</xliff:g> iyalandwa, <xliff:g id="PROGRESS">%2$s</xliff:g> kuqediwe"</string>
+ <string name="app_waiting_download_title" msgid="7053938513995617849">"<xliff:g id="NAME">%1$s</xliff:g> ilinde ukufakwa"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"Faka kusikrini sasekhaya"</string>
<string name="action_move_here" msgid="2170188780612570250">"Hambisa into lapha"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"Into ingezwe kusikrini sasekhaya"</string>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 827332a..c5be9f2 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -83,6 +83,13 @@
<attr name="pageIndicator" format="reference" />
</declare-styleable>
+ <!-- BaseContainerView specific attributes. These attributes are used to customize
+ AllApps view and WidgetsView in xml. -->
+ <declare-styleable name="BaseContainerView">
+ <!-- Drawable to use for the reveal animation -->
+ <attr name="revealBackground" format="reference" />
+ </declare-styleable>
+
<!-- XML attributes used by default_workspace.xml -->
<declare-styleable name="Favorite">
<attr name="className" format="string" />
@@ -93,8 +100,8 @@
<attr name="y" format="string" />
<attr name="spanX" format="string" />
<attr name="spanY" format="string" />
- <attr name="icon" format="reference" />
- <attr name="title" format="reference" />
+ <attr name="icon" format="reference" />
+ <attr name="title" format="string" />
<attr name="uri" format="string" />
</declare-styleable>
diff --git a/res/values/config.xml b/res/values/config.xml
index 93c6d14..c8a610d 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -67,20 +67,20 @@
<!-- Hotseat -->
<bool name="hotseat_transpose_layout_with_orientation">true</bool>
- <!-- Memory debugging, including a memory dump icon -->
- <bool name="debug_memory_enabled">false</bool>
-
<!-- Name of a subclass of com.android.launcher3.AppFilter used to
filter the activities shown in the launcher. Can be empty. -->
<string name="app_filter_class" translatable="false"></string>
- <!-- Name of a subclass of com.android.launcher3.BuildInfo used to
- get build information. Can be empty. -->
- <string name="build_info_class" translatable="false"></string>
+ <!-- Name of a packages that com.android.launcher3.action.LAUNCH
+ should be targeting. Can be empty. -->
+ <string-array name="launch_broadcast_targets" translatable="false"></string-array>
<!-- View ID to use for QSB widget -->
<item type="id" name="qsb_widget" />
+ <!-- View ID used by cell layout to jail its content -->
+ <item type="id" name="cell_layout_jail_id" />
+
<!-- Accessibility actions -->
<item type="id" name="action_remove" />
<item type="id" name="action_uninstall" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 3672179..af4ae5e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -19,8 +19,18 @@
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">6dp</dimen>
- <dimen name="dynamic_grid_search_bar_max_width">500dp</dimen>
- <dimen name="dynamic_grid_search_bar_height">56dp</dimen>
+ <dimen name="dynamic_grid_search_bar_height">48dp</dimen>
+ <!-- We want 46dp extra for the tall search bar. -->
+ <dimen name="dynamic_grid_search_bar_height_tall">94dp</dimen>
+ <dimen name="qsb_internal_padding_top">8dp</dimen>
+ <dimen name="qsb_internal_padding_bottom">8dp</dimen>
+ <dimen name="dynamic_grid_search_bar_extra_top_padding">0dp</dimen>
+ <!-- Reduce the space between the status bar and the search bar when the search bar is tall -->
+ <dimen name="dynamic_grid_search_bar_negative_top_padding_short">-4dp</dimen>
+ <dimen name="dynamic_grid_search_bar_bottom_padding">4dp</dimen>
+ <!-- Reduce the padding between the search bar and workspace when the search bar is tall -->
+ <dimen name="dynamic_grid_search_bar_bottom_negative_padding_short">-6dp</dimen>
+ <dimen name="dynamic_grid_search_bar_bottom_padding_tablet">16dp</dimen>
<dimen name="dynamic_grid_page_indicator_height">20dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
<dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
@@ -50,6 +60,10 @@
<dimen name="toolbar_button_horizontal_padding">12dip</dimen>
<!-- Container -->
+ <item name="container_margin" format="fraction" type="fraction">0%</item>
+ <dimen name="container_min_margin">8dp</dimen>
+ <dimen name="container_max_width">0dp</dimen>
+
<!-- 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 -->
@@ -63,10 +77,11 @@
<dimen name="container_fastscroll_popup_text_size">48dp</dimen>
<!-- All Apps -->
+ <dimen name="all_apps_button_scale_down">0dp</dimen>
<dimen name="all_apps_grid_view_start_margin">0dp</dimen>
<dimen name="all_apps_grid_section_y_offset">8dp</dimen>
<dimen name="all_apps_grid_section_text_size">24sp</dimen>
- <dimen name="all_apps_search_bar_height">48dp</dimen>
+ <dimen name="all_apps_search_bar_height">60dp</dimen>
<dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
<dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
<dimen name="all_apps_icon_width_gap">24dp</dimen>
@@ -79,7 +94,20 @@
<dimen name="all_apps_background_canvas_width">700dp</dimen>
<dimen name="all_apps_background_canvas_height">475dp</dimen>
-<!-- Widget tray -->
+ <!-- Search bar in All Apps -->
+ <dimen name="all_apps_header_max_elevation">4dp</dimen>
+ <dimen name="all_apps_header_scroll_to_elevation">16dp</dimen>
+ <dimen name="all_apps_header_shadow_height">6dp</dimen>
+
+ <!-- The overflow is used to create a bottom border, by drawing other three sides
+ outside the bounds. Ensure that:
+ all_apps_search_bar_bg_overflow < (-3 * all_apps_search_bar_divider_width)
+ -6dp is picked at random, any smaller value would do.
+ -->
+ <dimen name="all_apps_search_bar_bg_overflow">-6dp</dimen>
+ <dimen name="all_apps_search_bar_divider_width">1dp</dimen>
+
+ <!-- Widget tray -->
<dimen name="widget_container_inset">8dp</dimen>
<dimen name="widget_preview_label_vertical_padding">8dp</dimen>
<dimen name="widget_preview_label_horizontal_padding">8dp</dimen>
@@ -136,5 +164,4 @@
<!-- Pending widget -->
<dimen name="pending_widget_min_padding">8dp</dimen>
<dimen name="pending_widget_elevation">2dp</dimen>
-
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index b544788..2838a22 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -45,18 +45,18 @@
<!-- SafeMode widget error string -->
<string name="safemode_widget_error">Widgets disabled in Safe mode</string>
- <string name="toggle_weight_watcher">Show Mem</string>
-
<!-- Widgets -->
<!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
<string name="long_press_widget_to_add">Touch & hold to pick up a widget.</string>
+ <!-- Accessibility spoken hint message in widget picker, which allows user to add a widget. Custom action is the label for additional accessibility actions available in this mode [CHAR_LIMIT=100] -->
+ <string name="long_accessible_way_to_add">Double-tap & hold to pick up a widget or use custom actions.</string>
<!-- The format string for the dimensions of a widget in the drawer -->
<!-- There is a special version of this format string for Farsi -->
<string name="widget_dims_format">%1$d \u00d7 %2$d</string>
<!-- All Apps -->
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
- <string name="all_apps_search_bar_hint">Search Apps</string>
+ <string name="all_apps_search_bar_hint">Search Apps…</string>
<!-- Loading apps text. [CHAR_LIMIT=50] -->
<string name="all_apps_loading_message">Loading Apps…</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
@@ -128,6 +128,8 @@
<string name="default_scroll_format">Page %1$d of %2$d</string>
<!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
<string name="workspace_scroll_format">Home screen %1$d of %2$d</string>
+ <!-- Description for a new page on homescreen[CHAR_LIMIT=none] -->
+ <string name="workspace_new_page">New home screen page</string>
<!-- Clings -->
<!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
@@ -161,9 +163,6 @@
<!-- Folder name format -->
<string name="folder_name_format">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
- <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
- <string name="debug_memory_activity">* HPROF</string>
-
<!-- Strings for the customization mode -->
<!-- Text for widget add button -->
<string name="widget_button_text">Widgets</string>
@@ -190,6 +189,10 @@
<string name="abandoned_promise_explanation">The app for this icon isn\'t installed.
You can remove it, or search for the app and install it manually.
</string>
+ <!-- Title for an app which is being downloaded. -->
+ <string name="app_downloading_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> downloading, <xliff:g id="progress" example="30%">%2$s</xliff:g> complete</string>
+ <!-- Title for an app whose download has been started. -->
+ <string name="app_waiting_download_title"><xliff:g id="name" example="Messenger">%1$s</xliff:g> waiting to install</string>
<!-- Strings for accessibility actions -->
<!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7d60cbe..4eee130 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -44,6 +44,8 @@
<item name="android:textColor">@color/quantum_panel_text_color</item>
<item name="android:drawablePadding">@dimen/dynamic_grid_icon_drawable_padding</item>
<item name="android:shadowRadius">0</item>
+ <item name="android:paddingLeft">4dp</item>
+ <item name="android:paddingRight">4dp</item>
<item name="customShadows">false</item>
</style>
diff --git a/res/xml/default_workspace_3x3.xml b/res/xml/default_workspace_3x3.xml
new file mode 100644
index 0000000..8d99404
--- /dev/null
+++ b/res/xml/default_workspace_3x3.xml
@@ -0,0 +1,88 @@
+<?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.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Messaging, [All Apps], Dialer -->
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+ <favorite launcher:uri="sms:" />
+ <favorite launcher:uri="smsto:" />
+ <favorite launcher:uri="mms:" />
+ <favorite launcher:uri="mmsto:" />
+ </resolve>
+
+ <!-- All Apps -->
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.DIAL;end" />
+ <favorite launcher:uri="tel:123" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.CALL_BUTTON;end" />
+ </resolve>
+
+ <!-- Bottom row -->
+ <resolve
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="-1" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+ <favorite launcher:uri="mailto:" />
+ </resolve>
+
+ <resolve
+ launcher:screen="0"
+ launcher:x="1"
+ launcher:y="-1" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+ <favorite launcher:uri="#Intent;type=images/*;end" />
+ </resolve>
+
+ <resolve
+ launcher:screen="0"
+ launcher:x="2"
+ launcher:y="-1" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
+ <favorite launcher:uri="market://details?id=com.android.launcher" />
+ </resolve>
+
+ <!-- Second last row -->
+ <resolve
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="-2" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+ <favorite launcher:uri="http://www.example.com/" />
+ </resolve>
+
+ <resolve
+ launcher:screen="0"
+ launcher:x="2"
+ launcher:y="-2" >
+ <favorite launcher:uri="#Intent;action=android.media.action.STILL_IMAGE_CAMERA;end" />
+ <favorite launcher:uri="#Intent;action=android.intent.action.CAMERA_BUTTON;end" />
+ </resolve>
+
+</favorites>
diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml
index 060a1f8..979a1b4 100644
--- a/res/xml/default_workspace_4x4.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -23,7 +23,7 @@
<resolve
launcher:screen="0"
launcher:x="0"
- launcher:y="3" >
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
<favorite launcher:uri="mailto:" />
</resolve>
@@ -31,7 +31,7 @@
<resolve
launcher:screen="0"
launcher:x="1"
- launcher:y="3" >
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
<favorite launcher:uri="#Intent;type=images/*;end" />
</resolve>
@@ -39,7 +39,7 @@
<resolve
launcher:screen="0"
launcher:x="3"
- launcher:y="3" >
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
<favorite launcher:uri="market://details?id=com.android.launcher" />
</resolve>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index 3226617..f9cc0e7 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -23,7 +23,7 @@
<resolve
launcher:screen="0"
launcher:x="0"
- launcher:y="4" >
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
<favorite launcher:uri="mailto:" />
@@ -32,7 +32,7 @@
<resolve
launcher:screen="0"
launcher:x="1"
- launcher:y="4" >
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
<favorite launcher:uri="#Intent;type=images/*;end" />
@@ -41,7 +41,7 @@
<resolve
launcher:screen="0"
launcher:x="4"
- launcher:y="4" >
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
<favorite launcher:uri="market://details?id=com.android.launcher" />
</resolve>
diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml
index bc236fb..8493c26 100644
--- a/res/xml/default_workspace_5x6.xml
+++ b/res/xml/default_workspace_5x6.xml
@@ -23,13 +23,13 @@
<favorite
launcher:screen="0"
launcher:x="0"
- launcher:y="4"
+ launcher:y="-1"
launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CONTACTS;end" />
<resolve
launcher:screen="0"
- launcher:x="5"
- launcher:y="4" >
+ launcher:x="-1"
+ launcher:y="-1" >
<favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MARKET;end" />
<favorite launcher:uri="market://details?id=com.android.launcher" />
</resolve>
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 3b25dca..c431593 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -22,6 +22,8 @@
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.FlagOp;
+import com.android.launcher3.util.StringFilter;
import java.util.ArrayList;
import java.util.HashSet;
@@ -118,6 +120,21 @@
}
}
+ /**
+ * Updates the apps for the given packageName and user based on {@param op}.
+ */
+ public void updatePackageFlags(StringFilter pkgFilter, UserHandleCompat user, FlagOp op) {
+ final List<AppInfo> data = this.data;
+ for (int i = data.size() - 1; i >= 0; i--) {
+ AppInfo info = data.get(i);
+ final ComponentName component = info.intent.getComponent();
+ if (info.user.equals(user) && pkgFilter.matches(component.getPackageName())) {
+ info.isDisabled = op.apply(info.isDisabled);
+ modified.add(info);
+ }
+ }
+ }
+
public void updateIconsAndLabels(HashSet<String> packages, UserHandleCompat user,
ArrayList<AppInfo> outUpdates) {
for (AppInfo info : data) {
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index c95d558..c7a6ae4 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -26,6 +26,7 @@
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.PackageManagerHelper;
import java.util.ArrayList;
import java.util.Arrays;
@@ -51,11 +52,6 @@
*/
boolean usingLowResIcon;
- /**
- * The time at which the app was first installed.
- */
- long firstInstallTime;
-
public ComponentName componentName;
static final int DOWNLOADED_FLAG = 1;
@@ -63,6 +59,11 @@
int flags = 0;
+ /**
+ * {@see ShortcutInfo#isDisabled}
+ */
+ int isDisabled = ShortcutInfo.DEFAULT;
+
AppInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
@@ -80,11 +81,22 @@
*/
public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
IconCache iconCache) {
+ this(context, info, user, iconCache,
+ UserManagerCompat.getInstance(context).isQuietModeEnabled(user));
+ }
+
+ public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
+ IconCache iconCache, boolean quietModeEnabled) {
this.componentName = info.getComponentName();
this.container = ItemInfo.NO_ID;
-
flags = initFlags(info);
- firstInstallTime = info.getFirstInstallTime();
+ if (PackageManagerHelper.isAppSuspended(info.getApplicationInfo())) {
+ isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+ if (quietModeEnabled) {
+ isDisabled |= ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+ }
+
iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
intent = makeLaunchIntent(context, info, user);
this.user = user;
@@ -109,7 +121,7 @@
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
flags = info.flags;
- firstInstallTime = info.firstInstallTime;
+ isDisabled = info.isDisabled;
iconBitmap = info.iconBitmap;
}
@@ -129,7 +141,6 @@
Log.d(tag, label + " size=" + list.size());
for (AppInfo info: list) {
Log.d(tag, " title=\"" + info.title + "\" iconBitmap=" + info.iconBitmap
- + " firstInstallTime=" + info.firstInstallTime
+ " componentName=" + info.componentName.getPackageName());
}
}
@@ -151,4 +162,9 @@
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
.putExtra(EXTRA_PROFILE, serialNumber);
}
+
+ @Override
+ public boolean isDisabled() {
+ return isDisabled != 0;
+ }
}
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index e6bf525..6b7ff88 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -11,10 +11,15 @@
import android.content.res.Resources;
import android.graphics.Rect;
import android.view.Gravity;
+import android.view.KeyEvent;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
-public class AppWidgetResizeFrame extends FrameLayout {
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+import com.android.launcher3.util.FocusLogic;
+
+public class AppWidgetResizeFrame extends FrameLayout implements View.OnKeyListener {
private static final int SNAP_DURATION = 150;
private static final float DIMMED_HANDLE_ALPHA = 0f;
private static final float RESIZE_THRESHOLD = 0.66f;
@@ -40,6 +45,8 @@
private final int[] mLastDirectionVector = new int[2];
private final int[] mTmpPt = new int[2];
+ private final DragViewStateAnnouncer mStateAnnouncer;
+
private boolean mLeftBorderActive;
private boolean mRightBorderActive;
private boolean mTopBorderActive;
@@ -78,6 +85,8 @@
mMinHSpan = info.minSpanX;
mMinVSpan = info.minSpanY;
+ mStateAnnouncer = DragViewStateAnnouncer.createFor(this);
+
setBackgroundResource(R.drawable.widget_resize_shadow);
setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
setPadding(0, 0, 0, 0);
@@ -137,6 +146,8 @@
// cells (same if not resized, or different) will be marked as occupied when the resize
// frame is dismissed.
mCellLayout.markCellsAsUnoccupiedForView(mWidgetView);
+
+ setOnKeyListener(this);
}
public boolean beginResizeIfPointInRegion(int x, int y) {
@@ -320,12 +331,18 @@
if (mCellLayout.createAreaForResize(cellX, cellY, spanX, spanY, mWidgetView,
mDirectionVector, onDismiss)) {
+ if (mStateAnnouncer != null && (lp.cellHSpan != spanX || lp.cellVSpan != spanY) ) {
+ mStateAnnouncer.announce(
+ mLauncher.getString(R.string.widget_resized, spanX, spanY));
+ }
+
lp.tmpCellX = cellX;
lp.tmpCellY = cellY;
lp.cellHSpan = spanX;
lp.cellVSpan = spanY;
mRunningVInc += vSpanDelta;
mRunningHInc += hSpanDelta;
+
if (!onDismiss) {
updateWidgetSizeRanges(mWidgetView, mLauncher, spanX, spanY);
}
@@ -462,5 +479,19 @@
set.setDuration(SNAP_DURATION);
set.start();
}
+
+ setFocusableInTouchMode(true);
+ requestFocus();
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ // Clear the frame and give focus to the widget host view when a directional key is pressed.
+ if (FocusLogic.shouldConsume(keyCode)) {
+ mDragLayer.clearAllResizeFrames();
+ mWidgetView.requestFocus();
+ return true;
+ }
+ return false;
}
}
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index b1d51ec..54ce0fd 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -13,7 +13,6 @@
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.compat.AppWidgetManagerCompat;
import java.util.ArrayList;
import java.util.List;
@@ -90,7 +89,7 @@
}
return null;
}
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+ }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
}
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 440e4e7..0d71a0c 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -125,8 +125,12 @@
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_TITLE = "title";
private static final String ATTR_SCREEN = "screen";
+
+ // x and y can be specified as negative integers, in which case -1 represents the
+ // last row / column, -2 represents the second last, and so on.
private static final String ATTR_X = "x";
private static final String ATTR_Y = "y";
+
private static final String ATTR_SPAN_X = "spanX";
private static final String ATTR_SPAN_Y = "spanY";
private static final String ATTR_ICON = "icon";
@@ -154,6 +158,8 @@
protected final int mLayoutId;
private final int mHotseatAllAppsRank;
+ private final int mRowCount;
+ private final int mColumnCount;
private final long[] mTemp = new long[2];
@Thunk final ContentValues mValues;
@@ -164,13 +170,6 @@
public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, Resources res,
int layoutId, String rootTag) {
- this(context, appWidgetHost, callback, res, layoutId, rootTag,
- LauncherAppState.getInstance().getInvariantDeviceProfile().hotseatAllAppsRank);
- }
-
- public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
- LayoutParserCallback callback, Resources res,
- int layoutId, String rootTag, int hotseatAllAppsRank) {
mContext = context;
mAppWidgetHost = appWidgetHost;
mCallback = callback;
@@ -181,7 +180,11 @@
mSourceRes = res;
mLayoutId = layoutId;
- mHotseatAllAppsRank = hotseatAllAppsRank;
+
+ InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+ mHotseatAllAppsRank = idp.hotseatAllAppsRank;
+ mRowCount = idp.numRows;
+ mColumnCount = idp.numColumns;
}
/**
@@ -261,8 +264,11 @@
mValues.put(Favorites.CONTAINER, container);
mValues.put(Favorites.SCREEN, screenId);
- mValues.put(Favorites.CELLX, getAttributeValue(parser, ATTR_X));
- mValues.put(Favorites.CELLY, getAttributeValue(parser, ATTR_Y));
+
+ mValues.put(Favorites.CELLX,
+ convertToDistanceFromEnd(getAttributeValue(parser, ATTR_X), mColumnCount));
+ mValues.put(Favorites.CELLY,
+ convertToDistanceFromEnd(getAttributeValue(parser, ATTR_Y), mRowCount));
TagParser tagParser = tagParserMap.get(parser.getName());
if (tagParser == null) {
@@ -648,6 +654,16 @@
}
}
+ private static String convertToDistanceFromEnd(String value, int endValue) {
+ if (!TextUtils.isEmpty(value)) {
+ int x = Integer.parseInt(value);
+ if (x < 0) {
+ return Integer.toString(endValue + x);
+ }
+ }
+ return value;
+ }
+
/**
* Return attribute value, attempting launcher-specific namespace first
* before falling back to anonymous attribute.
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index c118240..538c24a 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -17,32 +17,34 @@
package com.android.launcher3;
import android.content.Context;
+import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
import android.util.AttributeSet;
-import android.util.Log;
-import android.widget.LinearLayout;
+import android.view.View;
+import android.widget.FrameLayout;
/**
* A base container view, which supports resizing.
*/
-public abstract class BaseContainerView extends LinearLayout implements Insettable {
+public abstract class BaseContainerView extends FrameLayout implements Insettable {
private final static String TAG = "BaseContainerView";
// The window insets
- private Rect mInsets = new Rect();
- // The bounds of the search bar. Only the left, top, right are used to inset the
- // search bar and the height is determined by the measurement of the layout
- private Rect mFixedSearchBarBounds = new Rect();
- // The computed bounds of the search bar
- private Rect mSearchBarBounds = new Rect();
- // The computed bounds of the container
- protected Rect mContentBounds = new Rect();
+ private final Rect mInsets = new Rect();
// The computed padding to apply to the container to achieve the container bounds
- private Rect mContentPadding = new Rect();
+ protected final Rect mContentPadding = new Rect();
// The inset to apply to the edges and between the search bar and the container
- private int mContainerBoundsInset;
- private boolean mHasSearchBar;
+ private final int mContainerBoundsInset;
+
+ private final Drawable mRevealDrawable;
+
+ private View mRevealView;
+ private View mContent;
+
+ protected final int mHorizontalPadding;
public BaseContainerView(Context context) {
this(context, null);
@@ -55,6 +57,30 @@
public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.BaseContainerView, defStyleAttr, 0);
+ mRevealDrawable = a.getDrawable(R.styleable.BaseContainerView_revealBackground);
+ a.recycle();
+
+ int maxSize = getResources().getDimensionPixelSize(R.dimen.container_max_width);
+ int minMargin = getResources().getDimensionPixelSize(R.dimen.container_min_margin);
+ int width = ((Launcher) context).getDeviceProfile().availableWidthPx;
+
+ if (maxSize > 0) {
+ mHorizontalPadding = Math.max(minMargin, (width - maxSize) / 2);
+ } else {
+ mHorizontalPadding = Math.max(minMargin,
+ (int) getResources().getFraction(R.fraction.container_margin, width, 1));
+ }
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mContent = findViewById(R.id.main_content);
+ mRevealView = findViewById(R.id.reveal_view);
}
@Override
@@ -63,20 +89,10 @@
updateBackgroundAndPaddings();
}
- protected void setHasSearchBar() {
- mHasSearchBar = true;
- }
-
/**
* Sets the search bar bounds for this container view to match.
*/
final public void setSearchBarBounds(Rect bounds) {
- if (LauncherAppState.isDogfoodBuild() && !isValidSearchBarBounds(bounds)) {
- Log.e(TAG, "Invalid search bar bounds: " + bounds);
- }
-
- mFixedSearchBarBounds.set(bounds);
-
// Post the updates since they can trigger a relayout, and this call can be triggered from
// a layout pass itself.
post(new Runnable() {
@@ -92,54 +108,46 @@
*/
protected void updateBackgroundAndPaddings() {
Rect padding;
- Rect searchBarBounds = new Rect();
- if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
- // Use the default bounds
- padding = new Rect(mInsets.left + mContainerBoundsInset,
- (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
- mInsets.right + mContainerBoundsInset,
- mInsets.bottom + mContainerBoundsInset);
+ padding = new Rect(
+ mHorizontalPadding,
+ mInsets.top + mContainerBoundsInset,
+ mHorizontalPadding,
+ mInsets.bottom + mContainerBoundsInset
+ );
- // Special case -- we have the search bar, but no specific bounds, so just give it
- // the inset bounds without a height.
- searchBarBounds.set(mInsets.left + mContainerBoundsInset,
- mInsets.top + mContainerBoundsInset,
- getMeasuredWidth() - (mInsets.right + mContainerBoundsInset), 0);
- } else {
- // Use the search bounds, if there is a search bar, the bounds will contain
- // the offsets for the insets so we can ignore that
- padding = new Rect(mFixedSearchBarBounds.left,
- (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
- getMeasuredWidth() - mFixedSearchBarBounds.right,
- mInsets.bottom + mContainerBoundsInset);
-
- // Use the search bounds
- searchBarBounds.set(mFixedSearchBarBounds);
- }
-
- // If either the computed container padding has changed, or the computed search bar bounds
- // has changed, then notify the container
- if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) {
+ // The container padding changed, notify the container.
+ if (!padding.equals(mContentPadding)) {
mContentPadding.set(padding);
- mContentBounds.set(padding.left, padding.top,
- getMeasuredWidth() - padding.right,
- getMeasuredHeight() - padding.bottom);
- mSearchBarBounds.set(searchBarBounds);
- onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
+ onUpdateBackgroundAndPaddings(padding);
}
}
- /**
- * To be implemented by container views to update themselves when the bounds changes.
- */
- protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding);
+ private void onUpdateBackgroundAndPaddings(Rect padding) {
+ // Apply the top-bottom padding to itself so that the launcher transition is
+ // clipped correctly
+ setPadding(0, padding.top, 0, padding.bottom);
- /**
- * Returns whether the search bar bounds we got are considered valid.
- */
- private boolean isValidSearchBarBounds(Rect searchBarBounds) {
- return !searchBarBounds.isEmpty() &&
- searchBarBounds.right <= getMeasuredWidth() &&
- searchBarBounds.bottom <= getMeasuredHeight();
+ InsetDrawable background = new InsetDrawable(mRevealDrawable,
+ padding.left, 0, padding.right, 0);
+ mRevealView.setBackground(background.getConstantState().newDrawable());
+ mContent.setBackground(background);
+
+ // We let the content have a intent background, but still have full width.
+ // This allows the scroll bar to be used responsive outside the background bounds as well.
+ mContent.setPadding(0, 0, 0, 0);
+
+ Rect bgPadding = new Rect();
+ background.getPadding(bgPadding);
+ onUpdateBgPadding(padding, bgPadding);
+ }
+
+ protected abstract void onUpdateBgPadding(Rect padding, Rect bgPadding);
+
+ public final View getContentView() {
+ return mContent;
+ }
+
+ public final View getRevealView() {
+ return mRevealView;
}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index f0d8b3b..f8ef1e1 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -52,8 +52,8 @@
public int rowIndex;
// The offset of the first visible row
public int rowTopOffset;
- // The height of a given row (they are currently all the same height)
- public int rowHeight;
+ // The adapter position of the first visible item
+ public int itemPos;
}
protected BaseRecyclerViewFastScrollBar mScrollbar;
@@ -187,15 +187,21 @@
}
/**
+ * Returns the visible height of the recycler view:
+ * VisibleHeight = View height - top padding - bottom padding
+ */
+ protected int getVisibleHeight() {
+ int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
+ return visibleHeight;
+ }
+
+ /**
* Returns the available scroll height:
* AvailableScrollHeight = Total height of the all items - last page height
- *
- * This assumes that all rows are the same height.
*/
- protected int getAvailableScrollHeight(int rowCount, int rowHeight) {
- int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
- int scrollHeight = getPaddingTop() + rowCount * rowHeight + getPaddingBottom();
- int availableScrollHeight = scrollHeight - visibleHeight;
+ protected int getAvailableScrollHeight(int rowCount) {
+ int totalHeight = getPaddingTop() + getTop(rowCount) + getPaddingBottom();
+ int availableScrollHeight = totalHeight - getVisibleHeight();
return availableScrollHeight;
}
@@ -204,8 +210,7 @@
* AvailableScrollBarHeight = Total height of the visible view - thumb height
*/
protected int getAvailableScrollBarHeight() {
- int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
- int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight();
+ int availableScrollBarHeight = getVisibleHeight() - mScrollbar.getThumbHeight();
return availableScrollBarHeight;
}
@@ -223,6 +228,13 @@
return defaultInactiveThumbColor;
}
+ /**
+ * Returns the scrollbar for this recycler view.
+ */
+ public BaseRecyclerViewFastScrollBar getScrollBar() {
+ return mScrollbar;
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
@@ -243,7 +255,7 @@
int rowCount) {
// Only show the scrollbar if there is height to be scrolled
int availableScrollBarHeight = getAvailableScrollBarHeight();
- int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight);
+ int availableScrollHeight = getAvailableScrollHeight(rowCount);
if (availableScrollHeight <= 0) {
mScrollbar.setThumbOffset(-1, -1);
return;
@@ -252,8 +264,7 @@
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
- int scrollY = getPaddingTop() +
- (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
+ int scrollY = getScrollTop(scrollPosState);
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -268,24 +279,47 @@
}
/**
+ * @return whether fast scrolling is supported in the current state.
+ */
+ protected boolean supportsFastScrolling() {
+ return true;
+ }
+
+ /**
+ * Maps the touch (from 0..1) to the adapter position that should be visible.
+ * <p>Override in each subclass of this base class.
+ *
+ * @return the scroll top of this recycler view.
+ */
+ protected int getScrollTop(ScrollPositionState scrollPosState) {
+ return getPaddingTop() + getTop(scrollPosState.rowIndex) -
+ scrollPosState.rowTopOffset;
+ }
+
+ /**
+ * Returns information about the item that the recycler view is currently scrolled to.
+ */
+ protected abstract void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask);
+
+ /**
+ * Returns the top (or y position) of the row at the specified index.
+ */
+ protected abstract int getTop(int rowIndex);
+
+ /**
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
- public abstract String scrollToPositionAtProgress(float touchFraction);
+ protected abstract String scrollToPositionAtProgress(float touchFraction);
/**
* Updates the bounds for the scrollbar.
* <p>Override in each subclass of this base class.
*/
- public abstract void onUpdateScrollbar(int dy);
+ protected abstract void onUpdateScrollbar(int dy);
/**
* <p>Override in each subclass of this base class.
*/
- public void onFastScrollCompleted() {}
-
- /**
- * Returns information about the item that the recycler view is currently scrolled to.
- */
- protected abstract void getCurScrollState(ScrollPositionState stateOut);
+ protected void onFastScrollCompleted() {}
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index fcee7e8..a680169 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -27,6 +27,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import com.android.launcher3.util.Thunk;
@@ -37,7 +38,7 @@
public class BaseRecyclerViewFastScrollBar {
public interface FastScrollFocusableView {
- void setFastScrollFocused(boolean focused, boolean animated);
+ void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated);
}
private final static int MAX_TRACK_ALPHA = 30;
@@ -189,7 +190,8 @@
// 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 && isNearThumb(downX, lastY) &&
+ if (!mIsDragging && !mIgnoreDragGesture && mRv.supportsFastScrolling() &&
+ isNearThumb(downX, lastY) &&
Math.abs(y - downY) > config.getScaledTouchSlop()) {
mRv.getParent().requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
@@ -198,7 +200,7 @@
}
mTouchOffset += (lastY - downY);
mPopup.animateVisibility(true);
- animateScrollbar(true);
+ showActiveScrollbar(true);
}
if (mIsDragging) {
// Update the fastscroller section name at this touch position
@@ -209,7 +211,7 @@
(bottom - top));
mPopup.setSectionName(sectionName);
mPopup.animateVisibility(!sectionName.isEmpty());
- mRv.invalidate(mPopup.updateFastScrollerBounds(mRv, lastY));
+ mRv.invalidate(mPopup.updateFastScrollerBounds(lastY));
mLastTouchY = boundedY;
}
break;
@@ -221,7 +223,7 @@
if (mIsDragging) {
mIsDragging = false;
mPopup.animateVisibility(false);
- animateScrollbar(false);
+ showActiveScrollbar(false);
}
break;
}
@@ -245,7 +247,7 @@
/**
* Animates the width and color of the scrollbar.
*/
- private void animateScrollbar(boolean isScrolling) {
+ private void showActiveScrollbar(boolean isScrolling) {
if (mScrollbarAnimator != null) {
mScrollbarAnimator.cancel();
}
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
index aeeb515..ebaba18 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java
@@ -77,26 +77,26 @@
* Updates the bounds for the fast scroller.
* @return the invalidation rect for this update.
*/
- public Rect updateFastScrollerBounds(BaseRecyclerView rv, int lastTouchY) {
+ public Rect updateFastScrollerBounds(int lastTouchY) {
mInvalidateRect.set(mBgBounds);
if (isVisible()) {
// Calculate the dimensions and position of the fast scroller popup
- int edgePadding = rv.getMaxScrollbarWidth();
+ int edgePadding = mRv.getMaxScrollbarWidth();
int bgPadding = (mBgOriginalSize - mTextBounds.height()) / 2;
int bgHeight = mBgOriginalSize;
int bgWidth = Math.max(mBgOriginalSize, mTextBounds.width() + (2 * bgPadding));
if (Utilities.isRtl(mRes)) {
- mBgBounds.left = rv.getBackgroundPadding().left + (2 * rv.getMaxScrollbarWidth());
+ mBgBounds.left = mRv.getBackgroundPadding().left + (2 * mRv.getMaxScrollbarWidth());
mBgBounds.right = mBgBounds.left + bgWidth;
} else {
- mBgBounds.right = rv.getWidth() - rv.getBackgroundPadding().right -
- (2 * rv.getMaxScrollbarWidth());
+ mBgBounds.right = mRv.getWidth() - mRv.getBackgroundPadding().right -
+ (2 * mRv.getMaxScrollbarWidth());
mBgBounds.left = mBgBounds.right - bgWidth;
}
mBgBounds.top = lastTouchY - (int) (FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR * bgHeight);
mBgBounds.top = Math.max(edgePadding,
- Math.min(mBgBounds.top, rv.getHeight() - edgePadding - bgHeight));
+ Math.min(mBgBounds.top, mRv.getHeight() - edgePadding - bgHeight));
mBgBounds.bottom = mBgBounds.top + bgHeight;
} else {
mBgBounds.setEmpty();
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 5070878..09ec60c 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -25,8 +24,9 @@
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Paint;
import android.graphics.Region;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
@@ -37,13 +37,13 @@
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
-import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.TextView;
import com.android.launcher3.IconCache.IconLoadRequest;
import com.android.launcher3.model.PackageItemInfo;
+import java.text.NumberFormat;
+
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
* because we want to make the bubble taller than the text and TextView's clip is
@@ -63,13 +63,6 @@
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
- private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
- private static final int FAST_SCROLL_FOCUS_MODE_NONE = 0;
- private static final int FAST_SCROLL_FOCUS_MODE_SCALE_ICON = 1;
- private static final int FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG = 2;
- private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
- private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;
-
private final Launcher mLauncher;
private Drawable mIcon;
private final Drawable mBackground;
@@ -93,12 +86,6 @@
private boolean mIgnorePressedStateChange;
private boolean mDisableRelayout = false;
- private ObjectAnimator mFastScrollFocusAnimator;
- private Paint mFastScrollFocusBgPaint;
- private float mFastScrollFocusFraction;
- private boolean mFastScrollFocused;
- private final int mFastScrollMode = FAST_SCROLL_FOCUS_MODE_SCALE_ICON;
-
private IconLoadRequest mIconLoadRequest;
public BubbleTextView(Context context) {
@@ -126,7 +113,7 @@
if (display == DISPLAY_WORKSPACE) {
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
} else if (display == DISPLAY_ALL_APPS) {
- setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.allAppsIconTextSizeSp);
defaultIconSize = grid.allAppsIconSizePx;
}
@@ -151,13 +138,6 @@
setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
}
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG) {
- mFastScrollFocusBgPaint = new Paint();
- mFastScrollFocusBgPaint.setAntiAlias(true);
- mFastScrollFocusBgPaint.setColor(
- getResources().getColor(R.color.container_fastscroll_thumb_active_color));
- }
-
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
}
@@ -170,8 +150,9 @@
Bitmap b = info.getIcon(iconCache);
FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
- iconDrawable.setGhostModeEnabled(info.isDisabled != 0);
-
+ if (info.isDisabled()) {
+ iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
+ }
setIcon(iconDrawable, mIconSize);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
@@ -185,7 +166,11 @@
}
public void applyFromApplicationInfo(AppInfo info) {
- setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
+ FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(info.iconBitmap);
+ if (info.isDisabled()) {
+ iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
+ }
+ setIcon(iconDrawable, mIconSize);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
@@ -211,6 +196,15 @@
}
/**
+ * Used for measurement only, sets some dummy values on this view.
+ */
+ public void applyDummyInfo() {
+ ColorDrawable d = new ColorDrawable();
+ setIcon(mLauncher.resizeIconDrawable(d), mIconSize);
+ setText("");
+ }
+
+ /**
* Overrides the default long press timeout.
*/
public void setLongPressTimeout(int longPressTimeout) {
@@ -259,7 +253,15 @@
private void updateIconState() {
if (mIcon instanceof FastBitmapDrawable) {
- ((FastBitmapDrawable) mIcon).setPressed(isPressed() || mStayPressed);
+ FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
+ if (getTag() instanceof ItemInfo
+ && ((ItemInfo) getTag()).isDisabled()) {
+ d.animateState(FastBitmapDrawable.State.DISABLED);
+ } else if (isPressed() || mStayPressed) {
+ d.animateState(FastBitmapDrawable.State.PRESSED);
+ } else {
+ d.animateState(FastBitmapDrawable.State.NORMAL);
+ }
}
}
@@ -362,18 +364,7 @@
@Override
public void draw(Canvas canvas) {
if (!mCustomShadowsEnabled) {
- // Draw the fast scroll focus bg if we have one
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG &&
- mFastScrollFocusFraction > 0f) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- int iconCenterX = getScrollX() + (getWidth() / 2);
- int iconCenterY = getScrollY() + getPaddingTop() + (grid.iconSizePx / 2);
- canvas.drawCircle(iconCenterX, iconCenterY,
- mFastScrollFocusFraction * (getWidth() / 2), mFastScrollFocusBgPaint);
- }
-
super.draw(canvas);
-
return;
}
@@ -469,6 +460,11 @@
((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));
+
if (mIcon != null) {
final PreloadIconDrawable preloadDrawable;
if (mIcon instanceof PreloadIconDrawable) {
@@ -533,8 +529,13 @@
*/
public void reapplyItemInfo(final ItemInfo info) {
if (getTag() == info) {
+ FastBitmapDrawable.State prevState = FastBitmapDrawable.State.NORMAL;
+ if (mIcon instanceof FastBitmapDrawable) {
+ prevState = ((FastBitmapDrawable) mIcon).getCurrentState();
+ }
mIconLoadRequest = null;
mDisableRelayout = true;
+
if (info instanceof AppInfo) {
applyFromApplicationInfo((AppInfo) info);
} else if (info instanceof ShortcutInfo) {
@@ -550,6 +551,13 @@
} else if (info instanceof PackageItemInfo) {
applyFromPackageItemInfo((PackageItemInfo) info);
}
+
+ // If we are reapplying over an old icon, then we should update the new icon to the same
+ // state as the old icon
+ if (mIcon instanceof FastBitmapDrawable) {
+ ((FastBitmapDrawable) mIcon).setState(prevState);
+ }
+
mDisableRelayout = false;
}
}
@@ -583,55 +591,53 @@
}
}
- // Setters & getters for the animation
- public void setFastScrollFocus(float fraction) {
- mFastScrollFocusFraction = fraction;
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_SCALE_ICON) {
- setScaleX(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
- setScaleY(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
- } else {
- invalidate();
- }
- }
-
- public float getFastScrollFocus() {
- return mFastScrollFocusFraction;
- }
-
@Override
- public void setFastScrollFocused(final boolean focused, boolean animated) {
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) {
+ public void setFastScrollFocusState(final FastBitmapDrawable.State focusState, boolean animated) {
+ // We can only set the fast scroll focus state on a FastBitmapDrawable
+ if (!(mIcon instanceof FastBitmapDrawable)) {
return;
}
- if (mFastScrollFocused != focused) {
- mFastScrollFocused = focused;
-
- if (animated) {
- // Clean up the previous focus animator
- if (mFastScrollFocusAnimator != null) {
- mFastScrollFocusAnimator.cancel();
- }
- mFastScrollFocusAnimator = ObjectAnimator.ofFloat(this, "fastScrollFocus",
- focused ? 1f : 0f);
- if (focused) {
- mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
- } else {
- mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
- }
- mFastScrollFocusAnimator.setDuration(focused ?
- FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
- mFastScrollFocusAnimator.start();
- } else {
- mFastScrollFocusFraction = focused ? 1f : 0f;
+ FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
+ if (animated) {
+ FastBitmapDrawable.State prevState = d.getCurrentState();
+ if (d.animateState(focusState)) {
+ // If the state was updated, then update the view accordingly
+ animate().scaleX(focusState.viewScale)
+ .scaleY(focusState.viewScale)
+ .setStartDelay(getStartDelayForStateChange(prevState, focusState))
+ .setDuration(d.getDurationForStateChange(prevState, focusState))
+ .start();
+ }
+ } else {
+ if (d.setState(focusState)) {
+ // If the state was updated, then update the view accordingly
+ animate().cancel();
+ setScaleX(focusState.viewScale);
+ setScaleY(focusState.viewScale);
}
}
}
/**
+ * Returns the start delay when animating between certain {@link FastBitmapDrawable} states.
+ */
+ private static int getStartDelayForStateChange(final FastBitmapDrawable.State fromState,
+ final FastBitmapDrawable.State toState) {
+ switch (toState) {
+ case NORMAL:
+ switch (fromState) {
+ case FAST_SCROLL_HIGHLIGHTED:
+ return FastBitmapDrawable.FAST_SCROLL_INACTIVE_DURATION / 4;
+ }
+ }
+ return 0;
+ }
+
+ /**
* Interface to be implemented by the grand parent to allow click shadow effect.
*/
- public static interface BubbleTextShadowHandler {
+ public interface BubbleTextShadowHandler {
void setPressedIcon(BubbleTextView icon, Bitmap background);
}
}
diff --git a/src/com/android/launcher3/BuildInfo.java b/src/com/android/launcher3/BuildInfo.java
deleted file mode 100644
index b49ee0d..0000000
--- a/src/com/android/launcher3/BuildInfo.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.android.launcher3;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-public class BuildInfo {
- private static final boolean DBG = false;
- private static final String TAG = "BuildInfo";
-
- public boolean isDogfoodBuild() {
- return false;
- }
-
- public static BuildInfo loadByName(String className) {
- if (TextUtils.isEmpty(className)) return new BuildInfo();
-
- if (DBG) Log.d(TAG, "Loading BuildInfo: " + className);
- try {
- Class<?> cls = Class.forName(className);
- return (BuildInfo) cls.newInstance();
- } catch (ClassNotFoundException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- } catch (InstantiationException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- } catch (IllegalAccessException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- } catch (ClassCastException e) {
- Log.e(TAG, "Bad BuildInfo class", e);
- }
- return new BuildInfo();
- }
-}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 2baa6d8..40a4678 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -34,6 +34,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
import android.widget.TextView;
@@ -123,6 +124,10 @@
mDrawable.setColorFilter(new ColorMatrixColorFilter(mCurrentFilter));
setTextColor(mHoverColor);
}
+ if (d.stateAnnouncer != null) {
+ d.stateAnnouncer.cancel();
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
}
@Override
@@ -301,14 +306,10 @@
setOnClickListener(enable ? this : null);
}
- protected String getAccessibilityDropConfirmation() {
- return null;
- }
-
@Override
public void onClick(View v) {
LauncherAppState.getInstance().getAccessibilityDelegate()
- .handleAccessibleDrop(this, null, getAccessibilityDropConfirmation());
+ .handleAccessibleDrop(this, null, null);
}
public int getTextColor() {
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 84e2d49..94e3e41 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -25,7 +25,6 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -53,6 +52,7 @@
import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.util.ParcelableSparseArray;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -66,7 +66,8 @@
public static final int WORKSPACE_ACCESSIBILITY_DRAG = 2;
public static final int FOLDER_ACCESSIBILITY_DRAG = 1;
- static final String TAG = "CellLayout";
+ private static final String TAG = "CellLayout";
+ private static final boolean LOGD = false;
private Launcher mLauncher;
@Thunk int mCellWidth;
@@ -84,6 +85,7 @@
private int mMaxGap;
private boolean mDropPending = false;
private boolean mIsDragTarget = true;
+ private boolean mJailContent = true;
// These are temporary variables to prevent having to allocate a new object just to
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
@@ -187,7 +189,6 @@
mLauncher = (Launcher) context;
DeviceProfile grid = mLauncher.getDeviceProfile();
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CellLayout, defStyle, 0);
mCellWidth = mCellHeight = -1;
mFixedCellWidth = mFixedCellHeight = -1;
@@ -201,10 +202,7 @@
mPreviousReorderDirection[0] = INVALID_DIRECTION;
mPreviousReorderDirection[1] = INVALID_DIRECTION;
- a.recycle();
-
setAlwaysDrawnWithCacheEnabled(false);
-
final Resources res = getResources();
mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
@@ -244,9 +242,7 @@
// If an animation is started and then stopped very quickly, we can still
// get spurious updates we've cleared the tag. Guard against this.
if (outline == null) {
- @SuppressWarnings("all") // suppress dead code warning
- final boolean debug = false;
- if (debug) {
+ if (LOGD) {
Object val = animation.getAnimatedValue();
Log.d(TAG, "anim " + thisIndex + " update: " + val +
", isStopped " + anim.isStopped());
@@ -424,10 +420,36 @@
}
}
- boolean getIsDragOverlapping() {
+ public boolean getIsDragOverlapping() {
return mIsDragOverlapping;
}
+ public void disableJailContent() {
+ mJailContent = false;
+ }
+
+ @Override
+ protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+ if (mJailContent) {
+ ParcelableSparseArray jail = getJailedArray(container);
+ super.dispatchSaveInstanceState(jail);
+ container.put(R.id.cell_layout_jail_id, jail);
+ } else {
+ super.dispatchSaveInstanceState(container);
+ }
+ }
+
+ @Override
+ protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+ super.dispatchRestoreInstanceState(mJailContent ? getJailedArray(container) : container);
+ }
+
+ private ParcelableSparseArray getJailedArray(SparseArray<Parcelable> container) {
+ final Parcelable parcelable = container.get(R.id.cell_layout_jail_id);
+ return parcelable instanceof ParcelableSparseArray ?
+ (ParcelableSparseArray) parcelable : new ParcelableSparseArray();
+ }
+
@Override
protected void onDraw(Canvas canvas) {
if (!mIsDragTarget) {
@@ -630,6 +652,9 @@
if (lp.cellVSpan < 0) lp.cellVSpan = mCountY;
child.setId(childId);
+ if (LOGD) {
+ Log.d(TAG, "Adding view to ShortcutsAndWidgetsContainer: " + child);
+ }
mShortcutsAndWidgets.addView(child, index, lp);
if (markCells) markCellsAsOccupiedForView(child);
@@ -873,9 +898,12 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() -
- (mCountX * mCellWidth);
- int left = getPaddingLeft() + (int) Math.ceil(offset / 2f);
+ boolean isFullscreen = mShortcutsAndWidgets.getChildCount() > 0 &&
+ ((LayoutParams) mShortcutsAndWidgets.getChildAt(0).getLayoutParams()).isFullscreen;
+ int left = getPaddingLeft();
+ if (!isFullscreen) {
+ left += (int) Math.ceil(getUnusedHorizontalSpace() / 2f);
+ }
int top = getPaddingTop();
mTouchFeedbackView.layout(left, top,
@@ -886,6 +914,15 @@
top + b - t);
}
+ /**
+ * Returns the amount of space left over after subtracting padding and cells. This space will be
+ * very small, a few pixels at most, and is a result of rounding down when calculating the cell
+ * width in {@link DeviceProfile#calculateCellWidth(int, int)}.
+ */
+ public int getUnusedHorizontalSpace() {
+ return getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - (mCountX * mCellWidth);
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
@@ -1018,8 +1055,8 @@
return false;
}
- void visualizeDropLocation(View v, Bitmap dragOutline, int originX, int originY, int cellX,
- int cellY, int spanX, int spanY, boolean resize, Point dragOffset, Rect dragRegion) {
+ void visualizeDropLocation(View v, Bitmap dragOutline, int cellX, int cellY, int spanX,
+ int spanY, boolean resize, DropTarget.DragObject dragObject) {
final int oldDragCellX = mDragCell[0];
final int oldDragCellY = mDragCell[1];
@@ -1028,6 +1065,9 @@
}
if (cellX != oldDragCellX || cellY != oldDragCellY) {
+ Point dragOffset = dragObject.dragView.getDragVisualizeOffset();
+ Rect dragRegion = dragObject.dragView.getDragRegion();
+
mDragCell[0] = cellX;
mDragCell[1] = cellY;
// Find the top left corner of the rect the object will occupy
@@ -1079,6 +1119,18 @@
mDragOutlineAnims[mDragOutlineCurrent].setTag(dragOutline);
mDragOutlineAnims[mDragOutlineCurrent].animateIn();
+
+ if (dragObject.stateAnnouncer != null) {
+ String msg;
+ if (isHotseat()) {
+ msg = getContext().getString(R.string.move_to_hotseat_position,
+ Math.max(cellX, cellY) + 1);
+ } else {
+ msg = getContext().getString(R.string.move_to_empty_cell,
+ cellY + 1, cellX + 1);
+ }
+ dragObject.stateAnnouncer.announce(msg);
+ }
}
}
@@ -2133,8 +2185,15 @@
}
ValueAnimator va = LauncherAnimUtils.ofFloat(child, 0f, 1f);
a = va;
- va.setRepeatMode(ValueAnimator.REVERSE);
- va.setRepeatCount(ValueAnimator.INFINITE);
+
+ // Animations are disabled in power save mode, causing the repeated animation to jump
+ // spastically between beginning and end states. Since this looks bad, we don't repeat
+ // the animation in power save mode.
+ if (!Utilities.isPowerSaverOn(getContext())) {
+ va.setRepeatMode(ValueAnimator.REVERSE);
+ va.setRepeatCount(ValueAnimator.INFINITE);
+ }
+
va.setDuration(mode == MODE_HINT ? HINT_DURATION : PREVIEW_DURATION);
va.setStartDelay((int) (Math.random() * 60));
va.addUpdateListener(new AnimatorUpdateListener() {
@@ -2655,6 +2714,7 @@
LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.dropped = true;
child.requestLayout();
+ markCellsAsOccupiedForView(child);
}
}
diff --git a/src/com/android/launcher3/CommonAppTypeParser.java b/src/com/android/launcher3/CommonAppTypeParser.java
index 5314ecf..001e044 100644
--- a/src/com/android/launcher3/CommonAppTypeParser.java
+++ b/src/com/android/launcher3/CommonAppTypeParser.java
@@ -25,7 +25,7 @@
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.backup.BackupProtos.Favorite;
+import com.android.launcher3.backup.nano.BackupProtos.Favorite;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParserException;
@@ -93,7 +93,7 @@
public MyLayoutParser() {
super(CommonAppTypeParser.this.mContext, null, CommonAppTypeParser.this,
- CommonAppTypeParser.this.mContext.getResources(), mResId, TAG_RESOLVE, 0);
+ CommonAppTypeParser.this.mContext.getResources(), mResId, TAG_RESOLVE);
}
@Override
diff --git a/src/com/android/launcher3/DefaultLayoutParser.java b/src/com/android/launcher3/DefaultLayoutParser.java
index 7b91c67..2bba380 100644
--- a/src/com/android/launcher3/DefaultLayoutParser.java
+++ b/src/com/android/launcher3/DefaultLayoutParser.java
@@ -48,9 +48,8 @@
}
public DefaultLayoutParser(Context context, AppWidgetHost appWidgetHost,
- LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag,
- int hotseatAllAppsRank) {
- super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag, hotseatAllAppsRank);
+ LayoutParserCallback callback, Resources sourceRes, int layoutId, String rootTag) {
+ super(context, appWidgetHost, callback, sourceRes, layoutId, rootTag);
}
@Override
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9c8659c..edaf525 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -19,7 +19,6 @@
import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.PointF;
-import android.os.AsyncTask;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnimationUtils;
@@ -67,44 +66,14 @@
/**
* Removes the item from the workspace. If the view is not null, it also removes the view.
- * @return true if the item was removed.
*/
- public static boolean removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
- if (item instanceof ShortcutInfo) {
- LauncherModel.deleteItemFromDatabase(launcher, item);
- } else if (item instanceof FolderInfo) {
- FolderInfo folder = (FolderInfo) item;
- launcher.removeFolder(folder);
- LauncherModel.deleteFolderContentsFromDatabase(launcher, folder);
- } else if (item instanceof LauncherAppWidgetInfo) {
- final LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) item;
-
- // Remove the widget from the workspace
- launcher.removeAppWidget(widget);
- LauncherModel.deleteItemFromDatabase(launcher, widget);
-
- final LauncherAppWidgetHost appWidgetHost = launcher.getAppWidgetHost();
-
- if (appWidgetHost != null && !widget.isCustomWidget()
- && widget.isWidgetIdValid()) {
- // Deleting an app widget ID is a void call but writes to disk before returning
- // to the caller...
- new AsyncTask<Void, Void, Void>() {
- public Void doInBackground(Void ... args) {
- appWidgetHost.deleteAppWidgetId(widget.appWidgetId);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
- } else {
- return false;
- }
-
- if (view != null) {
- launcher.getWorkspace().removeWorkspaceItem(view);
- launcher.getWorkspace().stripEmptyScreens();
- }
- return true;
+ public static void removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
+ // Remove the item from launcher and the db, we can ignore the containerInfo in this call
+ // because we already remove the drag view from the folder (if the drag originated from
+ // a folder) in Folder.beginDrag()
+ launcher.removeItem(view, item, true /* deleteFromDb */);
+ launcher.getWorkspace().stripEmptyScreens();
+ launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed));
}
@Override
@@ -156,9 +125,4 @@
dragLayer.animateView(d.dragView, fling, duration, tInterpolator, onAnimationEndRunnable,
DragLayer.ANIMATION_END_DISAPPEAR, null);
}
-
- @Override
- protected String getAccessibilityDropConfirmation() {
- return getResources().getString(R.string.item_removed);
- }
}
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 774594f..380c6b1 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -49,6 +49,12 @@
public final int heightPx;
public final int availableWidthPx;
public final int availableHeightPx;
+ /**
+ * The maximum amount of left/right workspace padding as a percentage of the screen width.
+ * To be clear, this means that up to 7% of the screen width can be used as left padding, and
+ * 7% of the screen width can be used as right padding.
+ */
+ private static final float MAX_HORIZONTAL_PADDING_PERCENT = 0.14f;
// Overview mode
private final int overviewModeMinIconZoneHeightPx;
@@ -84,18 +90,25 @@
public int hotseatCellWidthPx;
public int hotseatCellHeightPx;
public int hotseatIconSizePx;
- private int hotseatBarHeightPx;
+ private int normalHotseatBarHeightPx, shortHotseatBarHeightPx;
+ private int hotseatBarHeightPx; // One of the above.
// All apps
public int allAppsNumCols;
public int allAppsNumPredictiveCols;
public int allAppsButtonVisualSize;
public final int allAppsIconSizePx;
- public final int allAppsIconTextSizePx;
+ public final float allAppsIconTextSizeSp;
// QSB
- private int searchBarSpaceWidthPx;
- private int searchBarSpaceHeightPx;
+ private int searchBarWidgetInternalPaddingTop, searchBarWidgetInternalPaddingBottom;
+ private int searchBarTopPaddingPx;
+ private int tallSearchBarNegativeTopPaddingPx, normalSearchBarTopExtraPaddingPx;
+ private int searchBarTopExtraPaddingPx; // One of the above.
+ private int normalSearchBarBottomPaddingPx, tallSearchBarBottomPaddingPx;
+ private int searchBarBottomPaddingPx; // One of the above.
+ private int normalSearchBarSpaceHeightPx, tallSearchBarSpaceHeightPx;
+ private int searchBarSpaceHeightPx; // One of the above.
public DeviceProfile(Context context, InvariantDeviceProfile inv,
Point minSize, Point maxSize,
@@ -139,7 +152,7 @@
res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
// AllApps uses the original non-scaled icon text size
- allAppsIconTextSizePx = Utilities.pxFromDp(inv.iconTextSize, dm);
+ allAppsIconTextSizeSp = inv.iconTextSize;
// AllApps uses the original non-scaled icon size
allAppsIconSizePx = Utilities.pxFromDp(inv.iconSize, dm);
@@ -167,7 +180,8 @@
private void computeAllAppsButtonSize(Context context) {
Resources res = context.getResources();
float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
- allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding));
+ allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding)) - context.getResources()
+ .getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
}
private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
@@ -196,10 +210,31 @@
hotseatIconSizePx = (int) (Utilities.pxFromDp(inv.hotseatIconSize, dm) * scale);
// Search Bar
- searchBarSpaceWidthPx = Math.min(widthPx,
- res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width));
- searchBarSpaceHeightPx = getSearchBarTopOffset()
- + res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
+ normalSearchBarSpaceHeightPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_search_bar_height);
+ tallSearchBarSpaceHeightPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_search_bar_height_tall);
+ searchBarWidgetInternalPaddingTop = res.getDimensionPixelSize(
+ R.dimen.qsb_internal_padding_top);
+ searchBarWidgetInternalPaddingBottom = res.getDimensionPixelSize(
+ R.dimen.qsb_internal_padding_bottom);
+ normalSearchBarTopExtraPaddingPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_search_bar_extra_top_padding);
+ tallSearchBarNegativeTopPaddingPx = res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_search_bar_negative_top_padding_short);
+ if (isTablet && !isVerticalBarLayout()) {
+ searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
+ normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding_tablet);
+ tallSearchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
+ } else {
+ searchBarTopPaddingPx = searchBarWidgetInternalPaddingTop;
+ normalSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom +
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_bottom_padding);
+ tallSearchBarBottomPaddingPx = searchBarWidgetInternalPaddingBottom
+ + res.getDimensionPixelSize(
+ R.dimen.dynamic_grid_search_bar_bottom_negative_padding_short);
+ }
// Calculate the actual text height
Paint textPaint = new Paint();
@@ -211,12 +246,16 @@
dragViewScale = (iconSizePx + scaleDps) / iconSizePx;
// Hotseat
- hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
+ normalHotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
+ shortHotseatBarHeightPx = iconSizePx + 2 * edgeMarginPx;
hotseatCellWidthPx = iconSizePx;
hotseatCellHeightPx = iconSizePx;
// Folder
- folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
+ int folderCellPadding = isTablet || isLandscape ? 6 * edgeMarginPx : 3 * edgeMarginPx;
+ // Don't let the folder get too close to the edges of the screen.
+ folderCellWidthPx = Math.min(cellWidthPx + folderCellPadding,
+ (availableWidthPx - 4 * edgeMarginPx) / inv.numFolderColumns);
folderCellHeightPx = cellHeightPx + edgeMarginPx;
folderBackgroundOffset = -edgeMarginPx;
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
@@ -231,34 +270,42 @@
int allAppsCellWidthGap =
res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap);
int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx;
- int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
+ int numAppsCols = (availableAppsWidthPx + allAppsCellWidthGap - appsViewLeftMarginPx) /
(allAppsIconSizePx + allAppsCellWidthGap);
int numPredictiveAppCols = Math.max(inv.minAllAppsPredictionColumns, numAppsCols);
allAppsNumCols = numAppsCols;
allAppsNumPredictiveCols = numPredictiveAppCols;
}
- /** Returns the search bar top offset */
- private int getSearchBarTopOffset() {
- if (isTablet && !isVerticalBarLayout()) {
- return 4 * edgeMarginPx;
- } else {
- return 2 * edgeMarginPx;
+ /** Returns the amount of extra space to allocate to the search bar for vertical padding. */
+ private int getSearchBarTotalVerticalPadding() {
+ return searchBarTopPaddingPx + searchBarTopExtraPaddingPx + searchBarBottomPaddingPx;
+ }
+
+ /** Returns the width and height of the search bar, ignoring any padding. */
+ public Point getSearchBarDimensForWidgetOpts(Resources res) {
+ Rect searchBarBounds = getSearchBarBounds(Utilities.isRtl(res));
+ if (isVerticalBarLayout()) {
+ return new Point(searchBarBounds.width(), searchBarBounds.height());
}
+ int widgetInternalPadding = searchBarWidgetInternalPaddingTop +
+ searchBarWidgetInternalPaddingBottom;
+ return new Point(searchBarBounds.width(), searchBarSpaceHeightPx + widgetInternalPadding);
}
/** Returns the search bar bounds in the current orientation */
public Rect getSearchBarBounds(boolean isLayoutRtl) {
Rect bounds = new Rect();
- if (isLandscape && transposeLayoutWithOrientation) {
+ if (isVerticalBarLayout()) {
if (isLayoutRtl) {
- bounds.set(availableWidthPx - searchBarSpaceHeightPx, edgeMarginPx,
+ bounds.set(availableWidthPx - normalSearchBarSpaceHeightPx, edgeMarginPx,
availableWidthPx, availableHeightPx - edgeMarginPx);
} else {
- bounds.set(0, edgeMarginPx, searchBarSpaceHeightPx,
+ bounds.set(0, edgeMarginPx, normalSearchBarSpaceHeightPx,
availableHeightPx - edgeMarginPx);
}
} else {
+ int boundsBottom = searchBarSpaceHeightPx + getSearchBarTotalVerticalPadding();
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
@@ -267,14 +314,13 @@
// that into account here too.
int gap = (int) ((width - 2 * edgeMarginPx -
(inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
- bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(),
- availableWidthPx - (edgeMarginPx + gap),
- searchBarSpaceHeightPx);
+ bounds.set(edgeMarginPx + gap, 0,
+ availableWidthPx - (edgeMarginPx + gap), boundsBottom);
} else {
bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
- getSearchBarTopOffset(),
+ 0,
availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
- defaultWidgetPadding.right), searchBarSpaceHeightPx);
+ defaultWidgetPadding.right), boundsBottom);
}
}
return bounds;
@@ -284,43 +330,46 @@
Rect getWorkspacePadding(boolean isLayoutRtl) {
Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
Rect padding = new Rect();
- if (isLandscape && transposeLayoutWithOrientation) {
+ if (isVerticalBarLayout()) {
// Pad the left and right of the workspace with search/hotseat bar sizes
if (isLayoutRtl) {
- padding.set(hotseatBarHeightPx, edgeMarginPx,
+ padding.set(normalHotseatBarHeightPx, edgeMarginPx,
searchBarBounds.width(), edgeMarginPx);
} else {
padding.set(searchBarBounds.width(), edgeMarginPx,
- hotseatBarHeightPx, edgeMarginPx);
+ normalHotseatBarHeightPx, edgeMarginPx);
}
} else {
+ int paddingTop = searchBarBounds.bottom;
+ int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
if (isTablet) {
// Pad the left and right of the workspace to ensure consistent spacing
// between all icons
float gapScale = 1f + (dragViewScale - 1f) / 2f;
int width = getCurrentWidth();
int height = getCurrentHeight();
- int paddingTop = searchBarBounds.bottom;
- int paddingBottom = hotseatBarHeightPx + pageIndicatorHeightPx;
- int availableWidth = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
- (inv.numColumns * gapScale * cellWidthPx)));
- int availableHeight = Math.max(0, height - paddingTop - paddingBottom
+ // The amount of screen space available for left/right padding.
+ int availablePaddingX = Math.max(0, width - (int) ((inv.numColumns * cellWidthPx) +
+ ((inv.numColumns - 1) * gapScale * cellWidthPx)));
+ availablePaddingX = (int) Math.min(availablePaddingX,
+ width * MAX_HORIZONTAL_PADDING_PERCENT);
+ int availablePaddingY = Math.max(0, height - paddingTop - paddingBottom
- (int) (2 * inv.numRows * cellHeightPx));
- padding.set(availableWidth / 2, paddingTop + availableHeight / 2,
- availableWidth / 2, paddingBottom + availableHeight / 2);
+ padding.set(availablePaddingX / 2, paddingTop + availablePaddingY / 2,
+ availablePaddingX / 2, paddingBottom + availablePaddingY / 2);
} else {
// Pad the top and bottom of the workspace with search/hotseat bar sizes
padding.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
- searchBarBounds.bottom,
+ paddingTop,
desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.right,
- hotseatBarHeightPx + pageIndicatorHeightPx);
+ paddingBottom);
}
}
return padding;
}
private int getWorkspacePageSpacing(boolean isLayoutRtl) {
- if ((isLandscape && transposeLayoutWithOrientation) || isLargeTablet) {
+ if (isVerticalBarLayout() || isLargeTablet) {
// In landscape mode the page spacing is set to the default.
return defaultPageSpacingPx;
} else {
@@ -341,7 +390,7 @@
// The rect returned will be extended to below the system ui that covers the workspace
Rect getHotseatRect() {
if (isVerticalBarLayout()) {
- return new Rect(availableWidthPx - hotseatBarHeightPx, 0,
+ return new Rect(availableWidthPx - normalHotseatBarHeightPx, 0,
Integer.MAX_VALUE, availableHeightPx);
} else {
return new Rect(0, availableHeightPx - hotseatBarHeightPx,
@@ -357,8 +406,9 @@
}
/**
- * When {@code true}, hotseat is on the bottom row when in landscape mode.
- * If {@code false}, hotseat is on the right column when in landscape mode.
+ * When {@code true}, the device is in landscape mode and the hotseat is on the right column.
+ * When {@code false}, either device is in portrait mode or the device is in landscape mode and
+ * the hotseat is on the bottom row.
*/
boolean isVerticalBarLayout() {
return isLandscape && transposeLayoutWithOrientation;
@@ -378,19 +428,38 @@
return visibleChildren;
}
+ // TODO(twickham): b/25154513
+ public void setSearchBarHeight(int searchBarHeight) {
+ if (searchBarHeight == LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL) {
+ hotseatBarHeightPx = shortHotseatBarHeightPx;
+ searchBarSpaceHeightPx = tallSearchBarSpaceHeightPx;
+ searchBarBottomPaddingPx = tallSearchBarBottomPaddingPx;
+ searchBarTopExtraPaddingPx = isPhone ? tallSearchBarNegativeTopPaddingPx
+ : normalSearchBarTopExtraPaddingPx;
+ } else {
+ hotseatBarHeightPx = normalHotseatBarHeightPx;
+ searchBarSpaceHeightPx = normalSearchBarSpaceHeightPx;
+ searchBarBottomPaddingPx = normalSearchBarBottomPaddingPx;
+ searchBarTopExtraPaddingPx = normalSearchBarTopExtraPaddingPx;
+ }
+ }
+
public void layout(Launcher launcher) {
FrameLayout.LayoutParams lp;
boolean hasVerticalBarLayout = isVerticalBarLayout();
final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
// Layout the search bar space
+ Rect searchBarBounds = getSearchBarBounds(isLayoutRtl);
View searchBar = launcher.getSearchDropTargetBar();
lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
+ lp.width = searchBarBounds.width();
+ lp.height = searchBarBounds.height();
+ lp.topMargin = searchBarTopExtraPaddingPx;
if (hasVerticalBarLayout) {
// Vertical search bar space -- The search bar is fixed in the layout to be on the left
// of the screen regardless of RTL
lp.gravity = Gravity.LEFT;
- lp.width = searchBarSpaceHeightPx;
LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
targets.setOrientation(LinearLayout.VERTICAL);
@@ -400,11 +469,7 @@
} else {
// Horizontal search bar space
- lp.gravity = Gravity.TOP;
- lp.height = searchBarSpaceHeightPx;
-
- LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
- targets.getLayoutParams().width = searchBarSpaceWidthPx;
+ lp.gravity = Gravity.TOP|Gravity.CENTER_HORIZONTAL;
}
searchBar.setLayoutParams(lp);
@@ -420,11 +485,18 @@
// Layout the hotseat
View hotseat = launcher.findViewById(R.id.hotseat);
lp = (FrameLayout.LayoutParams) hotseat.getLayoutParams();
+ // We want the edges of the hotseat to line up with the edges of the workspace, but the
+ // icons in the hotseat are a different size, and so don't line up perfectly. To account for
+ // this, we pad the left and right of the hotseat with half of the difference of a workspace
+ // cell vs a hotseat cell.
+ float workspaceCellWidth = (float) getCurrentWidth() / inv.numColumns;
+ float hotseatCellWidth = (float) getCurrentWidth() / inv.numHotseatIcons;
+ int hotseatAdjustment = Math.round((workspaceCellWidth - hotseatCellWidth) / 2);
if (hasVerticalBarLayout) {
// Vertical hotseat -- The hotseat is fixed in the layout to be on the right of the
// screen regardless of RTL
lp.gravity = Gravity.RIGHT;
- lp.width = hotseatBarHeightPx;
+ lp.width = normalHotseatBarHeightPx;
lp.height = LayoutParams.MATCH_PARENT;
hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
} else if (isTablet) {
@@ -432,17 +504,18 @@
lp.gravity = Gravity.BOTTOM;
lp.width = LayoutParams.MATCH_PARENT;
lp.height = hotseatBarHeightPx;
- hotseat.setPadding(edgeMarginPx + padding.left, 0,
- edgeMarginPx + padding.right,
- 2 * edgeMarginPx);
+ hotseat.findViewById(R.id.layout).setPadding(
+ hotseatAdjustment + padding.left, 0,
+ hotseatAdjustment + padding.right, 2 * edgeMarginPx);
} else {
// For phones, layout the hotseat without any bottom margin
// to ensure that we have space for the folders
lp.gravity = Gravity.BOTTOM;
lp.width = LayoutParams.MATCH_PARENT;
lp.height = hotseatBarHeightPx;
- hotseat.findViewById(R.id.layout).setPadding(2 * edgeMarginPx, 0,
- 2 * edgeMarginPx, 0);
+ hotseat.findViewById(R.id.layout).setPadding(
+ hotseatAdjustment + padding.left, 0,
+ hotseatAdjustment + padding.right, 0);
}
hotseat.setLayoutParams(lp);
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 2191455..2f0bfe5 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -32,8 +32,10 @@
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -145,8 +147,6 @@
/**
* Used to create a new DragLayer from XML.
- *
- * @param context The application's context.
*/
public DragController(Launcher launcher) {
Resources r = launcher.getResources();
@@ -239,6 +239,9 @@
mDragObject = new DropTarget.DragObject();
+ final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
+ registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
+
mDragObject.dragComplete = false;
if (mIsAccessibleDrag) {
// For an accessible drag, we assume the view is being dragged from the center.
@@ -248,14 +251,12 @@
} else {
mDragObject.xOffset = mMotionDownX - (dragLayerX + dragRegionLeft);
mDragObject.yOffset = mMotionDownY - (dragLayerY + dragRegionTop);
+ mDragObject.stateAnnouncer = DragViewStateAnnouncer.createFor(dragView);
}
mDragObject.dragSource = source;
mDragObject.dragInfo = dragInfo;
- final DragView dragView = mDragObject.dragView = new DragView(mLauncher, b, registrationX,
- registrationY, 0, 0, b.getWidth(), b.getHeight(), initialDragViewScale);
-
if (dragOffset != null) {
dragView.setDragVisualizeOffset(new Point(dragOffset));
}
@@ -340,15 +341,15 @@
}
endDrag();
}
- public void onAppsRemoved(final ArrayList<String> packageNames, HashSet<ComponentName> cns) {
+
+ public void onAppsRemoved(final HashSet<String> packageNames, HashSet<ComponentName> cns) {
// Cancel the current drag if we are removing an app that we are dragging
if (mDragObject != null) {
Object rawDragInfo = mDragObject.dragInfo;
if (rawDragInfo instanceof ShortcutInfo) {
ShortcutInfo dragInfo = (ShortcutInfo) rawDragInfo;
for (ComponentName componentName : cns) {
- // Added null checks to prevent NPE we've seen in the wild
- if (dragInfo != null && dragInfo.intent != null) {
+ if (dragInfo.intent != null) {
ComponentName cn = dragInfo.intent.getComponent();
boolean isSameComponent = cn != null && (cn.equals(componentName) ||
packageNames.contains(cn.getPackageName()));
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 1c18747..ad9063c 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -565,10 +565,6 @@
resizeFrame.snapToWidget(false);
}
- public void animateViewIntoPosition(DragView dragView, final View child) {
- animateViewIntoPosition(dragView, child, null, null);
- }
-
public void animateViewIntoPosition(DragView dragView, final int[] pos, float alpha,
float scaleX, float scaleY, int animationEndStyle, Runnable onFinishRunnable,
int duration) {
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index 2acfc61..a584667 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -321,10 +321,10 @@
setTranslationY(touchY - mRegistrationY);
// Post the animation to skip other expensive work happening on the first frame
post(new Runnable() {
- public void run() {
- mAnim.start();
- }
- });
+ public void run() {
+ mAnim.start();
+ }
+ });
}
public void cancelAnimation() {
diff --git a/src/com/android/launcher3/DropTarget.java b/src/com/android/launcher3/DropTarget.java
index c8fac54..4340591 100644
--- a/src/com/android/launcher3/DropTarget.java
+++ b/src/com/android/launcher3/DropTarget.java
@@ -19,6 +19,8 @@
import android.graphics.PointF;
import android.graphics.Rect;
+import com.android.launcher3.accessibility.DragViewStateAnnouncer;
+
/**
* Interface defining an object that can receive a drag.
*
@@ -64,6 +66,8 @@
/** Defers removing the DragView from the DragLayer until after the drop animation. */
public boolean deferDragViewCleanupPostAnimation = true;
+ public DragViewStateAnnouncer stateAnnouncer;
+
public DragObject() {
}
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 28e923e..d7f1d86 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.graphics.Bitmap;
@@ -28,13 +29,40 @@
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.SparseArray;
+import android.view.animation.DecelerateInterpolator;
public class FastBitmapDrawable extends Drawable {
- static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
+ /**
+ * The possible states that a FastBitmapDrawable can be in.
+ */
+ public enum State {
+
+ NORMAL (0f, 0f, 1f, new DecelerateInterpolator()),
+ PRESSED (0f, 100f / 255f, 1f, CLICK_FEEDBACK_INTERPOLATOR),
+ FAST_SCROLL_HIGHLIGHTED (0f, 0f, 1.15f, new DecelerateInterpolator()),
+ FAST_SCROLL_UNHIGHLIGHTED (0f, 0f, 1f, new DecelerateInterpolator()),
+ DISABLED (1f, 0.5f, 1f, new DecelerateInterpolator());
+
+ public final float desaturation;
+ public final float brightness;
+ /**
+ * Used specifically by the view drawing this FastBitmapDrawable.
+ */
+ public final float viewScale;
+ public final TimeInterpolator interpolator;
+
+ State(float desaturation, float brightness, float viewScale, TimeInterpolator interpolator) {
+ this.desaturation = desaturation;
+ this.brightness = brightness;
+ this.viewScale = viewScale;
+ this.interpolator = interpolator;
+ }
+ }
+
+ public static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
@Override
public float getInterpolation(float input) {
@@ -47,42 +75,46 @@
}
}
};
- static final long CLICK_FEEDBACK_DURATION = 2000;
+ public static final int CLICK_FEEDBACK_DURATION = 2000;
+ public static final int FAST_SCROLL_HIGHLIGHT_DURATION = 225;
+ public static final int FAST_SCROLL_UNHIGHLIGHT_DURATION = 150;
+ public static final int FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION = 225;
+ public static final int FAST_SCROLL_INACTIVE_DURATION = 275;
- private static final int PRESSED_BRIGHTNESS = 100;
- private static ColorMatrix sGhostModeMatrix;
- private static final ColorMatrix sTempMatrix = new ColorMatrix();
+ // Since we don't need 256^2 values for combinations of both the brightness and saturation, we
+ // reduce the value space to a smaller value V, which reduces the number of cached
+ // ColorMatrixColorFilters that we need to keep to V^2
+ private static final int REDUCED_FILTER_VALUE_SPACE = 48;
- /**
- * Store the brightness colors filters to optimize animations during icon press. This
- * only works for non-ghost-mode icons.
- */
- private static final SparseArray<ColorFilter> sCachedBrightnessFilter =
- new SparseArray<ColorFilter>();
+ // A cache of ColorFilters for optimizing brightness and saturation animations
+ private static final SparseArray<ColorFilter> sCachedFilter = new SparseArray<>();
- private static final int GHOST_MODE_MIN_COLOR_RANGE = 130;
+ // Temporary matrices used for calculation
+ private static final ColorMatrix sTempBrightnessMatrix = new ColorMatrix();
+ private static final ColorMatrix sTempFilterMatrix = new ColorMatrix();
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
private final Bitmap mBitmap;
- private int mAlpha;
+ private State mState = State.NORMAL;
+ // The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
+ // as a result, can be used to compose the key for the cached ColorMatrixColorFilters
+ private int mDesaturation = 0;
private int mBrightness = 0;
- private boolean mGhostModeEnabled = false;
+ private int mAlpha = 255;
+ private int mPrevUpdateKey = Integer.MAX_VALUE;
- private boolean mPressed = false;
- private ObjectAnimator mPressedAnimator;
+ // Animators for the fast bitmap drawable's properties
+ private AnimatorSet mPropertyAnimator;
public FastBitmapDrawable(Bitmap b) {
- mAlpha = 255;
mBitmap = b;
setBounds(0, 0, b.getWidth(), b.getHeight());
}
@Override
public void draw(Canvas canvas) {
- final Rect r = getBounds();
- // Draw the bitmap into the bounding rect
- canvas.drawBitmap(mBitmap, null, r, mPaint);
+ canvas.drawBitmap(mBitmap, null, getBounds(), mPaint);
}
@Override
@@ -136,96 +168,191 @@
}
/**
- * When enabled, the icon is grayed out and the contrast is increased to give it a 'ghost'
- * appearance.
+ * Animates this drawable to a new state.
+ *
+ * @return whether the state has changed.
*/
- public void setGhostModeEnabled(boolean enabled) {
- if (mGhostModeEnabled != enabled) {
- mGhostModeEnabled = enabled;
+ public boolean animateState(State newState) {
+ State prevState = mState;
+ if (mState != newState) {
+ mState = newState;
+
+ mPropertyAnimator = cancelAnimator(mPropertyAnimator);
+ mPropertyAnimator = new AnimatorSet();
+ mPropertyAnimator.playTogether(
+ ObjectAnimator
+ .ofFloat(this, "desaturation", newState.desaturation),
+ ObjectAnimator
+ .ofFloat(this, "brightness", newState.brightness));
+ mPropertyAnimator.setInterpolator(newState.interpolator);
+ mPropertyAnimator.setDuration(getDurationForStateChange(prevState, newState));
+ mPropertyAnimator.setStartDelay(getStartDelayForStateChange(prevState, newState));
+ mPropertyAnimator.start();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Immediately sets this drawable to a new state.
+ *
+ * @return whether the state has changed.
+ */
+ public boolean setState(State newState) {
+ if (mState != newState) {
+ mState = newState;
+
+ mPropertyAnimator = cancelAnimator(mPropertyAnimator);
+
+ setDesaturation(newState.desaturation);
+ setBrightness(newState.brightness);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the current state.
+ */
+ public State getCurrentState() {
+ return mState;
+ }
+
+ /**
+ * Returns the duration for the state change animation.
+ */
+ public static int getDurationForStateChange(State fromState, State toState) {
+ switch (toState) {
+ case NORMAL:
+ switch (fromState) {
+ case PRESSED:
+ return 0;
+ case FAST_SCROLL_HIGHLIGHTED:
+ case FAST_SCROLL_UNHIGHLIGHTED:
+ return FAST_SCROLL_INACTIVE_DURATION;
+ }
+ case PRESSED:
+ return CLICK_FEEDBACK_DURATION;
+ case FAST_SCROLL_HIGHLIGHTED:
+ return FAST_SCROLL_HIGHLIGHT_DURATION;
+ case FAST_SCROLL_UNHIGHLIGHTED:
+ switch (fromState) {
+ case NORMAL:
+ // When animating from normal state, take a little longer
+ return FAST_SCROLL_UNHIGHLIGHT_FROM_NORMAL_DURATION;
+ default:
+ return FAST_SCROLL_UNHIGHLIGHT_DURATION;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the start delay when animating between certain fast scroll states.
+ */
+ public static int getStartDelayForStateChange(State fromState, State toState) {
+ switch (toState) {
+ case FAST_SCROLL_UNHIGHLIGHTED:
+ switch (fromState) {
+ case NORMAL:
+ return FAST_SCROLL_UNHIGHLIGHT_DURATION / 4;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Sets the saturation of this icon, 0 [full color] -> 1 [desaturated]
+ */
+ public void setDesaturation(float desaturation) {
+ int newDesaturation = (int) Math.floor(desaturation * REDUCED_FILTER_VALUE_SPACE);
+ if (mDesaturation != newDesaturation) {
+ mDesaturation = newDesaturation;
updateFilter();
}
}
- public void setPressed(boolean pressed) {
- if (mPressed != pressed) {
- mPressed = pressed;
- if (mPressed) {
- mPressedAnimator = ObjectAnimator
- .ofInt(this, "brightness", PRESSED_BRIGHTNESS)
- .setDuration(CLICK_FEEDBACK_DURATION);
- mPressedAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
- mPressedAnimator.start();
- } else if (mPressedAnimator != null) {
- mPressedAnimator.cancel();
- setBrightness(0);
- }
- }
- invalidateSelf();
+ public float getDesaturation() {
+ return (float) mDesaturation / REDUCED_FILTER_VALUE_SPACE;
}
- public boolean isGhostModeEnabled() {
- return mGhostModeEnabled;
- }
-
- public int getBrightness() {
- return mBrightness;
- }
-
- public void setBrightness(int brightness) {
- if (mBrightness != brightness) {
- mBrightness = brightness;
+ /**
+ * Sets the brightness of this icon, 0 [no add. brightness] -> 1 [2bright2furious]
+ */
+ public void setBrightness(float brightness) {
+ int newBrightness = (int) Math.floor(brightness * REDUCED_FILTER_VALUE_SPACE);
+ if (mBrightness != newBrightness) {
+ mBrightness = newBrightness;
updateFilter();
- invalidateSelf();
}
}
+ public float getBrightness() {
+ return (float) mBrightness / REDUCED_FILTER_VALUE_SPACE;
+ }
+
+ /**
+ * Updates the paint to reflect the current brightness and saturation.
+ */
private void updateFilter() {
- if (mGhostModeEnabled) {
- if (sGhostModeMatrix == null) {
- sGhostModeMatrix = new ColorMatrix();
- sGhostModeMatrix.setSaturation(0);
+ boolean usePorterDuffFilter = false;
+ int key = -1;
+ if (mDesaturation > 0) {
+ key = (mDesaturation << 16) | mBrightness;
+ } else if (mBrightness > 0) {
+ // Compose a key with a fully saturated icon if we are just animating brightness
+ key = (1 << 16) | mBrightness;
- // For ghost mode, set the color range to [GHOST_MODE_MIN_COLOR_RANGE, 255]
- float range = (255 - GHOST_MODE_MIN_COLOR_RANGE) / 255.0f;
- sTempMatrix.set(new float[] {
- range, 0, 0, 0, GHOST_MODE_MIN_COLOR_RANGE,
- 0, range, 0, 0, GHOST_MODE_MIN_COLOR_RANGE,
- 0, 0, range, 0, GHOST_MODE_MIN_COLOR_RANGE,
- 0, 0, 0, 1, 0 });
- sGhostModeMatrix.preConcat(sTempMatrix);
- }
+ // We found that in L, ColorFilters cause drawing artifacts with shadows baked into
+ // icons, so just use a PorterDuff filter when we aren't animating saturation
+ usePorterDuffFilter = true;
+ }
- if (mBrightness == 0) {
- mPaint.setColorFilter(new ColorMatrixColorFilter(sGhostModeMatrix));
- } else {
- setBrightnessMatrix(sTempMatrix, mBrightness);
- sTempMatrix.postConcat(sGhostModeMatrix);
- mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
- }
- } else if (mBrightness != 0) {
- ColorFilter filter = sCachedBrightnessFilter.get(mBrightness);
+ // Debounce multiple updates on the same frame
+ if (key == mPrevUpdateKey) {
+ return;
+ }
+ mPrevUpdateKey = key;
+
+ if (key != -1) {
+ ColorFilter filter = sCachedFilter.get(key);
if (filter == null) {
- filter = new PorterDuffColorFilter(Color.argb(mBrightness, 255, 255, 255),
- PorterDuff.Mode.SRC_ATOP);
- sCachedBrightnessFilter.put(mBrightness, filter);
+ float brightnessF = getBrightness();
+ int brightnessI = (int) (255 * brightnessF);
+ if (usePorterDuffFilter) {
+ filter = new PorterDuffColorFilter(Color.argb(brightnessI, 255, 255, 255),
+ PorterDuff.Mode.SRC_ATOP);
+ } else {
+ float saturationF = 1f - getDesaturation();
+ sTempFilterMatrix.setSaturation(saturationF);
+ if (mBrightness > 0) {
+ // Brightness: C-new = C-old*(1-amount) + amount
+ float scale = 1f - brightnessF;
+ float[] mat = sTempBrightnessMatrix.getArray();
+ mat[0] = scale;
+ mat[6] = scale;
+ mat[12] = scale;
+ mat[4] = brightnessI;
+ mat[9] = brightnessI;
+ mat[14] = brightnessI;
+ sTempFilterMatrix.preConcat(sTempBrightnessMatrix);
+ }
+ filter = new ColorMatrixColorFilter(sTempFilterMatrix);
+ }
+ sCachedFilter.append(key, filter);
}
mPaint.setColorFilter(filter);
} else {
mPaint.setColorFilter(null);
}
+ invalidateSelf();
}
- private static void setBrightnessMatrix(ColorMatrix matrix, int brightness) {
- // Brightness: C-new = C-old*(1-amount) + amount
- float scale = 1 - brightness / 255.0f;
- matrix.setScale(scale, scale, scale, 1);
- float[] array = matrix.getArray();
-
- // Add the amount to RGB components of the matrix, as per the above formula.
- // Fifth elements in the array correspond to the constant being added to
- // red, blue, green, and alpha channel respectively.
- array[4] = brightness;
- array[9] = brightness;
- array[14] = brightness;
+ private AnimatorSet cancelAnimator(AnimatorSet animator) {
+ if (animator != null) {
+ animator.removeAllListeners();
+ animator.cancel();
+ }
+ return null;
}
}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index 57aec32..d59c644 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -45,6 +45,23 @@
}
}
+/**
+ * A keyboard listener we set on full screen pages (e.g. custom content).
+ */
+class FullscreenKeyEventListener implements View.OnKeyListener {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT || keyCode == KeyEvent.KEYCODE_DPAD_RIGHT
+ || keyCode == KeyEvent.KEYCODE_PAGE_DOWN || keyCode == KeyEvent.KEYCODE_PAGE_UP) {
+ // Handle the key event just like a workspace icon would in these cases. In this case,
+ // it will basically act as if there is a single icon in the top left (so you could
+ // think of the fullscreen page as a focusable fullscreen widget).
+ return FocusHelper.handleIconKeyEvent(v, keyCode, event);
+ }
+ return false;
+ }
+}
+
public class FocusHelper {
private static final String TAG = "FocusHelper";
@@ -72,7 +89,6 @@
KeyEvent.keyCodeToString(keyCode)));
}
-
if (!(v.getParent() instanceof ShortcutAndWidgetContainer)) {
if (LauncherAppState.isDogfoodBuild()) {
throw new IllegalStateException("Parent of the focused item is not supported.");
@@ -84,8 +100,6 @@
// Initialize variables.
final ShortcutAndWidgetContainer itemContainer = (ShortcutAndWidgetContainer) v.getParent();
final CellLayout cellLayout = (CellLayout) itemContainer.getParent();
- final int countX = cellLayout.getCountX();
- final int countY = cellLayout.getCountY();
final int iconIndex = itemContainer.indexOfChild(v);
final FolderPagedView pagedView = (FolderPagedView) cellLayout.getParent();
@@ -96,8 +110,8 @@
int[][] matrix = FocusLogic.createSparseMatrix(cellLayout);
// Process focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
- countY, matrix, iconIndex, pageIndex, pageCount, isLayoutRtl);
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
+ pageCount, isLayoutRtl);
if (newIconIndex == FocusLogic.NOOP) {
handleNoopKey(keyCode, v);
return consume;
@@ -114,7 +128,8 @@
pagedView.snapToPage(pageIndex - 1);
child = newParent.getChildAt(
((newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN)
- ^ newParent.invertLayoutHorizontally()) ? 0 : countX - 1, row);
+ ^ newParent.invertLayoutHorizontally()) ? 0 : matrix.length - 1,
+ row);
}
break;
case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
@@ -128,7 +143,7 @@
newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex - 1);
if (newParent != null) {
pagedView.snapToPage(pageIndex - 1);
- child = newParent.getChildAt(countX - 1, countY - 1);
+ child = newParent.getChildAt(matrix.length - 1, matrix[0].length - 1);
}
break;
case FocusLogic.NEXT_PAGE_FIRST_ITEM:
@@ -143,7 +158,8 @@
newParent = getCellLayoutChildrenForIndex(pagedView, pageIndex + 1);
if (newParent != null) {
pagedView.snapToPage(pageIndex + 1);
- child = FocusLogic.getAdjacentChildInNextPage(newParent, v, newIconIndex);
+ child = FocusLogic.getAdjacentChildInNextFolderPage(
+ newParent, v, newIconIndex);
}
break;
case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
@@ -174,7 +190,7 @@
}
/**
- * Handles key events in the workspace hot seat (bottom of the screen).
+ * Handles key events in the workspace hotseat (bottom of the screen).
* <p>Currently we don't special case for the phone UI in different orientations, even though
* the hotseat is on the side in landscape mode. This is to ensure that accessibility
* consistency is maintained across rotations.
@@ -185,7 +201,8 @@
return consume;
}
- DeviceProfile profile = ((Launcher) v.getContext()).getDeviceProfile();
+ final Launcher launcher = (Launcher) v.getContext();
+ final DeviceProfile profile = launcher.getDeviceProfile();
if (DEBUG) {
Log.v(TAG, String.format(
@@ -194,15 +211,13 @@
}
// Initialize the variables.
+ final Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
final ShortcutAndWidgetContainer hotseatParent = (ShortcutAndWidgetContainer) v.getParent();
final CellLayout hotseatLayout = (CellLayout) hotseatParent.getParent();
- Hotseat hotseat = (Hotseat) hotseatLayout.getParent();
- Workspace workspace = (Workspace) v.getRootView().findViewById(R.id.workspace);
+ final ItemInfo itemInfo = (ItemInfo) v.getTag();
int pageIndex = workspace.getNextPage();
int pageCount = workspace.getChildCount();
- int countX = -1;
- int countY = -1;
int iconIndex = hotseatParent.indexOfChild(v);
int iconRank = ((CellLayout.LayoutParams) hotseatLayout.getShortcutsAndWidgets()
.getChildAt(iconIndex).getLayoutParams()).cellX;
@@ -221,51 +236,91 @@
if (keyCode == KeyEvent.KEYCODE_DPAD_UP &&
!profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
- true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank,
- iconRank == profile.inv.hotseatAllAppsRank /* include all apps icon */);
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ true /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
iconIndex += iconParent.getChildCount();
- countX = iconLayout.getCountX();
- countY = iconLayout.getCountY() + hotseatLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT &&
profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout,
- false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank,
- iconRank == profile.inv.hotseatAllAppsRank /* include all apps icon */);
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ false /* hotseat horizontal */, profile.inv.hotseatAllAppsRank);
iconIndex += iconParent.getChildCount();
- countX = iconLayout.getCountX() + hotseatLayout.getCountX();
- countY = iconLayout.getCountY();
parent = iconParent;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
profile.isVerticalBarLayout()) {
keyCode = KeyEvent.KEYCODE_PAGE_DOWN;
- }else {
+ } else if (isUninstallKeyChord(e)) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout);
+ if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
+ UninstallDropTarget.startUninstallActivity(launcher, itemInfo);
+ }
+ } else if (isDeleteKeyChord(e)) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout);
+ launcher.removeItem(v, itemInfo, true /* deleteFromDb */);
+ } else {
// For other KEYCODE_DPAD_LEFT and KEYCODE_DPAD_RIGHT navigation, do not use the
// matrix extended with hotseat.
matrix = FocusLogic.createSparseMatrix(hotseatLayout);
- countX = hotseatLayout.getCountX();
- countY = hotseatLayout.getCountY();
parent = hotseatParent;
}
// Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
- countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
+ pageCount, Utilities.isRtl(v.getResources()));
View newIcon = null;
- if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
- newIcon = parent.getChildAt(0);
- // TODO(hyunyoungs): handle cases where the child is not an icon but
- // a folder or a widget.
- workspace.snapToPage(pageIndex + 1);
+ switch (newIconIndex) {
+ case FocusLogic.NEXT_PAGE_FIRST_ITEM:
+ parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
+ newIcon = parent.getChildAt(0);
+ // TODO(hyunyoungs): handle cases where the child is not an icon but
+ // a folder or a widget.
+ workspace.snapToPage(pageIndex + 1);
+ break;
+ case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
+ parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+ newIcon = parent.getChildAt(0);
+ // TODO(hyunyoungs): handle cases where the child is not an icon but
+ // a folder or a widget.
+ workspace.snapToPage(pageIndex - 1);
+ break;
+ case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
+ parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
+ newIcon = parent.getChildAt(parent.getChildCount() - 1);
+ // TODO(hyunyoungs): handle cases where the child is not an icon but
+ // a folder or a widget.
+ workspace.snapToPage(pageIndex - 1);
+ break;
+ case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
+ case FocusLogic.PREVIOUS_PAGE_RIGHT_COLUMN:
+ // Go to the previous page but keep the focus on the same hotseat icon.
+ workspace.snapToPage(pageIndex - 1);
+ // If the page we are going to is fullscreen, have it take the focus from hotseat.
+ CellLayout prevPage = (CellLayout) workspace.getPageAt(pageIndex - 1);
+ boolean isPrevPageFullscreen = ((CellLayout.LayoutParams) prevPage
+ .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
+ if (isPrevPageFullscreen) {
+ workspace.getPageAt(pageIndex - 1).requestFocus();
+ }
+ break;
+ case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
+ case FocusLogic.NEXT_PAGE_RIGHT_COLUMN:
+ // Go to the next page but keep the focus on the same hotseat icon.
+ workspace.snapToPage(pageIndex + 1);
+ // If the page we are going to is fullscreen, have it take the focus from hotseat.
+ CellLayout nextPage = (CellLayout) workspace.getPageAt(pageIndex + 1);
+ boolean isNextPageFullscreen = ((CellLayout.LayoutParams) nextPage
+ .getShortcutsAndWidgets().getChildAt(0).getLayoutParams()).isFullscreen;
+ if (isNextPageFullscreen) {
+ workspace.getPageAt(pageIndex + 1).requestFocus();
+ }
+ break;
}
if (parent == iconParent && newIconIndex >= iconParent.getChildCount()) {
newIconIndex -= iconParent.getChildCount();
}
if (parent != null) {
- if (newIcon == null && newIconIndex >=0) {
+ if (newIcon == null && newIconIndex >= 0) {
newIcon = parent.getChildAt(newIconIndex);
}
if (newIcon != null) {
@@ -301,11 +356,10 @@
final ViewGroup tabs = (ViewGroup) dragLayer.findViewById(R.id.search_drop_target_bar);
final Hotseat hotseat = (Hotseat) dragLayer.findViewById(R.id.hotseat);
+ final ItemInfo itemInfo = (ItemInfo) v.getTag();
final int iconIndex = parent.indexOfChild(v);
final int pageIndex = workspace.indexOfChild(iconLayout);
final int pageCount = workspace.getChildCount();
- int countX = iconLayout.getCountX();
- int countY = iconLayout.getCountY();
CellLayout hotseatLayout = (CellLayout) hotseat.getChildAt(0);
ShortcutAndWidgetContainer hotseatParent = hotseatLayout.getShortcutsAndWidgets();
@@ -315,27 +369,30 @@
// to take a user to the hotseat. For other dpad navigation, do not use the matrix extended
// with the hotseat.
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN && !profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, true /* horizontal */,
- profile.inv.hotseatAllAppsRank,
- !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
- countY = countY + 1;
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ true /* horizontal */, profile.inv.hotseatAllAppsRank);
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT &&
profile.isVerticalBarLayout()) {
- matrix = FocusLogic.createSparseMatrix(iconLayout, hotseatLayout, false /* horizontal */,
- profile.inv.hotseatAllAppsRank,
- !hotseat.hasIcons() /* ignore all apps icon, unless there are no other icons */);
- countX = countX + 1;
- } else if (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) {
- workspace.removeWorkspaceItem(v);
- return consume;
+ matrix = FocusLogic.createSparseMatrixWithHotseat(iconLayout, hotseatLayout,
+ false /* horizontal */, profile.inv.hotseatAllAppsRank);
+ } else if (isUninstallKeyChord(e)) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout);
+ if (UninstallDropTarget.supportsDrop(launcher, itemInfo)) {
+ UninstallDropTarget.startUninstallActivity(launcher, itemInfo);
+ }
+ } else if (isDeleteKeyChord(e)) {
+ matrix = FocusLogic.createSparseMatrix(iconLayout);
+ launcher.removeItem(v, itemInfo, true /* deleteFromDb */);
} else {
matrix = FocusLogic.createSparseMatrix(iconLayout);
}
// Process the focus.
- int newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX,
- countY, matrix, iconIndex, pageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ int newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, iconIndex, pageIndex,
+ pageCount, Utilities.isRtl(v.getResources()));
+ boolean isRtl = Utilities.isRtl(v.getResources());
View newIcon = null;
+ CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex);
switch (newIconIndex) {
case FocusLogic.NOOP:
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
@@ -350,32 +407,37 @@
}
int row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
- workspace.snapToPage(newPageIndex);
if (parent != null) {
- workspace.snapToPage(newPageIndex);
iconLayout = (CellLayout) parent.getParent();
- matrix = FocusLogic.createSparseMatrix(iconLayout,
- iconLayout.getCountX(), row);
- newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
- matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
- Utilities.isRtl(v.getResources()));
- newIcon = parent.getChildAt(newIconIndex);
+ matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout,
+ iconLayout.getCountX(), row);
+ newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
+ newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
+ newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
+ newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else {
+ newIcon = parent.getChildAt(newIconIndex);
+ }
}
break;
case FocusLogic.PREVIOUS_PAGE_FIRST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
- newIcon = parent.getChildAt(0);
- workspace.snapToPage(pageIndex - 1);
+ workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
+ newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
+ workspace.snapToPage(pageIndex - 1);
+ }
break;
case FocusLogic.PREVIOUS_PAGE_LAST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
- newIcon = parent.getChildAt(parent.getChildCount() - 1);
- workspace.snapToPage(pageIndex - 1);
+ newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex, isRtl);
break;
case FocusLogic.NEXT_PAGE_FIRST_ITEM:
- parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
- newIcon = parent.getChildAt(0);
- workspace.snapToPage(pageIndex + 1);
+ newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex, isRtl);
break;
case FocusLogic.NEXT_PAGE_LEFT_COLUMN:
case FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN:
@@ -383,24 +445,37 @@
if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LEFT_COLUMN) {
newPageIndex = pageIndex - 1;
}
- workspace.snapToPage(newPageIndex);
row = ((CellLayout.LayoutParams) v.getLayoutParams()).cellY;
parent = getCellLayoutChildrenForIndex(workspace, newPageIndex);
if (parent != null) {
- workspace.snapToPage(newPageIndex);
iconLayout = (CellLayout) parent.getParent();
- matrix = FocusLogic.createSparseMatrix(iconLayout, -1, row);
- newIconIndex = FocusLogic.handleKeyEvent(keyCode, countX + 1, countY,
- matrix, FocusLogic.PIVOT, newPageIndex, pageCount,
- Utilities.isRtl(v.getResources()));
- newIcon = parent.getChildAt(newIconIndex);
+ matrix = FocusLogic.createSparseMatrixWithPivotColumn(iconLayout, -1, row);
+ newIconIndex = FocusLogic.handleKeyEvent(keyCode, matrix, FocusLogic.PIVOT,
+ newPageIndex, pageCount, Utilities.isRtl(v.getResources()));
+ if (newIconIndex == FocusLogic.NEXT_PAGE_FIRST_ITEM) {
+ newIcon = handleNextPageFirstItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else if (newIconIndex == FocusLogic.PREVIOUS_PAGE_LAST_ITEM) {
+ newIcon = handlePreviousPageLastItem(workspace, hotseatLayout, pageIndex,
+ isRtl);
+ } else {
+ newIcon = parent.getChildAt(newIconIndex);
+ }
}
break;
case FocusLogic.CURRENT_PAGE_FIRST_ITEM:
- newIcon = parent.getChildAt(0);
+ newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
+ }
break;
case FocusLogic.CURRENT_PAGE_LAST_ITEM:
- newIcon = parent.getChildAt(parent.getChildCount() - 1);
+ newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout, isRtl);
+ }
break;
default:
// current page, some item.
@@ -457,4 +532,81 @@
break;
}
}
+
+ /**
+ * Returns whether the key event represents a valid uninstall key chord.
+ */
+ private static boolean isUninstallKeyChord(KeyEvent event) {
+ int keyCode = event.getKeyCode();
+ return (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) &&
+ event.hasModifiers(KeyEvent.META_CTRL_ON | KeyEvent.META_SHIFT_ON);
+ }
+
+ /**
+ * Returns whether the key event represents a valid delete key chord.
+ */
+ private static boolean isDeleteKeyChord(KeyEvent event) {
+ int keyCode = event.getKeyCode();
+ return (keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL) &&
+ event.hasModifiers(KeyEvent.META_CTRL_ON);
+ }
+
+ private static View handlePreviousPageLastItem(Workspace workspace, CellLayout hotseatLayout,
+ int pageIndex, boolean isRtl) {
+ if (pageIndex - 1 < 0) {
+ return null;
+ }
+ CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex - 1);
+ View newIcon = getFirstFocusableIconInReverseReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReverseReadingOrder(hotseatLayout,isRtl);
+ workspace.snapToPage(pageIndex - 1);
+ }
+ return newIcon;
+ }
+
+ private static View handleNextPageFirstItem(Workspace workspace, CellLayout hotseatLayout,
+ int pageIndex, boolean isRtl) {
+ if (pageIndex + 1 >= workspace.getPageCount()) {
+ return null;
+ }
+ CellLayout workspaceLayout = (CellLayout) workspace.getChildAt(pageIndex + 1);
+ View newIcon = getFirstFocusableIconInReadingOrder(workspaceLayout, isRtl);
+ if (newIcon == null) {
+ // Check the hotseat if no focusable item was found on the workspace.
+ newIcon = getFirstFocusableIconInReadingOrder(hotseatLayout, isRtl);
+ workspace.snapToPage(pageIndex + 1);
+ }
+ return newIcon;
+ }
+
+ private static View getFirstFocusableIconInReadingOrder(CellLayout cellLayout, boolean isRtl) {
+ View icon;
+ int countX = cellLayout.getCountX();
+ for (int y = 0; y < cellLayout.getCountY(); y++) {
+ int increment = isRtl ? -1 : 1;
+ for (int x = isRtl ? countX - 1 : 0; 0 <= x && x < countX; x += increment) {
+ if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
+ return icon;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static View getFirstFocusableIconInReverseReadingOrder(CellLayout cellLayout,
+ boolean isRtl) {
+ View icon;
+ int countX = cellLayout.getCountX();
+ for (int y = cellLayout.getCountY() - 1; y >= 0; y--) {
+ int increment = isRtl ? 1 : -1;
+ for (int x = isRtl ? 0 : countX - 1; 0 <= x && x < countX; x += increment) {
+ if ((icon = cellLayout.getChildAt(x, y)) != null && icon.isFocusable()) {
+ return icon;
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
index ecf93e4..58b38eb 100644
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ b/src/com/android/launcher3/FocusIndicatorView.java
@@ -30,6 +30,7 @@
// It can be any number >0. The view is resized using scaleX and scaleY.
static final int DEFAULT_LAYOUT_SIZE = 100;
+
private static final float MIN_VISIBLE_ALPHA = 0.2f;
private static final long ANIM_DURATION = 150;
@@ -41,6 +42,7 @@
private View mLastFocusedView;
private boolean mInitiated;
+ private final OnFocusChangeListener mHideIndicatorOnFocusListener;
private Pair<View, Boolean> mPendingCall;
@@ -52,6 +54,16 @@
super(context, attrs);
setAlpha(0);
setBackgroundColor(getResources().getColor(R.color.focused_background));
+
+ mHideIndicatorOnFocusListener = new OnFocusChangeListener() {
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ if (hasFocus) {
+ endCurrentAnimation();
+ setAlpha(0);
+ }
+ }
+ };
}
@Override
@@ -66,6 +78,13 @@
}
}
+ /**
+ * Sets the alpha of this FocusIndicatorView to 0 when a view with this listener receives focus.
+ */
+ public View.OnFocusChangeListener getHideIndicatorOnFocusListener() {
+ return mHideIndicatorOnFocusListener;
+ }
+
@Override
public void onFocusChange(View v, boolean hasFocus) {
mPendingCall = null;
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index c1aa356..471c324 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -57,6 +57,7 @@
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.UiThreadCircularReveal;
@@ -318,9 +319,10 @@
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
String.format(getContext().getString(R.string.folder_renamed), newTitle));
}
- // In order to clear the focus from the text field, we set the focus on ourself. This
- // ensures that every time the field is clicked, focus is gained, giving reliable behavior.
- requestFocus();
+
+ // This ensures that focus is gained every time the field is clicked, which selects all
+ // the text and brings up the soft keyboard if necessary.
+ mFolderName.clearFocus();
Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
mIsEditingName = false;
@@ -414,13 +416,15 @@
/**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
- * @param context The application's context.
+ * @param launcher The main activity.
*
* @return A new UserFolder.
*/
@SuppressLint("InflateParams")
static Folder fromXml(Launcher launcher) {
- return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, null);
+ return (Folder) launcher.getLayoutInflater().inflate(
+ FeatureFlags.LAUNCHER3_ICON_NORMALIZATION
+ ? R.layout.user_folder_icon_normalized : R.layout.user_folder, null);
}
/**
@@ -451,6 +455,11 @@
mContent.snapToPageImmediately(0);
}
+ // This is set to true in close(), but isn't reset to false until onDropCompleted(). This
+ // leads to an consistent 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;
+
Animator openFolderAnim = null;
final Runnable onCompleteRunnable;
if (!Utilities.ATLEAST_LOLLIPOP) {
@@ -541,10 +550,7 @@
public void onAnimationEnd(Animator animation) {
mState = STATE_OPEN;
- if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
- }
-
+ onCompleteRunnable.run();
mContent.setFocusOnFirstChild();
}
});
@@ -639,9 +645,8 @@
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
- onCloseComplete();
setLayerType(LAYER_TYPE_NONE, null);
- mState = STATE_SMALL;
+ close(true);
}
@Override
public void onAnimationStart(Animator animation) {
@@ -655,6 +660,34 @@
oa.start();
}
+ public void close(boolean wasAnimated) {
+ // TODO: Clear all active animations.
+ DragLayer parent = (DragLayer) getParent();
+ if (parent != null) {
+ parent.removeView(this);
+ }
+ mDragController.removeDropTarget(this);
+ clearFocus();
+ if (wasAnimated) {
+ mFolderIcon.requestFocus();
+ }
+
+ if (mRearrangeOnClose) {
+ rearrangeChildren();
+ mRearrangeOnClose = false;
+ }
+ if (getItemCount() <= 1) {
+ if (!mDragInProgress && !mSuppressFolderDeletion) {
+ replaceFolderWithFinalItem();
+ } else if (mDragInProgress) {
+ mDeleteFolderOnDropCompleted = true;
+ }
+ }
+ mSuppressFolderDeletion = false;
+ clearDragInfo();
+ mState = STATE_SMALL;
+ }
+
public boolean acceptDrop(DragObject d) {
final ItemInfo item = (ItemInfo) d.dragInfo;
final int itemType = item.itemType;
@@ -706,6 +739,11 @@
mReorderAlarm.setOnAlarmListener(mReorderAlarmListener);
mReorderAlarm.setAlarm(REORDER_DELAY);
mPrevTargetRank = mTargetRank;
+
+ if (d.stateAnnouncer != null) {
+ d.stateAnnouncer.announce(getContext().getString(R.string.move_to_position,
+ mTargetRank + 1));
+ }
}
float x = r[0];
@@ -867,7 +905,6 @@
// Show the animation, next time something is added to the folder.
mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, false, mLauncher);
}
-
}
@Override
@@ -1082,63 +1119,42 @@
return mContent.getItemCount();
}
- @Thunk void onCloseComplete() {
- DragLayer parent = (DragLayer) getParent();
- if (parent != null) {
- parent.removeView(this);
- }
- mDragController.removeDropTarget((DropTarget) this);
- clearFocus();
- mFolderIcon.requestFocus();
-
- if (mRearrangeOnClose) {
- rearrangeChildren();
- mRearrangeOnClose = false;
- }
- if (getItemCount() <= 1) {
- if (!mDragInProgress && !mSuppressFolderDeletion) {
- replaceFolderWithFinalItem();
- } else if (mDragInProgress) {
- mDeleteFolderOnDropCompleted = true;
- }
- }
- mSuppressFolderDeletion = false;
- clearDragInfo();
- }
-
@Thunk void replaceFolderWithFinalItem() {
// Add the last remaining child to the workspace in place of the folder
Runnable onCompleteRunnable = new Runnable() {
@Override
public void run() {
- CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container, mInfo.screenId);
+ int itemCount = mInfo.contents.size();
+ if (itemCount <= 1) {
+ View newIcon = null;
- View child = null;
- // Move the item from the folder to the workspace, in the position of the folder
- if (getItemCount() == 1) {
- ShortcutInfo finalItem = mInfo.contents.get(0);
- child = mLauncher.createShortcut(cellLayout, finalItem);
- LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
- mInfo.screenId, mInfo.cellX, mInfo.cellY);
- }
- if (getItemCount() <= 1) {
- // Remove the folder
- LauncherModel.deleteItemFromDatabase(mLauncher, mInfo);
- if (cellLayout != null) {
- // b/12446428 -- sometimes the cell layout has already gone away?
- cellLayout.removeView(mFolderIcon);
+ if (itemCount == 1) {
+ // Move the item from the folder to the workspace, in the position of the
+ // folder
+ CellLayout cellLayout = mLauncher.getCellLayout(mInfo.container,
+ mInfo.screenId);
+ ShortcutInfo finalItem = mInfo.contents.remove(0);
+ newIcon = mLauncher.createShortcut(cellLayout, finalItem);
+ LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
+ mInfo.screenId, mInfo.cellX, mInfo.cellY);
}
+
+ // Remove the folder
+ mLauncher.removeItem(mFolderIcon, mInfo, true /* deleteFromDb */);
if (mFolderIcon instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) mFolderIcon);
}
- mLauncher.removeFolder(mInfo);
- }
- // We add the child after removing the folder to prevent both from existing at
- // the same time in the CellLayout. We need to add the new item with addInScreenFromBind()
- // to ensure that hotseat items are placed correctly.
- if (child != null) {
- mLauncher.getWorkspace().addInScreenFromBind(child, mInfo.container, mInfo.screenId,
- mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
+
+ if (newIcon != null) {
+ // We add the child after removing the folder to prevent both from existing
+ // at the same time in the CellLayout. We need to add the new item with
+ // addInScreenFromBind() to ensure that hotseat items are placed correctly.
+ mLauncher.getWorkspace().addInScreenFromBind(newIcon, mInfo.container,
+ mInfo.screenId, mInfo.cellX, mInfo.cellY, mInfo.spanX, mInfo.spanY);
+
+ // Focus the newly created child
+ newIcon.requestFocus();
+ }
}
}
};
@@ -1155,15 +1171,37 @@
return mDestroyed;
}
- // This method keeps track of the last item in the folder for the purposes
+ // This method keeps track of the first and last item in the folder for the purposes
// of keyboard focus
public void updateTextViewFocus() {
- View lastChild = mContent.getLastItem();
- if (lastChild != null) {
+ final View firstChild = mContent.getFirstItem();
+ final View lastChild = mContent.getLastItem();
+ if (firstChild != null && lastChild != null) {
mFolderName.setNextFocusDownId(lastChild.getId());
mFolderName.setNextFocusRightId(lastChild.getId());
mFolderName.setNextFocusLeftId(lastChild.getId());
mFolderName.setNextFocusUpId(lastChild.getId());
+ // Hitting TAB from the folder name wraps around to the first item on the current
+ // folder page, and hitting SHIFT+TAB from that item wraps back to the folder name.
+ mFolderName.setNextFocusForwardId(firstChild.getId());
+ // When clicking off the folder when editing the name, this Folder gains focus. When
+ // pressing an arrow key from that state, give the focus to the first item.
+ this.setNextFocusDownId(firstChild.getId());
+ this.setNextFocusRightId(firstChild.getId());
+ this.setNextFocusLeftId(firstChild.getId());
+ this.setNextFocusUpId(firstChild.getId());
+ // When pressing shift+tab in the above state, give the focus to the last item.
+ setOnKeyListener(new OnKeyListener() {
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ boolean isShiftPlusTab = keyCode == KeyEvent.KEYCODE_TAB &&
+ event.hasModifiers(KeyEvent.META_SHIFT_ON);
+ if (isShiftPlusTab && Folder.this.isFocused()) {
+ return lastChild.requestFocus();
+ }
+ return false;
+ }
+ });
}
}
@@ -1284,7 +1322,11 @@
rearrangeChildren();
}
if (getItemCount() <= 1) {
- replaceFolderWithFinalItem();
+ if (mInfo.opened) {
+ mLauncher.closeFolder(this, true);
+ } else {
+ replaceFolderWithFinalItem();
+ }
}
}
@@ -1326,8 +1368,12 @@
}
public void onFocusChange(View v, boolean hasFocus) {
- if (v == mFolderName && hasFocus) {
- startEditingFolderName();
+ if (v == mFolderName) {
+ if (hasFocus) {
+ startEditingFolderName();
+ } else {
+ dismissEditingName();
+ }
}
}
@@ -1339,7 +1385,7 @@
}
@Override
- public void fillInLaunchSourceData(Bundle sourceData) {
+ public void fillInLaunchSourceData(View v, Bundle sourceData) {
// Fill in from the folder icon's launch source provider first
Stats.LaunchSourceUtils.populateSourceDataFromAncestorProvider(mFolderIcon, sourceData);
sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER, Stats.SUB_CONTAINER_FOLDER);
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 8d534d2..d7b55b3 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -520,7 +520,7 @@
}
class PreviewItemDrawingParams {
- PreviewItemDrawingParams(float transX, float transY, float scale, int overlayAlpha) {
+ PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
this.transX = transX;
this.transY = transY;
this.scale = scale;
@@ -529,7 +529,7 @@
float transX;
float transY;
float scale;
- int overlayAlpha;
+ float overlayAlpha;
Drawable drawable;
}
@@ -561,7 +561,7 @@
float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection) + getPaddingTop();
float transX = (mAvailableSpaceInPreview - scaledSize) / 2;
float totalScale = mBaselineIconScale * scale;
- final int overlayAlpha = (int) (80 * (1 - r));
+ final float overlayAlpha = (80 * (1 - r)) / 255f;
if (params == null) {
params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
@@ -585,12 +585,12 @@
d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
if (d instanceof FastBitmapDrawable) {
FastBitmapDrawable fd = (FastBitmapDrawable) d;
- int oldBrightness = fd.getBrightness();
+ float oldBrightness = fd.getBrightness();
fd.setBrightness(params.overlayAlpha);
d.draw(canvas);
fd.setBrightness(oldBrightness);
} else {
- d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
+ d.setColorFilter(Color.argb((int) (params.overlayAlpha * 255), 255, 255, 255),
PorterDuff.Mode.SRC_ATOP);
d.draw(canvas);
d.clearColorFilter();
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index cc9c573..d503d2c 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -402,16 +402,28 @@
return !ALLOW_FOLDER_SCROLL && getItemCount() >= mMaxItemsPerPage;
}
+ public View getFirstItem() {
+ if (getChildCount() < 1) {
+ return null;
+ }
+ ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+ if (mGridCountX > 0) {
+ return currContainer.getChildAt(0, 0);
+ } else {
+ return currContainer.getChildAt(0);
+ }
+ }
+
public View getLastItem() {
if (getChildCount() < 1) {
return null;
}
- ShortcutAndWidgetContainer lastContainer = getCurrentCellLayout().getShortcutsAndWidgets();
- int lastRank = lastContainer.getChildCount() - 1;
+ ShortcutAndWidgetContainer currContainer = getCurrentCellLayout().getShortcutsAndWidgets();
+ int lastRank = currContainer.getChildCount() - 1;
if (mGridCountX > 0) {
- return lastContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
+ return currContainer.getChildAt(lastRank % mGridCountX, lastRank / mGridCountX);
} else {
- return lastContainer.getChildAt(lastRank);
+ return currContainer.getChildAt(lastRank);
}
}
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index 5ff85d6..0d68e33 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -164,6 +164,9 @@
int bitmapWidth = (int) (rect.width() * view.getScaleX());
int bitmapHeight = (int) (rect.height() * view.getScaleY());
+ if (bitmapHeight <= 0 || bitmapWidth <= 0) {
+ return null;
+ }
int key = (bitmapWidth << 16) | bitmapHeight;
Bitmap cache = mBitmapCache.get(key);
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 17fdeb1..3e83876 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -17,13 +17,13 @@
package com.android.launcher3;
import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
@@ -70,7 +70,7 @@
public void setOnLongClickListener(OnLongClickListener l) {
mContent.setOnLongClickListener(l);
}
-
+
/* Get the orientation invariant order of the item in the hotseat for persistence. */
int getOrderInHotseat(int x, int y) {
return mHasVerticalHotseat ? (mContent.getCountY() - y - 1) : x;
@@ -118,6 +118,10 @@
Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
mLauncher.resizeIconDrawable(d);
+ int scaleDownPx = getResources().getDimensionPixelSize(R.dimen.all_apps_button_scale_down);
+ Rect bounds = d.getBounds();
+ d.setBounds(bounds.left, bounds.top + scaleDownPx / 2, bounds.right - scaleDownPx,
+ bounds.bottom - scaleDownPx / 2);
allAppsButton.setCompoundDrawables(null, d, null, null);
allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
@@ -150,7 +154,7 @@
}
@Override
- public void fillInLaunchSourceData(Bundle sourceData) {
+ public void fillInLaunchSourceData(View v, Bundle sourceData) {
sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOTSEAT);
}
}
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 59ab839..c72c2f9 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -28,7 +28,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -45,8 +45,10 @@
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SQLiteCacheHelper;
import com.android.launcher3.util.Thunk;
import java.util.Collections;
@@ -116,7 +118,7 @@
mUserManager = UserManagerCompat.getInstance(mContext);
mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mIconDpi = inv.fillResIconDpi;
- mIconDb = new IconDB(context);
+ mIconDb = new IconDB(context, inv.iconBitmapSize);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
@@ -179,15 +181,7 @@
private Bitmap makeDefaultIcon(UserHandleCompat user) {
Drawable unbadged = getFullResDefaultActivityIcon();
- Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
- Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
- Math.max(d.getIntrinsicHeight(), 1),
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
- d.setBounds(0, 0, b.getWidth(), b.getHeight());
- d.draw(c);
- c.setBitmap(null);
- return b;
+ return Utilities.createBadgedIconBitmap(unbadged, user, mContext);
}
/**
@@ -237,9 +231,9 @@
public synchronized void removeIconsForPkg(String packageName, UserHandleCompat user) {
removeFromMemCacheLocked(packageName, user);
long userSerial = mUserManager.getSerialNumberForUser(user);
- mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME,
+ mIconDb.delete(
IconDB.COLUMN_COMPONENT + " LIKE ? AND " + IconDB.COLUMN_USER + " = ?",
- new String[] {packageName + "/%", Long.toString(userSerial)});
+ new String[]{packageName + "/%", Long.toString(userSerial)});
}
public void updateDbIcons(Set<String> ignorePackagesForMainUser) {
@@ -282,58 +276,65 @@
componentMap.put(app.getComponentName(), app);
}
- Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME,
- new String[] {IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
- IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION,
- IconDB.COLUMN_SYSTEM_STATE},
- IconDB.COLUMN_USER + " = ? ",
- new String[] {Long.toString(userSerial)},
- null, null, null);
-
- final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT);
- final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED);
- final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION);
- final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID);
- final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
-
HashSet<Integer> itemsToRemove = new HashSet<Integer>();
Stack<LauncherActivityInfoCompat> appsToUpdate = new Stack<>();
- while (c.moveToNext()) {
- String cn = c.getString(indexComponent);
- ComponentName component = ComponentName.unflattenFromString(cn);
- PackageInfo info = pkgInfoMap.get(component.getPackageName());
- if (info == null) {
- if (!ignorePackages.contains(component.getPackageName())) {
+ Cursor c = null;
+ try {
+ c = mIconDb.query(
+ new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
+ IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION,
+ IconDB.COLUMN_SYSTEM_STATE},
+ IconDB.COLUMN_USER + " = ? ",
+ new String[]{Long.toString(userSerial)});
+
+ final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT);
+ final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED);
+ final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION);
+ final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID);
+ final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
+
+ while (c.moveToNext()) {
+ String cn = c.getString(indexComponent);
+ ComponentName component = ComponentName.unflattenFromString(cn);
+ PackageInfo info = pkgInfoMap.get(component.getPackageName());
+ if (info == null) {
+ if (!ignorePackages.contains(component.getPackageName())) {
+ remove(component, user);
+ itemsToRemove.add(c.getInt(rowIndex));
+ }
+ continue;
+ }
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) {
+ // Application is not present
+ continue;
+ }
+
+ long updateTime = c.getLong(indexLastUpdate);
+ int version = c.getInt(indexVersion);
+ LauncherActivityInfoCompat app = componentMap.remove(component);
+ if (version == info.versionCode && updateTime == info.lastUpdateTime &&
+ TextUtils.equals(mSystemState, c.getString(systemStateIndex))) {
+ continue;
+ }
+ if (app == null) {
remove(component, user);
itemsToRemove.add(c.getInt(rowIndex));
+ } else {
+ appsToUpdate.add(app);
}
- continue;
}
- if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) {
- // Application is not present
- continue;
- }
-
- long updateTime = c.getLong(indexLastUpdate);
- int version = c.getInt(indexVersion);
- LauncherActivityInfoCompat app = componentMap.remove(component);
- if (version == info.versionCode && updateTime == info.lastUpdateTime &&
- TextUtils.equals(mSystemState, c.getString(systemStateIndex))) {
- continue;
- }
- if (app == null) {
- remove(component, user);
- itemsToRemove.add(c.getInt(rowIndex));
- } else {
- appsToUpdate.add(app);
+ } catch (SQLiteException e) {
+ Log.d(TAG, "Error reading icon cache", e);
+ // Continue updating whatever we have read so far
+ } finally {
+ if (c != null) {
+ c.close();
}
}
- c.close();
if (!itemsToRemove.isEmpty()) {
- mIconDb.getWritableDatabase().delete(IconDB.TABLE_NAME,
- Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove),
- null);
+ mIconDb.delete(
+ Utilities.createDbSelectionQuery(IconDB.COLUMN_ROWID, itemsToRemove), null);
}
// Insert remaining apps.
@@ -363,8 +364,7 @@
values.put(IconDB.COLUMN_USER, userSerial);
values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
- mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
- SQLiteDatabase.CONFLICT_REPLACE);
+ mIconDb.insertOrReplace(values);
}
@Thunk ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app,
@@ -380,7 +380,8 @@
}
if (entry == null) {
entry = new CacheEntry();
- entry.icon = Utilities.createIconBitmap(app.getBadgedIcon(mIconDpi), mContext);
+ entry.icon = Utilities.createBadgedIconBitmap(
+ app.getIcon(mIconDpi), app.getUser(), mContext);
}
entry.title = app.getLabel();
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, app.getUser());
@@ -542,7 +543,8 @@
// Check the DB first.
if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
if (info != null) {
- entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext);
+ entry.icon = Utilities.createBadgedIconBitmap(
+ info.getIcon(mIconDpi), info.getUser(), mContext);
} else {
if (usePackageIcon) {
CacheEntry packageEntry = getEntryForPackageLocked(
@@ -623,9 +625,8 @@
if (appInfo == null) {
throw new NameNotFoundException("ApplicationInfo is null");
}
- Drawable drawable = mUserManager.getBadgedDrawableForUser(
- appInfo.loadIcon(mPackageManager), user);
- entry.icon = Utilities.createIconBitmap(drawable, mContext);
+ entry.icon = Utilities.createBadgedIconBitmap(
+ appInfo.loadIcon(mPackageManager), user, mContext);
entry.title = appInfo.loadLabel(mPackageManager);
entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
entry.isLowResIcon = false;
@@ -678,19 +679,18 @@
label, Color.TRANSPARENT);
values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString());
values.put(IconDB.COLUMN_USER, userSerial);
- mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
- SQLiteDatabase.CONFLICT_REPLACE);
+ mIconDb.insertOrReplace(values);
}
private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) {
- Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME,
- new String[] {lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
+ Cursor c = null;
+ try {
+ c = mIconDb.query(
+ new String[]{lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON,
IconDB.COLUMN_LABEL},
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
- new String[] {cacheKey.componentName.flattenToString(),
- Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))},
- null, null, null);
- try {
+ new String[]{cacheKey.componentName.flattenToString(),
+ Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))});
if (c.moveToNext()) {
entry.icon = loadIconNoResize(c, 0, lowRes ? mLowResOptions : null);
entry.isLowResIcon = lowRes;
@@ -704,8 +704,12 @@
}
return true;
}
+ } catch (SQLiteException e) {
+ Log.d(TAG, "Error reading icon cache", e);
} finally {
- c.close();
+ if (c != null) {
+ c.close();
+ }
}
return false;
}
@@ -751,9 +755,9 @@
LauncherActivityInfoCompat app = mAppsToUpdate.pop();
String cn = app.getComponentName().flattenToString();
ContentValues values = updateCacheAndGetContentValues(app, true);
- mIconDb.getWritableDatabase().update(IconDB.TABLE_NAME, values,
+ mIconDb.update(values,
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
- new String[] {cn, Long.toString(mUserSerial)});
+ new String[]{cn, Long.toString(mUserSerial)});
mUpdatedPackages.add(app.getComponentName().getPackageName());
if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
@@ -788,9 +792,12 @@
mSystemState = Locale.getDefault().toString();
}
- private static final class IconDB extends SQLiteOpenHelper {
+ private static final class IconDB extends SQLiteCacheHelper {
private final static int DB_VERSION = 7;
+ private final static int RELEASE_VERSION = DB_VERSION +
+ (FeatureFlags.LAUNCHER3_ICON_NORMALIZATION ? 1 : 0);
+
private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid";
private final static String COLUMN_COMPONENT = "componentName";
@@ -802,12 +809,14 @@
private final static String COLUMN_LABEL = "label";
private final static String COLUMN_SYSTEM_STATE = "system_state";
- public IconDB(Context context) {
- super(context, LauncherFiles.APP_ICONS_DB, null, DB_VERSION);
+ public IconDB(Context context, int iconPixelSize) {
+ super(context, LauncherFiles.APP_ICONS_DB,
+ (RELEASE_VERSION << 16) + iconPixelSize,
+ TABLE_NAME);
}
@Override
- public void onCreate(SQLiteDatabase db) {
+ protected void onCreateTable(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
COLUMN_COMPONENT + " TEXT NOT NULL, " +
COLUMN_USER + " INTEGER NOT NULL, " +
@@ -820,25 +829,6 @@
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ") " +
");");
}
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion != newVersion) {
- clearDB(db);
- }
- }
-
- @Override
- public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion != newVersion) {
- clearDB(db);
- }
- }
-
- private void clearDB(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
}
private ContentValues newContentValues(Bitmap icon, String label, int lowResBackgroundColor) {
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 3fc8fc0..921e90c 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -82,18 +82,17 @@
strings = new HashSet<String>(strings);
}
strings.add(encoded);
- sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).commit();
+ sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, strings).apply();
}
}
}
- public static void removeFromInstallQueue(Context context, ArrayList<String> packageNames,
+ public static void removeFromInstallQueue(Context context, HashSet<String> packageNames,
UserHandleCompat user) {
if (packageNames.isEmpty()) {
return;
}
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ SharedPreferences sp = Utilities.getPrefs(context);
synchronized(sLock) {
Set<String> strings = sp.getStringSet(APPS_PENDING_INSTALL, null);
if (DBG) {
@@ -111,7 +110,7 @@
newStringsIter.remove();
}
}
- sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).commit();
+ sp.edit().putStringSet(APPS_PENDING_INSTALL, newStrings).apply();
}
}
}
@@ -132,7 +131,7 @@
infos.add(info);
}
}
- sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).commit();
+ sharedPrefs.edit().putStringSet(APPS_PENDING_INSTALL, new HashSet<String>()).apply();
return infos;
}
}
@@ -145,40 +144,53 @@
if (!ACTION_INSTALL_SHORTCUT.equals(data.getAction())) {
return;
}
-
- PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
- if (info.launchIntent == null || info.label == null) {
- if (DBG) Log.e(TAG, "Invalid install shortcut intent");
- return;
+ PendingInstallShortcutInfo info = createPendingInfo(context, data);
+ if (info != null) {
+ queuePendingShortcutInfo(info, context);
}
-
- info = convertToLauncherActivityIfPossible(info);
- queuePendingShortcutInfo(info, context);
}
- public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
+ /**
+ * @return true is the extra is either null or is of type {@param type}
+ */
+ private static boolean isValidExtraType(Intent intent, String key, Class type) {
+ Object extra = intent.getParcelableExtra(key);
+ return extra == null || type.isInstance(extra);
+ }
+
+ /**
+ * Verifies the intent and creates a {@link PendingInstallShortcutInfo}
+ */
+ private static PendingInstallShortcutInfo createPendingInfo(Context context, Intent data) {
+ if (!isValidExtraType(data, Intent.EXTRA_SHORTCUT_INTENT, Intent.class) ||
+ !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
+ Intent.ShortcutIconResource.class)) ||
+ !(isValidExtraType(data, Intent.EXTRA_SHORTCUT_ICON, Bitmap.class))) {
+
+ if (DBG) Log.e(TAG, "Invalid install shortcut intent");
+ return null;
+ }
+
PendingInstallShortcutInfo info = new PendingInstallShortcutInfo(data, context);
if (info.launchIntent == null || info.label == null) {
if (DBG) Log.e(TAG, "Invalid install shortcut intent");
return null;
}
- info = convertToLauncherActivityIfPossible(info);
- return info.getShortcutInfo();
+
+ return convertToLauncherActivityIfPossible(info);
}
- static void queueInstallShortcut(LauncherActivityInfoCompat info, Context context) {
- queuePendingShortcutInfo(new PendingInstallShortcutInfo(info, context), context);
+ public static ShortcutInfo fromShortcutIntent(Context context, Intent data) {
+ PendingInstallShortcutInfo info = createPendingInfo(context, data);
+ return info == null ? null : info.getShortcutInfo();
}
private static void queuePendingShortcutInfo(PendingInstallShortcutInfo info, Context context) {
// Queue the item up for adding if launcher has not loaded properly yet
- LauncherAppState.setApplicationContext(context.getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
boolean launcherNotLoaded = app.getModel().getCallback() == null;
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
- addToInstallQueue(sp, info);
+ addToInstallQueue(Utilities.getPrefs(context), info);
if (!mUseInstallQueue && !launcherNotLoaded) {
flushInstallQueue(context);
}
@@ -192,22 +204,21 @@
flushInstallQueue(context);
}
static void flushInstallQueue(Context context) {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = context.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ SharedPreferences sp = Utilities.getPrefs(context);
ArrayList<PendingInstallShortcutInfo> installQueue = getAndClearInstallQueue(sp, context);
if (!installQueue.isEmpty()) {
Iterator<PendingInstallShortcutInfo> iter = installQueue.iterator();
ArrayList<ItemInfo> addShortcuts = new ArrayList<ItemInfo>();
while (iter.hasNext()) {
final PendingInstallShortcutInfo pendingInfo = iter.next();
- final Intent intent = pendingInfo.launchIntent;
// If the intent specifies a package, make sure the package exists
String packageName = pendingInfo.getTargetPackage();
if (!TextUtils.isEmpty(packageName)) {
UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
if (!LauncherModel.isValidPackage(context, packageName, myUserHandle)) {
- if (DBG) Log.d(TAG, "Ignoring shortcut for absent package:" + intent);
+ if (DBG) Log.d(TAG, "Ignoring shortcut for absent package: "
+ + pendingInfo.launchIntent);
continue;
}
}
@@ -352,7 +363,7 @@
return packageName;
}
- public boolean isLuncherActivity() {
+ public boolean isLauncherActivity() {
return activityInfo != null;
}
}
@@ -395,9 +406,7 @@
}
return new PendingInstallShortcutInfo(data, context);
- } catch (JSONException e) {
- Log.d(TAG, "Exception reading shortcut to add: " + e);
- } catch (URISyntaxException e) {
+ } catch (JSONException | URISyntaxException e) {
Log.d(TAG, "Exception reading shortcut to add: " + e);
}
return null;
@@ -410,7 +419,7 @@
*/
private static PendingInstallShortcutInfo convertToLauncherActivityIfPossible(
PendingInstallShortcutInfo original) {
- if (original.isLuncherActivity()) {
+ if (original.isLauncherActivity()) {
// Already an activity target
return original;
}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index ae204c4..d601322 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -19,7 +19,6 @@
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Point;
-import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
@@ -66,25 +65,25 @@
*/
public int numFolderRows;
public int numFolderColumns;
- float iconSize;
- int iconBitmapSize;
- int fillResIconDpi;
- float iconTextSize;
+ public float iconSize;
+ public int iconBitmapSize;
+ public int fillResIconDpi;
+ public float iconTextSize;
/**
* Number of icons inside the hotseat area.
*/
- float numHotseatIcons;
+ public int numHotseatIcons;
float hotseatIconSize;
int defaultLayoutId;
// Derived invariant properties
- int hotseatAllAppsRank;
+ public int hotseatAllAppsRank;
DeviceProfile landscapeProfile;
DeviceProfile portraitProfile;
- InvariantDeviceProfile() {
+ public InvariantDeviceProfile() {
}
public InvariantDeviceProfile(InvariantDeviceProfile p) {
@@ -95,7 +94,7 @@
}
InvariantDeviceProfile(String n, float w, float h, int r, int c, int fr, int fc, int maapc,
- float is, float its, float hs, float his, int dlId) {
+ float is, float its, int hs, float his, int dlId) {
// Ensure that we have an odd number of hotseat items (since we need to place all apps)
if (hs % 2 == 0) {
throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
@@ -116,7 +115,7 @@
defaultLayoutId = dlId;
}
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
+ @TargetApi(23)
InvariantDeviceProfile(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
@@ -174,9 +173,9 @@
// width, height, #rows, #columns, #folder rows, #folder columns,
// iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
- 255, 300, 2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+ 255, 300, 2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_3x3));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
- 255, 400, 3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+ 255, 400, 3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_3x3));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
275, 420, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
@@ -184,9 +183,9 @@
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
- 335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
- predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+ predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
+ 335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
406, 694, 5, 5, 4, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5));
// The tablet profile is odd in that the landscape orientation
@@ -195,9 +194,9 @@
575, 904, 5, 6, 4, 5, 4, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6));
// Larger tablet profiles always have system bars on the top & bottom
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
- 727, 1207, 5, 6, 4, 5, 4, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6));
+ 727, 1207, 5, 6, 4, 5, 4, 76, 14.4f, 7, 76, R.xml.default_workspace_5x6));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
- 1527, 2527, 7, 7, 6, 6, 4, 100, 20, 7, 72, R.xml.default_workspace_4x4));
+ 1527, 2527, 7, 7, 6, 6, 4, 100, 20, 7, 72, R.xml.default_workspace_5x6));
return predefinedDeviceProfiles;
}
@@ -252,8 +251,8 @@
ArrayList<InvariantDeviceProfile> pointsByNearness = points;
Collections.sort(pointsByNearness, new Comparator<InvariantDeviceProfile>() {
public int compare(InvariantDeviceProfile a, InvariantDeviceProfile b) {
- return (int) (dist(width, height, a.minWidthDps, a.minHeightDps)
- - dist(width, height, b.minWidthDps, b.minHeightDps));
+ return Float.compare(dist(width, height, a.minWidthDps, a.minHeightDps),
+ dist(width, height, b.minWidthDps, b.minHeightDps));
}
});
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index f7e0ea4..c7a6807 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -197,4 +197,11 @@
+ " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ " user=" + user + ")";
}
+
+ /**
+ * Whether this item is disabled.
+ */
+ public boolean isDisabled() {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d6bc07c..ff120f7 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -52,6 +52,7 @@
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
@@ -72,7 +73,6 @@
import android.text.method.TextKeyListener;
import android.util.Log;
import android.view.Display;
-import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -85,13 +85,11 @@
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
-import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.Advanceable;
-import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
@@ -99,6 +97,7 @@
import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.PagedView.PageSwitchListener;
import com.android.launcher3.allapps.AllAppsContainerView;
+import com.android.launcher3.allapps.DefaultAppSearchController;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
@@ -107,6 +106,7 @@
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
+import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.PendingAddWidgetInfo;
import com.android.launcher3.widget.WidgetHostViewLoader;
@@ -120,11 +120,11 @@
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
/**
* Default launcher application.
@@ -136,7 +136,7 @@
static final boolean LOGD = false;
static final boolean PROFILE_STARTUP = false;
- static final boolean DEBUG_WIDGETS = true;
+ static final boolean DEBUG_WIDGETS = false;
static final boolean DEBUG_STRICT_MODE = false;
static final boolean DEBUG_RESUME_TIME = false;
static final boolean DEBUG_DUMP_LOG = false;
@@ -195,8 +195,6 @@
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_INFO = "launcher.add_widget_info";
// Type: parcelable
private static final String RUNTIME_STATE_PENDING_ADD_WIDGET_ID = "launcher.add_widget_id";
- // Type: int[]
- private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
@@ -205,9 +203,6 @@
static final String ACTION_FIRST_LOAD_COMPLETE =
"com.android.launcher3.action.FIRST_LOAD_COMPLETE";
- public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem";
- public static final boolean SHOW_WEIGHT_WATCHER_DEFAULT = false;
-
private static final String QSB_WIDGET_ID = "qsb_widget_id";
private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider";
@@ -230,9 +225,6 @@
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
private static final int ACTIVITY_START_DELAY = 1000;
- private HashMap<Integer, Integer> mItemIdToViewId = new HashMap<Integer, Integer>();
- private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
-
// 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;
@@ -248,7 +240,8 @@
private View mPageIndicators;
@Thunk DragLayer mDragLayer;
private DragController mDragController;
- private View mWeightWatcher;
+
+ public View mWeightWatcher;
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
@@ -348,6 +341,8 @@
private DeviceProfile mDeviceProfile;
+ private boolean mMoveToDefaultScreenFromNewIntent;
+
// This is set to the view that launched the activity that navigated the user away from
// launcher. Since there is no callback for when the activity has finished launching, enable
// the press state and keep this reference to reset the press state when we return to launcher.
@@ -356,10 +351,9 @@
protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
new HashMap<String, CustomAppWidget>();
- private static final boolean ENABLE_CUSTOM_WIDGET_TEST = false;
static {
- if (ENABLE_CUSTOM_WIDGET_TEST) {
- sCustomAppWidgets.put(DummyWidget.class.getName(), new DummyWidget());
+ if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
+ TestingUtils.addDummyWidget(sCustomAppWidgets);
}
}
@@ -425,17 +419,15 @@
super.onCreate(savedInstanceState);
- LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
// Load configuration-specific DeviceProfile
mDeviceProfile = getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE ?
- app.getInvariantDeviceProfile().landscapeProfile
- : app.getInvariantDeviceProfile().portraitProfile;
+ app.getInvariantDeviceProfile().landscapeProfile
+ : app.getInvariantDeviceProfile().portraitProfile;
- mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
- Context.MODE_PRIVATE);
+ mSharedPrefs = Utilities.getPrefs(this);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
@@ -463,6 +455,8 @@
setContentView(R.layout.launcher);
+ app.getInvariantDeviceProfile().landscapeProfile.setSearchBarHeight(getSearchBarHeight());
+ app.getInvariantDeviceProfile().portraitProfile.setSearchBarHeight(getSearchBarHeight());
setupViews();
mDeviceProfile.layout(this);
@@ -494,11 +488,11 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mCloseSystemDialogsReceiver, filter);
- mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext());
+ mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation);
// In case we are on a device with locked rotation, we should look at preferences to check
// if the user has specifically allowed rotation.
if (!mRotationEnabled) {
- mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext(), false);
+ mRotationEnabled = Utilities.isAllowRotationPrefEnabled(getApplicationContext());
}
// On large interfaces, or on devices that a user has specifically enabled screen rotation,
@@ -656,34 +650,12 @@
return !isWorkspaceLoading();
}
- @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
- public static int generateViewId() {
- if (Utilities.ATLEAST_JB_MR1) {
- return View.generateViewId();
- } else {
- // View.generateViewId() is not available. The following fallback logic is a copy
- // of its implementation.
- for (;;) {
- final int result = sNextGeneratedId.get();
- // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
- int newValue = result + 1;
- if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
- if (sNextGeneratedId.compareAndSet(result, newValue)) {
- return result;
- }
- }
- }
- }
-
public int getViewIdForItem(ItemInfo info) {
- // This cast is safe given the > 2B range for int.
- int itemId = (int) info.id;
- if (mItemIdToViewId.containsKey(itemId)) {
- return mItemIdToViewId.get(itemId);
- }
- int viewId = generateViewId();
- mItemIdToViewId.put(itemId, viewId);
- return viewId;
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ // This cast is safe as long as the id < 0x00FFFFFF
+ // Since we jail all the dynamically generated views, there should be no clashes
+ // with any other views.
+ return (int) info.id;
}
/**
@@ -751,6 +723,9 @@
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
if (resultCode == RESULT_OK && mWorkspace.isInOverviewMode()) {
+ // User could have free-scrolled between pages before picking a wallpaper; make sure
+ // we move to the closest one now to avoid visual jump.
+ mWorkspace.setCurrentPage(mWorkspace.getPageNearestToCenterOfScreen());
showWorkspace(false);
}
return;
@@ -927,8 +902,7 @@
}
@Thunk void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
- CellLayout cellLayout =
- (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+ CellLayout cellLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
Runnable onCompleteRunnable = null;
int animationType = 0;
@@ -965,6 +939,9 @@
protected void onStop() {
super.onStop();
FirstFrameAnimatorHelper.setIsVisible(false);
+ if (Utilities.isNycOrAbove()) {
+ mAppWidgetHost.stopListening();
+ }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStop();
@@ -975,6 +952,9 @@
protected void onStart() {
super.onStart();
FirstFrameAnimatorHelper.setIsVisible(true);
+ if (Utilities.isNycOrAbove()) {
+ mAppWidgetHost.startListening();
+ }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onStart();
@@ -1062,21 +1042,30 @@
// Consequently, the widgets will be inflated in the orientation of the foreground activity
// (framework issue). On resuming, we ensure that any widgets are inflated for the current
// orientation.
- getWorkspace().reinflateWidgetsIfNecessary();
+ if (!isWorkspaceLoading()) {
+ getWorkspace().reinflateWidgetsIfNecessary();
+ }
reinflateQSBIfNecessary();
if (DEBUG_RESUME_TIME) {
Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime));
}
- if (mWorkspace.getCustomContentCallbacks() != null) {
+ // We want to suppress callbacks about CustomContent being shown if we have just received
+ // onNewIntent while the user was present within launcher. In that case, we post a call
+ // to move the user to the main screen (which will occur after onResume). We don't want to
+ // have onHide (from onPause), then onShow, then onHide again, which we get if we don't
+ // suppress here.
+ if (mWorkspace.getCustomContentCallbacks() != null
+ && !mMoveToDefaultScreenFromNewIntent) {
// If we are resuming and the custom content is the current page, we call onShow().
- // It is also poassible that onShow will instead be called slightly after first layout
+ // It is also possible that onShow will instead be called slightly after first layout
// if PagedView#setRestorePage was set to the custom content page in onCreate().
if (mWorkspace.isOnOrMovingToCustomContent()) {
mWorkspace.getCustomContentCallbacks().onShow(true);
}
}
+ mMoveToDefaultScreenFromNewIntent = false;
updateInteraction(Workspace.State.NORMAL, mWorkspace.getState());
mWorkspace.onResume();
@@ -1222,7 +1211,7 @@
} else {
// On devices with a locked orientation, we will at least have the allow rotation
// setting.
- return !Utilities.isRotationAllowedForDevice(this);
+ return !getResources().getBoolean(R.bool.allow_rotation);
}
}
@@ -1291,6 +1280,29 @@
return handled;
}
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (keyCode == KeyEvent.KEYCODE_MENU) {
+ // Ignore the menu key if we are currently dragging or are on the custom content screen
+ if (!isOnCustomContent() && !mDragController.isDragging()) {
+ // Close any open folders
+ closeFolder();
+
+ // Stop resizing any widgets
+ mWorkspace.exitWidgetResizeMode();
+
+ // Show the overview mode if we are on the workspace
+ if (mState == State.WORKSPACE && !mWorkspace.isInOverviewMode() &&
+ !mWorkspace.isSwitchingState()) {
+ mOverviewPanel.requestFocus();
+ showOverviewMode(true, true /* requestButtonFocus */);
+ }
+ }
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
private String getTypedText() {
return mDefaultKeySsb.toString();
}
@@ -1322,7 +1334,6 @@
*
* @param savedState The previous state.
*/
- @SuppressWarnings("unchecked")
private void restoreState(Bundle savedState) {
if (savedState == null) {
return;
@@ -1358,9 +1369,6 @@
setWaitingForResult(true);
mRestoring = true;
}
-
- mItemIdToViewId = (HashMap<Integer, Integer>)
- savedState.getSerializable(RUNTIME_STATE_VIEW_IDS);
}
/**
@@ -1389,45 +1397,8 @@
mHotseat.setOnLongClickListener(this);
}
- mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
- mWidgetsButton = findViewById(R.id.widget_button);
- mWidgetsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- if (!mWorkspace.isSwitchingState()) {
- onClickAddWidgetButton(arg0);
- }
- }
- });
- mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
-
- View wallpaperButton = findViewById(R.id.wallpaper_button);
- wallpaperButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- if (!mWorkspace.isSwitchingState()) {
- onClickWallpaperPicker(arg0);
- }
- }
- });
- wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
-
- View settingsButton = findViewById(R.id.settings_button);
- if (hasSettings()) {
- settingsButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- if (!mWorkspace.isSwitchingState()) {
- onClickSettingsButton(arg0);
- }
- }
- });
- settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
- } else {
- settingsButton.setVisibility(View.GONE);
- }
-
- mOverviewPanel.setAlpha(0f);
+ // Setup the overview panel
+ setupOverviewPanel();
// Setup the workspace
mWorkspace.setHapticFeedbackEnabled(false);
@@ -1445,7 +1416,7 @@
if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
} else {
- mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
+ mAppsView.setSearchBarController(new DefaultAppSearchController());
}
// Setup the drag controller (drop targets have to be added in reverse order in priority)
@@ -1458,22 +1429,69 @@
mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar());
}
- if (getResources().getBoolean(R.bool.debug_memory_enabled)) {
- Log.v(TAG, "adding WeightWatcher");
- mWeightWatcher = new WeightWatcher(this);
- mWeightWatcher.setAlpha(0.5f);
- ((FrameLayout) mLauncherView).addView(mWeightWatcher,
- new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT,
- FrameLayout.LayoutParams.WRAP_CONTENT,
- Gravity.BOTTOM)
- );
-
- boolean show = shouldShowWeightWatcher();
- mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
+ if (TestingUtils.MEMORY_DUMP_ENABLED) {
+ TestingUtils.addWeightWatcher(this);
}
}
+ private void setupOverviewPanel() {
+ mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
+
+ // Long-clicking buttons in the overview panel does the same thing as clicking them.
+ OnLongClickListener performClickOnLongClick = new OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ return v.performClick();
+ }
+ };
+
+ // Bind wallpaper button actions
+ View wallpaperButton = findViewById(R.id.wallpaper_button);
+ wallpaperButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (!mWorkspace.isSwitchingState()) {
+ onClickWallpaperPicker(view);
+ }
+ }
+ });
+ wallpaperButton.setOnLongClickListener(performClickOnLongClick);
+ wallpaperButton.setOnTouchListener(getHapticFeedbackTouchListener());
+
+ // Bind widget button actions
+ mWidgetsButton = findViewById(R.id.widget_button);
+ mWidgetsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (!mWorkspace.isSwitchingState()) {
+ onClickAddWidgetButton(view);
+ }
+ }
+ });
+ mWidgetsButton.setOnLongClickListener(performClickOnLongClick);
+ mWidgetsButton.setOnTouchListener(getHapticFeedbackTouchListener());
+
+ // Bind settings actions
+ View settingsButton = findViewById(R.id.settings_button);
+ boolean hasSettings = hasSettings();
+ if (hasSettings) {
+ settingsButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ if (!mWorkspace.isSwitchingState()) {
+ onClickSettingsButton(view);
+ }
+ }
+ });
+ settingsButton.setOnLongClickListener(performClickOnLongClick);
+ settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
+ } else {
+ settingsButton.setVisibility(View.GONE);
+ }
+
+ mOverviewPanel.setAlpha(0f);
+ }
+
/**
* Sets the all apps button. This method is called from {@link Hotseat}.
*/
@@ -1582,8 +1600,7 @@
ItemInfo info = mPendingAddInfo;
if (appWidgetInfo == null) {
- appWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(this,
- mAppWidgetManager.getAppWidgetInfo(appWidgetId));
+ appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(appWidgetId);
}
if (appWidgetInfo.isCustomWidget) {
@@ -1610,18 +1627,28 @@
// The AppWidgetHostView has already been inflated and instantiated
launcherInfo.hostView = hostView;
}
- launcherInfo.hostView.setTag(launcherInfo);
launcherInfo.hostView.setVisibility(View.VISIBLE);
- launcherInfo.notifyWidgetSizeChanged(this);
-
- mWorkspace.addInScreen(launcherInfo.hostView, container, screenId, info.cellX,
- info.cellY, launcherInfo.spanX, launcherInfo.spanY, isWorkspaceLocked());
-
- addWidgetToAutoAdvanceIfNeeded(launcherInfo.hostView, appWidgetInfo);
+ addAppWidgetToWorkspace(launcherInfo, appWidgetInfo, isWorkspaceLocked());
}
resetAddInfo();
}
+ private void addAppWidgetToWorkspace(LauncherAppWidgetInfo item,
+ LauncherAppWidgetProviderInfo appWidgetInfo, boolean insert) {
+ item.hostView.setTag(item);
+ item.onBindAppWidget(this);
+
+ item.hostView.setFocusable(true);
+ item.hostView.setOnFocusChangeListener(mFocusHandler);
+
+ mWorkspace.addInScreen(item.hostView, item.container, item.screenId,
+ item.cellX, item.cellY, item.spanX, item.spanY, insert);
+
+ if (!item.isCustomWidget()) {
+ addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
+ }
+ }
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -1635,7 +1662,10 @@
// processing a multi-step drop
if (mAppsView != null && mWidgetsView != null &&
mPendingAddInfo.container == ItemInfo.NO_ID) {
- showWorkspace(false);
+ if (!showWorkspace(false)) {
+ // If we are already on the workspace, then manually reset all apps
+ mAppsView.reset();
+ }
}
} else if (Intent.ACTION_USER_PRESENT.equals(action)) {
mUserPresent = true;
@@ -1668,31 +1698,10 @@
}
registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
- setupTransparentSystemBarsForLollipop();
mAttached = true;
mVisible = true;
}
- /**
- * Sets up transparent navigation and status bars in Lollipop.
- * This method is a no-op for other platform versions.
- */
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- private void setupTransparentSystemBarsForLollipop() {
- if (Utilities.ATLEAST_LOLLIPOP) {
- Window window = getWindow();
- window.getAttributes().systemUiVisibility |=
- (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
- | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
- window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
- window.setStatusBarColor(Color.TRANSPARENT);
- window.setNavigationBarColor(Color.TRANSPARENT);
- }
- }
-
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -1730,14 +1739,14 @@
mWorkspace.postDelayed(mBuildLayersRunnable, 500);
final ViewTreeObserver.OnDrawListener listener = this;
mWorkspace.post(new Runnable() {
- public void run() {
- if (mWorkspace != null &&
- mWorkspace.getViewTreeObserver() != null) {
- mWorkspace.getViewTreeObserver().
- removeOnDrawListener(listener);
- }
+ public void run() {
+ if (mWorkspace != null &&
+ mWorkspace.getViewTreeObserver() != null) {
+ mWorkspace.getViewTreeObserver().
+ removeOnDrawListener(listener);
}
- });
+ }
+ });
return;
}
});
@@ -1795,7 +1804,7 @@
}
});
- void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
+ private void addWidgetToAutoAdvanceIfNeeded(View hostView, AppWidgetProviderInfo appWidgetInfo) {
if (appWidgetInfo == null || appWidgetInfo.autoAdvanceViewId == -1) return;
View v = hostView.findViewById(appWidgetInfo.autoAdvanceViewId);
if (v instanceof Advanceable) {
@@ -1805,18 +1814,13 @@
}
}
- void removeWidgetToAutoAdvance(View hostView) {
+ private void removeWidgetToAutoAdvance(View hostView) {
if (mWidgetsToAdvance.containsKey(hostView)) {
mWidgetsToAdvance.remove(hostView);
updateAutoAdvanceState();
}
}
- public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
- removeWidgetToAutoAdvance(launcherInfo.hostView);
- launcherInfo.hostView = null;
- }
-
public void showOutOfSpaceMessage(boolean isHotseatLayout) {
int strId = (isHotseatLayout ? R.string.hotseat_out_of_space : R.string.out_of_space);
Toast.makeText(this, getString(strId), Toast.LENGTH_SHORT).show();
@@ -1898,7 +1902,7 @@
// In all these cases, only animate if we're already on home
mWorkspace.exitWidgetResizeMode();
- closeFolder();
+ closeFolder(alreadyOnHome);
exitSpringLoadedDragMode();
// If we are already on home, then just animate back to the workspace,
@@ -1911,7 +1915,7 @@
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
- InputMethodManager imm = (InputMethodManager)getSystemService(
+ InputMethodManager imm = (InputMethodManager) getSystemService(
INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
@@ -1943,10 +1947,16 @@
mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
openFolder == null && moveToDefaultScreen) {
+
+ // We use this flag to suppress noisy callbacks above custom content state
+ // from onResume.
+ mMoveToDefaultScreenFromNewIntent = true;
mWorkspace.post(new Runnable() {
@Override
public void run() {
- mWorkspace.moveToDefaultScreen(true);
+ if (mWorkspace != null) {
+ mWorkspace.moveToDefaultScreen(true);
+ }
}
});
}
@@ -1976,7 +1986,7 @@
outState.putInt(RUNTIME_STATE, mState.ordinal());
// We close any open folder since it will not be re-opened, and we need to make sure
// this state is reflected.
- closeFolder();
+ closeFolder(false);
if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
mWaitingForResult) {
@@ -1992,7 +2002,6 @@
// Save the current widgets tray?
// TODO(hyunyoungs)
- outState.putSerializable(RUNTIME_STATE_VIEW_IDS, mItemIdToViewId);
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onSaveInstanceState(outState);
@@ -2162,15 +2171,6 @@
}
}
- public void startSearchFromAllApps(View v, Intent searchIntent, String searchQuery) {
- if (mLauncherCallbacks != null && mLauncherCallbacks.startSearchFromAllApps(searchQuery)) {
- return;
- }
-
- // If not handled, then just start the provided search intent
- startActivitySafely(v, searchIntent, null);
- }
-
public boolean isOnCustomContent() {
return mWorkspace.isOnOrMovingToCustomContent();
}
@@ -2178,22 +2178,9 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
- if (!isOnCustomContent()) {
- // Close any open folders
- closeFolder();
- // Stop resizing any widgets
- mWorkspace.exitWidgetResizeMode();
- if (!mWorkspace.isInOverviewMode()) {
- // Show the overview mode
- showOverviewMode(true);
- } else {
- showWorkspace(true);
- }
- }
if (mLauncherCallbacks != null) {
return mLauncherCallbacks.onPrepareOptionsMenu(menu);
}
-
return false;
}
@@ -2243,8 +2230,11 @@
mPendingAddInfo.dropPos = null;
}
- void addAppWidgetImpl(final int appWidgetId, final ItemInfo info, final
+ void addAppWidgetFromDropImpl(final int appWidgetId, final ItemInfo info, final
AppWidgetHostView boundWidget, final LauncherAppWidgetProviderInfo appWidgetInfo) {
+ if (LOGD) {
+ Log.d(TAG, "Adding widget from drop");
+ }
addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
}
@@ -2352,8 +2342,14 @@
AppWidgetHostView hostView = info.boundWidget;
int appWidgetId;
if (hostView != null) {
+ // In the case where we've prebound the widget, we remove it from the DragLayer
+ if (LOGD) {
+ Log.d(TAG, "Removing widget view from drag layer and setting boundWidget to null");
+ }
+ getDragLayer().removeView(hostView);
+
appWidgetId = hostView.getAppWidgetId();
- addAppWidgetImpl(appWidgetId, info, hostView, info.info);
+ addAppWidgetFromDropImpl(appWidgetId, info, hostView, info.info);
// Clear the boundWidget so that it doesn't get destroyed.
info.boundWidget = null;
@@ -2366,7 +2362,7 @@
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
appWidgetId, info.info, options);
if (success) {
- addAppWidgetImpl(appWidgetId, info, null, info.info);
+ addAppWidgetFromDropImpl(appWidgetId, info, null, info.info);
} else {
mPendingAddWidgetInfo = info.info;
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
@@ -2402,10 +2398,72 @@
return newFolder;
}
- void removeFolder(FolderInfo folder) {
+ /**
+ * Unbinds the view for the specified item, and removes the item and all its children.
+ *
+ * @param v the view being removed.
+ * @param itemInfo the {@link ItemInfo} for this view.
+ * @param deleteFromDb whether or not to delete this item from the db.
+ */
+ public boolean removeItem(View v, ItemInfo itemInfo, boolean deleteFromDb) {
+ if (itemInfo instanceof ShortcutInfo) {
+ // Remove the shortcut from the folder before removing it from launcher
+ FolderInfo folderInfo = sFolders.get(itemInfo.container);
+ if (folderInfo != null) {
+ folderInfo.remove((ShortcutInfo) itemInfo);
+ } else {
+ mWorkspace.removeWorkspaceItem(v);
+ }
+ if (deleteFromDb) {
+ LauncherModel.deleteItemFromDatabase(this, itemInfo);
+ }
+ } else if (itemInfo instanceof FolderInfo) {
+ final FolderInfo folderInfo = (FolderInfo) itemInfo;
+ unbindFolder(folderInfo);
+ mWorkspace.removeWorkspaceItem(v);
+ if (deleteFromDb) {
+ LauncherModel.deleteFolderAndContentsFromDatabase(this, folderInfo);
+ }
+ } else if (itemInfo instanceof LauncherAppWidgetInfo) {
+ final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
+ mWorkspace.removeWorkspaceItem(v);
+ removeWidgetToAutoAdvance(widgetInfo.hostView);
+ widgetInfo.hostView = null;
+ if (deleteFromDb) {
+ deleteWidgetInfo(widgetInfo);
+ }
+
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Unbinds any launcher references to the folder.
+ */
+ private void unbindFolder(FolderInfo folder) {
sFolders.remove(folder.id);
}
+ /**
+ * Deletes the widget info and the widget id.
+ */
+ private void deleteWidgetInfo(final LauncherAppWidgetInfo widgetInfo) {
+ final LauncherAppWidgetHost appWidgetHost = getAppWidgetHost();
+ if (appWidgetHost != null && !widgetInfo.isCustomWidget() && widgetInfo.isWidgetIdValid()) {
+ // Deleting an app widget ID is a void call but writes to disk before returning
+ // to the caller...
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
+ appWidgetHost.deleteAppWidgetId(widgetInfo.appWidgetId);
+ return null;
+ }
+ }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
+ }
+ LauncherModel.deleteItemFromDatabase(this, widgetInfo);
+ }
+
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -2469,6 +2527,9 @@
if (mAppWidgetHost != null) {
mAppWidgetHost.startListening();
}
+
+ // Recreate the QSB, as the widget has been reset.
+ bindSearchProviderChanged();
}
/**
@@ -2535,10 +2596,10 @@
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
if (v.isReadyForClickSetup()) {
int widgetId = info.appWidgetId;
- AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
+ LauncherAppWidgetProviderInfo appWidgetInfo =
+ mAppWidgetManager.getLauncherAppWidgetInfo(widgetId);
if (appWidgetInfo != null) {
- mPendingAddWidgetInfo = LauncherAppWidgetProviderInfo.fromProviderInfo(
- this, appWidgetInfo);
+ mPendingAddWidgetInfo = appWidgetInfo;
mPendingAddInfo.copyFrom(info);
mPendingAddWidgetId = widgetId;
@@ -2620,25 +2681,15 @@
final ShortcutInfo shortcut = (ShortcutInfo) tag;
if (shortcut.isDisabled != 0) {
- int error = R.string.activity_not_available;
- if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
- error = R.string.safemode_shortcut_error;
- }
- Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
- return;
- }
-
- final Intent intent = shortcut.intent;
-
- // Check for special shortcuts
- if (intent.getComponent() != null) {
- final String shortcutClass = intent.getComponent().getClassName();
-
- if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
- MemoryDumpActivity.startDump(this);
- return;
- } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
- toggleShowWeightWatcher();
+ if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SUSPENDED) != 0
+ || (shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_QUIET_USER) != 0) {
+ // Launch activity anyway, framework will tell the user why the app is suspended.
+ } else {
+ int error = R.string.activity_not_available;
+ if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
+ error = R.string.safemode_shortcut_error;
+ }
+ Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
return;
}
}
@@ -2728,7 +2779,7 @@
if (openFolder != null) {
folderScreen = mWorkspace.getPageForView(openFolder);
// .. and close it
- closeFolder(openFolder);
+ closeFolder(openFolder, true);
if (folderScreen != mWorkspace.getCurrentPage()) {
// Close any folder open on the current screen
closeFolder();
@@ -2765,7 +2816,10 @@
*/
protected void onClickWallpaperPicker(View v) {
if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
- startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName()),
+ int pageScroll = mWorkspace.getScrollForPage(mWorkspace.getPageNearestToCenterOfScreen());
+ float offset = mWorkspace.mWallpaperOffset.wallpaperOffsetForScroll(pageScroll);
+ startActivityForResult(new Intent(Intent.ACTION_SET_WALLPAPER).setPackage(getPackageName())
+ .putExtra(WallpaperPickerActivity.EXTRA_WALLPAPER_OFFSET, offset),
REQUEST_PICK_WALLPAPER);
if (mLauncherCallbacks != null) {
@@ -3063,9 +3117,13 @@
}
oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.start();
+ if (Utilities.isPowerSaverOn(this)) {
+ // Animations are disabled in battery saver mode, so just skip to the end state.
+ oa.end();
+ }
}
- private void shrinkAndFadeInFolderIcon(final FolderIcon fi) {
+ private void shrinkAndFadeInFolderIcon(final FolderIcon fi, boolean animate) {
if (fi == null) return;
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1.0f);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
@@ -3091,6 +3149,9 @@
}
});
oa.start();
+ if (!animate) {
+ oa.end();
+ }
}
/**
@@ -3134,30 +3195,38 @@
}
public void closeFolder() {
+ closeFolder(true);
+ }
+
+ public void closeFolder(boolean animate) {
Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
if (folder != null) {
if (folder.isEditingName()) {
folder.dismissEditingName();
}
- closeFolder(folder);
+ closeFolder(folder, animate);
}
}
- public void closeFolder(Folder folder) {
+ public void closeFolder(Folder folder, boolean animate) {
folder.getInfo().opened = false;
ViewGroup parent = (ViewGroup) folder.getParent().getParent();
if (parent != null) {
FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
- shrinkAndFadeInFolderIcon(fi);
+ shrinkAndFadeInFolderIcon(fi, animate);
if (fi != null) {
((CellLayout.LayoutParams) fi.getLayoutParams()).canReorder = true;
}
}
- folder.animateClosed();
+ if (animate) {
+ folder.animateClosed();
+ } else {
+ folder.close(false);
+ }
- // Notify the accessibility manager that this folder "window" has disappeard and no
- // longer occludeds the workspace items
+ // Notify the accessibility manager that this folder "window" has disappeared and no
+ // longer occludes the workspace items
getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
@@ -3296,20 +3365,32 @@
}
}
- public void showWorkspace(boolean animated) {
- showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
+ /**
+ * @return whether or not the Launcher state changed.
+ */
+ public boolean showWorkspace(boolean animated) {
+ return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
}
- public void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
- showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
+ /**
+ * @return whether or not the Launcher state changed.
+ */
+ public boolean showWorkspace(boolean animated, Runnable onCompleteRunnable) {
+ return showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
onCompleteRunnable);
}
- protected void showWorkspace(int snapToPage, boolean animated) {
- showWorkspace(snapToPage, animated, null);
+ /**
+ * @return whether or not the Launcher state changed.
+ */
+ protected boolean showWorkspace(int snapToPage, boolean animated) {
+ return showWorkspace(snapToPage, animated, null);
}
- void showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) {
+ /**
+ * @return whether or not the Launcher state changed.
+ */
+ boolean showWorkspace(int snapToPage, boolean animated, Runnable onCompleteRunnable) {
boolean changed = mState != State.WORKSPACE ||
mWorkspace.getState() != Workspace.State.NORMAL;
if (changed) {
@@ -3335,14 +3416,38 @@
getWindow().getDecorView()
.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
}
+ return changed;
}
+ /**
+ * Shows the overview button.
+ */
void showOverviewMode(boolean animated) {
+ showOverviewMode(animated, false);
+ }
+
+ /**
+ * Shows the overview button, and if {@param requestButtonFocus} is set, will force the focus
+ * onto one of the overview panel buttons.
+ */
+ void showOverviewMode(boolean animated, boolean requestButtonFocus) {
+ Runnable postAnimRunnable = null;
+ if (requestButtonFocus) {
+ postAnimRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Hitting the menu button when in touch mode does not trigger touch mode to
+ // be disabled, so if requested, force focus on one of the overview panel
+ // buttons.
+ mOverviewPanel.requestFocusFromTouch();
+ }
+ };
+ }
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.OVERVIEW,
WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
- null /* onCompleteRunnable */);
+ postAnimRunnable);
mState = State.WORKSPACE;
}
@@ -3493,12 +3598,12 @@
// TODO
}
- protected void disableVoiceButtonProxy(boolean disable) {
- // NO-OP
+ public boolean launcherCallbacksProvidesSearch() {
+ return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch());
}
public View getOrCreateQsbBar() {
- if (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()) {
+ if (launcherCallbacksProvidesSearch()) {
return mLauncherCallbacks.getQsbBar();
}
@@ -3512,12 +3617,40 @@
opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY,
AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX);
- SharedPreferences sp = getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), MODE_PRIVATE);
- int widgetId = sp.getInt(QSB_WIDGET_ID, -1);
+ // Determine the min and max dimensions of the widget.
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile portraitProfile = app.getInvariantDeviceProfile().portraitProfile;
+ DeviceProfile landscapeProfile = app.getInvariantDeviceProfile().landscapeProfile;
+ float density = getResources().getDisplayMetrics().density;
+ Point searchDimens = portraitProfile.getSearchBarDimensForWidgetOpts(getResources());
+ int maxHeight = (int) (searchDimens.y / density);
+ int minHeight = maxHeight;
+ int maxWidth = (int) (searchDimens.x / density);
+ int minWidth = maxWidth;
+ if (!landscapeProfile.isVerticalBarLayout()) {
+ searchDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(getResources());
+ maxHeight = (int) Math.max(maxHeight, searchDimens.y / density);
+ minHeight = (int) Math.min(minHeight, searchDimens.y / density);
+ maxWidth = (int) Math.max(maxWidth, searchDimens.x / density);
+ minWidth = (int) Math.min(minWidth, searchDimens.x / density);
+ }
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth);
+ opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth);
+ if (LOGD) {
+ Log.d(TAG, "QSB widget options: maxHeight=" + maxHeight + " minHeight=" + minHeight
+ + " maxWidth=" + maxWidth + " minWidth=" + minWidth);
+ }
+
+ if (mLauncherCallbacks != null) {
+ opts.putAll(mLauncherCallbacks.getAdditionalSearchWidgetOptions());
+ }
+
+ int widgetId = mSharedPrefs.getInt(QSB_WIDGET_ID, -1);
AppWidgetProviderInfo widgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
if (!searchProvider.provider.flattenToString().equals(
- sp.getString(QSB_WIDGET_PROVIDER, null))
+ mSharedPrefs.getString(QSB_WIDGET_PROVIDER, null))
|| (widgetInfo == null)
|| !widgetInfo.provider.equals(searchProvider.provider)) {
// A valid widget is not already bound.
@@ -3535,10 +3668,10 @@
widgetId = -1;
}
- sp.edit()
+ mSharedPrefs.edit()
.putInt(QSB_WIDGET_ID, widgetId)
.putString(QSB_WIDGET_PROVIDER, searchProvider.provider.flattenToString())
- .commit();
+ .apply();
}
mAppWidgetHost.setQsbWidgetId(widgetId);
@@ -3709,30 +3842,6 @@
}
}
- private boolean shouldShowWeightWatcher() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
- boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, SHOW_WEIGHT_WATCHER_DEFAULT);
-
- return show;
- }
-
- private void toggleShowWeightWatcher() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_PRIVATE);
- boolean show = sp.getBoolean(SHOW_WEIGHT_WATCHER, true);
-
- show = !show;
-
- SharedPreferences.Editor editor = sp.edit();
- editor.putBoolean(SHOW_WEIGHT_WATCHER, show);
- editor.commit();
-
- if (mWeightWatcher != null) {
- mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
- }
- }
-
public void bindAppsAdded(final ArrayList<Long> newScreens,
final ArrayList<ItemInfo> addNotAnimated,
final ArrayList<ItemInfo> addAnimated,
@@ -3775,6 +3884,7 @@
*
* Implementation of the method from LauncherModel.Callbacks.
*/
+ @Override
public void bindItems(final ArrayList<ItemInfo> shortcuts, final int start, final int end,
final boolean forceAnimateIcons) {
Runnable r = new Runnable() {
@@ -3893,6 +4003,16 @@
sFolders = folders.clone();
}
+ private void bindSafeModeWidget(LauncherAppWidgetInfo item) {
+ PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, true);
+ view.updateIcon(mIconCache);
+ item.hostView = view;
+ item.hostView.updateAppWidget(null);
+ item.hostView.setOnClickListener(this);
+ addAppWidgetToWorkspace(item, null, false);
+ mWorkspace.requestLayout();
+ }
+
/**
* Add the views for a widget to the workspace.
*
@@ -3908,19 +4028,31 @@
return;
}
+ if (mIsSafeModeEnabled) {
+ bindSafeModeWidget(item);
+ return;
+ }
+
final long start = DEBUG_WIDGETS ? SystemClock.uptimeMillis() : 0;
if (DEBUG_WIDGETS) {
Log.d(TAG, "bindAppWidget: " + item);
}
- final Workspace workspace = mWorkspace;
- LauncherAppWidgetProviderInfo appWidgetInfo =
- LauncherModel.getProviderInfo(this, item.providerName, item.user);
+ final LauncherAppWidgetProviderInfo appWidgetInfo;
- if (!mIsSafeModeEnabled
- && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
- && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+ if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)) {
+ // If the provider is not ready, bind as a pending widget.
+ appWidgetInfo = null;
+ } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ // The widget id is not valid. Try to find the widget based on the provider info.
+ appWidgetInfo = mAppWidgetManager.findProvider(item.providerName, item.user);
+ } else {
+ appWidgetInfo = mAppWidgetManager.getLauncherAppWidgetInfo(item.appWidgetId);
+ }
+ // If the provider is ready, but the width is not yet restored, try to restore it.
+ if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) &&
+ (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
if (appWidgetInfo == null) {
if (DEBUG_WIDGETS) {
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -3932,7 +4064,7 @@
}
// If we do not have a valid id, try to bind an id.
- if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) {
+ if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
// Note: This assumes that the id remap broadcast is received before this step.
// If that is not the case, the id remap will be ignored and user may see the
// click to setup view.
@@ -3941,8 +4073,7 @@
pendingInfo.spanY = item.spanY;
pendingInfo.minSpanX = item.minSpanX;
pendingInfo.minSpanY = item.minSpanY;
- Bundle options = null;
- WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+ Bundle options = WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
@@ -3969,44 +4100,42 @@
: LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
LauncherModel.updateItemInDatabase(this, item);
- } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0)
+ } else if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_UI_NOT_READY)
&& (appWidgetInfo.configure == null)) {
- // If the ID is already valid, verify if we need to configure or not.
+ // The widget was marked as UI not ready, but there is no configure activity to
+ // update the UI.
item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
LauncherModel.updateItemInDatabase(this, item);
}
}
- if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
- final int appWidgetId = item.appWidgetId;
+ if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
if (DEBUG_WIDGETS) {
Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component "
+ appWidgetInfo.provider);
}
- item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ // Verify that we own the widget
+ if (appWidgetInfo == null) {
+ Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
+ deleteWidgetInfo(item);
+ return;
+ }
+
+ item.hostView = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
item.minSpanX = appWidgetInfo.minSpanX;
item.minSpanY = appWidgetInfo.minSpanY;
+ addAppWidgetToWorkspace(item, appWidgetInfo, false);
} else {
- appWidgetInfo = null;
PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
mIsSafeModeEnabled);
view.updateIcon(mIconCache);
item.hostView = view;
item.hostView.updateAppWidget(null);
item.hostView.setOnClickListener(this);
+ addAppWidgetToWorkspace(item, null, false);
}
-
- item.hostView.setTag(item);
- item.onBindAppWidget(this);
-
- workspace.addInScreen(item.hostView, item.container, item.screenId, item.cellX,
- item.cellY, item.spanX, item.spanY, false);
- if (!item.isCustomWidget()) {
- addWidgetToAutoAdvanceIfNeeded(item.hostView, appWidgetInfo);
- }
-
- workspace.requestLayout();
+ mWorkspace.requestLayout();
if (DEBUG_WIDGETS) {
Log.d(TAG, "bound widget id="+item.appWidgetId+" in "
@@ -4128,10 +4257,18 @@
return mDeviceProfile.isVerticalBarLayout();
}
+ /** Returns the search bar bounds in pixels. */
protected Rect getSearchBarBounds() {
return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources()));
}
+ public int getSearchBarHeight() {
+ if (mLauncherCallbacks != null) {
+ return mLauncherCallbacks.getSearchBarHeight();
+ }
+ return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL;
+ }
+
public void bindSearchProviderChanged() {
if (mSearchDropTargetBar == null) {
return;
@@ -4235,7 +4372,7 @@
}
mWorkspace.removeItemsByComponentName(removedComponents, user);
// Notify the drag controller
- mDragController.onAppsRemoved(new ArrayList<String>(), removedComponents);
+ mDragController.onAppsRemoved(new HashSet<String>(), removedComponents);
}
}
@@ -4259,43 +4396,44 @@
}
/**
- * A package was uninstalled. We take both the super set of packageNames
+ * A package was uninstalled/updated. We take both the super set of packageNames
* in addition to specific applications to remove, the reason being that
* this can be called when a package is updated as well. In that scenario,
- * we only remove specific components from the workspace, where as
+ * we only remove specific components from the workspace and hotseat, where as
* package-removal should clear all items by package name.
- *
- * @param reason if non-zero, the icons are not permanently removed, rather marked as disabled.
- * Implementation of the method from LauncherModel.Callbacks.
*/
@Override
- public void bindComponentsRemoved(final ArrayList<String> packageNames,
- final ArrayList<AppInfo> appInfos, final UserHandleCompat user, final int reason) {
+ public void bindWorkspaceComponentsRemoved(
+ final HashSet<String> packageNames, final HashSet<ComponentName> components,
+ final UserHandleCompat user) {
Runnable r = new Runnable() {
public void run() {
- bindComponentsRemoved(packageNames, appInfos, user, reason);
+ bindWorkspaceComponentsRemoved(packageNames, components, user);
}
};
if (waitUntilResume(r)) {
return;
}
+ if (!packageNames.isEmpty()) {
+ mWorkspace.removeItemsByPackageName(packageNames, user);
+ }
+ if (!components.isEmpty()) {
+ mWorkspace.removeItemsByComponentName(components, user);
+ }
+ // Notify the drag controller
+ mDragController.onAppsRemoved(packageNames, components);
- if (reason == 0) {
- HashSet<ComponentName> removedComponents = new HashSet<ComponentName>();
- for (AppInfo info : appInfos) {
- removedComponents.add(info.componentName);
- }
- if (!packageNames.isEmpty()) {
- mWorkspace.removeItemsByPackageName(packageNames, user);
- }
- if (!removedComponents.isEmpty()) {
- mWorkspace.removeItemsByComponentName(removedComponents, user);
- }
- // Notify the drag controller
- mDragController.onAppsRemoved(packageNames, removedComponents);
+ }
- } else {
- mWorkspace.disableShortcutsByPackageName(packageNames, user, reason);
+ @Override
+ public void bindAppInfosRemoved(final ArrayList<AppInfo> appInfos) {
+ Runnable r = new Runnable() {
+ public void run() {
+ bindAppInfosRemoved(appInfos);
+ }
+ };
+ if (waitUntilResume(r)) {
+ return;
}
// Update AllApps
@@ -4304,15 +4442,15 @@
}
}
- private Runnable mBindPackagesUpdatedRunnable = new Runnable() {
+ private Runnable mBindWidgetModelRunnable = new Runnable() {
public void run() {
- bindAllPackages(mWidgetsModel);
+ bindWidgetsModel(mWidgetsModel);
}
};
@Override
- public void bindAllPackages(final WidgetsModel model) {
- if (waitUntilResume(mBindPackagesUpdatedRunnable, true)) {
+ public void bindWidgetsModel(WidgetsModel model) {
+ if (waitUntilResume(mBindWidgetModelRunnable, true)) {
mWidgetsModel = model;
return;
}
@@ -4323,6 +4461,13 @@
}
}
+ @Override
+ public void notifyWidgetProvidersChanged() {
+ if (mWorkspace.getState().shouldUpdateWidget) {
+ mModel.refreshAndBindWidgetsAndShortcuts(this, mWidgetsView.isEmpty());
+ }
+ }
+
private int mapConfigurationOriActivityInfoOri(int configOri) {
final Display d = getWindowManager().getDefaultDisplay();
int naturalOri = Configuration.ORIENTATION_LANDSCAPE;
@@ -4630,8 +4775,9 @@
/**
* Resizes an icon drawable to the correct icon size.
*/
- public void resizeIconDrawable(Drawable icon) {
+ public Drawable resizeIconDrawable(Drawable icon) {
icon.setBounds(0, 0, mDeviceProfile.iconSizePx, mDeviceProfile.iconSizePx);
+ return icon;
}
/**
@@ -4753,7 +4899,15 @@
}
return null;
}
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+ }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
+ }
+ }
+
+ public static List<View> getFolderContents(View icon) {
+ if (icon instanceof FolderIcon) {
+ return ((FolderIcon) icon).getFolder().getItemsInReadingOrder();
+ } else {
+ return Collections.EMPTY_LIST;
}
}
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 6a248a3..fa20f03 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -24,7 +24,6 @@
import android.annotation.TargetApi;
import android.os.Build;
import android.view.View;
-import android.view.ViewAnimationUtils;
import android.view.ViewTreeObserver;
import com.android.launcher3.util.UiThreadCircularReveal;
@@ -59,24 +58,25 @@
// it should be cancelled
public static void startAnimationAfterNextDraw(final Animator animator, final View view) {
view.getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
- private boolean mStarted = false;
- public void onDraw() {
- if (mStarted) return;
- mStarted = true;
- // Use this as a signal that the animation was cancelled
- if (animator.getDuration() == 0) {
- return;
- }
- animator.start();
+ private boolean mStarted = false;
- final ViewTreeObserver.OnDrawListener listener = this;
- view.post(new Runnable() {
- public void run() {
- view.getViewTreeObserver().removeOnDrawListener(listener);
- }
- });
+ public void onDraw() {
+ if (mStarted) return;
+ mStarted = true;
+ // Use this as a signal that the animation was cancelled
+ if (animator.getDuration() == 0) {
+ return;
}
- });
+ animator.start();
+
+ final ViewTreeObserver.OnDrawListener listener = this;
+ view.post(new Runnable() {
+ public void run() {
+ view.getViewTreeObserver().removeOnDrawListener(listener);
+ }
+ });
+ }
+ });
}
public static void onDestroyActivity() {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d87ad67..e1ade10 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -20,13 +20,15 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.os.UserManager;
import android.util.Log;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
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.util.ConfigMonitor;
+import com.android.launcher3.util.TestingUtils;
import com.android.launcher3.util.Thunk;
import java.lang.ref.WeakReference;
@@ -34,7 +36,6 @@
public class LauncherAppState {
private final AppFilter mAppFilter;
- private final BuildInfo mBuildInfo;
@Thunk final LauncherModel mModel;
private final IconCache mIconCache;
private final WidgetPreviewLoader mWidgetCache;
@@ -79,8 +80,8 @@
Log.v(Launcher.TAG, "LauncherAppState inited");
- if (sContext.getResources().getBoolean(R.bool.debug_memory_enabled)) {
- MemoryTracker.startTrackingMe(sContext, "L");
+ if (TestingUtils.MEMORY_DUMP_ENABLED) {
+ TestingUtils.startTrackingMemory(sContext);
}
mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
@@ -88,7 +89,6 @@
mWidgetCache = new WidgetPreviewLoader(sContext, mIconCache);
mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
- mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
mModel = new LauncherModel(this, mIconCache, mAppFilter);
LauncherAppsCompat.getInstance(sContext).addOnAppsChangedCallback(mModel);
@@ -100,9 +100,15 @@
// For handling managed profiles
filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
+ filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE);
+ filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE);
sContext.registerReceiver(mModel, filter);
UserManagerCompat.getInstance(sContext).enableAndResetCache();
+ new ConfigMonitor(sContext).register();
+
+ sContext.registerReceiver(
+ new WallpaperChangedReceiver(), new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED));
}
/**
@@ -152,14 +158,10 @@
return sLauncherProvider.get();
}
- public static String getSharedPreferencesKey() {
- return LauncherFiles.SHARED_PREFERENCES_KEY;
- }
-
public WidgetPreviewLoader getWidgetCache() {
return mWidgetCache;
}
-
+
public void onWallpaperChanged() {
mWallpaperChangedSinceLastCheck = true;
}
@@ -175,6 +177,6 @@
}
public static boolean isDogfoodBuild() {
- return getInstance().mBuildInfo.isDogfoodBuild();
+ return FeatureFlags.IS_ALPHA_BUILD || FeatureFlags.IS_DEV_BUILD;
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 6c3a1e8..1510558 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -20,6 +20,7 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.os.DeadObjectException;
import android.os.TransactionTooLargeException;
import android.view.LayoutInflater;
import android.view.View;
@@ -69,7 +70,8 @@
try {
super.startListening();
} catch (Exception e) {
- if (e.getCause() instanceof TransactionTooLargeException) {
+ if (e.getCause() instanceof TransactionTooLargeException ||
+ e.getCause() instanceof DeadObjectException) {
// We're willing to let this slide. The exception is being caused by the list of
// RemoteViews which is being passed back. The startListening relationship will
// have been established by this point, and we will end up populating the
@@ -80,12 +82,6 @@
}
}
- @Override
- public void stopListening() {
- super.stopListening();
- clearViews();
- }
-
public void addProviderChangeListener(Runnable callback) {
mProviderChangeListeners.add(callback);
}
@@ -100,6 +96,10 @@
callback.run();
}
}
+
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ mLauncher.notifyWidgetProvidersChanged();
+ }
}
public AppWidgetHostView createView(Context context, int appWidgetId,
@@ -125,5 +125,8 @@
LauncherAppWidgetProviderInfo info = LauncherAppWidgetProviderInfo.fromProviderInfo(
mLauncher, appWidget);
super.onProviderChanged(appWidgetId, info);
+ // The super method updates the dimensions of the providerInfo. Update the
+ // launcher spans accordingly.
+ info.initSpans();
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index cf461a5..4185257 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -19,6 +19,8 @@
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.graphics.Rect;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -28,6 +30,8 @@
import com.android.launcher3.DragLayer.TouchCompleteListener;
+import java.util.ArrayList;
+
/**
* {@inheritDoc}
*/
@@ -43,6 +47,8 @@
private float mSlop;
+ private boolean mChildrenFocused;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
@@ -51,6 +57,8 @@
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDragLayer = ((Launcher) context).getDragLayer();
setAccessibilityDelegate(LauncherAppState.getInstance().getAccessibilityDelegate());
+
+ setBackgroundResource(R.drawable.widget_internal_focus_bg);
}
@Override
@@ -175,6 +183,108 @@
@Override
public int getDescendantFocusability() {
- return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+ return mChildrenFocused ? ViewGroup.FOCUS_BEFORE_DESCENDANTS
+ : ViewGroup.FOCUS_BLOCK_DESCENDANTS;
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent event) {
+ if (mChildrenFocused && event.getKeyCode() == KeyEvent.KEYCODE_ESCAPE
+ && event.getAction() == KeyEvent.ACTION_UP) {
+ mChildrenFocused = false;
+ requestFocus();
+ return true;
+ }
+ return super.dispatchKeyEvent(event);
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+ event.startTracking();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ if (event.isTracking()) {
+ if (!mChildrenFocused && keyCode == KeyEvent.KEYCODE_ENTER) {
+ mChildrenFocused = true;
+ ArrayList<View> focusableChildren = getFocusables(FOCUS_FORWARD);
+ focusableChildren.remove(this);
+ int childrenCount = focusableChildren.size();
+ switch (childrenCount) {
+ case 0:
+ mChildrenFocused = false;
+ break;
+ case 1: {
+ if (getTag() instanceof ItemInfo) {
+ ItemInfo item = (ItemInfo) getTag();
+ if (item.spanX == 1 && item.spanY == 1) {
+ focusableChildren.get(0).performClick();
+ mChildrenFocused = false;
+ return true;
+ }
+ }
+ // continue;
+ }
+ default:
+ focusableChildren.get(0).requestFocus();
+ return true;
+ }
+ }
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
+ protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+ if (gainFocus) {
+ mChildrenFocused = false;
+ dispatchChildFocus(false);
+ }
+ super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+ }
+
+ @Override
+ public void requestChildFocus(View child, View focused) {
+ super.requestChildFocus(child, focused);
+ dispatchChildFocus(focused != null);
+ if (focused != null) {
+ focused.setFocusableInTouchMode(false);
+ }
+ }
+
+ @Override
+ public void clearChildFocus(View child) {
+ super.clearChildFocus(child);
+ dispatchChildFocus(false);
+ }
+
+ @Override
+ public boolean dispatchUnhandledMove(View focused, int direction) {
+ return mChildrenFocused;
+ }
+
+ private void dispatchChildFocus(boolean childIsFocused) {
+ // The host view's background changes when selected, to indicate the focus is inside.
+ setSelected(childIsFocused);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ try {
+ super.onLayout(changed, left, top, right, bottom);
+ } catch (final RuntimeException e) {
+ post(new Runnable() {
+ @Override
+ public void run() {
+ // Update the widget with 0 Layout id, to reset the view to error view.
+ updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
+ }
+ });
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 882f7e2..55edf45 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -123,17 +123,11 @@
*/
void onBindAppWidget(Launcher launcher) {
if (!mHasNotifiedInitialWidgetSizeChanged) {
- notifyWidgetSizeChanged(launcher);
+ AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
+ mHasNotifiedInitialWidgetSizeChanged = true;
}
}
- /**
- * Trigger an update callback to the widget to notify it that its size has changed.
- */
- void notifyWidgetSizeChanged(Launcher launcher) {
- AppWidgetResizeFrame.updateWidgetSizeRanges(hostView, launcher, spanX, spanY);
- mHasNotifiedInitialWidgetSizeChanged = true;
- }
@Override
public String toString() {
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 71a08a8..28d8052 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -59,7 +59,7 @@
initSpans();
}
- private void initSpans() {
+ public void initSpans() {
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index 8eb4e63..1703e41 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -20,25 +20,26 @@
import android.app.backup.BackupDataInput;
import android.app.backup.BackupManager;
import android.content.Context;
+import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.ParcelFileDescriptor;
import android.util.Log;
-import com.android.launcher3.model.MigrateFromRestoreTask;
+import com.android.launcher3.model.GridSizeMigrationTask;
import java.io.IOException;
public class LauncherBackupAgentHelper extends BackupAgentHelper {
- private static final String TAG = "LauncherBackupAgentHelper";
+ private static final String TAG = "LauncherBAHelper";
+
+ private static final String KEY_LAST_NOTIFIED_TIME = "backup_manager_last_notified";
private static final String LAUNCHER_DATA_PREFIX = "L";
static final boolean VERBOSE = false;
static final boolean DEBUG = false;
- private static BackupManager sBackupManager;
-
/**
* Notify the backup manager that out database is dirty.
*
@@ -47,10 +48,29 @@
* @param context application context
*/
public static void dataChanged(Context context) {
- if (sBackupManager == null) {
- sBackupManager = new BackupManager(context);
+ dataChanged(context, 0);
+ }
+
+ /**
+ * Notify the backup manager that out database is dirty.
+ *
+ * <P>This does not force an immediate backup.
+ *
+ * @param context application context
+ * @param throttleMs duration in ms for which two consecutive calls to backup manager should
+ * not be made.
+ */
+ public static void dataChanged(Context context, long throttleMs) {
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ long now = System.currentTimeMillis();
+ long lastTime = prefs.getLong(KEY_LAST_NOTIFIED_TIME, 0);
+
+ // User can manually change the system time, which could lead to now < lastTime.
+ // Re-backup in that case, as the backup will have a wrong lastModifiedTime.
+ if (now < lastTime || now >= (lastTime + throttleMs)) {
+ BackupManager.dataChanged(context.getPackageName());
+ prefs.edit().putLong(KEY_LAST_NOTIFIED_TIME, now).apply();
}
- sBackupManager.dataChanged();
}
private LauncherBackupHelper mHelper;
@@ -91,18 +111,16 @@
if (hasData && mHelper.restoreSuccessful) {
LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
- LauncherClings.synchonouslyMarkFirstRunClingDismissed(this);
+ LauncherClings.markFirstRunClingDismissed(this);
// Rank was added in v4.
if (mHelper.restoredBackupVersion <= 3) {
LauncherAppState.getLauncherProvider().updateFolderItemsRank();
}
- if (MigrateFromRestoreTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
- MigrateFromRestoreTask.markForMigration(getApplicationContext(),
- (int) mHelper.migrationCompatibleProfileData.desktopCols,
- (int) mHelper.migrationCompatibleProfileData.desktopRows,
- mHelper.widgetSizes);
+ if (GridSizeMigrationTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
+ GridSizeMigrationTask.markForMigration(getApplicationContext(),
+ mHelper.widgetSizes, mHelper.migrationCompatibleProfileData);
}
LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 48d9351..ea67ac8 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -41,18 +41,19 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.WorkspaceScreens;
-import com.android.launcher3.backup.BackupProtos;
-import com.android.launcher3.backup.BackupProtos.CheckedMessage;
-import com.android.launcher3.backup.BackupProtos.DeviceProfieData;
-import com.android.launcher3.backup.BackupProtos.Favorite;
-import com.android.launcher3.backup.BackupProtos.Journal;
-import com.android.launcher3.backup.BackupProtos.Key;
-import com.android.launcher3.backup.BackupProtos.Resource;
-import com.android.launcher3.backup.BackupProtos.Screen;
-import com.android.launcher3.backup.BackupProtos.Widget;
+import com.android.launcher3.backup.nano.BackupProtos;
+import com.android.launcher3.backup.nano.BackupProtos.CheckedMessage;
+import com.android.launcher3.backup.nano.BackupProtos.DeviceProfieData;
+import com.android.launcher3.backup.nano.BackupProtos.Favorite;
+import com.android.launcher3.backup.nano.BackupProtos.Journal;
+import com.android.launcher3.backup.nano.BackupProtos.Key;
+import com.android.launcher3.backup.nano.BackupProtos.Resource;
+import com.android.launcher3.backup.nano.BackupProtos.Screen;
+import com.android.launcher3.backup.nano.BackupProtos.Widget;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.model.MigrateFromRestoreTask;
+import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.util.Thunk;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import com.google.protobuf.nano.MessageNano;
@@ -315,14 +316,13 @@
return true;
}
- if (MigrateFromRestoreTask.ENABLED &&
- (oldProfile.desktopCols - currentProfile.desktopCols <= 1) &&
- (oldProfile.desktopRows - currentProfile.desktopRows <= 1)) {
- // Allow desktop migration when row and/or column count contracts by 1.
-
+ if (GridSizeMigrationTask.ENABLED) {
+ // One time migrate the workspace when launcher starts.
migrationCompatibleProfileData = initDeviceProfileData(mIdp);
migrationCompatibleProfileData.desktopCols = oldProfile.desktopCols;
migrationCompatibleProfileData.desktopRows = oldProfile.desktopRows;
+ migrationCompatibleProfileData.hotseatCount = oldProfile.hotseatCount;
+ migrationCompatibleProfileData.allappsRank = oldProfile.allappsRank;
return true;
}
return false;
@@ -661,12 +661,14 @@
+ getUserSelectionArg();
Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
where, null, null);
+ AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(mContext);
try {
cursor.moveToPosition(-1);
while(cursor.moveToNext()) {
final long id = cursor.getLong(ID_INDEX);
final String providerName = cursor.getString(APPWIDGET_PROVIDER_INDEX);
final ComponentName provider = ComponentName.unflattenFromString(providerName);
+
Key key = null;
String backupKey = null;
if (provider != null) {
@@ -685,11 +687,14 @@
} else if (backupKey != null) {
if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
- if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
- UserHandleCompat user = UserHandleCompat.myUserHandle();
- writeRowToBackup(key, packWidget(dpi, provider, user), data);
- mKeys.add(key);
- backupWidgetCount ++;
+ LauncherAppWidgetProviderInfo widgetInfo = widgetManager
+ .getLauncherAppWidgetInfo(cursor.getInt(APPWIDGET_ID_INDEX));
+ if (widgetInfo != null) {
+ if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
+ writeRowToBackup(key, packWidget(dpi, widgetInfo), data);
+ mKeys.add(key);
+ backupWidgetCount ++;
+ }
} else {
if (VERBOSE) Log.v(TAG, "deferring widget backup " + backupKey);
// too many widgets for this pass, request another.
@@ -768,13 +773,10 @@
try {
Key key = Key.parseFrom(Base64.decode(backupKey, Base64.DEFAULT));
if (key.checksum != checkKey(key)) {
- key = null;
throw new InvalidBackupException("invalid key read from stream" + backupKey);
}
return key;
- } catch (InvalidProtocolBufferNanoException e) {
- throw new InvalidBackupException(e);
- } catch (IllegalArgumentException e) {
+ } catch (InvalidProtocolBufferNanoException | IllegalArgumentException e) {
throw new InvalidBackupException(e);
}
}
@@ -1008,16 +1010,14 @@
}
/** Serialize a widget for persistence, including a checksum wrapper. */
- private Widget packWidget(int dpi, ComponentName provider, UserHandleCompat user) {
- final LauncherAppWidgetProviderInfo info =
- LauncherModel.getProviderInfo(mContext, provider, user);
+ private Widget packWidget(int dpi, LauncherAppWidgetProviderInfo info) {
Widget widget = new Widget();
- widget.provider = provider.flattenToShortString();
+ widget.provider = info.provider.flattenToShortString();
widget.label = info.label;
widget.configure = info.configure != null;
if (info.icon != 0) {
widget.icon = new Resource();
- Drawable fullResIcon = mIconCache.getFullResIcon(provider.getPackageName(), info.icon);
+ Drawable fullResIcon = mIconCache.getFullResIcon(info.provider.getPackageName(), info.icon);
Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext);
widget.icon.data = Utilities.flattenBitmap(icon);
widget.icon.dpi = dpi;
@@ -1026,7 +1026,6 @@
Point spans = info.getMinSpans(mIdp, mContext);
widget.minSpanX = spans.x;
widget.minSpanY = spans.y;
-
return widget;
}
@@ -1131,9 +1130,8 @@
* @param journal a Journal protocol buffer
*/
private void writeJournal(ParcelFileDescriptor newState, Journal journal) {
- FileOutputStream outStream = null;
try {
- outStream = new FileOutputStream(newState.getFileDescriptor());
+ FileOutputStream outStream = new FileOutputStream(newState.getFileDescriptor());
final byte[] journalBytes = writeCheckedBytes(journal);
outStream.write(journalBytes);
if (VERBOSE) Log.v(TAG, "wrote " + journalBytes.length + " bytes of journal");
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index e34bd57..772fbf3 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -77,12 +77,14 @@
public boolean providesSearch();
public boolean startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, Rect sourceBounds);
+ @Deprecated
public boolean startSearchFromAllApps(String query);
@Deprecated
public void startVoice();
public boolean hasCustomContentToLeft();
public void populateCustomContentContainer();
public View getQsbBar();
+ public Bundle getAdditionalSearchWidgetOptions();
/*
* Extensions points for adding / replacing some other aspects of the Launcher experience.
@@ -99,6 +101,9 @@
public boolean isLauncherPreinstalled();
public AllAppsSearchBarController getAllAppsSearchBarController();
public List<ComponentKey> getPredictedApps();
+ public static final 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();
/**
* Returning true will immediately result in a call to {@link #setLauncherOverlayView(ViewGroup,
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 18fe8ef..5c1e830 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -73,9 +73,7 @@
LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
| LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
// Set the flag to skip the folder cling
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sp.edit();
+ SharedPreferences.Editor editor = Utilities.getPrefs(mLauncher).edit();
editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
editor.apply();
// Disable the migration cling
@@ -253,11 +251,9 @@
!sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
}
- public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
- SharedPreferences prefs = ctx.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true);
- editor.commit();
+ public static void markFirstRunClingDismissed(Context ctx) {
+ Utilities.getPrefs(ctx).edit()
+ .putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true)
+ .apply();
}
}
diff --git a/src/com/android/launcher3/LauncherFiles.java b/src/com/android/launcher3/LauncherFiles.java
index c08cd0b..6ce2293 100644
--- a/src/com/android/launcher3/LauncherFiles.java
+++ b/src/com/android/launcher3/LauncherFiles.java
@@ -34,7 +34,7 @@
WALLPAPER_CROP_PREFERENCES_KEY + XML,
WALLPAPER_IMAGES_DB,
WIDGET_PREVIEWS_DB,
- MANAGED_USER_PREFERENCES_KEY,
+ MANAGED_USER_PREFERENCES_KEY + XML,
APP_ICONS_DB));
// TODO: Delete these files on upgrade
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b5922c6..c8860e3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,7 +17,6 @@
package com.android.launcher3;
import android.app.SearchManager;
-import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -34,14 +33,12 @@
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Parcelable;
import android.os.Process;
import android.os.SystemClock;
-import android.os.TransactionTooLargeException;
import android.provider.BaseColumns;
import android.text.TextUtils;
import android.util.Log;
@@ -55,12 +52,15 @@
import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.model.MigrateFromRestoreTask;
+import com.android.launcher3.model.GridSizeMigrationTask;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.CursorIconInfo;
+import com.android.launcher3.util.FlagOp;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
+import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.StringFilter;
import com.android.launcher3.util.Thunk;
import java.lang.ref.WeakReference;
@@ -68,7 +68,6 @@
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -98,7 +97,6 @@
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
private static final long INVALID_SCREEN_ID = -1L;
- @Thunk final boolean mAppsCanBeOnRemoveableStorage;
private final boolean mOldContentProviderExists;
@Thunk final LauncherAppState mApp;
@@ -137,9 +135,9 @@
@Thunk WeakReference<Callbacks> mCallbacks;
// < only access in worker thread >
- AllAppsList mBgAllAppsList;
+ private final AllAppsList mBgAllAppsList;
// Entire list of widgets.
- WidgetsModel mBgWidgetsModel;
+ private final WidgetsModel mBgWidgetsModel;
// The lock that must be acquired before referencing any static bg data structures. Unlike
// other locks, this one can generally be held long-term because we never expect any of these
@@ -166,9 +164,6 @@
// sBgWorkspaceScreens is the ordered set of workspace screens.
static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
- // sBgWidgetProviders is the set of widget providers including custom internal widgets
- public static HashMap<ComponentKey, LauncherAppWidgetProviderInfo> sBgWidgetProviders;
-
// sPendingPackages is a set of packages which could be on sdcard and are not available yet
static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
new HashMap<UserHandleCompat, HashSet<String>>();
@@ -201,9 +196,12 @@
ArrayList<ShortcutInfo> removed, UserHandleCompat user);
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
public void bindRestoreItemsChange(HashSet<ItemInfo> updates);
- public void bindComponentsRemoved(ArrayList<String> packageNames,
- ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
- public void bindAllPackages(WidgetsModel model);
+ public void bindWorkspaceComponentsRemoved(
+ HashSet<String> packageNames, HashSet<ComponentName> components,
+ UserHandleCompat user);
+ public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos);
+ public void notifyWidgetProvidersChanged();
+ public void bindWidgetsModel(WidgetsModel model);
public void bindSearchProviderChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
@@ -217,7 +215,6 @@
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Context context = app.getContext();
- mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
String oldProvider = context.getString(R.string.old_launcher_provider_uri);
// This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
// resource string.
@@ -1068,8 +1065,6 @@
/**
* Removes the specified item from the database
- * @param context
- * @param item
*/
public static void deleteItemFromDatabase(Context context, final ItemInfo item) {
ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
@@ -1079,8 +1074,6 @@
/**
* Removes the specified items from the database
- * @param context
- * @param item
*/
static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
final ContentResolver cr = context.getContentResolver();
@@ -1171,9 +1164,9 @@
}
/**
- * Remove the contents of the specified folder from the database
+ * Remove the specified folder and all its contents from the database.
*/
- public static void deleteFolderContentsFromDatabase(Context context, final FolderInfo info) {
+ public static void deleteFolderAndContentsFromDatabase(Context context, final FolderInfo info) {
final ContentResolver cr = context.getContentResolver();
Runnable r = new Runnable() {
@@ -1235,20 +1228,8 @@
@Override
public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
boolean replacing) {
- if (!replacing) {
- enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
- user));
- if (mAppsCanBeOnRemoveableStorage) {
- // Only rebind if we support removable storage. It catches the
- // case where
- // apps on the external sd card need to be reloaded
- startLoaderFromBackground();
- }
- } else {
- // If we are replacing then just update the packages in the list
- enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
- packageNames, user));
- }
+ enqueuePackageUpdated(
+ new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE, packageNames, user));
}
@Override
@@ -1261,6 +1242,20 @@
}
}
+ @Override
+ public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
+ enqueuePackageUpdated(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_SUSPEND, packageNames,
+ user));
+ }
+
+ @Override
+ public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
+ enqueuePackageUpdated(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_UNSUSPEND, packageNames,
+ user));
+ }
+
/**
* Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
* ACTION_PACKAGE_CHANGED.
@@ -1282,6 +1277,14 @@
|| LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
UserManagerCompat.getInstance(context).enableAndResetCache();
forceReload();
+ } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_AVAILABLE.equals(action) ||
+ LauncherAppsCompat.ACTION_MANAGED_PROFILE_UNAVAILABLE.equals(action)) {
+ UserHandleCompat user = UserHandleCompat.fromIntent(intent);
+ if (user != null) {
+ enqueuePackageUpdated(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE,
+ new String[0], user));
+ }
}
}
@@ -1733,23 +1736,10 @@
int countX = profile.numColumns;
int countY = profile.numRows;
- if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) {
- long migrationStartTime = System.currentTimeMillis();
- Log.v(TAG, "Starting workspace migration after restore");
- try {
- MigrateFromRestoreTask task = new MigrateFromRestoreTask(mContext);
- // Clear the flags before starting the task, so that we do not run the task
- // again, in case there was an uncaught error.
- MigrateFromRestoreTask.clearFlags(mContext);
- task.execute();
- } catch (Exception e) {
- Log.e(TAG, "Error during grid migration", e);
-
- // Clear workspace.
- mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
- }
- Log.v(TAG, "Workspace migration completed in "
- + (System.currentTimeMillis() - migrationStartTime));
+ if (GridSizeMigrationTask.ENABLED &&
+ !GridSizeMigrationTask.migrateGridIfNeeded(mContext)) {
+ // Migration failed. Clear workspace.
+ mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE;
}
if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
@@ -1773,8 +1763,8 @@
.getInstance(mContext).updateAndGetActiveSessionCache();
sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
- final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
- final ArrayList<Long> restoredRows = new ArrayList<Long>();
+ final ArrayList<Long> itemsToRemove = new ArrayList<>();
+ final ArrayList<Long> restoredRows = new ArrayList<>();
final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
final Cursor c = contentResolver.query(contentUri, null, null, null, null);
@@ -1783,6 +1773,7 @@
// Load workspace in reverse order to ensure that latest items are loaded first (and
// before any earlier duplicates)
final LongArrayMap<ItemInfo[][]> occupied = new LongArrayMap<>();
+ HashMap<ComponentKey, AppWidgetProviderInfo> widgetProvidersMap = null;
try {
final int idIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites._ID);
@@ -1819,8 +1810,11 @@
final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
+ final LongSparseArray<Boolean> quietMode = new LongSparseArray<>();
for (UserHandleCompat user : mUserManager.getUserProfiles()) {
- allUsers.put(mUserManager.getSerialNumberForUser(user), user);
+ long serialNo = mUserManager.getSerialNumberForUser(user);
+ allUsers.put(serialNo, user);
+ quietMode.put(serialNo, mUserManager.isQuietModeEnabled(user));
}
ShortcutInfo info;
@@ -1831,6 +1825,7 @@
long serialNumber;
Intent intent;
UserHandleCompat user;
+ String targetPackage;
while (!mStopped && c.moveToNext()) {
try {
@@ -1849,6 +1844,7 @@
int promiseType = c.getInt(restoredIndex);
int disabledState = 0;
boolean itemReplaced = false;
+ targetPackage = null;
if (user == null) {
// User has been deleted remove the item.
itemsToRemove.add(id);
@@ -1862,6 +1858,9 @@
cn.getPackageName(), user);
boolean validComponent = validPkg &&
launcherApps.isActivityEnabledForProfile(cn, user);
+ if (validPkg) {
+ targetPackage = cn.getPackageName();
+ }
if (validComponent) {
if (restored) {
@@ -1869,6 +1868,9 @@
restoredRows.add(id);
restored = false;
}
+ if (quietMode.get(serialNumber)) {
+ disabledState = ShortcutInfo.FLAG_DISABLED_QUIET_USER;
+ }
} else if (validPkg) {
intent = null;
if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
@@ -1937,9 +1939,8 @@
itemsToRemove.add(id);
continue;
}
- } else if (launcherApps.isAppEnabled(
- manager, cn.getPackageName(),
- PackageManager.GET_UNINSTALLED_PACKAGES)) {
+ } else if (PackageManagerHelper.isAppOnSdcard(
+ manager, cn.getPackageName())) {
// Package is present but not available.
allowMissingTarget = true;
disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
@@ -1982,7 +1983,7 @@
if (itemReplaced) {
if (user.equals(UserHandleCompat.myUserHandle())) {
- info = getAppShortcutInfo(manager, intent, user, context, null,
+ info = getAppShortcutInfo(intent, user, context, null,
cursorIconInfo.iconIndex, titleIndex,
false, useLowResIcon);
} else {
@@ -2005,12 +2006,17 @@
}
} else if (itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- info = getAppShortcutInfo(manager, intent, user, context, c,
+ info = getAppShortcutInfo(intent, user, context, c,
cursorIconInfo.iconIndex, titleIndex,
allowMissingTarget, useLowResIcon);
} else {
info = getShortcutInfo(c, context, titleIndex, cursorIconInfo);
+ // Shortcuts are only available on the primary profile
+ if (PackageManagerHelper.isAppSuspended(manager, targetPackage)) {
+ 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
@@ -2038,7 +2044,7 @@
if (info.promisedIntent != null) {
info.promisedIntent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
}
- info.isDisabled = disabledState;
+ info.isDisabled |= disabledState;
if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
}
@@ -2141,10 +2147,14 @@
final boolean wasProviderReady = (restoreStatus &
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
- final LauncherAppWidgetProviderInfo provider =
- LauncherModel.getProviderInfo(context,
+ if (widgetProvidersMap == null) {
+ widgetProvidersMap = AppWidgetManagerCompat
+ .getInstance(mContext).getAllProvidersMap();
+ }
+ final AppWidgetProviderInfo provider = widgetProvidersMap.get(
+ new ComponentKey(
ComponentName.unflattenFromString(savedProvider),
- user);
+ user));
final boolean isProviderReady = isValidProvider(provider);
if (!isSafeMode && !customWidget &&
@@ -2387,7 +2397,7 @@
Collections.sort(allWorkspaceItems, new Comparator<ItemInfo>() {
@Override
public int compare(ItemInfo lhs, ItemInfo rhs) {
- return (int) (lhs.container - rhs.container);
+ return Utilities.longCompare(lhs.container, rhs.container);
}
});
for (ItemInfo info : allWorkspaceItems) {
@@ -2469,7 +2479,7 @@
lhs.cellY * cellCountX + lhs.cellX);
long rr = (rhs.container * containerOffset + rhs.screenId * screenOffset +
rhs.cellY * cellCountX + rhs.cellX);
- return (int) (lr - rr);
+ return Utilities.longCompare(lr, rr);
}
});
}
@@ -2757,18 +2767,16 @@
@SuppressWarnings("unchecked")
final ArrayList<AppInfo> list
= (ArrayList<AppInfo>) mBgAllAppsList.data.clone();
- final WidgetsModel widgetList = mBgWidgetsModel.clone();
Runnable r = new Runnable() {
public void run() {
final long t = SystemClock.uptimeMillis();
final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
if (callbacks != null) {
callbacks.bindAllApplications(list);
- callbacks.bindAllPackages(widgetList);
}
if (DEBUG_LOADERS) {
Log.d(TAG, "bound all " + list.size() + " apps from cache in "
- + (SystemClock.uptimeMillis()-t) + "ms");
+ + (SystemClock.uptimeMillis() - t) + "ms");
}
}
};
@@ -2808,12 +2816,12 @@
if (apps == null || apps.isEmpty()) {
return;
}
-
+ boolean quietMode = mUserManager.isQuietModeEnabled(user);
// Create the ApplicationInfos
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfoCompat app = apps.get(i);
// This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
+ mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, quietMode));
}
final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
@@ -2856,7 +2864,7 @@
callbacks.bindAllApplications(added);
if (DEBUG_LOADERS) {
Log.d(TAG, "bound " + added.size() + " apps in "
- + (SystemClock.uptimeMillis() - bindTime) + "ms");
+ + (SystemClock.uptimeMillis() - bindTime) + "ms");
}
} else {
Log.i(TAG, "not binding apps: no Launcher activity");
@@ -2865,8 +2873,6 @@
});
// Cleanup any data stored for a deleted user.
ManagedProfileHeuristic.processAllUsers(profiles, mContext);
-
- loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */);
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
+ (SystemClock.uptimeMillis() - loadTime) + "ms");
@@ -2933,9 +2939,6 @@
}
});
}
-
- // Reload widget list. No need to refresh, as we only want to update the icons and labels.
- loadAndBindWidgetsAndShortcuts(callbacks, false);
}
void enqueuePackageUpdated(PackageUpdatedTask task) {
@@ -2958,10 +2961,7 @@
packagesUnavailable.clear();
for (String pkg : entry.getValue()) {
if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
- boolean packageOnSdcard = launcherApps.isAppEnabled(
- manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
- if (packageOnSdcard) {
- Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
+ if (PackageManagerHelper.isAppOnSdcard(manager, pkg)) {
packagesUnavailable.add(pkg);
} else {
Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
@@ -2993,7 +2993,9 @@
public static final int OP_UPDATE = 2;
public static final int OP_REMOVE = 3; // uninstlled
public static final int OP_UNAVAILABLE = 4; // external media unmounted
-
+ public static final int OP_SUSPEND = 5; // package suspended
+ public static final int OP_UNSUSPEND = 6; // package unsuspended
+ public static final int OP_USER_AVAILABILITY_CHANGE = 7; // user available/unavailable
public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
mOp = op;
@@ -3010,6 +3012,8 @@
final String[] packages = mPackages;
final int N = packages.length;
+ FlagOp flagOp = FlagOp.NO_OP;
+ StringFilter pkgFilter = StringFilter.of(new HashSet<>(Arrays.asList(packages)));
switch (mOp) {
case OP_ADD: {
for (int i=0; i<N; i++) {
@@ -3031,6 +3035,8 @@
mBgAllAppsList.updatePackage(context, packages[i], mUser);
mApp.getWidgetCache().removePackage(packages[i], mUser);
}
+ // Since package was just updated, the target must be available now.
+ flagOp = FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
break;
case OP_REMOVE: {
ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(context, mUser);
@@ -3049,6 +3055,23 @@
mBgAllAppsList.removePackage(packages[i], mUser);
mApp.getWidgetCache().removePackage(packages[i], mUser);
}
+ flagOp = FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE);
+ break;
+ case OP_SUSPEND:
+ case OP_UNSUSPEND:
+ flagOp = mOp == OP_SUSPEND ?
+ FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED) :
+ FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_SUSPENDED);
+ if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.(un)suspend " + N);
+ mBgAllAppsList.updatePackageFlags(pkgFilter, mUser, flagOp);
+ break;
+ case OP_USER_AVAILABILITY_CHANGE:
+ flagOp = UserManagerCompat.getInstance(context).isQuietModeEnabled(mUser)
+ ? FlagOp.addFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER)
+ : FlagOp.removeFlag(ShortcutInfo.FLAG_DISABLED_QUIET_USER);
+ // We want to update all packages for this user.
+ pkgFilter = StringFilter.matchesAll();
+ mBgAllAppsList.updatePackageFlags(pkgFilter, mUser, flagOp);
break;
}
@@ -3057,11 +3080,11 @@
final ArrayList<AppInfo> removedApps = new ArrayList<AppInfo>();
if (mBgAllAppsList.added.size() > 0) {
- added = new ArrayList<AppInfo>(mBgAllAppsList.added);
+ added = new ArrayList<>(mBgAllAppsList.added);
mBgAllAppsList.added.clear();
}
if (mBgAllAppsList.modified.size() > 0) {
- modified = new ArrayList<AppInfo>(mBgAllAppsList.modified);
+ modified = new ArrayList<>(mBgAllAppsList.modified);
mBgAllAppsList.modified.clear();
}
if (mBgAllAppsList.removed.size() > 0) {
@@ -3069,14 +3092,7 @@
mBgAllAppsList.removed.clear();
}
- final Callbacks callbacks = getCallback();
- if (callbacks == null) {
- Log.w(TAG, "Nobody to tell about the new app. Launcher is probably loading.");
- return;
- }
-
- final HashMap<ComponentName, AppInfo> addedOrUpdatedApps =
- new HashMap<ComponentName, AppInfo>();
+ final HashMap<ComponentName, AppInfo> addedOrUpdatedApps = new HashMap<>();
if (added != null) {
addAppsToAllApps(context, added);
@@ -3086,6 +3102,7 @@
}
if (modified != null) {
+ final Callbacks callbacks = getCallback();
final ArrayList<AppInfo> modifiedFinal = modified;
for (AppInfo ai : modified) {
addedOrUpdatedApps.put(ai.componentName, ai);
@@ -3102,12 +3119,11 @@
}
// Update shortcut infos
- if (mOp == OP_ADD || mOp == OP_UPDATE) {
+ if (mOp == OP_ADD || flagOp != FlagOp.NO_OP) {
final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
- HashSet<String> packageSet = new HashSet<String>(Arrays.asList(packages));
synchronized (sBgLock) {
for (ItemInfo info : sBgItemsIdMap) {
if (info instanceof ShortcutInfo && mUser.equals(info.user)) {
@@ -3117,7 +3133,7 @@
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
- && packageSet.contains(si.iconResource.packageName)) {
+ && pkgFilter.matches(si.iconResource.packageName)) {
Bitmap icon = Utilities.createIconBitmap(
si.iconResource.packageName,
si.iconResource.resourceName, context);
@@ -3129,7 +3145,7 @@
}
ComponentName cn = si.getTargetComponent();
- if (cn != null && packageSet.contains(cn.getPackageName())) {
+ if (cn != null && pkgFilter.matches(cn.getPackageName())) {
AppInfo appInfo = addedOrUpdatedApps.get(cn);
if (si.isPromise()) {
@@ -3177,9 +3193,9 @@
infoUpdated = true;
}
- if ((si.isDisabled & ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE) != 0) {
- // Since package was just updated, the target must be available now.
- si.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
+ int oldDisabledFlags = si.isDisabled;
+ si.isDisabled = flagOp.apply(si.isDisabled);
+ if (si.isDisabled != oldDisabledFlags) {
shortcutUpdated = true;
}
}
@@ -3190,11 +3206,11 @@
if (infoUpdated) {
updateItemInDatabase(context, si);
}
- } else if (info instanceof LauncherAppWidgetInfo) {
+ } else if (info instanceof LauncherAppWidgetInfo && mOp == OP_ADD) {
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
- && packageSet.contains(widgetInfo.providerName.getPackageName())) {
+ && pkgFilter.matches(widgetInfo.providerName.getPackageName())) {
widgetInfo.restoreStatus &=
~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
@@ -3212,6 +3228,7 @@
}
if (!updatedShortcuts.isEmpty() || !removedShortcuts.isEmpty()) {
+ final Callbacks callbacks = getCallback();
mHandler.post(new Runnable() {
public void run() {
@@ -3227,6 +3244,7 @@
}
}
if (!widgets.isEmpty()) {
+ final Callbacks callbacks = getCallback();
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = getCallback();
@@ -3238,171 +3256,104 @@
}
}
- final ArrayList<String> removedPackageNames =
- new ArrayList<String>();
- if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) {
+ final HashSet<String> removedPackages = new HashSet<>();
+ final HashSet<ComponentName> removedComponents = new HashSet<>();
+ if (mOp == OP_REMOVE) {
// Mark all packages in the broadcast to be removed
- removedPackageNames.addAll(Arrays.asList(packages));
+ Collections.addAll(removedPackages, packages);
+
+ // No need to update the removedComponents as
+ // removedPackages is a super-set of removedComponents
} else if (mOp == OP_UPDATE) {
// Mark disabled packages in the broadcast to be removed
for (int i=0; i<N; i++) {
if (isPackageDisabled(context, packages[i], mUser)) {
- removedPackageNames.add(packages[i]);
+ removedPackages.add(packages[i]);
}
}
+
+ // Update removedComponents as some components can get removed during package update
+ for (AppInfo info : removedApps) {
+ removedComponents.add(info.componentName);
+ }
}
- if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
- final int removeReason;
- if (mOp == OP_UNAVAILABLE) {
- removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
- } else {
- // Remove all the components associated with this package
- for (String pn : removedPackageNames) {
- deletePackageFromDatabase(context, pn, mUser);
- }
- // Remove all the specific components
- for (AppInfo a : removedApps) {
- ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
- deleteItemsFromDatabase(context, infos);
- }
- removeReason = 0;
+ if (!removedPackages.isEmpty() || !removedComponents.isEmpty()) {
+ for (String pn : removedPackages) {
+ deletePackageFromDatabase(context, pn, mUser);
+ }
+ for (ComponentName cn : removedComponents) {
+ deleteItemsFromDatabase(context, getItemInfoForComponentName(cn, mUser));
}
// Remove any queued items from the install queue
- InstallShortcutReceiver.removeFromInstallQueue(context, removedPackageNames, mUser);
+ InstallShortcutReceiver.removeFromInstallQueue(context, removedPackages, mUser);
+
// Call the components-removed callback
+ final Callbacks callbacks = getCallback();
mHandler.post(new Runnable() {
public void run() {
Callbacks cb = getCallback();
if (callbacks == cb && cb != null) {
- callbacks.bindComponentsRemoved(
- removedPackageNames, removedApps, mUser, removeReason);
+ callbacks.bindWorkspaceComponentsRemoved(
+ removedPackages, removedComponents, mUser);
}
}
});
}
- // Update widgets
- if (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE) {
- // Always refresh for a package event on secondary user
- boolean needToRefresh = !mUser.equals(UserHandleCompat.myUserHandle());
-
- // Refresh widget list, if the package already had a widget.
- synchronized (sBgLock) {
- if (sBgWidgetProviders != null) {
- HashSet<String> pkgSet = new HashSet<>();
- Collections.addAll(pkgSet, mPackages);
-
- for (ComponentKey key : sBgWidgetProviders.keySet()) {
- needToRefresh |= key.user.equals(mUser) &&
- pkgSet.contains(key.componentName.getPackageName());
+ if (!removedApps.isEmpty()) {
+ // Remove corresponding apps from All-Apps
+ final Callbacks callbacks = getCallback();
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = getCallback();
+ if (callbacks == cb && cb != null) {
+ callbacks.bindAppInfosRemoved(removedApps);
}
}
- }
-
- if (!needToRefresh && mOp != OP_REMOVE) {
- // Refresh widget list, if there is any newly added widget
- PackageManager pm = context.getPackageManager();
- for (String pkg : mPackages) {
- needToRefresh |= !pm.queryBroadcastReceivers(
- new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
- .setPackage(pkg), 0).isEmpty();
- }
- }
-
- loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh);
+ });
}
- // Write all the logs to disk
- mHandler.post(new Runnable() {
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.dumpLogsToLocalData();
+ // Notify launcher of widget update. From marshmallow onwards we use AppWidgetHost to
+ // get widget update signals.
+ if (!Utilities.ATLEAST_MARSHMALLOW &&
+ (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE)) {
+ final Callbacks callbacks = getCallback();
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = getCallback();
+ if (callbacks == cb && cb != null) {
+ callbacks.notifyWidgetProvidersChanged();
+ }
}
- }
- });
- }
- }
-
- public static List<LauncherAppWidgetProviderInfo> getWidgetProviders(Context context,
- boolean refresh) {
- ArrayList<LauncherAppWidgetProviderInfo> results =
- new ArrayList<LauncherAppWidgetProviderInfo>();
- try {
- synchronized (sBgLock) {
- if (sBgWidgetProviders == null || refresh) {
- HashMap<ComponentKey, LauncherAppWidgetProviderInfo> tmpWidgetProviders
- = new HashMap<>();
- AppWidgetManagerCompat wm = AppWidgetManagerCompat.getInstance(context);
- LauncherAppWidgetProviderInfo info;
-
- List<AppWidgetProviderInfo> widgets = wm.getAllProviders();
- for (AppWidgetProviderInfo pInfo : widgets) {
- info = LauncherAppWidgetProviderInfo.fromProviderInfo(context, pInfo);
- UserHandleCompat user = wm.getUser(info);
- tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
- }
-
- Collection<CustomAppWidget> customWidgets = Launcher.getCustomAppWidgets().values();
- for (CustomAppWidget widget : customWidgets) {
- info = new LauncherAppWidgetProviderInfo(context, widget);
- UserHandleCompat user = wm.getUser(info);
- tmpWidgetProviders.put(new ComponentKey(info.provider, user), info);
- }
- // Replace the global list at the very end, so that if there is an exception,
- // previously loaded provider list is used.
- sBgWidgetProviders = tmpWidgetProviders;
- }
- results.addAll(sBgWidgetProviders.values());
- return results;
- }
- } catch (Exception e) {
- if (e.getCause() instanceof TransactionTooLargeException) {
- // 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
- // onResume is called to refresh the widget provider list.
- synchronized (sBgLock) {
- if (sBgWidgetProviders != null) {
- results.addAll(sBgWidgetProviders.values());
- }
- return results;
- }
- } else {
- throw e;
+ });
}
}
}
- public static LauncherAppWidgetProviderInfo getProviderInfo(Context ctx, ComponentName name,
- UserHandleCompat user) {
- synchronized (sBgLock) {
- if (sBgWidgetProviders == null) {
- getWidgetProviders(ctx, false /* refresh */);
+ private void bindWidgetsModel(final Callbacks callbacks, final WidgetsModel model) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ Callbacks cb = getCallback();
+ if (callbacks == cb && cb != null) {
+ callbacks.bindWidgetsModel(model);
+ }
}
- return sBgWidgetProviders.get(new ComponentKey(name, user));
- }
+ });
}
- public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {
-
+ public void refreshAndBindWidgetsAndShortcuts(
+ final Callbacks callbacks, final boolean bindFirst) {
runOnWorkerThread(new Runnable() {
@Override
public void run() {
- updateWidgetsModel(refresh);
- final WidgetsModel model = mBgWidgetsModel.clone();
-
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Callbacks cb = getCallback();
- if (callbacks == cb && cb != null) {
- callbacks.bindAllPackages(model);
- }
- }
- });
+ if (bindFirst && !mBgWidgetsModel.isEmpty()) {
+ bindWidgetsModel(callbacks, mBgWidgetsModel.clone());
+ }
+ final WidgetsModel model = mBgWidgetsModel.updateAndClone(mApp.getContext());
+ bindWidgetsModel(callbacks, model);
// update the Widget entries inside DB on the worker thread.
LauncherAppState.getInstance().getWidgetCache().removeObsoletePreviews(
model.getRawList());
@@ -3410,20 +3361,6 @@
});
}
- /**
- * Returns a list of ResolveInfos/AppWidgetInfos.
- *
- * @see #loadAndBindWidgetsAndShortcuts
- */
- @Thunk void updateWidgetsModel(boolean refresh) {
- PackageManager packageManager = mApp.getContext().getPackageManager();
- final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
- widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
- Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
- widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
- mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
- }
-
@Thunk static boolean isPackageDisabled(Context context, String packageName,
UserHandleCompat user) {
final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
@@ -3511,7 +3448,7 @@
*
* If c is not null, then it will be used to fill in missing data like the title and icon.
*/
- public ShortcutInfo getAppShortcutInfo(PackageManager manager, Intent intent,
+ public ShortcutInfo getAppShortcutInfo(Intent intent,
UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
boolean allowMissingTarget, boolean useLowResIcon) {
if (user == null) {
@@ -3521,7 +3458,7 @@
ComponentName componentName = intent.getComponent();
if (componentName == null) {
- Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
+ Log.d(TAG, "Missing component found in getShortcutInfo");
return null;
}
@@ -3541,6 +3478,10 @@
info.setIcon(icon == null ? mIconCache.getDefaultIcon(user) : icon);
}
+ if (lai != null && PackageManagerHelper.isAppSuspended(lai.getApplicationInfo())) {
+ info.isDisabled = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
+ }
+
// from the db
if (TextUtils.isEmpty(info.title) && c != null) {
info.title = Utilities.trim(c.getString(titleIndex));
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8791e9e..7c4b78d 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -57,7 +57,6 @@
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.Thunk;
-import java.io.File;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
@@ -79,11 +78,15 @@
private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
@Thunk LauncherProviderChangeListener mListener;
- @Thunk DatabaseHelper mOpenHelper;
+ protected DatabaseHelper mOpenHelper;
@Override
public boolean onCreate() {
final Context context = getContext();
+ // The content provider exists for the entire duration of the launcher main process and
+ // is the first component to get created. Initializing application context here ensures
+ // that LauncherAppState always exists in the main process.
+ LauncherAppState.setApplicationContext(context.getApplicationContext());
StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskWrites();
mOpenHelper = new DatabaseHelper(context);
StrictMode.setThreadPolicy(oldPolicy);
@@ -110,6 +113,15 @@
}
}
+ /**
+ * Overridden in tests
+ */
+ protected synchronized void createDbIfNotExists() {
+ if (mOpenHelper == null) {
+ mOpenHelper = new DatabaseHelper(getContext());
+ }
+ }
+
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
@@ -256,21 +268,25 @@
switch (method) {
case LauncherSettings.Settings.METHOD_GET_BOOLEAN: {
Bundle result = new Bundle();
- result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
- getContext().getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE)
- .getBoolean(arg, extras.getBoolean(
- LauncherSettings.Settings.EXTRA_DEFAULT_VALUE)));
+ if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(arg)) {
+ result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+ Utilities.isAllowRotationPrefEnabled(getContext()));
+ } else {
+ result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
+ Utilities.getPrefs(getContext()).getBoolean(arg, extras.getBoolean(
+ LauncherSettings.Settings.EXTRA_DEFAULT_VALUE)));
+ }
return result;
}
case LauncherSettings.Settings.METHOD_SET_BOOLEAN: {
boolean value = extras.getBoolean(LauncherSettings.Settings.EXTRA_VALUE);
- getContext().getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE)
- .edit().putBoolean(arg, value).apply();
+ Utilities.getPrefs(getContext()).edit().putBoolean(arg, value).apply();
if (mListener != null) {
mListener.onSettingsChanged(arg, value);
}
+ if (extras.getBoolean(LauncherSettings.Settings.NOTIFY_BACKUP)) {
+ LauncherBackupAgentHelper.dataChanged(getContext());
+ }
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, value);
return result;
@@ -315,7 +331,10 @@
return folderIds;
}
- private void notifyListeners() {
+ /**
+ * Overridden in tests
+ */
+ protected void notifyListeners() {
// always notify the backup agent
LauncherBackupAgentHelper.dataChanged(getContext());
if (mListener != null) {
@@ -331,10 +350,6 @@
return mOpenHelper.generateNewItemId();
}
- public void updateMaxItemId(long id) {
- mOpenHelper.updateMaxItemId(id);
- }
-
public long generateNewScreenId() {
return mOpenHelper.generateNewScreenId();
}
@@ -347,11 +362,7 @@
}
public void clearFlagEmptyDbCreated() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE)
- .edit()
- .remove(EMPTY_DATABASE_CREATED)
- .commit();
+ Utilities.getPrefs(getContext()).edit().remove(EMPTY_DATABASE_CREATED).commit();
}
/**
@@ -362,8 +373,7 @@
* 4) The default configuration for the particular device
*/
synchronized public void loadDefaultFavoritesIfNecessary() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ SharedPreferences sp = Utilities.getPrefs(getContext());
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Log.d(TAG, "loading default workspace");
@@ -463,17 +473,13 @@
public void deleteDatabase() {
// Are you sure? (y/n)
- final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
- final File dbFile = new File(db.getPath());
- mOpenHelper.close();
- if (dbFile.exists()) {
- SQLiteDatabase.deleteDatabase(dbFile);
- }
- mOpenHelper = new DatabaseHelper(getContext());
- mOpenHelper.mListener = mListener;
+ mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
}
- private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
+ /**
+ * The class is subclassed in tests to create an in-memory db.
+ */
+ protected static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
private final Context mContext;
@Thunk final AppWidgetHost mAppWidgetHost;
private long mMaxItemId = -1;
@@ -488,6 +494,16 @@
mContext = context;
mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
+ // Table creation sometimes fails silently, which leads to a crash loop.
+ // This way, we will try to create a table every time after crash, so the device
+ // would eventually be able to recover.
+ if (!tableExists(TABLE_FAVORITES) || !tableExists(TABLE_WORKSPACE_SCREENS)) {
+ Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
+ // This operation is a no-op if the table already exists.
+ addFavoritesTable(getWritableDatabase(), true);
+ addWorkspacesTable(getWritableDatabase(), true);
+ }
+
// In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
// the DB here
if (mMaxItemId == -1) {
@@ -498,6 +514,30 @@
}
}
+ /**
+ * Constructor used only in tests.
+ */
+ public DatabaseHelper(Context context, String tableName) {
+ super(context, tableName, null, DATABASE_VERSION);
+ mContext = context;
+
+ mAppWidgetHost = null;
+ mMaxItemId = initializeMaxItemId(getWritableDatabase());
+ mMaxScreenId = initializeMaxScreenId(getWritableDatabase());
+ }
+
+ private boolean tableExists(String tableName) {
+ Cursor c = getReadableDatabase().query(
+ true, "sqlite_master", new String[] {"tbl_name"},
+ "tbl_name = ?", new String[] {tableName},
+ null, null, null, null, null);
+ try {
+ return c.getCount() > 0;
+ } finally {
+ c.close();
+ }
+ }
+
public boolean wasNewDbCreated() {
return mNewDbCreated;
}
@@ -510,37 +550,8 @@
mMaxScreenId = 0;
mNewDbCreated = true;
- UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
- long userSerialNumber = userManager.getSerialNumberForUser(
- UserHandleCompat.myUserHandle());
-
- db.execSQL("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," +
- "isShortcut INTEGER," +
- "iconType INTEGER," +
- "iconPackage TEXT," +
- "iconResource TEXT," +
- "icon BLOB," +
- "uri TEXT," +
- "displayMode INTEGER," +
- "appWidgetProvider TEXT," +
- "modified INTEGER NOT NULL DEFAULT 0," +
- "restored INTEGER NOT NULL DEFAULT 0," +
- "profileId INTEGER DEFAULT " + userSerialNumber + "," +
- "rank INTEGER NOT NULL DEFAULT 0," +
- "options INTEGER NOT NULL DEFAULT 0" +
- ");");
- addWorkspacesTable(db);
+ addFavoritesTable(db, false);
+ addWorkspacesTable(db, false);
// Database was just created, so wipe any previous widgets
if (mAppWidgetHost != null) {
@@ -565,14 +576,59 @@
// Fresh and clean launcher DB.
mMaxItemId = initializeMaxItemId(db);
- setFlagEmptyDbCreated();
-
- // When a new DB is created, remove all previously stored managed profile information.
- ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(), mContext);
+ onEmptyDbCreated();
}
- private void addWorkspacesTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + TABLE_WORKSPACE_SCREENS + " (" +
+ /**
+ * Overriden in tests.
+ */
+ protected void onEmptyDbCreated() {
+ // Set the flag for empty DB
+ Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
+
+ // When a new DB is created, remove all previously stored managed profile information.
+ ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(),
+ mContext);
+ }
+
+ protected long getDefaultUserSerial() {
+ return UserManagerCompat.getInstance(mContext).getSerialNumberForUser(
+ UserHandleCompat.myUserHandle());
+ }
+
+ private void addFavoritesTable(SQLiteDatabase db, boolean optional) {
+ String ifNotExists = optional ? " IF NOT EXISTS " : "";
+ db.execSQL("CREATE TABLE " + ifNotExists + 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," +
+ "isShortcut INTEGER," +
+ "iconType INTEGER," +
+ "iconPackage TEXT," +
+ "iconResource TEXT," +
+ "icon BLOB," +
+ "uri TEXT," +
+ "displayMode INTEGER," +
+ "appWidgetProvider TEXT," +
+ "modified INTEGER NOT NULL DEFAULT 0," +
+ "restored INTEGER NOT NULL DEFAULT 0," +
+ "profileId INTEGER DEFAULT " + getDefaultUserSerial() + "," +
+ "rank INTEGER NOT NULL DEFAULT 0," +
+ "options INTEGER NOT NULL DEFAULT 0" +
+ ");");
+ }
+
+ private void addWorkspacesTable(SQLiteDatabase db, boolean optional) {
+ String ifNotExists = optional ? " IF NOT EXISTS " : "";
+ db.execSQL("CREATE TABLE " + ifNotExists + TABLE_WORKSPACE_SCREENS + " (" +
LauncherSettings.WorkspaceScreens._ID + " INTEGER PRIMARY KEY," +
LauncherSettings.WorkspaceScreens.SCREEN_RANK + " INTEGER," +
LauncherSettings.ChangeLogColumns.MODIFIED + " INTEGER NOT NULL DEFAULT 0" +
@@ -611,15 +667,7 @@
}
private void setFlagJustLoadedOldDb() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
- sp.edit().putBoolean(EMPTY_DATABASE_CREATED, false).commit();
- }
-
- private void setFlagEmptyDbCreated() {
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
- sp.edit().putBoolean(EMPTY_DATABASE_CREATED, true).commit();
+ Utilities.getPrefs(mContext).edit().putBoolean(EMPTY_DATABASE_CREATED, false).commit();
}
@Override
@@ -632,7 +680,7 @@
// With the new shrink-wrapped and re-orderable workspaces, it makes sense
// to persist workspace screens and their relative order.
mMaxScreenId = 0;
- addWorkspacesTable(db);
+ addWorkspacesTable(db, false);
}
case 13: {
db.beginTransaction();
@@ -678,7 +726,7 @@
// available (tablet users). Because one of the possible cling flows (migration)
// is very destructive (wipes out workspaces), we want to prevent this from showing
// until clear data. We do so by marking that the clings have been shown.
- LauncherClings.synchonouslyMarkFirstRunClingDismissed(mContext);
+ LauncherClings.markFirstRunClingDismissed(mContext);
}
case 17: {
// No-op
@@ -828,7 +876,7 @@
}
db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
- addWorkspacesTable(db);
+ addWorkspacesTable(db, false);
// Add all screen ids back
int total = sortedIDs.size();
@@ -926,10 +974,6 @@
return dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
}
- public void updateMaxItemId(long id) {
- mMaxItemId = id + 1;
- }
-
public void checkId(String table, ContentValues values) {
long id = values.getAsLong(LauncherSettings.BaseLauncherColumns._ID);
if (table == LauncherProvider.TABLE_WORKSPACE_SCREENS) {
diff --git a/src/com/android/launcher3/LauncherRootView.java b/src/com/android/launcher3/LauncherRootView.java
index e8c11c4..55a512f 100644
--- a/src/com/android/launcher3/LauncherRootView.java
+++ b/src/com/android/launcher3/LauncherRootView.java
@@ -1,17 +1,71 @@
package com.android.launcher3;
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.view.View;
public class LauncherRootView extends InsettableFrameLayout {
+
+ private final Paint mOpaquePaint;
+ private boolean mDrawRightInsetBar;
+ private int mRightInsetBarWidth;
+
+ private View mAlignedView;
+
public LauncherRootView(Context context, AttributeSet attrs) {
super(context, attrs);
+
+ mOpaquePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mOpaquePaint.setColor(Color.BLACK);
+ mOpaquePaint.setStyle(Paint.Style.FILL);
}
@Override
+ protected void onFinishInflate() {
+ if (getChildCount() > 0) {
+ // LauncherRootView contains only one child, which should be aligned
+ // based on the horizontal insets.
+ mAlignedView = getChildAt(0);
+ }
+ super.onFinishInflate();
+ }
+
+ @TargetApi(23)
+ @Override
protected boolean fitSystemWindows(Rect insets) {
- setInsets(insets);
+ mDrawRightInsetBar = insets.right > 0 &&
+ (!Utilities.ATLEAST_MARSHMALLOW ||
+ getContext().getSystemService(ActivityManager.class).isLowRamDevice());
+ mRightInsetBarWidth = insets.right;
+ setInsets(mDrawRightInsetBar ? new Rect(0, insets.top, 0, insets.bottom) : insets);
+
+ if (mAlignedView != null && mDrawRightInsetBar) {
+ // Apply margins on aligned view to handle left/right insets.
+ MarginLayoutParams lp = (MarginLayoutParams) mAlignedView.getLayoutParams();
+ if (lp.leftMargin != insets.left || lp.rightMargin != insets.right) {
+ lp.leftMargin = insets.left;
+ lp.rightMargin = insets.right;
+ mAlignedView.setLayoutParams(lp);
+ }
+ }
+
return true; // I'll take it from here
}
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ super.dispatchDraw(canvas);
+
+ // If the right inset is opaque, draw a black rectangle to ensure that is stays opaque.
+ if (mDrawRightInsetBar) {
+ int width = getWidth();
+ canvas.drawRect(width - mRightInsetBarWidth, 0, width, getHeight(), mOpaquePaint);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 8a5804f..55e6395 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -113,7 +113,7 @@
/**
* The content:// style URL for this table
*/
- static final Uri CONTENT_URI = Uri.parse("content://" +
+ public static final Uri CONTENT_URI = Uri.parse("content://" +
ProviderConfig.AUTHORITY + "/" + TABLE_NAME);
/**
@@ -324,5 +324,8 @@
public static final String EXTRA_VALUE = "value";
public static final String EXTRA_DEFAULT_VALUE = "default_value";
+
+ // Extra for set_boolean method to also notify the backup manager of the change.
+ public static final String NOTIFY_BACKUP = "notify_backup";
}
}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index cdde8c1..b95e2b0 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -22,6 +22,7 @@
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.res.Resources;
import android.util.Log;
@@ -79,13 +80,18 @@
*/
public class LauncherStateTransitionAnimation {
+ private static final float FINAL_REVEAL_ALPHA_FOR_WIDGETS = 0.3f;
+
/**
* Private callbacks made during transition setup.
*/
- static abstract class PrivateTransitionCallbacks {
- float getMaterialRevealViewFinalAlpha(View revealView) {
- return 0;
+ private static class PrivateTransitionCallbacks {
+ private final float materialRevealViewFinalAlpha;
+
+ PrivateTransitionCallbacks(float revealAlpha) {
+ materialRevealViewFinalAlpha = revealAlpha;
}
+
float getMaterialRevealViewStartFinalRadius() {
return 0;
}
@@ -96,7 +102,7 @@
void onTransitionComplete() {}
}
- public static final String TAG = "LauncherStateTransitionAnimation";
+ public static final String TAG = "LSTAnimation";
// Flags to determine how to set the layers on views before the transition animation
public static final int BUILD_LAYER = 0;
@@ -120,11 +126,7 @@
final boolean animated, final boolean startSearchAfterTransition) {
final AllAppsContainerView toView = mLauncher.getAppsView();
final View buttonView = mLauncher.getAllAppsButton();
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
- @Override
- public float getMaterialRevealViewFinalAlpha(View revealView) {
- return 1f;
- }
+ PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@Override
public float getMaterialRevealViewStartFinalRadius() {
int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
@@ -151,8 +153,7 @@
};
// Only animate the search bar if animating from spring loaded mode back to all apps
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
- Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
- toView.getRevealView(), toView.getSearchBarView(), animated, cb);
+ Workspace.State.NORMAL_HIDDEN, buttonView, toView, animated, cb);
}
/**
@@ -163,15 +164,9 @@
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
- @Override
- public float getMaterialRevealViewFinalAlpha(View revealView) {
- return 0.3f;
- }
- };
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
- Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
- toView.getRevealView(), null, animated, cb);
+ Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, animated,
+ new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS));
}
/**
@@ -199,16 +194,15 @@
* Creates and starts a new animation to a particular overlay view.
*/
@SuppressLint("NewApi")
- private AnimatorSet startAnimationToOverlay(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final View buttonView, final View toView,
- final View contentView, final View revealView, final View overlaySearchBarView,
+ private AnimatorSet startAnimationToOverlay(
+ final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+ final View buttonView, final BaseContainerView toView,
final boolean animated, final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
final boolean material = Utilities.ATLEAST_LOLLIPOP;
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
- final int itemsAlphaStagger =
- res.getInteger(R.integer.config_overlayItemsAlphaStagger);
+ final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger);
final View fromView = mLauncher.getWorkspace();
@@ -226,11 +220,17 @@
animated, layerViews);
// Animate the search bar
- startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
- animated ? revealDuration : 0, overlaySearchBarView);
+ startWorkspaceSearchBarAnimation(
+ toWorkspaceState, animated ? revealDuration : 0, animation);
+
+ Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
+
+ final View contentView = toView.getContentView();
if (animated && initialized) {
// Setup the reveal view animation
+ final View revealView = toView.getRevealView();
+
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
float revealRadius = (float) Math.hypot(width / 2, height / 2);
@@ -244,9 +244,9 @@
final float revealViewToXDrift;
final float revealViewToYDrift;
if (material) {
- int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
- buttonView, null);
- revealViewToAlpha = pCb.getMaterialRevealViewFinalAlpha(revealView);
+ int[] buttonViewToPanelDelta = Utilities.getCenterDeltaInScreenSpace(
+ revealView, buttonView, null);
+ revealViewToAlpha = pCb.materialRevealViewFinalAlpha;
revealViewToYDrift = buttonViewToPanelDelta[1];
revealViewToXDrift = buttonViewToPanelDelta[0];
} else {
@@ -271,15 +271,6 @@
layerViews.put(revealView, BUILD_AND_SET_LAYER);
animation.play(panelAlphaAndDrift);
- if (overlaySearchBarView != null) {
- overlaySearchBarView.setAlpha(0f);
- ObjectAnimator searchBarAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 0f, 1f);
- searchBarAlpha.setDuration(100);
- searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
- layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
- animation.play(searchBarAlpha);
- }
-
// Setup the animation for the content view
contentView.setVisibility(View.VISIBLE);
contentView.setAlpha(0f);
@@ -342,11 +333,12 @@
animation.play(workspaceAnim);
}
+ animation.play(updateTransitionStepAnim);
+
// Dispatch the prepare transition signal
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
-
final AnimatorSet stateAnimation = animation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
@@ -402,18 +394,32 @@
}
/**
+ * Returns an Animator that calls {@link #dispatchOnLauncherTransitionStep(View, float)} on
+ * {@param fromView} and {@param toView} as the animation interpolates.
+ *
+ * This is a bit hacky: we create a dummy ValueAnimator just for the AnimatorUpdateListener.
+ */
+ private Animator dispatchOnLauncherTransitionStepAnim(final View fromView, final View toView) {
+ ValueAnimator updateAnimator = ValueAnimator.ofFloat(0, 1);
+ updateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ dispatchOnLauncherTransitionStep(fromView, animation.getAnimatedFraction());
+ dispatchOnLauncherTransitionStep(toView, animation.getAnimatedFraction());
+ }
+ });
+ return updateAnimator;
+ }
+
+ /**
* Starts and animation to the workspace from the apps view.
*/
private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState,
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
AllAppsContainerView appsView = mLauncher.getAppsView();
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
- @Override
- float getMaterialRevealViewFinalAlpha(View revealView) {
- // No alpha anim from all apps
- return 1f;
- }
+ // No alpha anim from all apps
+ PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks(1f) {
@Override
float getMaterialRevealViewStartFinalRadius() {
int allAppsButtonSize = mLauncher.getDeviceProfile().allAppsButtonVisualSize;
@@ -441,9 +447,8 @@
};
// Only animate the search bar if animating to spring loaded mode from all apps
mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
- toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
- appsView.getRevealView(), appsView.getSearchBarView(), animated,
- onCompleteRunnable, cb);
+ toWorkspacePage, mLauncher.getAllAppsButton(), appsView,
+ animated, onCompleteRunnable, cb);
}
/**
@@ -453,11 +458,8 @@
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
- PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
- @Override
- float getMaterialRevealViewFinalAlpha(View revealView) {
- return 0.3f;
- }
+ PrivateTransitionCallbacks cb =
+ new PrivateTransitionCallbacks(FINAL_REVEAL_ALPHA_FOR_WIDGETS) {
@Override
public AnimatorListenerAdapter getMaterialRevealViewAnimatorListener(
final View revealView, final View widgetsButtonView) {
@@ -469,19 +471,20 @@
};
}
};
- mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
- toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
- widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
- onCompleteRunnable, cb);
+ mCurrentAnimation = startAnimationToWorkspaceFromOverlay(
+ fromWorkspaceState, toWorkspaceState,
+ toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
+ animated, onCompleteRunnable, cb);
}
/**
* Creates and starts a new animation to the workspace.
*/
- private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState,
- final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView,
- final View fromView, final View contentView, final View revealView,
- final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable,
+ private AnimatorSet startAnimationToWorkspaceFromOverlay(
+ final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+ final int toWorkspacePage,
+ final View buttonView, final BaseContainerView fromView,
+ final boolean animated, final Runnable onCompleteRunnable,
final PrivateTransitionCallbacks pCb) {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
@@ -506,8 +509,10 @@
toWorkspacePage, animated, layerViews);
// Animate the search bar
- startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
- animated ? revealDuration : 0, overlaySearchBarView);
+ startWorkspaceSearchBarAnimation(
+ toWorkspaceState, animated ? revealDuration : 0, animation);
+
+ Animator updateTransitionStepAnim = dispatchOnLauncherTransitionStepAnim(fromView, toView);
if (animated && initialized) {
// Play the workspace animation
@@ -515,6 +520,10 @@
animation.play(workspaceAnim);
}
+ animation.play(updateTransitionStepAnim);
+ final View revealView = fromView.getRevealView();
+ final View contentView = fromView.getContentView();
+
// hideAppsCustomizeHelper is called in some cases when it is already hidden
// don't perform all these no-op animations. In particularly, this was causing
// the all-apps button to pop in and out.
@@ -562,7 +571,7 @@
// Setup animation for the reveal panel alpha
final float revealViewToAlpha = !material ? 0f :
- pCb.getMaterialRevealViewFinalAlpha(revealView);
+ pCb.materialRevealViewFinalAlpha;
if (revealViewToAlpha != 1f) {
ObjectAnimator panelAlpha = ObjectAnimator.ofFloat(revealView, "alpha",
1f, revealViewToAlpha);
@@ -590,16 +599,6 @@
itemsAlpha.setInterpolator(decelerateInterpolator);
animation.play(itemsAlpha);
- if (overlaySearchBarView != null) {
- overlaySearchBarView.setAlpha(1f);
- ObjectAnimator searchAlpha = ObjectAnimator.ofFloat(overlaySearchBarView, "alpha", 1f, 0f);
- searchAlpha.setDuration(material ? 100 : 150);
- searchAlpha.setInterpolator(decelerateInterpolator);
- searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
- layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
- animation.play(searchAlpha);
- }
-
if (material) {
// Animate the all apps button
float finalRadius = pCb.getMaterialRevealViewStartFinalRadius();
@@ -615,11 +614,11 @@
}
animation.play(reveal);
}
-
- dispatchOnLauncherTransitionPrepare(fromView, animated, true);
- dispatchOnLauncherTransitionPrepare(toView, animated, true);
}
+ dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+ dispatchOnLauncherTransitionPrepare(toView, animated, true);
+
animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -645,9 +644,6 @@
contentView.setTranslationY(0);
contentView.setAlpha(1);
}
- if (overlaySearchBarView != null) {
- overlaySearchBarView.setAlpha(1f);
- }
// This can hold unnecessary references to views.
cleanupAnimation();
@@ -703,35 +699,11 @@
/**
* Coordinates the workspace search bar animation along with the launcher state animation.
*/
- private void startWorkspaceSearchBarAnimation(AnimatorSet animation,
- final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, int duration,
- View overlaySearchBar) {
+ private void startWorkspaceSearchBarAnimation(
+ final Workspace.State toWorkspaceState, int duration, AnimatorSet animation) {
final SearchDropTargetBar.State toSearchBarState =
- toWorkspaceState.getSearchDropTargetBarState();
-
- if (overlaySearchBar != null) {
- if ((toWorkspaceState == Workspace.State.NORMAL) &&
- (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN)) {
- // If we are transitioning from the overlay to the workspace, then show the
- // workspace search bar immediately and let the overlay search bar fade out on top
- mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
- } else if (fromWorkspaceState == Workspace.State.NORMAL) {
- // If we are transitioning from the workspace to the overlay, then keep the
- // workspace search bar visible until the overlay search bar fades in on top
- animation.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0);
- }
- });
- } else {
- // Otherwise, then just animate the workspace search bar normally
- mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
- }
- } else {
- // If there is no overlay search bar, then just animate the workspace search bar
- mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration);
- }
+ toWorkspaceState.searchDropTargetBarState;
+ mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration, animation);
}
/**
diff --git a/src/com/android/launcher3/PageIndicatorMarker.java b/src/com/android/launcher3/PageIndicatorMarker.java
index f012db7..7bf21dd 100644
--- a/src/com/android/launcher3/PageIndicatorMarker.java
+++ b/src/com/android/launcher3/PageIndicatorMarker.java
@@ -45,6 +45,7 @@
}
protected void onFinishInflate() {
+ super.onFinishInflate();
mActiveMarker = (ImageView) findViewById(R.id.active);
mInactiveMarker = (ImageView) findViewById(R.id.inactive);
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 05f0a05..2dcff35 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -28,6 +28,7 @@
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.os.Build;
import android.os.Bundle;
import android.os.Parcel;
@@ -87,8 +88,6 @@
private int mFreeScrollMinScrollX = -1;
private int mFreeScrollMaxScrollX = -1;
- static final int AUTOMATIC_PAGE_SPACING = -1;
-
protected int mFlingThresholdVelocity;
protected int mMinFlingVelocity;
protected int mMinSnapVelocity;
@@ -133,8 +132,6 @@
protected final static int TOUCH_STATE_NEXT_PAGE = 3;
protected final static int TOUCH_STATE_REORDERING = 4;
- protected final static float ALPHA_QUANTIZE_LEVEL = 0.0001f;
-
protected int mTouchState = TOUCH_STATE_REST;
protected boolean mForceScreenScrolled = false;
@@ -144,12 +141,9 @@
private int mMaximumVelocity;
protected int mPageLayoutWidthGap;
protected int mPageLayoutHeightGap;
- protected int mCellCountX = 0;
- protected int mCellCountY = 0;
protected boolean mCenterPagesVertically;
protected boolean mAllowOverScroll = true;
protected int[] mTempVisiblePagesRange = new int[2];
- protected boolean mForceDrawAllChildrenNextFrame;
protected static final int INVALID_POINTER = -1;
@@ -198,6 +192,7 @@
private static final float[] sTmpPoint = new float[2];
private static final int[] sTmpIntPoint = new int[2];
private static final Rect sTmpRect = new Rect();
+ private static final RectF sTmpRectF = new RectF();
protected final Rect mInsets = new Rect();
protected final boolean mIsRtl;
@@ -819,7 +814,7 @@
childWidthMode = MeasureSpec.EXACTLY;
childHeightMode = MeasureSpec.EXACTLY;
- childWidth = getViewportWidth() - mInsets.left - mInsets.right;
+ childWidth = getViewportWidth();
childHeight = getViewportHeight();
}
if (referenceChildWidth == 0) {
@@ -1057,22 +1052,26 @@
protected void getVisiblePages(int[] range) {
final int pageCount = getChildCount();
- sTmpIntPoint[0] = sTmpIntPoint[1] = 0;
-
range[0] = -1;
range[1] = -1;
if (pageCount > 0) {
- int viewportWidth = getViewportWidth();
+ final int visibleLeft = -getLeft();
+ final int visibleRight = visibleLeft + getViewportWidth();
int curScreen = 0;
int count = getChildCount();
for (int i = 0; i < count; i++) {
View currPage = getPageAt(i);
- sTmpIntPoint[0] = 0;
- Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false);
- if (sTmpIntPoint[0] > viewportWidth) {
+ // Verify if the page bounds are within the visible range.
+ sTmpRectF.left = 0;
+ sTmpRectF.right = currPage.getMeasuredWidth();
+ currPage.getMatrix().mapRect(sTmpRectF);
+ sTmpRectF.offset(currPage.getLeft() - getScrollX(), 0);
+ getMatrix().mapRect(sTmpRectF);
+
+ if (sTmpRectF.left > visibleRight || sTmpRectF.right < visibleLeft) {
if (range[0] == -1) {
continue;
} else {
@@ -1080,15 +1079,6 @@
}
}
- sTmpIntPoint[0] = currPage.getMeasuredWidth();
- Utilities.getDescendantCoordRelativeToParent(currPage, this, sTmpIntPoint, false);
- if (sTmpIntPoint[0] < 0) {
- if (range[0] == -1) {
- continue;
- } else {
- break;
- }
- }
curScreen = i;
if (range[0] < 0) {
range[0] = curScreen;
@@ -1136,8 +1126,7 @@
for (int i = pageCount - 1; i >= 0; i--) {
final View v = getPageAt(i);
if (v == mDragView) continue;
- if (mForceDrawAllChildrenNextFrame ||
- (leftScreen <= i && i <= rightScreen && shouldDrawChild(v))) {
+ if (leftScreen <= i && i <= rightScreen && shouldDrawChild(v)) {
drawChild(canvas, v, drawingTime);
}
}
@@ -1146,7 +1135,6 @@
drawChild(canvas, mDragView, drawingTime);
}
- mForceDrawAllChildrenNextFrame = false;
canvas.restore();
}
}
@@ -1177,6 +1165,7 @@
canvas.rotate(90);
getEdgeVerticalPostion(sTmpIntPoint);
+
canvas.translate(sTmpIntPoint[0] - display.top, -display.width());
mEdgeGlowRight.setSize(sTmpIntPoint[1] - sTmpIntPoint[0], display.width());
if (mEdgeGlowRight.draw(canvas)) {
@@ -1219,7 +1208,17 @@
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- // XXX-RTL: This will be fixed in a future CL
+ if (super.dispatchUnhandledMove(focused, direction)) {
+ return true;
+ }
+
+ if (mIsRtl) {
+ if (direction == View.FOCUS_LEFT) {
+ direction = View.FOCUS_RIGHT;
+ } else if (direction == View.FOCUS_RIGHT) {
+ direction = View.FOCUS_LEFT;
+ }
+ }
if (direction == View.FOCUS_LEFT) {
if (getCurrentPage() > 0) {
snapToPage(getCurrentPage() - 1);
@@ -1231,7 +1230,7 @@
return true;
}
}
- return super.dispatchUnhandledMove(focused, direction);
+ return false;
}
@Override
@@ -2072,11 +2071,6 @@
whichPage = validateNewPage(whichPage);
mNextPage = whichPage;
- View focusedChild = getFocusedChild();
- if (focusedChild != null && whichPage != mCurrentPage &&
- focusedChild == getPageAt(mCurrentPage)) {
- focusedChild.clearFocus();
- }
pageBeginMoving();
awakenScrollBars(duration);
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 40eadab..1c02904 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -133,7 +133,7 @@
// 3) Setup icon in the center and app icon in the top right corner.
if (mDisabledForSafeMode) {
FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon);
- disabledIcon.setGhostModeEnabled(true);
+ disabledIcon.setState(FastBitmapDrawable.State.DISABLED);
mCenterDrawable = disabledIcon;
mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
@@ -218,7 +218,7 @@
mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
mCenterDrawable.setBounds(mRect);
} else {
- float iconSize = Math.min(availableWidth, availableHeight);
+ float iconSize = Math.max(0, Math.min(availableWidth, availableHeight));
// Use twice the setting size factor, as the setting is drawn at a corner and the
// icon is drawn in the center.
@@ -231,26 +231,30 @@
int actualIconSize = (int) Math.min(iconSize, grid.iconSizePx);
- // Recreate the setup text.
- mSetupTextLayout = new StaticLayout(
- getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
- Layout.Alignment.ALIGN_CENTER, 1, 0, true);
- int textHeight = mSetupTextLayout.getHeight();
+ // Icon top when we do not draw the text
+ int iconTop = (getHeight() - actualIconSize) / 2;
+ mSetupTextLayout = null;
- // Extra icon size due to the setting icon
- float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor
- + grid.iconDrawablePaddingPx;
+ if (availableWidth > 0) {
+ // Recreate the setup text.
+ mSetupTextLayout = new StaticLayout(
+ getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
+ Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+ int textHeight = mSetupTextLayout.getHeight();
- int iconTop;
- if (minHeightWithText < availableHeight) {
- // We can draw the text as well
- iconTop = (getHeight() - textHeight -
- grid.iconDrawablePaddingPx - actualIconSize) / 2;
+ // Extra icon size due to the setting icon
+ float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor
+ + grid.iconDrawablePaddingPx;
- } else {
- // The text will not fit. Only draw the icons.
- iconTop = (getHeight() - actualIconSize) / 2;
- mSetupTextLayout = null;
+ if (minHeightWithText < availableHeight) {
+ // We can draw the text as well
+ iconTop = (getHeight() - textHeight -
+ grid.iconDrawablePaddingPx - actualIconSize) / 2;
+
+ } else {
+ // We can't draw the text. Let the iconTop be same as before.
+ mSetupTextLayout = null;
+ }
}
mRect.set(0, 0, actualIconSize, actualIconSize);
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index 45e4b2c..908c8b9 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -179,7 +179,8 @@
mPaint.setColor(getIndicatorColor());
}
if (mIcon instanceof FastBitmapDrawable) {
- ((FastBitmapDrawable) mIcon).setGhostModeEnabled(level <= 0);
+ ((FastBitmapDrawable) mIcon).setState(level <= 0 ?
+ FastBitmapDrawable.State.DISABLED : FastBitmapDrawable.State.NORMAL);
}
invalidateSelf();
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 772a334..f7288a0 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -18,12 +18,18 @@
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.Rect;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import com.android.launcher3.util.Thunk;
@@ -34,35 +40,32 @@
*/
public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
+ private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f);
+ private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f);
+ private static final TimeInterpolator DEFAULT_INTERPOLATOR = new AccelerateInterpolator();
+
/** The different states that the search bar space can be in. */
public enum State {
- INVISIBLE (0f, 0f),
- SEARCH_BAR (1f, 0f),
- DROP_TARGET (0f, 1f);
+ INVISIBLE (0f, 0f, 0f),
+ INVISIBLE_TRANSLATED (0f, 0f, -1f),
+ SEARCH_BAR (1f, 0f, 0f),
+ DROP_TARGET (0f, 1f, 0f);
private final float mSearchBarAlpha;
private final float mDropTargetBarAlpha;
+ private final float mTranslation;
- State(float sbAlpha, float dtbAlpha) {
+ State(float sbAlpha, float dtbAlpha, float translation) {
mSearchBarAlpha = sbAlpha;
mDropTargetBarAlpha = dtbAlpha;
+ mTranslation = translation;
}
- float getSearchBarAlpha() {
- return mSearchBarAlpha;
- }
-
- float getDropTargetBarAlpha() {
- return mDropTargetBarAlpha;
- }
}
private static int DEFAULT_DRAG_FADE_DURATION = 175;
- private LauncherViewPropertyAnimator mDropTargetBarAnimator;
- private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
- private static final AccelerateInterpolator sAccelerateInterpolator =
- new AccelerateInterpolator();
+ private AnimatorSet mCurrentAnimation;
private State mState = State.SEARCH_BAR;
@Thunk View mQSB;
@@ -116,49 +119,11 @@
// Create the various fade animations
mDropTargetBar.setAlpha(0f);
- mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar);
- mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator);
- mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // Ensure that the view is visible for the animation
- mDropTargetBar.setVisibility(View.VISIBLE);
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mDropTargetBar != null) {
- AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
- }
- }
- });
+ AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
}
public void setQsbSearchBar(View qsb) {
mQSB = qsb;
- if (mQSB != null) {
- // Update the search ber animation
- mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB);
- mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator);
- mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // Ensure that the view is visible for the animation
- if (mQSB != null) {
- mQSB.setVisibility(View.VISIBLE);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- if (mQSB != null) {
- AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
- }
- }
- });
- } else {
- mQSBSearchBarAnimator = null;
- }
}
/**
@@ -166,6 +131,10 @@
* the state is applied immediately.
*/
public void animateToState(State newState, int duration) {
+ animateToState(newState, duration, null);
+ }
+
+ public void animateToState(State newState, int duration, AnimatorSet animation) {
if (mState != newState) {
mState = newState;
@@ -174,30 +143,63 @@
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
- animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
- duration);
- animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
- duration);
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
+ }
+ mCurrentAnimation = null;
+
+ if (duration > 0) {
+ mCurrentAnimation = new AnimatorSet();
+ mCurrentAnimation.setDuration(duration);
+
+ animateAlpha(mDropTargetBar, mState.mDropTargetBarAlpha, DEFAULT_INTERPOLATOR);
+ } else {
+ mDropTargetBar.setAlpha(mState.mDropTargetBarAlpha);
+ AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+ }
+
+ if (mQSB != null) {
+ boolean isVertical = ((Launcher) getContext()).getDeviceProfile()
+ .isVerticalBarLayout();
+ float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight();
+
+ if (duration > 0) {
+ int translationChange = Float.compare(mQSB.getTranslationY(), translation);
+
+ animateAlpha(mQSB, mState.mSearchBarAlpha,
+ translationChange == 0 ? DEFAULT_INTERPOLATOR
+ : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR
+ : MOVE_UP_INTERPOLATOR));
+
+ if (translationChange != 0) {
+ mCurrentAnimation.play(
+ ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation));
+ }
+ } else {
+ mQSB.setTranslationY(translation);
+ mQSB.setAlpha(mState.mSearchBarAlpha);
+ AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
+ }
+ }
+
+ // Start the final animation
+ if (duration > 0) {
+ if (animation != null) {
+ animation.play(mCurrentAnimation);
+ } else {
+ mCurrentAnimation.start();
+ }
+ }
}
}
- /**
- * Convenience method to animate the alpha of a view using hardware layers.
- */
- private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
- int duration) {
- if (v == null) {
- return;
- }
-
- animator.cancel();
+ private void animateAlpha(View v, float alpha, TimeInterpolator interpolator) {
if (Float.compare(v.getAlpha(), alpha) != 0) {
- if (duration > 0) {
- animator.alpha(alpha).withLayer().setDuration(duration).start();
- } else {
- v.setAlpha(alpha);
- AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled);
- }
+ ObjectAnimator anim = ObjectAnimator.ofFloat(v, View.ALPHA, alpha);
+ anim.setInterpolator(interpolator);
+ anim.addListener(new ViewVisiblilyUpdateHandler(v));
+ mCurrentAnimation.play(anim);
}
}
@@ -253,4 +255,24 @@
mDeleteDropTarget.enableAccessibleDrag(enable);
mUninstallDropTarget.enableAccessibleDrag(enable);
}
+
+ private class ViewVisiblilyUpdateHandler extends AnimatorListenerAdapter {
+ private final View mView;
+
+ ViewVisiblilyUpdateHandler(View v) {
+ mView = v;
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ // Ensure that the view is visible for the animation
+ mView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation){
+ AlphaUpdateListener.updateVisibility(mView, mAccessibilityEnabled);
+ }
+
+ }
}
diff --git a/src/com/android/launcher3/ShortcutAndWidgetContainer.java b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
index 157b48a..21e72e9 100644
--- a/src/com/android/launcher3/ShortcutAndWidgetContainer.java
+++ b/src/com/android/launcher3/ShortcutAndWidgetContainer.java
@@ -23,7 +23,6 @@
import android.graphics.Rect;
import android.view.View;
import android.view.ViewGroup;
-import android.view.ViewGroup.LayoutParams;
public class ShortcutAndWidgetContainer extends ViewGroup {
static final String TAG = "CellLayoutChildren";
@@ -164,8 +163,7 @@
lp.height = getMeasuredHeight();
}
int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
- int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height,
- MeasureSpec.EXACTLY);
+ int childheightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childheightMeasureSpec);
}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5766cf2..60e080e 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -52,7 +52,7 @@
public static final int FLAG_AUTOINTALL_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_AUTOINTALL_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;
@@ -113,6 +113,16 @@
public static final int FLAG_DISABLED_NOT_AVAILABLE = 2;
/**
+ * Indicates that the icon is disabled as the app is suspended
+ */
+ public static final int FLAG_DISABLED_SUSPENDED = 4;
+
+ /**
+ * Indicates that the icon is disabled as the user is in quiet mode.
+ */
+ public static final int FLAG_DISABLED_QUIET_USER = 8;
+
+ /**
* Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
* sd-card is not available).
*/
@@ -126,19 +136,14 @@
private int mInstallProgress;
/**
- * Refer {@link AppInfo#firstInstallTime}.
- */
- public long firstInstallTime;
-
- /**
- * TODO move this to {@link status}
+ * TODO move this to {@link #status}
*/
int flags = 0;
/**
* If this shortcut is a placeholder, then intent will be a market intent for the package, and
* this will hold the original intent from the database. Otherwise, null.
- * Refer {@link #FLAG_RESTORE_PENDING}, {@link #FLAG_INSTALL_PENDING}
+ * Refer {@link #FLAG_RESTORED_ICON}, {@link #FLAG_AUTOINTALL_ICON}
*/
Intent promisedIntent;
@@ -172,7 +177,6 @@
mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all
customIcon = info.customIcon;
flags = info.flags;
- firstInstallTime = info.firstInstallTime;
user = info.user;
status = info.status;
}
@@ -184,7 +188,7 @@
intent = new Intent(info.intent);
customIcon = false;
flags = info.flags;
- firstInstallTime = info.firstInstallTime;
+ isDisabled = info.isDisabled;
}
public void setIcon(Bitmap b) {
@@ -293,8 +297,12 @@
shortcut.intent = AppInfo.makeLaunchIntent(context, info, info.getUser());
shortcut.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
shortcut.flags = AppInfo.initFlags(info);
- shortcut.firstInstallTime = info.getFirstInstallTime();
return shortcut;
}
+
+ @Override
+ public boolean isDisabled() {
+ return isDisabled != 0;
+ }
}
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index cb0e252..d46c939 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -31,7 +31,7 @@
* Implemented by containers to provide a launch source for a given child.
*/
public interface LaunchSourceProvider {
- void fillInLaunchSourceData(Bundle sourceData);
+ void fillInLaunchSourceData(View v, Bundle sourceData);
}
/**
@@ -70,7 +70,7 @@
}
if (provider != null) {
- provider.fillInLaunchSourceData(sourceData);
+ provider.fillInLaunchSourceData(v, sourceData);
} else if (LauncherAppState.isDogfoodBuild()) {
throw new RuntimeException("Expected LaunchSourceProvider");
}
@@ -132,6 +132,7 @@
final String flat = intent.toUri(0);
Intent broadcastIntent = new Intent(ACTION_LAUNCH).putExtra(EXTRA_INTENT, flat);
+
if (shortcut != null) {
broadcastIntent.putExtra(EXTRA_CONTAINER, shortcut.container)
.putExtra(EXTRA_SCREEN, shortcut.screenId)
@@ -142,6 +143,11 @@
Bundle sourceExtras = LaunchSourceUtils.createSourceData();
LaunchSourceUtils.populateSourceDataFromAncestorProvider(v, sourceExtras);
broadcastIntent.putExtra(EXTRA_SOURCE, sourceExtras);
- mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
+
+ String[] packages = mLauncher.getResources().getStringArray(R.array.launch_broadcast_targets);
+ for(String p: packages) {
+ broadcastIntent.setPackage(p);
+ mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
+ }
}
}
diff --git a/src/com/android/launcher3/ToggleWeightWatcher.java b/src/com/android/launcher3/ToggleWeightWatcher.java
deleted file mode 100644
index 33701a2..0000000
--- a/src/com/android/launcher3/ToggleWeightWatcher.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.launcher3;
-
-import android.app.Activity;
-
-public class ToggleWeightWatcher extends Activity {
-
-}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index adedd33..271e581 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -46,8 +46,12 @@
import android.graphics.drawable.PaintDrawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.PowerManager;
import android.os.Process;
+import android.text.Spannable;
+import android.text.SpannableString;
import android.text.TextUtils;
+import android.text.style.TtsSpan;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Pair;
@@ -56,11 +60,19 @@
import android.view.View;
import android.widget.Toast;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.util.IconNormalizer;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.Set;
+import java.util.concurrent.Executor;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -105,10 +117,29 @@
public static final boolean ATLEAST_JB_MR2 =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2;
- // To turn on these properties, type
- // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
- private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
- private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
+ public static boolean isNycOrAbove() {
+ // TODO: Replace using reflection with looking at the API version once
+ // Build.VERSION.SDK_INT gets bumped to 24. b/22942492.
+ try {
+ View.class.getDeclaredField("DRAG_FLAG_OPAQUE");
+ // View.DRAG_FLAG_OPAQUE doesn't exist in M-release, so it's an indication of N+.
+ return true;
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ }
+
+ // These values are same as that in {@link AsyncTask}.
+ private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
+ private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
+ private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
+ private static final int KEEP_ALIVE = 1;
+ /**
+ * An {@link Executor} to be used with async task with no limit on the queue size.
+ */
+ public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
+ CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
+ TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
@@ -116,16 +147,24 @@
return Log.isLoggable(propertyName, Log.VERBOSE);
}
- public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) {
- SharedPreferences sharedPrefs = context.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE | (multiProcess ?
- Context.MODE_MULTI_PROCESS : 0));
- boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
- return sForceEnableRotation || allowRotationPref;
- }
-
- public static boolean isRotationAllowedForDevice(Context context) {
- return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation);
+ public static boolean isAllowRotationPrefEnabled(Context context) {
+ boolean allowRotationPref = false;
+ if (isNycOrAbove()) {
+ // If the device was scaled, used the original dimensions to determine if rotation
+ // is allowed of not.
+ try {
+ // TODO: Use the actual field when the API is finalized.
+ int originalDensity =
+ DisplayMetrics.class.getField("DENSITY_DEVICE_STABLE").getInt(null);
+ Resources res = context.getResources();
+ int originalSmallestWidth = res.getConfiguration().smallestScreenWidthDp
+ * res.getDisplayMetrics().densityDpi / originalDensity;
+ allowRotationPref = originalSmallestWidth >= 600;
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+ return getPrefs(context).getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, allowRotationPref);
}
public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
@@ -175,9 +214,41 @@
}
/**
+ * Returns a bitmap suitable for the all apps view. The icon is badged for {@param user}.
+ * The bitmap is also visually normalized with other icons.
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public static Bitmap createBadgedIconBitmap(
+ Drawable icon, UserHandleCompat user, Context context) {
+ float scale = FeatureFlags.LAUNCHER3_ICON_NORMALIZATION ?
+ IconNormalizer.getInstance().getScale(icon) : 1;
+ Bitmap bitmap = createIconBitmap(icon, context, scale);
+ if (Utilities.ATLEAST_LOLLIPOP && user != null
+ && !UserHandleCompat.myUserHandle().equals(user)) {
+ BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
+ Drawable badged = context.getPackageManager().getUserBadgedIcon(
+ drawable, user.getUser());
+ if (badged instanceof BitmapDrawable) {
+ return ((BitmapDrawable) badged).getBitmap();
+ } else {
+ return createIconBitmap(badged, context);
+ }
+ } else {
+ return bitmap;
+ }
+ }
+
+ /**
* Returns a bitmap suitable for the all apps view.
*/
public static Bitmap createIconBitmap(Drawable icon, Context context) {
+ return createIconBitmap(icon, context, 1.0f /* scale */);
+ }
+
+ /**
+ * @param scale the scale to apply before drawing {@param icon} on the canvas
+ */
+ public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) {
synchronized (sCanvas) {
final int iconBitmapSize = getIconBitmapSize();
@@ -192,7 +263,7 @@
// Ensure the bitmap has a density.
BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
Bitmap bitmap = bitmapDrawable.getBitmap();
- if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
+ if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) {
bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics());
}
}
@@ -233,7 +304,10 @@
sOldBounds.set(icon.getBounds());
icon.setBounds(left, top, left+width, top+height);
+ canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2);
icon.draw(canvas);
+ canvas.restore();
icon.setBounds(sOldBounds);
canvas.setBitmap(null);
@@ -290,7 +364,7 @@
}
/**
- * Inverse of {@link #getDescendantCoordRelativeToSelf(View, int[])}.
+ * Inverse of {@link #getDescendantCoordRelativeToParent(View, View, int[], boolean)}.
*/
public static float mapCoordInSelfToDescendent(View descendant, View root,
int[] coord) {
@@ -710,4 +784,62 @@
public static String createDbSelectionQuery(String columnName, Iterable<?> values) {
return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
}
+
+ /**
+ * Wraps a message with a TTS span, so that a different message is spoken than
+ * what is getting displayed.
+ * @param msg original message
+ * @param ttsMsg message to be spoken
+ */
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public static CharSequence wrapForTts(CharSequence msg, String ttsMsg) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
+ SpannableString spanned = new SpannableString(msg);
+ spanned.setSpan(new TtsSpan.TextBuilder(ttsMsg).build(),
+ 0, spanned.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ return spanned;
+ } else {
+ return msg;
+ }
+ }
+
+ /**
+ * Replacement for Long.compare() which was added in API level 19.
+ */
+ public static int longCompare(long lhs, long rhs) {
+ return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
+ }
+
+ public static SharedPreferences getPrefs(Context context) {
+ return context.getSharedPreferences(
+ LauncherFiles.SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public static boolean isPowerSaverOn(Context context) {
+ PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ return ATLEAST_LOLLIPOP && powerManager.isPowerSaveMode();
+ }
+
+ /**
+ * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
+ * This allows the badging to be done based on the action bitmap size rather than
+ * the scaled bitmap size.
+ */
+ private static class FixedSizeBitmapDrawable extends BitmapDrawable {
+
+ public FixedSizeBitmapDrawable(Bitmap bitmap) {
+ super(null, bitmap);
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return getBitmap().getWidth();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return getBitmap().getWidth();
+ }
+ }
}
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
index 2d5612f..c24fbff 100644
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ b/src/com/android/launcher3/WallpaperChangedReceiver.java
@@ -22,8 +22,6 @@
public class WallpaperChangedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent data) {
- LauncherAppState.setApplicationContext(context.getApplicationContext());
- LauncherAppState appState = LauncherAppState.getInstance();
- appState.onWallpaperChanged();
+ LauncherAppState.getInstance().onWallpaperChanged();
}
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 3460555..314dd8a 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -10,7 +10,6 @@
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
@@ -32,6 +31,7 @@
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.SQLiteCacheHelper;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.widget.WidgetCell;
@@ -65,7 +65,7 @@
private final Context mContext;
private final IconCache mIconCache;
private final UserManagerCompat mUserManager;
- private final AppWidgetManagerCompat mManager;
+ private final AppWidgetManagerCompat mWidgetManager;
private final CacheDb mDb;
private final int mProfileBadgeMargin;
@@ -75,7 +75,7 @@
public WidgetPreviewLoader(Context context, IconCache iconCache) {
mContext = context;
mIconCache = iconCache;
- mManager = AppWidgetManagerCompat.getInstance(context);
+ mWidgetManager = AppWidgetManagerCompat.getInstance(context);
mUserManager = UserManagerCompat.getInstance(context);
mDb = new CacheDb(context);
mWorkerHandler = new Handler(LauncherModel.getWorkerLooper());
@@ -96,7 +96,7 @@
WidgetCacheKey key = getObjectKey(o, size);
PreviewLoadTask task = new PreviewLoadTask(key, o, previewWidth, previewHeight, caller);
- task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ task.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
return new PreviewLoadRequest(task);
}
@@ -104,7 +104,7 @@
* The DB holds the generated previews for various components. Previews can also have different
* sizes (landscape vs portrait).
*/
- private static class CacheDb extends SQLiteOpenHelper {
+ private static class CacheDb extends SQLiteCacheHelper {
private static final int DB_VERSION = 4;
private static final String TABLE_NAME = "shortcut_and_widget_previews";
@@ -117,11 +117,11 @@
private static final String COLUMN_PREVIEW_BITMAP = "preview_bitmap";
public CacheDb(Context context) {
- super(context, LauncherFiles.WIDGET_PREVIEWS_DB, null, DB_VERSION);
+ super(context, LauncherFiles.WIDGET_PREVIEWS_DB, DB_VERSION, TABLE_NAME);
}
@Override
- public void onCreate(SQLiteDatabase database) {
+ public void onCreateTable(SQLiteDatabase database) {
database.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (" +
COLUMN_COMPONENT + " TEXT NOT NULL, " +
COLUMN_USER + " INTEGER NOT NULL, " +
@@ -133,32 +133,13 @@
"PRIMARY KEY (" + COLUMN_COMPONENT + ", " + COLUMN_USER + ", " + COLUMN_SIZE + ") " +
");");
}
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion != newVersion) {
- clearDB(db);
- }
- }
-
- @Override
- public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- if (oldVersion != newVersion) {
- clearDB(db);
- }
- }
-
- private void clearDB(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
- onCreate(db);
- }
}
private WidgetCacheKey getObjectKey(Object o, String size) {
// should cache the string builder
if (o instanceof LauncherAppWidgetProviderInfo) {
LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) o;
- return new WidgetCacheKey(info.provider, mManager.getUser(info), size);
+ return new WidgetCacheKey(info.provider, mWidgetManager.getUser(info), size);
} else {
ResolveInfo info = (ResolveInfo) o;
return new WidgetCacheKey(
@@ -176,13 +157,7 @@
values.put(CacheDb.COLUMN_VERSION, versions[0]);
values.put(CacheDb.COLUMN_LAST_UPDATED, versions[1]);
values.put(CacheDb.COLUMN_PREVIEW_BITMAP, Utilities.flattenBitmap(preview));
-
- try {
- mDb.getWritableDatabase().insertWithOnConflict(CacheDb.TABLE_NAME, null, values,
- SQLiteDatabase.CONFLICT_REPLACE);
- } catch (SQLException e) {
- Log.e(TAG, "Error saving image to DB", e);
- }
+ mDb.insertOrReplace(values);
}
public void removePackage(String packageName, UserHandleCompat user) {
@@ -194,13 +169,9 @@
mPackageVersions.remove(packageName);
}
- try {
- mDb.getWritableDatabase().delete(CacheDb.TABLE_NAME,
- CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?",
- new String[] {packageName, Long.toString(userSerial)});
- } catch (SQLException e) {
- Log.e(TAG, "Unable to delete items from DB", e);
- }
+ mDb.delete(
+ CacheDb.COLUMN_PACKAGE + " = ? AND " + CacheDb.COLUMN_USER + " = ?",
+ new String[]{packageName, Long.toString(userSerial)});
}
/**
@@ -222,7 +193,7 @@
pkg = ((ResolveInfo) obj).activityInfo.packageName;
} else {
LauncherAppWidgetProviderInfo info = (LauncherAppWidgetProviderInfo) obj;
- user = mManager.getUser(info);
+ user = mWidgetManager.getUser(info);
pkg = info.provider.getPackageName();
}
@@ -238,10 +209,10 @@
LongSparseArray<HashSet<String>> packagesToDelete = new LongSparseArray<>();
Cursor c = null;
try {
- c = mDb.getReadableDatabase().query(CacheDb.TABLE_NAME,
- new String[] {CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE,
- CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION},
- null, null, null, null, null);
+ c = mDb.query(
+ new String[]{CacheDb.COLUMN_USER, CacheDb.COLUMN_PACKAGE,
+ CacheDb.COLUMN_LAST_UPDATED, CacheDb.COLUMN_VERSION},
+ null, null);
while (c.moveToNext()) {
long userId = c.getLong(0);
String pkg = c.getString(1);
@@ -274,7 +245,7 @@
}
}
} catch (SQLException e) {
- Log.e(TAG, "Error updatating widget previews", e);
+ Log.e(TAG, "Error updating widget previews", e);
} finally {
if (c != null) {
c.close();
@@ -288,16 +259,15 @@
@Thunk Bitmap readFromDb(WidgetCacheKey key, Bitmap recycle, PreviewLoadTask loadTask) {
Cursor cursor = null;
try {
- cursor = mDb.getReadableDatabase().query(
- CacheDb.TABLE_NAME,
- new String[] { CacheDb.COLUMN_PREVIEW_BITMAP },
- CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND " + CacheDb.COLUMN_SIZE + " = ?",
- new String[] {
+ cursor = mDb.query(
+ new String[]{CacheDb.COLUMN_PREVIEW_BITMAP},
+ CacheDb.COLUMN_COMPONENT + " = ? AND " + CacheDb.COLUMN_USER + " = ? AND "
+ + CacheDb.COLUMN_SIZE + " = ?",
+ new String[]{
key.componentName.flattenToString(),
Long.toString(mUserManager.getSerialNumberForUser(key.user)),
key.size
- },
- null, null, null);
+ });
// If cancelled, skip getting the blob and decoding it into a bitmap
if (loadTask.isCancelled()) {
return null;
@@ -335,6 +305,17 @@
}
}
+ /**
+ * Generates the widget preview from either the {@link AppWidgetManagerCompat} or cache
+ * and add badge at the bottom right corner.
+ *
+ * @param launcher
+ * @param info information about the widget
+ * @param maxPreviewWidth width of the preview on either workspace or tray
+ * @param preview bitmap that can be recycled
+ * @param preScaledWidthOut return the width of the returned bitmap
+ * @return
+ */
public Bitmap generateWidgetPreview(Launcher launcher, LauncherAppWidgetProviderInfo info,
int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
// Load the preview image if possible
@@ -342,7 +323,7 @@
Drawable drawable = null;
if (info.previewImage != 0) {
- drawable = mManager.loadPreview(info);
+ drawable = mWidgetManager.loadPreview(info);
if (drawable != null) {
drawable = mutateOnMainThread(drawable);
} else {
@@ -357,6 +338,7 @@
int previewWidth;
int previewHeight;
+
Bitmap tileBitmap = null;
if (widgetPreviewExists) {
@@ -428,8 +410,9 @@
float iconScale = Math.min((float) smallestSide / (appIconSize + 2 * minOffset), scale);
try {
- Drawable icon = mutateOnMainThread(mManager.loadIcon(info, mIconCache));
+ Drawable icon = mWidgetManager.loadIcon(info, mIconCache);
if (icon != null) {
+ icon = mutateOnMainThread(icon);
int hoffset = (int) ((tileW - appIconSize * iconScale) / 2) + x;
int yoffset = (int) ((tileH - appIconSize * iconScale) / 2);
icon.setBounds(hoffset, yoffset,
@@ -437,11 +420,13 @@
yoffset + (int) (appIconSize * iconScale));
icon.draw(c);
}
- } catch (Resources.NotFoundException e) { }
+ } catch (Resources.NotFoundException e) {
+ }
c.setBitmap(null);
}
+ int imageWidth = Math.min(preview.getWidth(), previewWidth + mProfileBadgeMargin);
int imageHeight = Math.min(preview.getHeight(), previewHeight + mProfileBadgeMargin);
- return mManager.getBadgeBitmap(info, preview, imageHeight);
+ return mWidgetManager.getBadgeBitmap(info, preview, imageWidth, imageHeight);
}
private Bitmap generateShortcutPreview(
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 856e3b8..bf33ccf 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,6 +67,8 @@
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
+import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
@@ -179,22 +181,20 @@
// in all apps or customize mode)
enum State {
- NORMAL (SearchDropTargetBar.State.SEARCH_BAR),
- NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE),
- SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET),
- OVERVIEW (SearchDropTargetBar.State.INVISIBLE),
- OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE);
+ NORMAL (SearchDropTargetBar.State.SEARCH_BAR, false),
+ NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false),
+ SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET, false),
+ OVERVIEW (SearchDropTargetBar.State.INVISIBLE, true),
+ OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true);
- private final SearchDropTargetBar.State mBarState;
+ public final SearchDropTargetBar.State searchDropTargetBarState;
+ public final boolean shouldUpdateWidget;
- State(SearchDropTargetBar.State searchBarState) {
- mBarState = searchBarState;
+ State(SearchDropTargetBar.State searchBarState, boolean shouldUpdateWidget) {
+ searchDropTargetBarState = searchBarState;
+ this.shouldUpdateWidget = shouldUpdateWidget;
}
-
- public SearchDropTargetBar.State getSearchDropTargetBarState() {
- return mBarState;
- }
- };
+ }
private State mState = State.NORMAL;
private boolean mIsSwitchingState = false;
@@ -358,7 +358,7 @@
if (getChildCount() > 0) {
// Use the first non-custom page to estimate the child position
CellLayout cl = (CellLayout) getChildAt(numCustomPages());
- Rect r = estimateItemPosition(cl, itemInfo, 0, 0, itemInfo.spanX, itemInfo.spanY);
+ Rect r = estimateItemPosition(cl, 0, 0, itemInfo.spanX, itemInfo.spanY);
size[0] = r.width();
size[1] = r.height();
if (springLoaded) {
@@ -373,8 +373,7 @@
}
}
- public Rect estimateItemPosition(CellLayout cl, ItemInfo pendingInfo,
- int hCell, int vCell, int hSpan, int vSpan) {
+ public Rect estimateItemPosition(CellLayout cl, int hCell, int vCell, int hSpan, int vSpan) {
Rect r = new Rect();
cl.cellToRect(hCell, vCell, hSpan, vSpan, r);
return r;
@@ -578,6 +577,7 @@
CellLayout customScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, this, false);
customScreen.disableDragTarget();
+ customScreen.disableJailContent();
mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
@@ -649,6 +649,10 @@
parent.removeView(customContent);
}
customScreen.removeAllViews();
+ customContent.setFocusable(true);
+ customContent.setOnKeyListener(new FullscreenKeyEventListener());
+ customContent.setOnFocusChangeListener(mLauncher.mFocusHandler
+ .getHideIndicatorOnFocusListener());
customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
mCustomContentDescription = description;
@@ -1118,9 +1122,9 @@
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
if (lahv != null && lahv.isReinflateRequired()) {
- mLauncher.removeAppWidget(info);
- // Remove the current widget which is inflated with the wrong orientation
- cl.removeView(lahv);
+ // Remove and rebind the current widget (which was inflated in the wrong
+ // orientation), but don't delete it from the database
+ mLauncher.removeItem(lahv, info, false /* deleteFromDb */);
mLauncher.bindAppWidget(info);
}
}
@@ -1326,7 +1330,7 @@
mLauncher.overrideWallpaperDimensions());
return null;
}
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+ }.executeOnExecutor(Utilities.THREAD_POOL_EXECUTOR);
}
protected void snapToPage(int whichPage, Runnable r) {
@@ -1410,7 +1414,7 @@
return false;
}
- private float wallpaperOffsetForCurrentScroll() {
+ public float wallpaperOffsetForScroll(int scroll) {
// TODO: do different behavior if it's a live wallpaper?
// Don't use up all the wallpaper parallax until you have at least
// MIN_PARALLAX_PAGE_SPAN pages
@@ -1449,7 +1453,7 @@
// Sometimes the left parameter of the pages is animated during a layout transition;
// this parameter offsets it to keep the wallpaper from animating as well
int adjustedScroll =
- getScrollX() - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
+ scroll - firstPageScrollX - getLayoutTransitionOffsetForPage(0);
float offset = Math.min(1, adjustedScroll / (float) scrollRange);
offset = Math.max(0, offset);
@@ -1463,6 +1467,10 @@
}
}
+ private float wallpaperOffsetForCurrentScroll() {
+ return wallpaperOffsetForScroll(getScrollX());
+ }
+
private int numEmptyScreensToIgnore() {
int numScrollingPages = getChildCount() - numCustomPages();
if (numScrollingPages >= MIN_PARALLAX_PAGE_SPAN && hasExtraEmptyScreen()) {
@@ -1537,6 +1545,13 @@
}
@Override
+ protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
+ if (!isSwitchingState()) {
+ super.determineScrollingStart(ev, touchSlopScale);
+ }
+ }
+
+ @Override
public void announceForAccessibility(CharSequence text) {
// Don't announce if apps is on top of us.
if (!mLauncher.isAppsViewVisible()) {
@@ -1606,7 +1621,7 @@
}
public boolean isOnOrMovingToCustomContent() {
- return hasCustomContent() && getNextPage() == 0;
+ return hasCustomContent() && getNextPage() == 0 && mRestorePage == INVALID_RESTORE_PAGE;
}
private void updateStateForCustomContent(int screenCenter) {
@@ -2001,10 +2016,16 @@
Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
toState, toPage, animated, layerViews);
+ boolean shouldNotifyWidgetChange = !mState.shouldUpdateWidget
+ && toState.shouldUpdateWidget;
// Update the current state
mState = toState;
updateAccessibilityFlags();
+ if (shouldNotifyWidgetChange) {
+ mLauncher.notifyWidgetProvidersChanged();
+ }
+
return workspaceAnim;
}
@@ -2020,7 +2041,7 @@
}
setImportantForAccessibility((mState == State.NORMAL || mState == State.OVERVIEW)
? IMPORTANT_FOR_ACCESSIBILITY_AUTO
- : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
} else {
int accessible = mState == State.NORMAL ?
IMPORTANT_FOR_ACCESSIBILITY_AUTO :
@@ -2054,6 +2075,7 @@
@Override
public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
mIsSwitchingState = true;
+ mTransitionProgress = 0;
// Invalidate here to ensure that the pages are rendered during the state change transition.
invalidate();
@@ -2469,14 +2491,17 @@
return true;
}
- boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell, float
- distance, boolean considerTimeout) {
+ boolean willCreateUserFolder(ItemInfo info, CellLayout target, int[] targetCell,
+ float distance, boolean considerTimeout) {
if (distance > mMaxDistanceForFolderCreation) return false;
View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
+ return willCreateUserFolder(info, dropOverView, considerTimeout);
+ }
+ boolean willCreateUserFolder(ItemInfo info, View dropOverView, boolean considerTimeout) {
if (dropOverView != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
- if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
+ if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
}
@@ -2493,7 +2518,7 @@
boolean aboveShortcut = (dropOverView.getTag() instanceof ShortcutInfo);
boolean willBecomeShortcut =
(info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
+ info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT);
return (aboveShortcut && willBecomeShortcut);
}
@@ -2502,10 +2527,13 @@
float distance) {
if (distance > mMaxDistanceForFolderCreation) return false;
View dropOverView = target.getChildAt(targetCell[0], targetCell[1]);
+ return willAddToExistingUserFolder(dragInfo, dropOverView);
+ }
+ boolean willAddToExistingUserFolder(Object dragInfo, View dropOverView) {
if (dropOverView != null) {
CellLayout.LayoutParams lp = (CellLayout.LayoutParams) dropOverView.getLayoutParams();
- if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.tmpCellY)) {
+ if (lp.useTmpCoords && (lp.tmpCellX != lp.cellX || lp.tmpCellY != lp.cellY)) {
return false;
}
}
@@ -2534,7 +2562,7 @@
if (v == null || hasntMoved || !mCreateUserFolderOnDrop) return false;
mCreateUserFolderOnDrop = false;
- final long screenId = (targetCell == null) ? mDragInfo.screenId : getIdForScreen(target);
+ final long screenId = getIdForScreen(target);
boolean aboveShortcut = (v.getTag() instanceof ShortcutInfo);
boolean willBecomeShortcut = (newView.getTag() instanceof ShortcutInfo);
@@ -3205,8 +3233,6 @@
mapPointFromSelfToChild(mDragTargetLayout, mDragViewVisualCenter, null);
}
- ItemInfo info = (ItemInfo) d.dragInfo;
-
int minSpanX = item.spanX;
int minSpanY = item.spanY;
if (item.minSpanX > 0 && item.minSpanY > 0) {
@@ -3225,11 +3251,7 @@
float targetCellDistance = mDragTargetLayout.getDistanceFromCell(
mDragViewVisualCenter[0], mDragViewVisualCenter[1], mTargetCell);
- final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0],
- mTargetCell[1]);
-
- manageFolderFeedback(info, mDragTargetLayout, mTargetCell,
- targetCellDistance, dragOverView, d.accessibleDrag);
+ manageFolderFeedback(mDragTargetLayout, mTargetCell, targetCellDistance, d);
boolean nearestDropOccupied = mDragTargetLayout.isNearestDropLocationOccupied((int)
mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], item.spanX,
@@ -3237,9 +3259,7 @@
if (!nearestDropOccupied) {
mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
- (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
- mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false,
- d.dragView.getDragVisualizeOffset(), d.dragView.getDragRegion());
+ mTargetCell[0], mTargetCell[1], item.spanX, item.spanY, false, d);
} else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER)
&& !mReorderAlarm.alarmPending() && (mLastReorderX != reorderX ||
mLastReorderY != reorderY)) {
@@ -3252,7 +3272,7 @@
// Otherwise, if we aren't adding to or creating a folder and there's no pending
// reorder, then we schedule a reorder
ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter,
- minSpanX, minSpanY, item.spanX, item.spanY, d.dragView, child);
+ minSpanX, minSpanY, item.spanX, item.spanY, d, child);
mReorderAlarm.setOnAlarmListener(listener);
mReorderAlarm.setAlarm(REORDER_TIMEOUT);
}
@@ -3266,28 +3286,34 @@
}
}
- private void manageFolderFeedback(ItemInfo info, CellLayout targetLayout,
- int[] targetCell, float distance, View dragOverView, boolean accessibleDrag) {
- boolean userFolderPending = willCreateUserFolder(info, targetLayout, targetCell, distance,
- false);
+ private void manageFolderFeedback(CellLayout targetLayout,
+ int[] targetCell, float distance, DragObject dragObject) {
+ if (distance > mMaxDistanceForFolderCreation) return;
+
+ final View dragOverView = mDragTargetLayout.getChildAt(mTargetCell[0], mTargetCell[1]);
+ ItemInfo info = (ItemInfo) dragObject.dragInfo;
+ boolean userFolderPending = willCreateUserFolder(info, dragOverView, false);
if (mDragMode == DRAG_MODE_NONE && userFolderPending &&
!mFolderCreationAlarm.alarmPending()) {
FolderCreationAlarmListener listener = new
FolderCreationAlarmListener(targetLayout, targetCell[0], targetCell[1]);
- if (!accessibleDrag) {
+ if (!dragObject.accessibleDrag) {
mFolderCreationAlarm.setOnAlarmListener(listener);
mFolderCreationAlarm.setAlarm(FOLDER_CREATION_TIMEOUT);
} else {
listener.onAlarm(mFolderCreationAlarm);
}
+
+ if (dragObject.stateAnnouncer != null) {
+ dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
+ .getDescriptionForDropOver(dragOverView, getContext()));
+ }
return;
}
- boolean willAddToFolder =
- willAddToExistingUserFolder(info, targetLayout, targetCell, distance);
-
+ boolean willAddToFolder = willAddToExistingUserFolder(info, dragOverView);
if (willAddToFolder && mDragMode == DRAG_MODE_NONE) {
mDragOverFolderIcon = ((FolderIcon) dragOverView);
mDragOverFolderIcon.onDragEnter(info);
@@ -3295,6 +3321,11 @@
targetLayout.clearDragOutlines();
}
setDragMode(DRAG_MODE_ADD_TO_FOLDER);
+
+ if (dragObject.stateAnnouncer != null) {
+ dragObject.stateAnnouncer.announce(WorkspaceAccessibilityHelper
+ .getDescriptionForDropOver(dragOverView, getContext()));
+ }
return;
}
@@ -3304,8 +3335,6 @@
if (mDragMode == DRAG_MODE_CREATE_FOLDER && !userFolderPending) {
setDragMode(DRAG_MODE_NONE);
}
-
- return;
}
class FolderCreationAlarmListener implements OnAlarmListener {
@@ -3337,18 +3366,18 @@
class ReorderAlarmListener implements OnAlarmListener {
float[] dragViewCenter;
int minSpanX, minSpanY, spanX, spanY;
- DragView dragView;
+ DragObject dragObject;
View child;
public ReorderAlarmListener(float[] dragViewCenter, int minSpanX, int minSpanY, int spanX,
- int spanY, DragView dragView, View child) {
+ int spanY, DragObject dragObject, View child) {
this.dragViewCenter = dragViewCenter;
this.minSpanX = minSpanX;
this.minSpanY = minSpanY;
this.spanX = spanX;
this.spanY = spanY;
this.child = child;
- this.dragView = dragView;
+ this.dragObject = dragObject;
}
public void onAlarm(Alarm alarm) {
@@ -3371,9 +3400,7 @@
boolean resize = resultSpan[0] != spanX || resultSpan[1] != spanY;
mDragTargetLayout.visualizeDropLocation(child, mDragOutline,
- (int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1],
- mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize,
- dragView.getDragVisualizeOffset(), dragView.getDragRegion());
+ mTargetCell[0], mTargetCell[1], resultSpan[0], resultSpan[1], resize, dragObject);
}
}
@@ -3492,11 +3519,11 @@
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|| pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
- View finalView = isWidget ? ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
+ AppWidgetHostView finalView = isWidget ?
+ ((PendingAddWidgetInfo) pendingInfo).boundWidget : null;
- if (finalView instanceof AppWidgetHostView && updateWidgetSize) {
- AppWidgetHostView awhv = (AppWidgetHostView) finalView;
- AppWidgetResizeFrame.updateWidgetSizeRanges(awhv, mLauncher, item.spanX,
+ if (finalView != null && updateWidgetSize) {
+ AppWidgetResizeFrame.updateWidgetSizeRanges(finalView, mLauncher, item.spanX,
item.spanY);
}
@@ -3596,14 +3623,13 @@
}
private void getFinalPositionForDropAnimation(int[] loc, float[] scaleXY,
- DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell,
- boolean external, boolean scale) {
+ DragView dragView, CellLayout layout, ItemInfo info, int[] targetCell, boolean scale) {
// Now we animate the dragView, (ie. the widget or shortcut preview) into its final
// location and size on the home screen.
int spanX = info.spanX;
int spanY = info.spanY;
- Rect r = estimateItemPosition(layout, info, targetCell[0], targetCell[1], spanX, spanY);
+ Rect r = estimateItemPosition(layout, targetCell[0], targetCell[1], spanX, spanY);
loc[0] = r.left;
loc[1] = r.top;
@@ -3624,14 +3650,15 @@
// The animation will scale the dragView about its center, so we need to center about
// the final location.
- loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2;
+ loc[0] -= (dragView.getMeasuredWidth() - cellLayoutScale * r.width()) / 2
+ - Math.ceil(layout.getUnusedHorizontalSpace() / 2f);
loc[1] -= (dragView.getMeasuredHeight() - cellLayoutScale * r.height()) / 2;
scaleXY[0] = dragViewScaleX * cellLayoutScale;
scaleXY[1] = dragViewScaleY * cellLayoutScale;
}
- public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, DragView dragView,
+ public void animateWidgetDrop(ItemInfo info, CellLayout cellLayout, final DragView dragView,
final Runnable onCompleteRunnable, int animationType, final View finalView,
boolean external) {
Rect from = new Rect();
@@ -3641,16 +3668,11 @@
float scaleXY[] = new float[2];
boolean scalePreview = !(info instanceof PendingAddShortcutInfo);
getFinalPositionForDropAnimation(finalPos, scaleXY, dragView, cellLayout, info, mTargetCell,
- external, scalePreview);
+ scalePreview);
Resources res = mLauncher.getResources();
final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
- // In the case where we've prebound the widget, we remove it from the DragLayer
- if (finalView instanceof AppWidgetHostView && external) {
- mLauncher.getDragLayer().removeView(finalView);
- }
-
boolean isWidget = info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET ||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_CUSTOM_APPWIDGET;
if ((animationType == ANIMATE_INTO_POSITION_AND_RESIZE || external) && finalView != null) {
@@ -3670,7 +3692,7 @@
if (animationType == ANIMATE_INTO_POSITION_AND_REMAIN) {
endStyle = DragLayer.ANIMATION_END_REMAIN_VISIBLE;
} else {
- endStyle = DragLayer.ANIMATION_END_DISAPPEAR;;
+ endStyle = DragLayer.ANIMATION_END_DISAPPEAR;
}
Runnable onComplete = new Runnable() {
@@ -4143,41 +4165,10 @@
});
}
- public void disableShortcutsByPackageName(final ArrayList<String> packages,
- final UserHandleCompat user, final int reason) {
- final HashSet<String> packageNames = new HashSet<String>();
- packageNames.addAll(packages);
-
- mapOverItems(MAP_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v, View parent) {
- if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
- ShortcutInfo shortcutInfo = (ShortcutInfo) info;
- ComponentName cn = shortcutInfo.getTargetComponent();
- if (user.equals(shortcutInfo.user) && cn != null
- && packageNames.contains(cn.getPackageName())) {
- shortcutInfo.isDisabled |= reason;
- BubbleTextView shortcut = (BubbleTextView) v;
- shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
-
- if (parent != null) {
- parent.invalidate();
- }
- }
- }
- // process all the shortcuts
- return false;
- }
- });
- }
-
// Removes ALL items that match a given package name, this is usually called when a package
// has been removed and we want to remove all components (widgets, shortcuts, apps) that
// belong to that package.
- void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
- final HashSet<String> packageNames = new HashSet<String>();
- packageNames.addAll(packages);
-
+ void removeItemsByPackageName(final HashSet<String> packageNames, final UserHandleCompat user) {
// Filter out all the ItemInfos that this is going to affect
final HashSet<ItemInfo> infos = new HashSet<ItemInfo>();
final HashSet<ComponentName> cns = new HashSet<ComponentName>();
@@ -4359,7 +4350,7 @@
}
public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
- ArrayList<String> packages = new ArrayList<String>(1);
+ HashSet<String> packages = new HashSet<>(1);
packages.add(packageName);
LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
removeItemsByPackageName(packages, user);
@@ -4383,13 +4374,22 @@
});
}
- void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
+ public void widgetsRestored(ArrayList<LauncherAppWidgetInfo> changedInfo) {
if (!changedInfo.isEmpty()) {
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
mLauncher.getAppWidgetHost());
- if (LauncherModel.getProviderInfo(getContext(),
- changedInfo.get(0).providerName,
- changedInfo.get(0).user) != null) {
+
+ LauncherAppWidgetInfo item = changedInfo.get(0);
+ final AppWidgetProviderInfo widgetInfo;
+ if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
+ widgetInfo = AppWidgetManagerCompat
+ .getInstance(mLauncher).findProvider(item.providerName, item.user);
+ } else {
+ widgetInfo = AppWidgetManagerCompat.getInstance(mLauncher)
+ .getAppWidgetInfo(item.appWidgetId);
+ }
+
+ if (widgetInfo != null) {
// Re-inflate the widgets which have changed status
widgetRefresh.run();
} else {
@@ -4468,8 +4468,21 @@
private String getPageDescription(int page) {
int delta = numCustomPages();
- return String.format(getContext().getString(R.string.workspace_scroll_format),
- page + 1 - delta, getChildCount() - delta);
+ int nScreens = getChildCount() - delta;
+ int extraScreenId = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
+ if (extraScreenId >= 0 && nScreens > 1) {
+ if (page == extraScreenId) {
+ return getContext().getString(R.string.workspace_new_page);
+ }
+ nScreens--;
+ }
+ if (nScreens == 0) {
+ // When the workspace is not loaded, we do not know how many screen will be bound.
+ return getContext().getString(R.string.all_apps_home_button_label);
+ }
+ return getContext().getString(R.string.workspace_scroll_format,
+ page + 1 - delta, nScreens);
+
}
public void getLocationInDragLayer(int[] loc) {
@@ -4477,7 +4490,7 @@
}
@Override
- public void fillInLaunchSourceData(Bundle sourceData) {
+ public void fillInLaunchSourceData(View v, Bundle sourceData) {
sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_HOMESCREEN);
sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage());
}
@@ -4519,12 +4532,9 @@
for (LauncherAppWidgetInfo info : mInfos) {
if (info.hostView instanceof PendingAppWidgetHostView) {
+ // Remove and rebind the current widget, but don't delete it from the database
PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
- mLauncher.removeAppWidget(info);
-
- CellLayout cl = (CellLayout) view.getParent().getParent();
- // Remove the current widget
- cl.removeView(view);
+ mLauncher.removeItem(view, info, false /* deleteFromDb */);
mLauncher.bindAppWidget(info);
}
}
diff --git a/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java b/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
new file mode 100644
index 0000000..8ff82dd
--- /dev/null
+++ b/src/com/android/launcher3/accessibility/DragViewStateAnnouncer.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.accessibility;
+
+import android.content.Context;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+
+/**
+ * Periodically sends accessibility events to announce ongoing state changed. Based on the
+ * implementation in ProgressBar.
+ */
+public class DragViewStateAnnouncer implements Runnable {
+
+ private static final int TIMEOUT_SEND_ACCESSIBILITY_EVENT = 200;
+
+ private final View mTargetView;
+
+ private DragViewStateAnnouncer(View view) {
+ mTargetView = view;
+ }
+
+ public void announce(CharSequence msg) {
+ mTargetView.setContentDescription(msg);
+ mTargetView.removeCallbacks(this);
+ mTargetView.postDelayed(this, TIMEOUT_SEND_ACCESSIBILITY_EVENT);
+ }
+
+ public void cancel() {
+ mTargetView.removeCallbacks(this);
+ }
+
+ @Override
+ public void run() {
+ mTargetView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
+ }
+
+ public static DragViewStateAnnouncer createFor(View v) {
+ if (((AccessibilityManager) v.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE))
+ .isEnabled()) {
+ return new DragViewStateAnnouncer(v);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index fe7b25e..2306b77 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -134,11 +134,8 @@
public boolean performAction(final View host, final ItemInfo item, int action) {
if (action == REMOVE) {
- if (DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host)) {
- announceConfirmation(R.string.item_removed);
- return true;
- }
- return false;
+ DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
+ return true;
} else if (action == INFO) {
InfoDropTarget.startDetailsActivityForInfo(item, mLauncher);
return true;
@@ -175,7 +172,7 @@
return true;
} else if (action == MOVE_TO_WORKSPACE) {
Folder folder = mLauncher.getWorkspace().getOpenFolder();
- mLauncher.closeFolder(folder);
+ mLauncher.closeFolder(folder, true);
ShortcutInfo info = (ShortcutInfo) item;
folder.getInfo().remove(info);
diff --git a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
index 80ddc13..73b824b 100644
--- a/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
+++ b/src/com/android/launcher3/accessibility/WorkspaceAccessibilityHelper.java
@@ -16,6 +16,7 @@
package com.android.launcher3.accessibility;
+import android.content.Context;
import android.text.TextUtils;
import android.view.View;
@@ -140,26 +141,30 @@
return mContext.getString(R.string.move_to_empty_cell, y + 1, x + 1);
}
} else {
- ItemInfo info = (ItemInfo) child.getTag();
- if (info instanceof ShortcutInfo) {
- return mContext.getString(R.string.create_folder_with, info.title);
- } else if (info instanceof FolderInfo) {
- if (TextUtils.isEmpty(info.title)) {
- // Find the first item in the folder.
- FolderInfo folder = (FolderInfo) info;
- ShortcutInfo firstItem = null;
- for (ShortcutInfo shortcut : folder.contents) {
- if (firstItem == null || firstItem.rank > shortcut.rank) {
- firstItem = shortcut;
- }
- }
+ return getDescriptionForDropOver(child, mContext);
+ }
+ }
- if (firstItem != null) {
- return mContext.getString(R.string.add_to_folder_with_app, firstItem.title);
+ public static String getDescriptionForDropOver(View overChild, Context context) {
+ ItemInfo info = (ItemInfo) overChild.getTag();
+ if (info instanceof ShortcutInfo) {
+ return context.getString(R.string.create_folder_with, info.title);
+ } else if (info instanceof FolderInfo) {
+ if (TextUtils.isEmpty(info.title)) {
+ // Find the first item in the folder.
+ FolderInfo folder = (FolderInfo) info;
+ ShortcutInfo firstItem = null;
+ for (ShortcutInfo shortcut : folder.contents) {
+ if (firstItem == null || firstItem.rank > shortcut.rank) {
+ firstItem = shortcut;
}
}
- return mContext.getString(R.string.add_to_folder, info.title);
+
+ if (firstItem != null) {
+ return context.getString(R.string.add_to_folder_with_app, firstItem.title);
+ }
}
+ return context.getString(R.string.add_to_folder, info.title);
}
return "";
}
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
index 117aca9..dafa73f 100644
--- a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -35,6 +35,7 @@
private float mXPercent;
private float mYPercent;
private int mGravity;
+ private int mAlpha;
/**
* @param gravity If one of the Gravity center values, the x and y offset will take the width
@@ -50,10 +51,11 @@
public void setAlpha(int alpha) {
mImage.setAlpha(alpha);
+ mAlpha = alpha;
}
public int getAlpha() {
- return mImage.getAlpha();
+ return mAlpha;
}
public void updateBounds(Rect bounds) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 88c6aca..c12f645 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -17,29 +17,29 @@
import android.annotation.SuppressLint;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.drawable.InsetDrawable;
import android.support.v7.widget.RecyclerView;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.method.TextKeyListener;
import android.util.AttributeSet;
import android.view.KeyEvent;
+import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
+
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
+import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Folder;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
@@ -48,7 +48,6 @@
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.util.ComponentKey;
-import com.android.launcher3.util.Thunk;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
@@ -133,19 +132,22 @@
private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
private static final int MAX_NUM_MERGES_PHONE = 2;
- @Thunk Launcher mLauncher;
- @Thunk AlphabeticalAppsList mApps;
- private AllAppsGridAdapter mAdapter;
- private RecyclerView.LayoutManager mLayoutManager;
- private RecyclerView.ItemDecoration mItemDecoration;
+ private final Launcher mLauncher;
+ private final AlphabeticalAppsList mApps;
+ private final AllAppsGridAdapter mAdapter;
+ private final RecyclerView.LayoutManager mLayoutManager;
+ private final RecyclerView.ItemDecoration mItemDecoration;
- @Thunk View mContent;
- @Thunk View mContainerView;
- @Thunk View mRevealView;
- @Thunk AllAppsRecyclerView mAppsRecyclerView;
- @Thunk AllAppsSearchBarController mSearchBarController;
- private ViewGroup mSearchBarContainerView;
- private View mSearchBarView;
+ // The computed bounds of the container
+ private final Rect mContentBounds = new Rect();
+
+ private AllAppsRecyclerView mAppsRecyclerView;
+ private AllAppsSearchBarController mSearchBarController;
+
+ private View mSearchContainer;
+ private ExtendedEditText mSearchInput;
+ private HeaderElevationController mElevationController;
+
private SpannableStringBuilder mSearchQueryBuilder = null;
private int mSectionNamesMargin;
@@ -157,14 +159,6 @@
// This coordinate is relative to its parent
private final Point mIconLastTouchPos = new Point();
- private View.OnClickListener mSearchClickListener = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent searchIntent = (Intent) v.getTag();
- mLauncher.startActivitySafely(v, searchIntent, null);
- }
- };
-
public AllAppsContainerView(Context context) {
this(context, null);
}
@@ -234,14 +228,8 @@
throw new RuntimeException("Expected search bar controller to only be set once");
}
mSearchBarController = searchController;
- mSearchBarController.initialize(mApps, this);
-
- // Add the new search view to the layout
- View searchBarView = searchController.getView(mSearchBarContainerView);
- mSearchBarContainerView.addView(searchBarView);
- mSearchBarContainerView.setVisibility(View.VISIBLE);
- mSearchBarView = searchBarView;
- setHasSearchBar();
+ mSearchBarController.initialize(mApps, mSearchInput, mLauncher, this);
+ mAdapter.setSearchController(mSearchBarController);
updateBackgroundAndPaddings();
}
@@ -254,34 +242,6 @@
}
/**
- * Returns the content view used for the launcher transitions.
- */
- public View getContentView() {
- return mContainerView;
- }
-
- /**
- * Returns the all apps search view.
- */
- public View getSearchBarView() {
- return mSearchBarView;
- }
-
- /**
- * Returns the reveal view used for the launcher transitions.
- */
- public View getRevealView() {
- return mRevealView;
- }
-
- /**
- * Returns an new instance of the default app search controller.
- */
- public AllAppsSearchBarController newDefaultAppSearchController() {
- return new DefaultAppSearchController(getContext(), this, mAppsRecyclerView);
- }
-
- /**
* Focuses the search field and begins an app search.
*/
public void startAppsSearch() {
@@ -290,28 +250,35 @@
}
}
+ /**
+ * Resets the state of AllApps.
+ */
+ public void reset() {
+ // Reset the search bar and base recycler view after transitioning home
+ mSearchBarController.reset();
+ mAppsRecyclerView.reset();
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
- boolean isRtl = Utilities.isRtl(getResources());
- mAdapter.setRtl(isRtl);
- mContent = findViewById(R.id.content);
// This is a focus listener that proxies focus from a view into the list view. This is to
// work around the search box from getting first focus and showing the cursor.
- View.OnFocusChangeListener focusProxyListener = new View.OnFocusChangeListener() {
+ getContentView().setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
mAppsRecyclerView.requestFocus();
}
}
- };
- mSearchBarContainerView = (ViewGroup) findViewById(R.id.search_box_container);
- mSearchBarContainerView.setOnFocusChangeListener(focusProxyListener);
- mContainerView = findViewById(R.id.all_apps_container);
- mContainerView.setOnFocusChangeListener(focusProxyListener);
- mRevealView = findViewById(R.id.all_apps_reveal);
+ });
+
+ mSearchContainer = findViewById(R.id.search_container);
+ mSearchInput = (ExtendedEditText) findViewById(R.id.search_box_input);
+ mElevationController = Utilities.ATLEAST_LOLLIPOP
+ ? new HeaderElevationController.ControllerVL(mSearchContainer)
+ : new HeaderElevationController.ControllerV16(mSearchContainer);
// Load the all apps recycler view
mAppsRecyclerView = (AllAppsRecyclerView) findViewById(R.id.apps_list_view);
@@ -319,10 +286,31 @@
mAppsRecyclerView.setLayoutManager(mLayoutManager);
mAppsRecyclerView.setAdapter(mAdapter);
mAppsRecyclerView.setHasFixedSize(true);
+ mAppsRecyclerView.addOnScrollListener(mElevationController);
+ mAppsRecyclerView.setElevationController(mElevationController);
+
if (mItemDecoration != null) {
mAppsRecyclerView.addItemDecoration(mItemDecoration);
}
+ // Precalculate the prediction icon and normal icon sizes
+ LayoutInflater layoutInflater = LayoutInflater.from(getContext());
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getResources().getDisplayMetrics().widthPixels, MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(
+ getResources().getDisplayMetrics().heightPixels, MeasureSpec.AT_MOST);
+
+ BubbleTextView icon = (BubbleTextView) layoutInflater.inflate(
+ R.layout.all_apps_icon, this, false);
+ icon.applyDummyInfo();
+ icon.measure(widthMeasureSpec, heightMeasureSpec);
+ BubbleTextView predIcon = (BubbleTextView) layoutInflater.inflate(
+ R.layout.all_apps_prediction_bar_icon, this, false);
+ predIcon.applyDummyInfo();
+ predIcon.measure(widthMeasureSpec, heightMeasureSpec);
+ mAppsRecyclerView.setPremeasuredIconHeights(predIcon.getMeasuredHeight(),
+ icon.getMeasuredHeight());
+
updateBackgroundAndPaddings();
}
@@ -333,9 +321,16 @@
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ mContentBounds.set(mContentPadding.left, mContentPadding.top,
+ MeasureSpec.getSize(widthMeasureSpec) - mContentPadding.right,
+ MeasureSpec.getSize(heightMeasureSpec) - mContentPadding.bottom);
+
// Update the number of items in the grid before we measure the view
- int availableWidth = !mContentBounds.isEmpty() ? mContentBounds.width() :
- MeasureSpec.getSize(widthMeasureSpec);
+ // TODO: mSectionNamesMargin is currently 0, but also account for it,
+ // if it's enabled in the future.
+ int availableWidth = (!mContentBounds.isEmpty() ? mContentBounds.width() :
+ MeasureSpec.getSize(widthMeasureSpec))
+ - 2 * mAppsRecyclerView.getMaxScrollbarWidth();
DeviceProfile grid = mLauncher.getDeviceProfile();
grid.updateAppsViewNumCols(getResources(), availableWidth);
if (mNumAppsPerRow != grid.allAppsNumCols ||
@@ -354,6 +349,12 @@
mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
+
+ if (mNumAppsPerRow > 0) {
+ int iconSize = availableWidth / mNumAppsPerRow;
+ int iconSpacing = (iconSize - grid.allAppsIconSizePx) / 2;
+ mSearchInput.setPaddingRelative(iconSpacing, 0, iconSpacing, 0);
+ }
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
@@ -365,51 +366,28 @@
* recycler view to handle touch events (for fast scrolling) all the way to the edge.
*/
@Override
- protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
- boolean isRtl = Utilities.isRtl(getResources());
-
- // TODO: Use quantum_panel instead of quantum_panel_shape
- InsetDrawable background = new InsetDrawable(
- getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0,
- padding.right, 0);
- Rect bgPadding = new Rect();
- background.getPadding(bgPadding);
- mContainerView.setBackground(background);
- mRevealView.setBackground(background.getConstantState().newDrawable());
+ protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
mAppsRecyclerView.updateBackgroundPadding(bgPadding);
mAdapter.updateBackgroundPadding(bgPadding);
-
- // Hack: We are going to let the recycler view take the full width, so reset the padding on
- // the container to zero after setting the background and apply the top-bottom padding to
- // the content view instead so that the launcher transition clips correctly.
- mContent.setPadding(0, padding.top, 0, padding.bottom);
- mContainerView.setPadding(0, 0, 0, 0);
+ mElevationController.updateBackgroundPadding(bgPadding);
// Pad the recycler view by the background padding plus the start margin (for the section
// names)
- int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getMaxScrollbarWidth());
+ int maxScrollBarWidth = mAppsRecyclerView.getMaxScrollbarWidth();
+ int startInset = Math.max(mSectionNamesMargin, maxScrollBarWidth);
int topBottomPadding = mRecyclerViewTopBottomPadding;
- if (isRtl) {
- mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getMaxScrollbarWidth(),
+ if (Utilities.isRtl(getResources())) {
+ mAppsRecyclerView.setPadding(padding.left + maxScrollBarWidth,
topBottomPadding, padding.right + startInset, topBottomPadding);
} else {
mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
- padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), topBottomPadding);
+ padding.right + maxScrollBarWidth, topBottomPadding);
}
- // Inset the search bar to fit its bounds above the container
- if (mSearchBarView != null) {
- Rect backgroundPadding = new Rect();
- if (mSearchBarView.getBackground() != null) {
- mSearchBarView.getBackground().getPadding(backgroundPadding);
- }
- LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
- mSearchBarContainerView.getLayoutParams();
- lp.leftMargin = searchBarBounds.left - backgroundPadding.left;
- lp.topMargin = searchBarBounds.top - backgroundPadding.top;
- lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right;
- mSearchBarContainerView.requestLayout();
- }
+ MarginLayoutParams lp = (MarginLayoutParams) mSearchContainer.getLayoutParams();
+ lp.leftMargin = padding.left;
+ lp.rightMargin = padding.right;
+ mSearchContainer.setLayoutParams(lp);
}
@Override
@@ -555,9 +533,7 @@
@Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
if (toWorkspace) {
- // Reset the search bar and base recycler view after transitioning home
- mSearchBarController.reset();
- mAppsRecyclerView.reset();
+ reset();
}
}
@@ -613,16 +589,18 @@
@Override
public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
if (apps != null) {
- mApps.setOrderedFilter(apps);
+ if (mApps.setOrderedFilter(apps)) {
+ mAppsRecyclerView.onSearchResultsChanged();
+ }
mAdapter.setLastSearchQuery(query);
- mAppsRecyclerView.onSearchResultsChanged();
}
}
@Override
public void clearSearchResult() {
- mApps.setOrderedFilter(null);
- mAppsRecyclerView.onSearchResultsChanged();
+ if (mApps.setOrderedFilter(null)) {
+ mAppsRecyclerView.onSearchResultsChanged();
+ }
// Clear the search query
mSearchQueryBuilder.clear();
diff --git a/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
new file mode 100644
index 0000000..73de45e
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsFastScrollHelper.java
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3.allapps;
+
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+
+import com.android.launcher3.BaseRecyclerView;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar;
+import com.android.launcher3.FastBitmapDrawable;
+import com.android.launcher3.util.Thunk;
+
+import java.util.HashSet;
+
+public class AllAppsFastScrollHelper implements AllAppsGridAdapter.BindViewCallback {
+
+ private static final int INITIAL_TOUCH_SETTLING_DURATION = 100;
+ private static final int REPEAT_TOUCH_SETTLING_DURATION = 200;
+ private static final float FAST_SCROLL_TOUCH_VELOCITY_BARRIER = 1900f;
+
+ private AllAppsRecyclerView mRv;
+ private AlphabeticalAppsList mApps;
+
+ // Keeps track of the current and targetted fast scroll section (the section to scroll to after
+ // the initial delay)
+ int mTargetFastScrollPosition = -1;
+ @Thunk String mCurrentFastScrollSection;
+ @Thunk String mTargetFastScrollSection;
+
+ // The settled states affect the delay before the fast scroll animation is applied
+ private boolean mHasFastScrollTouchSettled;
+ private boolean mHasFastScrollTouchSettledAtLeastOnce;
+
+ // Set of all views animated during fast scroll. We keep track of these ourselves since there
+ // is no way to reset a view once it gets scrapped or recycled without other hacks
+ private HashSet<BaseRecyclerViewFastScrollBar.FastScrollFocusableView> mTrackedFastScrollViews =
+ new HashSet<>();
+
+ // Smooth fast-scroll animation frames
+ @Thunk int mFastScrollFrameIndex;
+ @Thunk final int[] mFastScrollFrames = new int[10];
+
+ /**
+ * This runnable runs a single frame of the smooth scroll animation and posts the next frame
+ * if necessary.
+ */
+ @Thunk Runnable mSmoothSnapNextFrameRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (mFastScrollFrameIndex < mFastScrollFrames.length) {
+ mRv.scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]);
+ mFastScrollFrameIndex++;
+ mRv.postOnAnimation(mSmoothSnapNextFrameRunnable);
+ }
+ }
+ };
+
+ /**
+ * This runnable updates the current fast scroll section to the target fastscroll section.
+ */
+ Runnable mFastScrollToTargetSectionRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Update to the target section
+ mCurrentFastScrollSection = mTargetFastScrollSection;
+ mHasFastScrollTouchSettled = true;
+ mHasFastScrollTouchSettledAtLeastOnce = true;
+ updateTrackedViewsFastScrollFocusState();
+ }
+ };
+
+ public AllAppsFastScrollHelper(AllAppsRecyclerView rv, AlphabeticalAppsList apps) {
+ mRv = rv;
+ mApps = apps;
+ }
+
+ public void onSetAdapter(AllAppsGridAdapter adapter) {
+ adapter.setBindViewCallback(this);
+ }
+
+ /**
+ * Smooth scrolls the recycler view to the given section.
+ *
+ * @return whether the fastscroller can scroll to the new section.
+ */
+ public boolean smoothScrollToSection(int scrollY, int availableScrollHeight,
+ AlphabeticalAppsList.FastScrollSectionInfo info) {
+ if (mTargetFastScrollPosition != info.fastScrollToItem.position) {
+ mTargetFastScrollPosition = info.fastScrollToItem.position;
+ smoothSnapToPosition(scrollY, availableScrollHeight, info);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Smoothly snaps to a given position. We do this manually by calculating the keyframes
+ * ourselves and animating the scroll on the recycler view.
+ */
+ private void smoothSnapToPosition(int scrollY, int availableScrollHeight,
+ AlphabeticalAppsList.FastScrollSectionInfo info) {
+ mRv.removeCallbacks(mSmoothSnapNextFrameRunnable);
+ mRv.removeCallbacks(mFastScrollToTargetSectionRunnable);
+
+ trackAllChildViews();
+ if (mHasFastScrollTouchSettled) {
+ // In this case, the user has already settled once (and the fast scroll state has
+ // animated) and they are just fine-tuning their section from the last section, so
+ // we should make it feel fast and update immediately.
+ mCurrentFastScrollSection = info.sectionName;
+ mTargetFastScrollSection = null;
+ updateTrackedViewsFastScrollFocusState();
+ } else {
+ // Otherwise, the user has scrubbed really far, and we don't want to distract the user
+ // with the flashing fast scroll state change animation in addition to the fast scroll
+ // section popup, so reset the views to normal, and wait for the touch to settle again
+ // before animating the fast scroll state.
+ mCurrentFastScrollSection = null;
+ mTargetFastScrollSection = info.sectionName;
+ mHasFastScrollTouchSettled = false;
+ updateTrackedViewsFastScrollFocusState();
+
+ // Delay scrolling to a new section until after some duration. If the user has been
+ // scrubbing a while and makes multiple big jumps, then reduce the time needed for the
+ // fast scroll to settle so it doesn't feel so long.
+ mRv.postDelayed(mFastScrollToTargetSectionRunnable,
+ mHasFastScrollTouchSettledAtLeastOnce ?
+ REPEAT_TOUCH_SETTLING_DURATION :
+ INITIAL_TOUCH_SETTLING_DURATION);
+ }
+
+ // Calculate the full animation from the current scroll position to the final scroll
+ // position, and then run the animation for the duration.
+ int newScrollY = Math.min(availableScrollHeight,
+ mRv.getPaddingTop() + mRv.getTop(info.fastScrollToItem.rowIndex));
+ int numFrames = mFastScrollFrames.length;
+ for (int i = 0; i < numFrames; i++) {
+ // TODO(winsonc): We can interpolate this as well.
+ mFastScrollFrames[i] = (newScrollY - scrollY) / numFrames;
+ }
+ mFastScrollFrameIndex = 0;
+ mRv.postOnAnimation(mSmoothSnapNextFrameRunnable);
+ }
+
+ public void onFastScrollCompleted() {
+ // TODO(winsonc): Handle the case when the user scrolls and releases before the animation
+ // runs
+
+ // Stop animating the fast scroll position and state
+ mRv.removeCallbacks(mSmoothSnapNextFrameRunnable);
+ mRv.removeCallbacks(mFastScrollToTargetSectionRunnable);
+
+ // Reset the tracking variables
+ mHasFastScrollTouchSettled = false;
+ mHasFastScrollTouchSettledAtLeastOnce = false;
+ mCurrentFastScrollSection = null;
+ mTargetFastScrollSection = null;
+ mTargetFastScrollPosition = -1;
+
+ updateTrackedViewsFastScrollFocusState();
+ mTrackedFastScrollViews.clear();
+ }
+
+ @Override
+ public void onBindView(AllAppsGridAdapter.ViewHolder holder) {
+ // Update newly bound views to the current fast scroll state if we are fast scrolling
+ if (mCurrentFastScrollSection != null || mTargetFastScrollSection != null) {
+ if (holder.mContent instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
+ BaseRecyclerViewFastScrollBar.FastScrollFocusableView v =
+ (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) holder.mContent;
+ updateViewFastScrollFocusState(v, holder.getPosition(), false /* animated */);
+ mTrackedFastScrollViews.add(v);
+ }
+ }
+ }
+
+ /**
+ * Starts tracking all the recycler view's children which are FastScrollFocusableViews.
+ */
+ private void trackAllChildViews() {
+ int childCount = mRv.getChildCount();
+ for (int i = 0; i < childCount; i++) {
+ View v = mRv.getChildAt(i);
+ if (v instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
+ mTrackedFastScrollViews.add((BaseRecyclerViewFastScrollBar.FastScrollFocusableView) v);
+ }
+ }
+ }
+
+ /**
+ * Updates the fast scroll focus on all the children.
+ */
+ private void updateTrackedViewsFastScrollFocusState() {
+ for (BaseRecyclerViewFastScrollBar.FastScrollFocusableView v : mTrackedFastScrollViews) {
+ RecyclerView.ViewHolder viewHolder = mRv.getChildViewHolder((View) v);
+ int pos = (viewHolder != null) ? viewHolder.getPosition() : -1;
+ updateViewFastScrollFocusState(v, pos, true);
+ }
+ }
+
+ /**
+ * Updates the fast scroll focus on all a given view.
+ */
+ private void updateViewFastScrollFocusState(BaseRecyclerViewFastScrollBar.FastScrollFocusableView v,
+ int pos, boolean animated) {
+ FastBitmapDrawable.State newState = FastBitmapDrawable.State.NORMAL;
+ if (mCurrentFastScrollSection != null && pos > -1) {
+ AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(pos);
+ boolean highlight = item.sectionName.equals(mCurrentFastScrollSection) &&
+ item.position == mTargetFastScrollPosition;
+ newState = highlight ?
+ FastBitmapDrawable.State.FAST_SCROLL_HIGHLIGHTED :
+ FastBitmapDrawable.State.FAST_SCROLL_UNHIGHLIGHTED;
+ }
+ v.setFastScrollFocusState(newState, animated);
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 1f95133..0460c91 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -24,12 +24,16 @@
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
+import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.CollectionItemInfoCompat;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.net.Uri;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
+import android.support.v7.widget.RecyclerView.Recycler;
+import android.support.v7.widget.RecyclerView.State;
+import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,7 +46,6 @@
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Thunk;
import java.util.HashMap;
import java.util.List;
@@ -69,6 +72,10 @@
// The message to continue to a market search when there are no filtered results
public static final int SEARCH_MARKET_VIEW_TYPE = 5;
+ public interface BindViewCallback {
+ public void onBindView(ViewHolder holder);
+ }
+
/**
* ViewHolder for each icon.
*/
@@ -98,18 +105,67 @@
// adapter views
final AccessibilityRecordCompat record = AccessibilityEventCompat
.asRecord(event);
+
+ // count the number of SECTION_BREAK_VIEW_TYPE that is wrongfully
+ // initialized as a node (also a row) for talk back.
+ int numEmptyNode = getEmptyRowForAccessibility(-1 /* no view type */);
+ record.setFromIndex(event.getFromIndex() - numEmptyNode);
+ record.setToIndex(event.getToIndex() - numEmptyNode);
record.setItemCount(mApps.getNumFilteredApps());
}
@Override
+ public void onInitializeAccessibilityNodeInfoForItem(Recycler recycler,
+ State state, View host, AccessibilityNodeInfoCompat info) {
+
+ int viewType = getItemViewType(host);
+ // Only initialize on node that is meaningful. Subtract empty row count.
+ if (viewType == ICON_VIEW_TYPE || viewType == PREDICTION_ICON_VIEW_TYPE) {
+ super.onInitializeAccessibilityNodeInfoForItem(recycler, state, host, info);
+ CollectionItemInfoCompat itemInfo = info.getCollectionItemInfo();
+ final CollectionItemInfoCompat dstItemInfo = CollectionItemInfoCompat.obtain(
+ itemInfo.getRowIndex() - getEmptyRowForAccessibility(viewType),
+ itemInfo.getRowSpan(),
+ itemInfo.getColumnIndex(),
+ itemInfo.getColumnSpan(),
+ itemInfo.isHeading(),
+ itemInfo.isSelected());
+ info.setCollectionItemInfo(dstItemInfo);
+ }
+ }
+
+ @Override
public int getRowCountForAccessibility(RecyclerView.Recycler recycler,
RecyclerView.State state) {
- if (mApps.hasNoFilteredResults()) {
- // Disregard the no-search-results text as a list item for accessibility
- return 0;
+ return super.getRowCountForAccessibility(recycler, state)
+ - getEmptyRowForAccessibility(-1 /* no view type */);
+ }
+
+ /**
+ * Returns the total number of SECTION_BREAK_VIEW_TYPE that is wrongfully
+ * initialized as a node (also a row) for talk back.
+ */
+ private int getEmptyRowForAccessibility(int viewType) {
+ int numEmptyNode = 0;
+ if (mApps.hasFilter()) {
+ // search result screen has only one SECTION_BREAK_VIEW
+ numEmptyNode = 1;
} else {
- return super.getRowCountForAccessibility(recycler, state);
+ // default all apps screen may have one or two SECTION_BREAK_VIEW
+ numEmptyNode = 1;
+ if (mApps.hasPredictedComponents()) {
+ if (viewType == PREDICTION_ICON_VIEW_TYPE) {
+ numEmptyNode = 1;
+ } else if (viewType == ICON_VIEW_TYPE) {
+ numEmptyNode = 2;
+ }
+ } else {
+ if (viewType == ICON_VIEW_TYPE) {
+ numEmptyNode = 1;
+ }
+ }
}
+ return numEmptyNode;
}
}
@@ -319,19 +375,29 @@
}
}
- private Launcher mLauncher;
- private LayoutInflater mLayoutInflater;
- @Thunk AlphabeticalAppsList mApps;
- private GridLayoutManager mGridLayoutMgr;
- private GridSpanSizer mGridSizer;
- private GridItemDecoration mItemDecoration;
- private View.OnTouchListener mTouchListener;
- private View.OnClickListener mIconClickListener;
- private View.OnLongClickListener mIconLongClickListener;
- @Thunk final Rect mBackgroundPadding = new Rect();
- @Thunk int mPredictionBarDividerOffset;
- @Thunk int mAppsPerRow;
- @Thunk boolean mIsRtl;
+ private final Launcher mLauncher;
+ private final LayoutInflater mLayoutInflater;
+ private final AlphabeticalAppsList mApps;
+ private final GridLayoutManager mGridLayoutMgr;
+ private final GridSpanSizer mGridSizer;
+ private final GridItemDecoration mItemDecoration;
+ private final View.OnTouchListener mTouchListener;
+ private final View.OnClickListener mIconClickListener;
+ private final View.OnLongClickListener mIconLongClickListener;
+
+ private final Rect mBackgroundPadding = new Rect();
+ private final boolean mIsRtl;
+
+ // Section drawing
+ private final int mSectionNamesMargin;
+ private final int mSectionHeaderOffset;
+ private final Paint mSectionTextPaint;
+ private final Paint mPredictedAppsDividerPaint;
+
+ private final int mPredictionBarDividerOffset;
+ private int mAppsPerRow;
+ private BindViewCallback mBindViewCallback;
+ private AllAppsSearchBarController mSearchController;
// The text to show when there are no search results and no market search handler.
private String mEmptySearchMessage;
@@ -343,14 +409,6 @@
private String mMarketSearchMessage;
// The intent to send off to the market app, updated each time the search query changes.
private Intent mMarketSearchIntent;
- // The last query that the user entered into the search field
- private String mLastSearchQuery;
-
- // Section drawing
- @Thunk int mSectionNamesMargin;
- @Thunk int mSectionHeaderOffset;
- @Thunk Paint mSectionTextPaint;
- @Thunk Paint mPredictedAppsDividerPaint;
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps,
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
@@ -369,28 +427,19 @@
mIconLongClickListener = iconLongClickListener;
mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset);
+ mIsRtl = Utilities.isRtl(res);
- mSectionTextPaint = new Paint();
+ mSectionTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
R.dimen.all_apps_grid_section_text_size));
mSectionTextPaint.setColor(res.getColor(R.color.all_apps_grid_section_text_color));
- mSectionTextPaint.setAntiAlias(true);
- mPredictedAppsDividerPaint = new Paint();
+ mPredictedAppsDividerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPredictedAppsDividerPaint.setStrokeWidth(Utilities.pxFromDp(1f, res.getDisplayMetrics()));
mPredictedAppsDividerPaint.setColor(0x1E000000);
- mPredictedAppsDividerPaint.setAntiAlias(true);
mPredictionBarDividerOffset =
(-res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_bottom_padding) +
res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding)) / 2;
-
- // Resolve the market app handling additional searches
- PackageManager pm = launcher.getPackageManager();
- ResolveInfo marketInfo = pm.resolveActivity(createMarketSearchIntent(""),
- PackageManager.MATCH_DEFAULT_ONLY);
- if (marketInfo != null) {
- mMarketAppName = marketInfo.loadLabel(pm).toString();
- }
}
/**
@@ -401,11 +450,16 @@
mGridLayoutMgr.setSpanCount(appsPerRow);
}
- /**
- * Sets whether we are in RTL mode.
- */
- public void setRtl(boolean rtl) {
- mIsRtl = rtl;
+ public void setSearchController(AllAppsSearchBarController searchController) {
+ mSearchController = searchController;
+
+ // Resolve the market app handling additional searches
+ PackageManager pm = mLauncher.getPackageManager();
+ ResolveInfo marketInfo = pm.resolveActivity(mSearchController.createMarketSearchIntent(""),
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (marketInfo != null) {
+ mMarketAppName = marketInfo.loadLabel(pm).toString();
+ }
}
/**
@@ -415,16 +469,22 @@
public void setLastSearchQuery(String query) {
Resources res = mLauncher.getResources();
String formatStr = res.getString(R.string.all_apps_no_search_results);
- mLastSearchQuery = query;
mEmptySearchMessage = String.format(formatStr, query);
if (mMarketAppName != null) {
mMarketSearchMessage = String.format(res.getString(R.string.all_apps_search_market_message),
mMarketAppName);
- mMarketSearchIntent = createMarketSearchIntent(query);
+ mMarketSearchIntent = mSearchController.createMarketSearchIntent(query);
}
}
/**
+ * Sets the callback for when views are bound.
+ */
+ public void setBindViewCallback(BindViewCallback cb) {
+ mBindViewCallback = cb;
+ }
+
+ /**
* Notifies the adapter of the background padding so that it can draw things correctly in the
* item decorator.
*/
@@ -486,7 +546,7 @@
searchMarketView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- mLauncher.startSearchFromAllApps(v, mMarketSearchIntent, mLastSearchQuery);
+ mLauncher.startActivitySafely(v, mMarketSearchIntent, null);
}
});
return new ViewHolder(searchMarketView);
@@ -529,6 +589,15 @@
}
break;
}
+ if (mBindViewCallback != null) {
+ mBindViewCallback.onBindView(holder);
+ }
+ }
+
+ @Override
+ public boolean onFailedToRecycleView(ViewHolder holder) {
+ // Always recycle and we will reset the view when it is bound
+ return true;
}
@Override
@@ -541,17 +610,4 @@
AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
return item.viewType;
}
-
- /**
- * Creates a new market search intent.
- */
- private Intent createMarketSearchIntent(String query) {
- Uri marketSearchUri = Uri.parse("market://search")
- .buildUpon()
- .appendQueryParameter("q", query)
- .build();
- Intent marketSearchIntent = new Intent(Intent.ACTION_VIEW);
- marketSearchIntent.setData(marketSearchUri);
- return marketSearchIntent;
- }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 2f66e2c..2b3d061 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,24 +15,21 @@
*/
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.os.Bundle;
-import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.BaseRecyclerViewFastScrollBar;
+import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Stats;
import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Thunk;
import java.util.List;
@@ -42,28 +39,22 @@
public class AllAppsRecyclerView extends BaseRecyclerView
implements Stats.LaunchSourceProvider {
- private static final int FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON = 0;
- private static final int FAST_SCROLL_MODE_FREE_SCROLL = 1;
-
- private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW = 0;
- private static final int FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS = 1;
-
private AlphabeticalAppsList mApps;
+ private AllAppsFastScrollHelper mFastScrollHelper;
+ private BaseRecyclerView.ScrollPositionState mScrollPosState =
+ new BaseRecyclerView.ScrollPositionState();
private int mNumAppsPerRow;
- @Thunk BaseRecyclerViewFastScrollBar.FastScrollFocusableView mLastFastScrollFocusedView;
- @Thunk int mPrevFastScrollFocusedPosition;
- @Thunk int mFastScrollFrameIndex;
- @Thunk final int[] mFastScrollFrames = new int[10];
+ // The specific icon heights that we use to calculate scroll
+ private int mPredictionIconHeight;
+ private int mIconHeight;
- private final int mFastScrollMode = FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON;
- private final int mScrollBarMode = FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW;
-
- private ScrollPositionState mScrollPosState = new ScrollPositionState();
-
+ // The empty-search result background
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
+ private HeaderElevationController mElevationController;
+
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -79,8 +70,8 @@
public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr);
-
Resources res = getResources();
+ addOnItemTouchListener(this);
mScrollbar.setDetachThumbOnFastScroll();
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
@@ -91,6 +82,11 @@
*/
public void setApps(AlphabeticalAppsList apps) {
mApps = apps;
+ mFastScrollHelper = new AllAppsFastScrollHelper(this, apps);
+ }
+
+ public void setElevationController(HeaderElevationController elevationController) {
+ mElevationController = elevationController;
}
/**
@@ -110,6 +106,14 @@
}
/**
+ * Sets the heights of the icons in this view (for scroll calculations).
+ */
+ public void setPremeasuredIconHeights(int predictionIconHeight, int iconHeight) {
+ mPredictionIconHeight = predictionIconHeight;
+ mIconHeight = iconHeight;
+ }
+
+ /**
* Scrolls this recycler view to the top.
*/
public void scrollToTop() {
@@ -118,6 +122,9 @@
mScrollbar.reattachThumbToScroll();
}
scrollToPosition(0);
+ if (mElevationController != null) {
+ mElevationController.reset();
+ }
}
/**
@@ -126,6 +133,7 @@
*/
@Override
protected void dispatchDraw(Canvas canvas) {
+ // Clip to ensure that we don't draw the overscroll effect beyond the background bounds
canvas.clipRect(mBackgroundPadding.left, mBackgroundPadding.top,
getWidth() - mBackgroundPadding.right,
getHeight() - mBackgroundPadding.bottom);
@@ -157,20 +165,25 @@
}
@Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
- // Bind event handlers
- addOnItemTouchListener(this);
- }
-
- @Override
- public void fillInLaunchSourceData(Bundle sourceData) {
+ public void fillInLaunchSourceData(View v, Bundle sourceData) {
sourceData.putString(Stats.SOURCE_EXTRA_CONTAINER, Stats.CONTAINER_ALL_APPS);
if (mApps.hasFilter()) {
sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
Stats.SUB_CONTAINER_ALL_APPS_SEARCH);
} else {
+ if (v instanceof BubbleTextView) {
+ BubbleTextView icon = (BubbleTextView) v;
+ int position = getChildPosition(icon);
+ if (position != NO_POSITION) {
+ List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
+ AlphabeticalAppsList.AdapterItem item = items.get(position);
+ if (item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
+ Stats.SUB_CONTAINER_ALL_APPS_PREDICTION);
+ return;
+ }
+ }
+ }
sourceData.putString(Stats.SOURCE_EXTRA_SUB_CONTAINER,
Stats.SUB_CONTAINER_ALL_APPS_A_Z);
}
@@ -212,63 +225,31 @@
List<AlphabeticalAppsList.FastScrollSectionInfo> fastScrollSections =
mApps.getFastScrollerSections();
AlphabeticalAppsList.FastScrollSectionInfo lastInfo = fastScrollSections.get(0);
- if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW) {
- for (int i = 1; i < fastScrollSections.size(); i++) {
- AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i);
- if (info.touchFraction > touchFraction) {
- break;
- }
- lastInfo = info;
+ for (int i = 1; i < fastScrollSections.size(); i++) {
+ AlphabeticalAppsList.FastScrollSectionInfo info = fastScrollSections.get(i);
+ if (info.touchFraction > touchFraction) {
+ break;
}
- } else if (mScrollBarMode == FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_SECTIONS){
- lastInfo = fastScrollSections.get((int) (touchFraction * (fastScrollSections.size() - 1)));
- } else {
- throw new RuntimeException("Unexpected scroll bar mode");
+ lastInfo = info;
}
- // Map the touch position back to the scroll of the recycler view
- getCurScrollState(mScrollPosState);
- int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
- LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
- if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
- layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
- }
-
- if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) {
- mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position;
-
- // Reset the last focused view
- if (mLastFastScrollFocusedView != null) {
- mLastFastScrollFocusedView.setFastScrollFocused(false, true);
- mLastFastScrollFocusedView = null;
- }
-
- if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) {
- smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState);
- } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
- final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
- if (vh != null &&
- vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
- mLastFastScrollFocusedView =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- mLastFastScrollFocusedView.setFastScrollFocused(true, true);
- }
- } else {
- throw new RuntimeException("Unexpected fast scroll mode");
- }
- }
+ // Update the fast scroll
+ int scrollY = getScrollTop(mScrollPosState);
+ int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows());
+ mFastScrollHelper.smoothScrollToSection(scrollY, availableScrollHeight, lastInfo);
return lastInfo.sectionName;
}
@Override
public void onFastScrollCompleted() {
super.onFastScrollCompleted();
- // Reset and clean up the last focused view
- if (mLastFastScrollFocusedView != null) {
- mLastFastScrollFocusedView.setFastScrollFocused(false, true);
- mLastFastScrollFocusedView = null;
- }
- mPrevFastScrollFocusedPosition = -1;
+ mFastScrollHelper.onFastScrollCompleted();
+ }
+
+ @Override
+ public void setAdapter(Adapter adapter) {
+ super.setAdapter(adapter);
+ mFastScrollHelper.onSetAdapter((AllAppsGridAdapter) adapter);
}
/**
@@ -286,7 +267,7 @@
// Find the index and height of the first visible row (all rows have the same height)
int rowCount = mApps.getNumAppRows();
- getCurScrollState(mScrollPosState);
+ getCurScrollState(mScrollPosState, -1);
if (mScrollPosState.rowIndex < 0) {
mScrollbar.setThumbOffset(-1, -1);
return;
@@ -294,7 +275,7 @@
// Only show the scrollbar if there is height to be scrolled
int availableScrollBarHeight = getAvailableScrollBarHeight();
- int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows(), mScrollPosState.rowHeight);
+ int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows());
if (availableScrollHeight <= 0) {
mScrollbar.setThumbOffset(-1, -1);
return;
@@ -303,8 +284,7 @@
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
- int scrollY = getPaddingTop() +
- (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset;
+ int scrollY = getScrollTop(mScrollPosState);
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -355,58 +335,12 @@
}
/**
- * This runnable runs a single frame of the smooth scroll animation and posts the next frame
- * if necessary.
- */
- @Thunk Runnable mSmoothSnapNextFrameRunnable = new Runnable() {
- @Override
- public void run() {
- if (mFastScrollFrameIndex < mFastScrollFrames.length) {
- scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]);
- mFastScrollFrameIndex++;
- postOnAnimation(mSmoothSnapNextFrameRunnable);
- } else {
- // Animation completed, set the fast scroll state on the target view
- final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
- if (vh != null &&
- vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView &&
- mLastFastScrollFocusedView != vh.itemView) {
- mLastFastScrollFocusedView =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- mLastFastScrollFocusedView.setFastScrollFocused(true, true);
- }
- }
- }
- };
-
- /**
- * Smoothly snaps to a given position. We do this manually by calculating the keyframes
- * ourselves and animating the scroll on the recycler view.
- */
- private void smoothSnapToPosition(final int position, ScrollPositionState scrollPosState) {
- removeCallbacks(mSmoothSnapNextFrameRunnable);
-
- // Calculate the full animation from the current scroll position to the final scroll
- // position, and then run the animation for the duration.
- int curScrollY = getPaddingTop() +
- (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
- int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight);
- int numFrames = mFastScrollFrames.length;
- for (int i = 0; i < numFrames; i++) {
- // TODO(winsonc): We can interpolate this as well.
- mFastScrollFrames[i] = (newScrollY - curScrollY) / numFrames;
- }
- mFastScrollFrameIndex = 0;
- postOnAnimation(mSmoothSnapNextFrameRunnable);
- }
-
- /**
* Returns the current scroll state of the apps rows.
*/
- protected void getCurScrollState(ScrollPositionState stateOut) {
+ protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) {
stateOut.rowIndex = -1;
stateOut.rowTopOffset = -1;
- stateOut.rowHeight = -1;
+ stateOut.itemPos = -1;
// Return early if there are no items or we haven't been measured
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
@@ -420,29 +354,31 @@
int position = getChildPosition(child);
if (position != NO_POSITION) {
AlphabeticalAppsList.AdapterItem item = items.get(position);
- if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
- item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ if ((item.viewType & viewTypeMask) != 0) {
stateOut.rowIndex = item.rowIndex;
stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
- stateOut.rowHeight = child.getHeight();
- break;
+ stateOut.itemPos = position;
+ return;
}
}
}
+ return;
}
- /**
- * Returns the scrollY for the given position in the adapter.
- */
- private int getScrollAtPosition(int position, int rowHeight) {
- AlphabeticalAppsList.AdapterItem item = mApps.getAdapterItems().get(position);
- if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
- item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
- int offset = item.rowIndex > 0 ? getPaddingTop() : 0;
- return offset + item.rowIndex * rowHeight;
- } else {
+ @Override
+ protected 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();
+ }
+
+ protected int getTop(int rowIndex) {
+ if (getChildCount() == 0 || rowIndex <= 0) {
return 0;
}
+
+ // The prediction bar icons have more padding, so account for that in the row offset
+ return mPredictionIconHeight + (rowIndex - 1) * mIconHeight;
}
/**
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
index 2b363c0..39d6dd5 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
+++ b/src/com/android/launcher3/allapps/AllAppsSearchBarController.java
@@ -15,11 +15,22 @@
*/
package com.android.launcher3.allapps;
-import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
import android.graphics.Rect;
+import android.net.Uri;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
import android.view.View;
-import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
+import com.android.launcher3.ExtendedEditText;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
@@ -27,53 +38,134 @@
/**
* An interface to a search box that AllApps can command.
*/
-public abstract class AllAppsSearchBarController {
+public abstract class AllAppsSearchBarController
+ implements TextWatcher, OnEditorActionListener, ExtendedEditText.OnBackKeyListener {
+ protected Launcher mLauncher;
protected AlphabeticalAppsList mApps;
protected Callbacks mCb;
+ protected ExtendedEditText mInput;
+
+ protected DefaultAppSearchAlgorithm mSearchAlgorithm;
+ protected InputMethodManager mInputMethodManager;
/**
* Sets the references to the apps model and the search result callback.
*/
- public final void initialize(AlphabeticalAppsList apps, Callbacks cb) {
+ public final void initialize(
+ AlphabeticalAppsList apps, ExtendedEditText input,
+ Launcher launcher, Callbacks cb) {
mApps = apps;
mCb = cb;
- onInitialize();
+ mLauncher = launcher;
+
+ mInput = input;
+ mInput.addTextChangedListener(this);
+ mInput.setOnEditorActionListener(this);
+ mInput.setOnBackKeyListener(this);
+
+ mInputMethodManager = (InputMethodManager)
+ mInput.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+
+ mSearchAlgorithm = onInitializeSearch();
}
/**
- * To be overridden by subclasses. This method will get called when the controller is set,
- * before getView().
+ * To be implemented by subclasses. This method will get called when the controller is set.
*/
- protected abstract void onInitialize();
+ protected abstract DefaultAppSearchAlgorithm onInitializeSearch();
- /**
- * Returns the search bar view.
- * @param parent the parent to attach the search bar view to.
- */
- public abstract View getView(ViewGroup parent);
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Do nothing
+ }
- /**
- * Focuses the search field to handle key events.
- */
- public abstract void focusSearchField();
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Do nothing
+ }
- /**
- * Returns whether the search field is focused.
- */
- public abstract boolean isSearchFieldFocused();
+ @Override
+ public void afterTextChanged(final Editable s) {
+ String query = s.toString();
+ if (query.isEmpty()) {
+ mSearchAlgorithm.cancel(true);
+ mCb.clearSearchResult();
+ } else {
+ mSearchAlgorithm.cancel(false);
+ mSearchAlgorithm.doSearch(query, mCb);
+ }
+ }
+
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // Skip if it's not the right action
+ if (actionId != EditorInfo.IME_ACTION_SEARCH) {
+ return false;
+ }
+ // Skip if the query is empty
+ String query = v.getText().toString();
+ if (query.isEmpty()) {
+ return false;
+ }
+ return mLauncher.startActivitySafely(v, createMarketSearchIntent(query), null);
+ }
+
+ @Override
+ public boolean onBackKey() {
+ // Only hide the search field if there is no query, or if there
+ // are no filtered results
+ String query = Utilities.trim(mInput.getEditableText().toString());
+ if (query.isEmpty() || mApps.hasNoFilteredResults()) {
+ reset();
+ return true;
+ }
+ return false;
+ }
/**
* Resets the search bar state.
*/
- public abstract void reset();
+ public void reset() {
+ unfocusSearchField();
+ mCb.clearSearchResult();
+ mInput.setText("");
+ mInputMethodManager.hideSoftInputFromWindow(mInput.getWindowToken(), 0);
+ }
+
+ protected void unfocusSearchField() {
+ View nextFocus = mInput.focusSearch(View.FOCUS_DOWN);
+ if (nextFocus != null) {
+ nextFocus.requestFocus();
+ }
+ }
/**
- * Returns whether the prediction bar should currently be visible depending on the state of
- * the search bar.
+ * Focuses the search field to handle key events.
*/
- @Deprecated
- public abstract boolean shouldShowPredictionBar();
+ public void focusSearchField() {
+ mInput.requestFocus();
+ mInputMethodManager.showSoftInput(mInput, InputMethodManager.SHOW_IMPLICIT);
+ }
+
+ /**
+ * Returns whether the search field is focused.
+ */
+ public boolean isSearchFieldFocused() {
+ return mInput.isFocused();
+ }
+
+ /**
+ * 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.
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index dac0df1..b533ce9 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -275,14 +275,21 @@
return (mSearchResults != null) && mFilteredApps.isEmpty();
}
+ public boolean hasPredictedComponents() {
+ return (mPredictedAppComponents != null && mPredictedAppComponents.size() > 0);
+ }
+
/**
* Sets the sorted list of filtered components.
*/
- public void setOrderedFilter(ArrayList<ComponentKey> f) {
+ public boolean setOrderedFilter(ArrayList<ComponentKey> f) {
if (mSearchResults != f) {
+ boolean same = mSearchResults != null && mSearchResults.equals(f);
mSearchResults = f;
updateAdapterItems();
+ return !same;
}
+ return false;
}
/**
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
index 3169f84..57747e3 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
@@ -15,261 +15,12 @@
*/
package com.android.launcher3.allapps;
-import android.content.Context;
-import android.text.Editable;
-import android.text.TextWatcher;
-import android.view.KeyEvent;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputMethodManager;
-import android.widget.TextView;
-import com.android.launcher3.ExtendedEditText;
-import com.android.launcher3.R;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.util.Thunk;
-
-import java.util.List;
-
-
/**
* The default search controller.
*/
-final class DefaultAppSearchController extends AllAppsSearchBarController
- implements TextWatcher, TextView.OnEditorActionListener, View.OnClickListener {
+public class DefaultAppSearchController extends AllAppsSearchBarController {
- private static final boolean ALLOW_SINGLE_APP_LAUNCH = true;
-
- private static final int FADE_IN_DURATION = 175;
- private static final int FADE_OUT_DURATION = 100;
- private static final int SEARCH_TRANSLATION_X_DP = 18;
-
- private final Context mContext;
- @Thunk final InputMethodManager mInputMethodManager;
-
- private DefaultAppSearchAlgorithm mSearchManager;
-
- private ViewGroup mContainerView;
- private View mSearchView;
- @Thunk View mSearchBarContainerView;
- private View mSearchButtonView;
- private View mDismissSearchButtonView;
- @Thunk
- ExtendedEditText mSearchBarEditView;
- @Thunk AllAppsRecyclerView mAppsRecyclerView;
- @Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() {
- @Override
- public void run() {
- mAppsRecyclerView.requestFocus();
- }
- };
-
- public DefaultAppSearchController(Context context, ViewGroup containerView,
- AllAppsRecyclerView appsRecyclerView) {
- mContext = context;
- mInputMethodManager = (InputMethodManager)
- mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
- mContainerView = containerView;
- mAppsRecyclerView = appsRecyclerView;
- }
-
- @Override
- public View getView(ViewGroup parent) {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
- mSearchView = inflater.inflate(R.layout.all_apps_search_bar, parent, false);
- mSearchView.setOnClickListener(this);
-
- mSearchButtonView = mSearchView.findViewById(R.id.search_button);
- mSearchBarContainerView = mSearchView.findViewById(R.id.search_container);
- mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
- mDismissSearchButtonView.setOnClickListener(this);
- mSearchBarEditView = (ExtendedEditText)
- mSearchBarContainerView.findViewById(R.id.search_box_input);
- mSearchBarEditView.addTextChangedListener(this);
- mSearchBarEditView.setOnEditorActionListener(this);
- mSearchBarEditView.setOnBackKeyListener(
- new ExtendedEditText.OnBackKeyListener() {
- @Override
- public boolean onBackKey() {
- // Only hide the search field if there is no query, or if there
- // are no filtered results
- String query = Utilities.trim(
- mSearchBarEditView.getEditableText().toString());
- if (query.isEmpty() || mApps.hasNoFilteredResults()) {
- hideSearchField(true, mFocusRecyclerViewRunnable);
- return true;
- }
- return false;
- }
- });
- return mSearchView;
- }
-
- @Override
- public void focusSearchField() {
- mSearchBarEditView.requestFocus();
- showSearchField();
- }
-
- @Override
- public boolean isSearchFieldFocused() {
- return mSearchBarEditView.isFocused();
- }
-
- @Override
- protected void onInitialize() {
- mSearchManager = new DefaultAppSearchAlgorithm(mApps.getApps());
- }
-
- @Override
- public void reset() {
- hideSearchField(false, null);
- }
-
- @Override
- public boolean shouldShowPredictionBar() {
- return false;
- }
-
- @Override
- public void onClick(View v) {
- if (v == mSearchView) {
- showSearchField();
- } else if (v == mDismissSearchButtonView) {
- hideSearchField(true, mFocusRecyclerViewRunnable);
- }
- }
-
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- // Do nothing
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- // Do nothing
- }
-
- @Override
- public void afterTextChanged(final Editable s) {
- String query = s.toString();
- if (query.isEmpty()) {
- mSearchManager.cancel(true);
- mCb.clearSearchResult();
- } else {
- mSearchManager.cancel(false);
- mSearchManager.doSearch(query, mCb);
- }
- }
-
- @Override
- public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
- // Skip if we disallow app-launch-on-enter
- if (!ALLOW_SINGLE_APP_LAUNCH) {
- return false;
- }
- // Skip if it's not the right action
- if (actionId != EditorInfo.IME_ACTION_SEARCH) {
- return false;
- }
- // Skip if there are more than one icon
- if (mApps.getNumFilteredApps() > 1) {
- return false;
- }
- // Otherwise, find the first icon, or fallback to the search-market-view and launch it
- List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
- for (int i = 0; i < items.size(); i++) {
- AlphabeticalAppsList.AdapterItem item = items.get(i);
- switch (item.viewType) {
- case AllAppsGridAdapter.ICON_VIEW_TYPE:
- case AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE:
- mAppsRecyclerView.getChildAt(i).performClick();
- mInputMethodManager.hideSoftInputFromWindow(
- mContainerView.getWindowToken(), 0);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Focuses the search field.
- */
- private void showSearchField() {
- // Show the search bar and focus the search
- final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
- mContext.getResources().getDisplayMetrics());
- mSearchBarContainerView.setVisibility(View.VISIBLE);
- mSearchBarContainerView.setAlpha(0f);
- mSearchBarContainerView.setTranslationX(translationX);
- mSearchBarContainerView.animate()
- .alpha(1f)
- .translationX(0)
- .setDuration(FADE_IN_DURATION)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mSearchBarEditView.requestFocus();
- mInputMethodManager.showSoftInput(mSearchBarEditView,
- InputMethodManager.SHOW_IMPLICIT);
- }
- });
- mSearchButtonView.animate()
- .alpha(0f)
- .translationX(-translationX)
- .setDuration(FADE_OUT_DURATION)
- .withLayer();
- }
-
- /**
- * Unfocuses the search field.
- */
- @Thunk void hideSearchField(boolean animated, final Runnable postAnimationRunnable) {
- mSearchManager.cancel(true);
-
- final boolean resetTextField = mSearchBarEditView.getText().toString().length() > 0;
- final int translationX = Utilities.pxFromDp(SEARCH_TRANSLATION_X_DP,
- mContext.getResources().getDisplayMetrics());
- if (animated) {
- // Hide the search bar and focus the recycler view
- mSearchBarContainerView.animate()
- .alpha(0f)
- .translationX(0)
- .setDuration(FADE_IN_DURATION)
- .withLayer()
- .withEndAction(new Runnable() {
- @Override
- public void run() {
- mSearchBarContainerView.setVisibility(View.INVISIBLE);
- if (resetTextField) {
- mSearchBarEditView.setText("");
- }
- mCb.clearSearchResult();
- if (postAnimationRunnable != null) {
- postAnimationRunnable.run();
- }
- }
- });
- mSearchButtonView.setTranslationX(-translationX);
- mSearchButtonView.animate()
- .alpha(1f)
- .translationX(0)
- .setDuration(FADE_OUT_DURATION)
- .withLayer();
- } else {
- mSearchBarContainerView.setVisibility(View.INVISIBLE);
- if (resetTextField) {
- mSearchBarEditView.setText("");
- }
- mCb.clearSearchResult();
- mSearchButtonView.setAlpha(1f);
- mSearchButtonView.setTranslationX(0f);
- if (postAnimationRunnable != null) {
- postAnimationRunnable.run();
- }
- }
- mInputMethodManager.hideSoftInputFromWindow(mContainerView.getWindowToken(), 0);
+ 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
new file mode 100644
index 0000000..07f583c
--- /dev/null
+++ b/src/com/android/launcher3/allapps/HeaderElevationController.java
@@ -0,0 +1,101 @@
+package com.android.launcher3.allapps;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.graphics.Rect;
+import android.graphics.drawable.GradientDrawable;
+import android.os.Build;
+import android.support.v7.widget.RecyclerView;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewOutlineProvider;
+import android.widget.FrameLayout;
+
+import com.android.launcher3.R;
+
+/**
+ * Helper class for controlling the header elevation in response to RecyclerView scroll.
+ */
+public abstract class HeaderElevationController extends RecyclerView.OnScrollListener {
+
+ private int mCurrentY = 0;
+
+ public void reset() {
+ mCurrentY = 0;
+ onScroll(mCurrentY);
+ }
+
+ @Override
+ public final void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ mCurrentY += dy;
+ onScroll(mCurrentY);
+ }
+
+ public void updateBackgroundPadding(Rect bgPadding) { }
+
+ abstract void onScroll(int scrollY);
+
+ public static class ControllerV16 extends HeaderElevationController {
+
+ private final View mShadow;
+ private final float mScrollToElevation;
+
+ public ControllerV16(View header) {
+ Resources res = header.getContext().getResources();
+ mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+
+ mShadow = new View(header.getContext());
+ mShadow.setBackground(new GradientDrawable(
+ GradientDrawable.Orientation.TOP_BOTTOM, new int[] {0x1E000000, 0x00000000}));
+ mShadow.setAlpha(0);
+
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ res.getDimensionPixelSize(R.dimen.all_apps_header_shadow_height));
+ lp.topMargin = ((FrameLayout.LayoutParams) header.getLayoutParams()).height;
+
+ ((ViewGroup) header.getParent()).addView(mShadow, lp);
+ }
+
+ @Override
+ public void onScroll(int scrollY) {
+ float elevationPct = (float) Math.min(scrollY, mScrollToElevation) /
+ mScrollToElevation;
+ mShadow.setAlpha(elevationPct);
+ }
+
+ @Override
+ public void updateBackgroundPadding(Rect bgPadding) {
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mShadow.getLayoutParams();
+ lp.leftMargin = bgPadding.left;
+ lp.rightMargin = bgPadding.right;
+ mShadow.requestLayout();
+ }
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public static class ControllerVL extends HeaderElevationController {
+
+ private final View mHeader;
+ private final float mMaxElevation;
+ private final float mScrollToElevation;
+
+ public ControllerVL(View header) {
+ mHeader = header;
+ mHeader.setOutlineProvider(ViewOutlineProvider.BOUNDS);
+
+ Resources res = header.getContext().getResources();
+ mMaxElevation = res.getDimension(R.dimen.all_apps_header_max_elevation);
+ mScrollToElevation = res.getDimension(R.dimen.all_apps_header_scroll_to_elevation);
+ }
+
+ @Override
+ public 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/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 434f13d..811cacf 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -20,6 +20,7 @@
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
@@ -28,7 +29,9 @@
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
+import java.util.HashMap;
import java.util.List;
public abstract class AppWidgetManagerCompat {
@@ -62,6 +65,11 @@
return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
}
+ public LauncherAppWidgetProviderInfo getLauncherAppWidgetInfo(int appWidgetId) {
+ AppWidgetProviderInfo info = getAppWidgetInfo(appWidgetId);
+ return info == null ? null : LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+ }
+
public abstract List<AppWidgetProviderInfo> getAllProviders();
public abstract String loadLabel(LauncherAppWidgetProviderInfo info);
@@ -79,6 +87,10 @@
public abstract Drawable loadIcon(LauncherAppWidgetProviderInfo info, IconCache cache);
public abstract Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
- int imageHeight);
+ int imageWidth, int imageHeight);
+ public abstract LauncherAppWidgetProviderInfo findProvider(
+ ComponentName provider, UserHandleCompat user);
+
+ public abstract HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap();
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index 463cf90..de9414e 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -21,6 +21,7 @@
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -31,7 +32,9 @@
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ComponentKey;
+import java.util.HashMap;
import java.util.List;
class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
@@ -88,7 +91,28 @@
@Override
public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
- int imageHeight) {
+ int imageWidth, int imageHeight) {
return bitmap;
}
+
+ @Override
+ public LauncherAppWidgetProviderInfo findProvider(
+ ComponentName provider, UserHandleCompat user) {
+ for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+ if (info.provider.equals(provider)) {
+ return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+ HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+ UserHandleCompat user = UserHandleCompat.myUserHandle();
+ for (AppWidgetProviderInfo info : mAppWidgetManager.getInstalledProviders()) {
+ result.put(new ComponentKey(info.provider, user), info);
+ }
+ return result;
+ }
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
index 13712d8..a1570e6 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -21,6 +21,7 @@
import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -40,8 +41,10 @@
import com.android.launcher3.IconCache;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.R;
+import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -90,9 +93,7 @@
AppWidgetHost host, int requestCode) {
try {
host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
- } catch (SecurityException e) {
+ } catch (ActivityNotFoundException | SecurityException e) {
Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
}
}
@@ -109,18 +110,24 @@
@Override
public Bitmap getBadgeBitmap(LauncherAppWidgetProviderInfo info, Bitmap bitmap,
- int imageHeight) {
+ int imageWidth, int imageHeight) {
if (info.isCustomWidget || info.getProfile().equals(android.os.Process.myUserHandle())) {
return bitmap;
}
// Add a user badge in the bottom right of the image.
final Resources res = mContext.getResources();
- final int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size);
final int badgeMinTop = res.getDimensionPixelSize(R.dimen.profile_badge_minimum_top);
+
+ // choose min between badge size defined for widget tray versus width, height of the image.
+ // Width, height of the image can be smaller than widget tray badge size when being dropped
+ // to the workspace.
+ final int badgeSize = Math.min(res.getDimensionPixelSize(R.dimen.profile_badge_size),
+ Math.min(imageWidth, imageHeight - badgeMinTop));
final Rect badgeLocation = new Rect(0, 0, badgeSize, badgeSize);
final int top = Math.max(imageHeight - badgeSize, badgeMinTop);
+
if (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
badgeLocation.offset(0, top);
} else {
@@ -141,4 +148,28 @@
c.setBitmap(null);
return bitmap;
}
+
+ @Override
+ public LauncherAppWidgetProviderInfo findProvider(ComponentName provider, UserHandleCompat user) {
+ for (AppWidgetProviderInfo info : mAppWidgetManager
+ .getInstalledProvidersForProfile(user.getUser())) {
+ if (info.provider.equals(provider)) {
+ return LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, info);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public HashMap<ComponentKey, AppWidgetProviderInfo> getAllProvidersMap() {
+ HashMap<ComponentKey, AppWidgetProviderInfo> result = new HashMap<>();
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ UserHandleCompat userHandle = UserHandleCompat.fromUser(user);
+ for (AppWidgetProviderInfo info :
+ mAppWidgetManager.getInstalledProvidersForProfile(user)) {
+ result.put(new ComponentKey(info.provider, userHandle), info);
+ }
+ }
+ return result;
+ }
}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
index 07ef0ef..0bc9588 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -33,7 +33,6 @@
public abstract Drawable getIcon(int density);
public abstract ApplicationInfo getApplicationInfo();
public abstract long getFirstInstallTime();
- public abstract Drawable getBadgedIcon(int density);
/**
* Creates a LauncherActivityInfoCompat for the primary user.
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
index ea51aac..fee0376 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
@@ -93,8 +93,4 @@
public String getName() {
return mActivityInfo.name;
}
-
- public Drawable getBadgedIcon(int density) {
- return getIcon(density);
- }
}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
index 4448758..67c5c27 100644
--- a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
@@ -55,8 +55,4 @@
public long getFirstInstallTime() {
return mLauncherActivityInfo.getFirstInstallTime();
}
-
- public Drawable getBadgedIcon(int density) {
- return mLauncherActivityInfo.getBadgedIcon(density);
- }
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 95e3ba9..237a9e9 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -19,9 +19,6 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.os.Bundle;
@@ -35,6 +32,10 @@
"android.intent.action.MANAGED_PROFILE_ADDED";
public static final String ACTION_MANAGED_PROFILE_REMOVED =
"android.intent.action.MANAGED_PROFILE_REMOVED";
+ public static final String ACTION_MANAGED_PROFILE_AVAILABLE =
+ "android.intent.action.MANAGED_PROFILE_AVAILABLE";
+ public static final String ACTION_MANAGED_PROFILE_UNAVAILABLE =
+ "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
public interface OnAppsChangedCallbackCompat {
void onPackageRemoved(String packageName, UserHandleCompat user);
@@ -42,6 +43,8 @@
void onPackageChanged(String packageName, UserHandleCompat user);
void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing);
void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
+ void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
+ void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
}
protected LauncherAppsCompat() {
@@ -75,13 +78,5 @@
public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user);
public abstract boolean isActivityEnabledForProfile(ComponentName component,
UserHandleCompat user);
-
- public boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
- try {
- ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
- return info != null && info.enabled;
- } catch (NameNotFoundException e) {
- return false;
- }
- }
-}
\ No newline at end of file
+ public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index 339c457..4e2fc05 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -27,11 +27,11 @@
import android.content.pm.ResolveInfo;
import android.graphics.Rect;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import com.android.launcher3.Utilities;
+import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -114,7 +114,7 @@
}
public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
- return isAppEnabled(mPm, packageName, 0);
+ return PackageManagerHelper.isAppEnabled(mPm, packageName);
}
public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
@@ -126,6 +126,10 @@
}
}
+ public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
+ return false;
+ }
+
private void unregisterForPackageIntents() {
mContext.unregisterReceiver(mPackageMonitor);
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
index fbf91b5..7270d02 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -36,7 +36,7 @@
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class LauncherAppsCompatVL extends LauncherAppsCompat {
- private LauncherApps mLauncherApps;
+ protected LauncherApps mLauncherApps;
private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks
= new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
@@ -106,6 +106,10 @@
return mLauncherApps.isActivityEnabled(component, user.getUser());
}
+ public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
+ return false;
+ }
+
private static class WrappedCallback extends LauncherApps.Callback {
private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
@@ -134,6 +138,14 @@
mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user),
replacing);
}
+
+ public void onPackagesSuspended(String[] packageNames, UserHandle user) {
+ mCallback.onPackagesSuspended(packageNames, UserHandleCompat.fromUser(user));
+ }
+
+ public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
+ mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
+ }
}
}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
index 3ad5101..4aa667e 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -43,7 +43,6 @@
PackageInstallerCompatVL(Context context) {
mInstaller = context.getPackageManager().getPackageInstaller();
- LauncherAppState.setApplicationContext(context.getApplicationContext());
mCache = LauncherAppState.getInstance().getIconCache();
mWorker = new Handler(LauncherModel.getWorkerLooper());
@@ -107,7 +106,7 @@
@Override
public void onProgressChanged(int sessionId, float progress) {
SessionInfo session = mInstaller.getSessionInfo(sessionId);
- if (session != null) {
+ if (session != null && session.getAppPackageName() != null) {
sendUpdate(new PackageInstallInfo(session.getAppPackageName(),
STATUS_INSTALLING,
(int) (session.getProgress() * 100)));
@@ -124,7 +123,7 @@
private void pushSessionDisplayToLauncher(int sessionId) {
SessionInfo session = mInstaller.getSessionInfo(sessionId);
- if (session != null) {
+ if (session != null && session.getAppPackageName() != null) {
addSessionInfoToCahce(session, UserHandleCompat.myUserHandle());
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index 567022b..50af21b 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -49,7 +49,7 @@
}
}
- UserHandle getUser() {
+ public UserHandle getUser() {
return mUser;
}
@@ -93,4 +93,14 @@
intent.putExtra(name, mUser);
}
}
+
+ public static UserHandleCompat fromIntent(Intent intent) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (user != null) {
+ return UserHandleCompat.fromUser(user);
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index f708004..978f922 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -17,8 +17,6 @@
package com.android.launcher3.compat;
import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.os.Build;
import com.android.launcher3.Utilities;
@@ -34,7 +32,9 @@
public static UserManagerCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
- if (Utilities.ATLEAST_LOLLIPOP) {
+ if (Utilities.isNycOrAbove()) {
+ sInstance = new UserManagerCompatVN(context.getApplicationContext());
+ } else if (Utilities.ATLEAST_LOLLIPOP) {
sInstance = new UserManagerCompatVL(context.getApplicationContext());
} else if (Utilities.ATLEAST_JB_MR1) {
sInstance = new UserManagerCompatV17(context.getApplicationContext());
@@ -54,7 +54,7 @@
public abstract List<UserHandleCompat> getUserProfiles();
public abstract long getSerialNumberForUser(UserHandleCompat user);
public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
- public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user);
public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user);
public abstract long getUserCreationTime(UserHandleCompat user);
+ public abstract boolean isQuietModeEnabled(UserHandleCompat user);
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index 85aee57..a006efd 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -16,8 +16,6 @@
package com.android.launcher3.compat;
-import android.graphics.drawable.Drawable;
-
import java.util.ArrayList;
import java.util.List;
@@ -36,11 +34,6 @@
return UserHandleCompat.myUserHandle();
}
- public Drawable getBadgedDrawableForUser(Drawable unbadged,
- UserHandleCompat user) {
- return unbadged;
- }
-
public long getSerialNumberForUser(UserHandleCompat user) {
return 0;
}
@@ -57,4 +50,9 @@
@Override
public void enableAndResetCache() {
}
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandleCompat user) {
+ return false;
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index dc3ec3c..c53d702 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -21,11 +21,9 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.UserHandle;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.LongArrayMap;
@@ -87,11 +85,6 @@
}
@Override
- public Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user) {
- return mPm.getUserBadgedIcon(unbadged, user.getUser());
- }
-
- @Override
public CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user) {
if (user == null) {
return label;
@@ -104,8 +97,7 @@
if (Utilities.ATLEAST_MARSHMALLOW) {
return mUserManager.getUserCreationTime(user.getUser());
}
- SharedPreferences prefs = mContext.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ SharedPreferences prefs = Utilities.getPrefs(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/UserManagerCompatVN.java b/src/com/android/launcher3/compat/UserManagerCompatVN.java
new file mode 100644
index 0000000..ae41e68
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVN.java
@@ -0,0 +1,55 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N)
+public class UserManagerCompatVN extends UserManagerCompatVL {
+
+ private static final String TAG = "UserManagerCompatVN";
+
+ UserManagerCompatVN(Context context) {
+ super(context);
+ }
+
+ @Override
+ public boolean isQuietModeEnabled(UserHandleCompat user) {
+ if (user != null) {
+ try {
+ //TODO: Replace with proper API call once google3 SDK is updated.
+ Method isQuietModeEnabledMethod = UserManager.class.getMethod("isQuietModeEnabled",
+ UserHandle.class);
+ return (boolean) isQuietModeEnabledMethod.invoke(mUserManager, user.getUser());
+ } catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException
+ | InvocationTargetException e) {
+ Log.e(TAG, "Running on N without isQuietModeEnabled", e);
+ } catch (IllegalArgumentException e) {
+ // TODO remove this when API is fixed to not throw this
+ // when called on user that isn't a managed profile.
+ }
+ }
+ return false;
+ }
+}
+
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
new file mode 100644
index 0000000..5012708
--- /dev/null
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -0,0 +1,37 @@
+/*
+ * 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.config;
+
+/**
+ * Defines a set of flags used to control various launcher behaviors
+ * All the flags must be defined as
+ * public static boolean LAUNCHER3_FLAG_NAME = true/false;
+ *
+ * Use LAUNCHER3_ prefix for prevent namespace conflicts.
+ */
+public final class FeatureFlags {
+
+ private FeatureFlags() {}
+
+ public static boolean IS_DEV_BUILD = false;
+ public static boolean IS_ALPHA_BUILD = false;
+ public static boolean IS_RELEASE_BUILD = true;
+
+ // Custom flags go below this
+ public static boolean LAUNCHER3_ICON_NORMALIZATION = false;
+
+}
diff --git a/src/com/android/launcher3/logging/UserEventLogger.java b/src/com/android/launcher3/logging/UserEventLogger.java
new file mode 100644
index 0000000..d05b683
--- /dev/null
+++ b/src/com/android/launcher3/logging/UserEventLogger.java
@@ -0,0 +1,4 @@
+package com.android.launcher3.logging;
+
+public abstract class UserEventLogger {
+}
\ No newline at end of file
diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java
new file mode 100644
index 0000000..8117122
--- /dev/null
+++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java
@@ -0,0 +1,1030 @@
+package com.android.launcher3.model;
+
+import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.database.Cursor;
+import android.graphics.Point;
+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;
+import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherProvider;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.backup.nano.BackupProtos;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+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;
+
+/**
+ * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
+ * result of restoring from a larger device or device density change.
+ */
+public class GridSizeMigrationTask {
+
+ public static boolean ENABLED = Utilities.isNycOrAbove();
+
+ private static final String TAG = "GridSizeMigrationTask";
+ private static final boolean DEBUG = true;
+
+ private static final String KEY_MIGRATION_SRC_WORKSPACE_SIZE = "migration_src_workspace_size";
+ private static final String KEY_MIGRATION_SRC_HOTSEAT_SIZE = "migration_src_hotseat_size";
+
+ // Set of entries indicating minimum size a widget can be resized to. This is used during
+ // restore in case the widget has not been installed yet.
+ private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size";
+
+ // These are carefully selected weights for various item types (Math.random?), to allow for
+ // the least absurd migration experience.
+ private static final float WT_SHORTCUT = 1;
+ private static final float WT_APPLICATION = 0.8f;
+ private static final float WT_WIDGET_MIN = 2;
+ private static final float WT_WIDGET_FACTOR = 0.6f;
+ private static final float WT_FOLDER_FACTOR = 0.5f;
+
+ private final Context mContext;
+ private final InvariantDeviceProfile mIdp;
+
+ private final HashMap<String, Point> mWidgetMinSize = new HashMap<>();
+ private final ContentValues mTempValues = new ContentValues();
+ private final ArrayList<Long> mEntryToRemove = new ArrayList<>();
+ private final ArrayList<ContentProviderOperation> mUpdateOperations = new ArrayList<>();
+ private final ArrayList<DbEntry> mCarryOver = new ArrayList<>();
+ private final HashSet<String> mValidPackages;
+
+ private final int mSrcX, mSrcY;
+ private final int mTrgX, mTrgY;
+ private final boolean mShouldRemoveX, mShouldRemoveY;
+
+ private final int mSrcHotseatSize;
+ private final int mSrcAllAppsRank;
+ private final int mDestHotseatSize;
+ private final int mDestAllAppsRank;
+
+ protected GridSizeMigrationTask(Context context, InvariantDeviceProfile idp,
+ HashSet<String> validPackages, HashMap<String, Point> widgetMinSize,
+ Point sourceSize, Point targetSize) {
+ mContext = context;
+ mValidPackages = validPackages;
+ mWidgetMinSize.putAll(widgetMinSize);
+ mIdp = idp;
+
+ mSrcX = sourceSize.x;
+ mSrcY = sourceSize.y;
+
+ mTrgX = targetSize.x;
+ mTrgY = targetSize.y;
+
+ mShouldRemoveX = mTrgX < mSrcX;
+ mShouldRemoveY = mTrgY < mSrcY;
+
+ // Non-used variables
+ mSrcHotseatSize = mSrcAllAppsRank = mDestHotseatSize = mDestAllAppsRank = -1;
+ }
+
+ protected GridSizeMigrationTask(Context context,
+ InvariantDeviceProfile idp, HashSet<String> validPackages,
+ int srcHotseatSize, int srcAllAppsRank,
+ int destHotseatSize, int destAllAppsRank) {
+ mContext = context;
+ mIdp = idp;
+ mValidPackages = validPackages;
+
+ mSrcHotseatSize = srcHotseatSize;
+ mSrcAllAppsRank = srcAllAppsRank;
+
+ mDestHotseatSize = destHotseatSize;
+ mDestAllAppsRank = destAllAppsRank;
+
+ // Non-used variables
+ mSrcX = mSrcY = mTrgX = mTrgY = -1;
+ mShouldRemoveX = mShouldRemoveY = false;
+ }
+
+ /**
+ * Applied all the pending DB operations
+ * @return true if any DB operation was commited.
+ */
+ private boolean applyOperations() throws Exception {
+ // Update items
+ if (!mUpdateOperations.isEmpty()) {
+ mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
+ }
+
+ if (!mEntryToRemove.isEmpty()) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove));
+ }
+ mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
+ Utilities.createDbSelectionQuery(
+ LauncherSettings.Favorites._ID, mEntryToRemove), null);
+ }
+
+ return !mUpdateOperations.isEmpty() || !mEntryToRemove.isEmpty();
+ }
+
+ /**
+ * To migrate hotseat, we load all the entries in order (LTR or RTL) and arrange them
+ * in the order in the new hotseat while keeping an empty space for all-apps. If the number of
+ * entries is more than what can fit in the new hotseat, we drop the entries with least weight.
+ * For weight calculation {@see #WT_SHORTCUT}, {@see #WT_APPLICATION}
+ * & {@see #WT_FOLDER_FACTOR}.
+ * @return true if any DB change was made
+ */
+ protected boolean migrateHotseat() throws Exception {
+ ArrayList<DbEntry> items = loadHotseatEntries();
+
+ int requiredCount = mDestHotseatSize - 1;
+
+ while (items.size() > requiredCount) {
+ // Pick the center item by default.
+ DbEntry toRemove = items.get(items.size() / 2);
+
+ // Find the item with least weight.
+ for (DbEntry entry : items) {
+ if (entry.weight < toRemove.weight) {
+ toRemove = entry;
+ }
+ }
+
+ mEntryToRemove.add(toRemove.id);
+ items.remove(toRemove);
+ }
+
+ // Update screen IDS
+ int newScreenId = 0;
+ for (DbEntry entry : items) {
+ if (entry.screenId != newScreenId) {
+ entry.screenId = newScreenId;
+
+ // These values does not affect the item position, but we should set them
+ // to something other than -1.
+ entry.cellX = newScreenId;
+ entry.cellY = 0;
+
+ update(entry);
+ }
+
+ newScreenId++;
+ if (newScreenId == mDestAllAppsRank) {
+ newScreenId++;
+ }
+ }
+
+ return applyOperations();
+ }
+
+ /**
+ * @return true if any DB change was made
+ */
+ protected boolean migrateWorkspace() throws Exception {
+ ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
+ if (allScreens.isEmpty()) {
+ throw new Exception("Unable to get workspace screens");
+ }
+
+ for (long screenId : allScreens) {
+ if (DEBUG) {
+ Log.d(TAG, "Migrating " + screenId);
+ }
+ migrateScreen(screenId);
+ }
+
+ if (!mCarryOver.isEmpty()) {
+ LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
+ for (DbEntry e : mCarryOver) {
+ itemMap.put(e.id, e);
+ }
+
+ do {
+ // Some items are still remaining. Try adding a few new screens.
+
+ // At every iteration, make sure that at least one item is removed from
+ // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
+ // break the loop and abort migration by throwing an exception.
+ OptimalPlacementSolution placement = new OptimalPlacementSolution(
+ new boolean[mTrgX][mTrgY], deepCopy(mCarryOver), true);
+ placement.find();
+ if (placement.finalPlacedItems.size() > 0) {
+ long newScreenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+ allScreens.add(newScreenId);
+ for (DbEntry item : placement.finalPlacedItems) {
+ if (!mCarryOver.remove(itemMap.get(item.id))) {
+ throw new Exception("Unable to find matching items");
+ }
+ item.screenId = newScreenId;
+ update(item);
+ }
+ } else {
+ throw new Exception("None of the items can be placed on an empty screen");
+ }
+
+ } while (!mCarryOver.isEmpty());
+
+ // Update screens
+ final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
+ mUpdateOperations.add(ContentProviderOperation.newDelete(uri).build());
+ int count = allScreens.size();
+ for (int i = 0; i < count; i++) {
+ ContentValues v = new ContentValues();
+ long screenId = allScreens.get(i);
+ v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+ v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+ mUpdateOperations.add(ContentProviderOperation.newInsert(uri).withValues(v).build());
+ }
+ }
+ return applyOperations();
+ }
+
+ /**
+ * Migrate a particular screen id.
+ * Strategy:
+ * 1) For all possible combinations of row and column, pick the one which causes the least
+ * data loss: {@link #tryRemove(int, int, ArrayList, float[])}
+ * 2) Maintain a list of all lost items before this screen, and add any new item lost from
+ * this screen to that list as well.
+ * 3) If all those items from the above list can be placed on this screen, place them
+ * (otherwise they are placed on a new screen).
+ */
+ private void migrateScreen(long screenId) {
+ ArrayList<DbEntry> items = loadWorkspaceEntries(screenId);
+
+ int removedCol = Integer.MAX_VALUE;
+ int removedRow = Integer.MAX_VALUE;
+
+ // removeWt represents the cost function for loss of items during migration, and moveWt
+ // represents the cost function for repositioning the items. moveWt is only considered if
+ // removeWt is same for two different configurations.
+ // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
+ // cost.
+ float removeWt = Float.MAX_VALUE;
+ float moveWt = Float.MAX_VALUE;
+ float[] outLoss = new float[2];
+ ArrayList<DbEntry> finalItems = null;
+
+ // Try removing all possible combinations
+ for (int x = 0; x < mSrcX; x++) {
+ for (int y = 0; y < mSrcY; y++) {
+ // Use a deep copy when trying out a particular combination as it can change
+ // the underlying object.
+ ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss);
+
+ if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
+ removeWt = outLoss[0];
+ moveWt = outLoss[1];
+ removedCol = mShouldRemoveX ? x : removedCol;
+ removedRow = mShouldRemoveY ? y : removedRow;
+ finalItems = itemsOnScreen;
+ }
+
+ // No need to loop over all rows, if a row removal is not needed.
+ if (!mShouldRemoveY) {
+ break;
+ }
+ }
+
+ if (!mShouldRemoveX) {
+ break;
+ }
+ }
+
+ if (DEBUG) {
+ Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
+ removedRow, removedCol, screenId));
+ }
+
+ LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
+ for (DbEntry e : deepCopy(items)) {
+ itemMap.put(e.id, e);
+ }
+
+ for (DbEntry item : finalItems) {
+ DbEntry org = itemMap.get(item.id);
+ itemMap.remove(item.id);
+
+ // Check if update is required
+ if (!item.columnsSame(org)) {
+ update(item);
+ }
+ }
+
+ // The remaining items in {@link #itemMap} are those which didn't get placed.
+ for (DbEntry item : itemMap) {
+ mCarryOver.add(item);
+ }
+
+ if (!mCarryOver.isEmpty() && removeWt == 0) {
+ // No new items were removed in this step. Try placing all the items on this screen.
+ boolean[][] occupied = new boolean[mTrgX][mTrgY];
+ for (DbEntry item : finalItems) {
+ markCells(occupied, item, true);
+ }
+
+ OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
+ deepCopy(mCarryOver), true);
+ placement.find();
+ if (placement.lowestWeightLoss == 0) {
+ // All items got placed
+
+ for (DbEntry item : placement.finalPlacedItems) {
+ item.screenId = screenId;
+ update(item);
+ }
+
+ mCarryOver.clear();
+ }
+ }
+ }
+
+ /**
+ * Updates an item in the DB.
+ */
+ private void update(DbEntry item) {
+ mTempValues.clear();
+ item.addToContentValues(mTempValues);
+ mUpdateOperations.add(ContentProviderOperation
+ .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
+ .withValues(mTempValues).build());
+ }
+
+ /**
+ * Tries the remove the provided row and column.
+ * @param items all the items on the screen under operation
+ * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
+ * with the overall item movement.
+ */
+ private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
+ float[] outLoss) {
+ boolean[][] occupied = new boolean[mTrgX][mTrgY];
+
+ col = mShouldRemoveX ? col : Integer.MAX_VALUE;
+ row = mShouldRemoveY ? row : Integer.MAX_VALUE;
+
+ ArrayList<DbEntry> finalItems = new ArrayList<>();
+ ArrayList<DbEntry> removedItems = new ArrayList<>();
+
+ for (DbEntry item : items) {
+ if ((item.cellX <= col && (item.spanX + item.cellX) > col)
+ || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
+ removedItems.add(item);
+ if (item.cellX >= col) item.cellX --;
+ if (item.cellY >= row) item.cellY --;
+ } else {
+ if (item.cellX > col) item.cellX --;
+ if (item.cellY > row) item.cellY --;
+ finalItems.add(item);
+ markCells(occupied, item, true);
+ }
+ }
+
+ OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
+ placement.find();
+ finalItems.addAll(placement.finalPlacedItems);
+ outLoss[0] = placement.lowestWeightLoss;
+ outLoss[1] = placement.lowestMoveCost;
+ return finalItems;
+ }
+
+ private void markCells(boolean[][] occupied, DbEntry item, boolean val) {
+ for (int i = item.cellX; i < (item.cellX + item.spanX); i++) {
+ for (int j = item.cellY; j < (item.cellY + item.spanY); j++) {
+ occupied[i][j] = val;
+ }
+ }
+ }
+
+ private boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) {
+ if (x + w > mTrgX) return false;
+ if (y + h > mTrgY) return false;
+
+ for (int i = 0; i < w; i++) {
+ for (int j = 0; j < h; j++) {
+ if (occupied[i + x][j + y]) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private class OptimalPlacementSolution {
+ private final ArrayList<DbEntry> itemsToPlace;
+ private final boolean[][] occupied;
+
+ // If set to true, item movement are not considered in move cost, leading to a more
+ // linear placement.
+ private final boolean ignoreMove;
+
+ float lowestWeightLoss = Float.MAX_VALUE;
+ float lowestMoveCost = Float.MAX_VALUE;
+ ArrayList<DbEntry> finalPlacedItems;
+
+ public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace) {
+ this(occupied, itemsToPlace, false);
+ }
+
+ public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace,
+ boolean ignoreMove) {
+ this.occupied = occupied;
+ this.itemsToPlace = itemsToPlace;
+ this.ignoreMove = ignoreMove;
+
+ // Sort the items such that larger widgets appear first followed by 1x1 items
+ Collections.sort(this.itemsToPlace);
+ }
+
+ public void find() {
+ find(0, 0, 0, new ArrayList<DbEntry>());
+ }
+
+ /**
+ * Recursively finds a placement for the provided items.
+ * @param index the position in {@link #itemsToPlace} to start looking at.
+ * @param weightLoss total weight loss upto this point
+ * @param moveCost total move cost upto this point
+ * @param itemsPlaced all the items already placed upto this point
+ */
+ public void find(int index, float weightLoss, float moveCost,
+ ArrayList<DbEntry> itemsPlaced) {
+ if ((weightLoss >= lowestWeightLoss) ||
+ ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
+ // Abort, as we already have a better solution.
+ return;
+
+ } else if (index >= itemsToPlace.size()) {
+ // End loop.
+ lowestWeightLoss = weightLoss;
+ lowestMoveCost = moveCost;
+
+ // Keep a deep copy of current configuration as it can change during recursion.
+ finalPlacedItems = deepCopy(itemsPlaced);
+ return;
+ }
+
+ DbEntry me = itemsToPlace.get(index);
+ int myX = me.cellX;
+ int myY = me.cellY;
+
+ // List of items to pass over if this item was placed.
+ ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
+ itemsIncludingMe.addAll(itemsPlaced);
+ itemsIncludingMe.add(me);
+
+ if (me.spanX > 1 || me.spanY > 1) {
+ // If the current item is a widget (and it greater than 1x1), try to place it at
+ // all possible positions. This is because a widget placed at one position can
+ // affect the placement of a different widget.
+ int myW = me.spanX;
+ int myH = me.spanY;
+
+ for (int y = 0; y < mTrgY; y++) {
+ for (int x = 0; x < mTrgX; x++) {
+ float newMoveCost = moveCost;
+ if (x != myX) {
+ me.cellX = x;
+ newMoveCost ++;
+ }
+ if (y != myY) {
+ me.cellY = y;
+ newMoveCost ++;
+ }
+ if (ignoreMove) {
+ newMoveCost = moveCost;
+ }
+
+ if (isVacant(occupied, x, y, myW, myH)) {
+ // place at this position and continue search.
+ markCells(occupied, me, true);
+ find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
+ markCells(occupied, me, false);
+ }
+
+ // Try resizing horizontally
+ if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) {
+ me.spanX --;
+ markCells(occupied, me, true);
+ // 1 extra move cost
+ find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
+ markCells(occupied, me, false);
+ me.spanX ++;
+ }
+
+ // Try resizing vertically
+ if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) {
+ me.spanY --;
+ markCells(occupied, me, true);
+ // 1 extra move cost
+ find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
+ markCells(occupied, me, false);
+ me.spanY ++;
+ }
+
+ // Try resizing horizontally & vertically
+ if (myH > me.minSpanY && myW > me.minSpanX &&
+ isVacant(occupied, x, y, myW - 1, myH - 1)) {
+ me.spanX --;
+ me.spanY --;
+ markCells(occupied, me, true);
+ // 2 extra move cost
+ find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
+ markCells(occupied, me, false);
+ me.spanX ++;
+ me.spanY ++;
+ }
+ me.cellX = myX;
+ me.cellY = myY;
+ }
+ }
+
+ // Finally also try a solution when this item is not included. Trying it in the end
+ // causes it to get skipped in most cases due to higher weight loss, and prevents
+ // unnecessary deep copies of various configurations.
+ find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
+ } else {
+ // Since this is a 1x1 item and all the following items are also 1x1, just place
+ // it at 'the most appropriate position' and hope for the best.
+ // The most appropriate position: one with lease straight line distance
+ int newDistance = Integer.MAX_VALUE;
+ int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
+
+ for (int y = 0; y < mTrgY; y++) {
+ for (int x = 0; x < mTrgX; x++) {
+ if (!occupied[x][y]) {
+ int dist = ignoreMove ? 0 :
+ ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
+ if (dist < newDistance) {
+ newX = x;
+ newY = y;
+ newDistance = dist;
+ }
+ }
+ }
+ }
+
+ if (newX < mTrgX && newY < mTrgY) {
+ float newMoveCost = moveCost;
+ if (newX != myX) {
+ me.cellX = newX;
+ newMoveCost ++;
+ }
+ if (newY != myY) {
+ me.cellY = newY;
+ newMoveCost ++;
+ }
+ if (ignoreMove) {
+ newMoveCost = moveCost;
+ }
+ markCells(occupied, me, true);
+ find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
+ markCells(occupied, me, false);
+ me.cellX = myX;
+ me.cellY = myY;
+
+ // Try to find a solution without this item, only if
+ // 1) there was at least one space, i.e., we were able to place this item
+ // 2) if the next item has the same weight (all items are already sorted), as
+ // if it has lower weight, that solution will automatically get discarded.
+ // 3) ignoreMove false otherwise, move cost is ignored and the weight will
+ // anyway be same.
+ if (index + 1 < itemsToPlace.size()
+ && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
+ find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
+ }
+ } else {
+ // No more space. Jump to the end.
+ for (int i = index + 1; i < itemsToPlace.size(); i++) {
+ weightLoss += itemsToPlace.get(i).weight;
+ }
+ find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
+ }
+ }
+ }
+ }
+
+ private ArrayList<DbEntry> loadHotseatEntries() {
+ Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{
+ Favorites._ID, // 0
+ Favorites.ITEM_TYPE, // 1
+ Favorites.INTENT, // 2
+ Favorites.SCREEN}, // 3
+ Favorites.CONTAINER + " = " + Favorites.CONTAINER_HOTSEAT, null, null, null);
+
+ final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
+ final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
+ final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
+ final int indexScreen = c.getColumnIndexOrThrow(Favorites.SCREEN);
+
+ ArrayList<DbEntry> entries = new ArrayList<>();
+ while (c.moveToNext()) {
+ DbEntry entry = new DbEntry();
+ entry.id = c.getLong(indexId);
+ entry.itemType = c.getInt(indexItemType);
+ entry.screenId = c.getLong(indexScreen);
+
+ if (entry.screenId >= mSrcHotseatSize) {
+ mEntryToRemove.add(entry.id);
+ continue;
+ }
+
+ try {
+ // calculate weight
+ switch (entry.itemType) {
+ case Favorites.ITEM_TYPE_SHORTCUT:
+ case Favorites.ITEM_TYPE_APPLICATION: {
+ verifyIntent(c.getString(indexIntent));
+ entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
+ ? WT_SHORTCUT : WT_APPLICATION;
+ break;
+ }
+ case Favorites.ITEM_TYPE_FOLDER: {
+ int total = getFolderItemsCount(entry.id);
+ if (total == 0) {
+ throw new Exception("Folder is empty");
+ }
+ entry.weight = WT_FOLDER_FACTOR * total;
+ break;
+ }
+ default:
+ throw new Exception("Invalid item type");
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing item " + entry.id, e);
+ }
+ mEntryToRemove.add(entry.id);
+ continue;
+ }
+ entries.add(entry);
+ }
+ c.close();
+ return entries;
+ }
+
+
+ /**
+ * Loads entries for a particular screen id.
+ */
+ private ArrayList<DbEntry> loadWorkspaceEntries(long screen) {
+ Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{
+ Favorites._ID, // 0
+ Favorites.ITEM_TYPE, // 1
+ Favorites.CELLX, // 2
+ Favorites.CELLY, // 3
+ Favorites.SPANX, // 4
+ Favorites.SPANY, // 5
+ Favorites.INTENT, // 6
+ Favorites.APPWIDGET_PROVIDER, // 7
+ Favorites.APPWIDGET_ID}, // 8
+ Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
+ + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
+
+ final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
+ final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
+ final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
+ final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
+ final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
+ final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
+ final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
+ final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
+ final int indexAppWidgetId = c.getColumnIndexOrThrow(Favorites.APPWIDGET_ID);
+
+ ArrayList<DbEntry> entries = new ArrayList<>();
+ while (c.moveToNext()) {
+ DbEntry entry = new DbEntry();
+ entry.id = c.getLong(indexId);
+ entry.itemType = c.getInt(indexItemType);
+ entry.cellX = c.getInt(indexCellX);
+ entry.cellY = c.getInt(indexCellY);
+ entry.spanX = c.getInt(indexSpanX);
+ entry.spanY = c.getInt(indexSpanY);
+ entry.screenId = screen;
+
+ try {
+ // calculate weight
+ switch (entry.itemType) {
+ case Favorites.ITEM_TYPE_SHORTCUT:
+ case Favorites.ITEM_TYPE_APPLICATION: {
+ verifyIntent(c.getString(indexIntent));
+ entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
+ ? WT_SHORTCUT : WT_APPLICATION;
+ break;
+ }
+ case Favorites.ITEM_TYPE_APPWIDGET: {
+ String provider = c.getString(indexAppWidgetProvider);
+ ComponentName cn = ComponentName.unflattenFromString(provider);
+ verifyPackage(cn.getPackageName());
+ entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
+ * entry.spanX * entry.spanY);
+
+ int widgetId = c.getInt(indexAppWidgetId);
+ LauncherAppWidgetProviderInfo pInfo = AppWidgetManagerCompat.getInstance(
+ mContext).getLauncherAppWidgetInfo(widgetId);
+ Point spans = pInfo == null ?
+ mWidgetMinSize.get(provider) : 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;
+ } else {
+ // Assume that the widget be resized down to 2x2
+ entry.minSpanX = entry.minSpanY = 2;
+ }
+
+ if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
+ throw new Exception("Widget can't be resized down to fit the grid");
+ }
+ break;
+ }
+ case Favorites.ITEM_TYPE_FOLDER: {
+ int total = getFolderItemsCount(entry.id);
+ if (total == 0) {
+ throw new Exception("Folder is empty");
+ }
+ entry.weight = WT_FOLDER_FACTOR * total;
+ break;
+ }
+ default:
+ throw new Exception("Invalid item type");
+ }
+ } catch (Exception e) {
+ if (DEBUG) {
+ Log.d(TAG, "Removing item " + entry.id, e);
+ }
+ mEntryToRemove.add(entry.id);
+ continue;
+ }
+ entries.add(entry);
+ }
+ c.close();
+ return entries;
+ }
+
+ /**
+ * @return the number of valid items in the folder.
+ */
+ private int getFolderItemsCount(long folderId) {
+ Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{Favorites._ID, Favorites.INTENT},
+ Favorites.CONTAINER + " = " + folderId, null, null, null);
+
+ int total = 0;
+ while (c.moveToNext()) {
+ try {
+ verifyIntent(c.getString(1));
+ total++;
+ } catch (Exception e) {
+ mEntryToRemove.add(c.getLong(0));
+ }
+ }
+ c.close();
+ return total;
+ }
+
+ /**
+ * Verifies if the intent should be restored.
+ */
+ private void verifyIntent(String intentStr) throws Exception {
+ Intent intent = Intent.parseUri(intentStr, 0);
+ if (intent.getComponent() != null) {
+ verifyPackage(intent.getComponent().getPackageName());
+ } else if (intent.getPackage() != null) {
+ // Only verify package if the component was null.
+ verifyPackage(intent.getPackage());
+ }
+ }
+
+ /**
+ * Verifies if the package should be restored
+ */
+ private void verifyPackage(String packageName) throws Exception {
+ if (!mValidPackages.contains(packageName)) {
+ throw new Exception("Package not available");
+ }
+ }
+
+ private static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
+
+ public float weight;
+
+ public DbEntry() { }
+
+ public DbEntry copy() {
+ DbEntry entry = new DbEntry();
+ entry.copyFrom(this);
+ entry.weight = weight;
+ entry.minSpanX = minSpanX;
+ entry.minSpanY = minSpanY;
+ return entry;
+ }
+
+ /**
+ * Comparator such that larger widgets come first, followed by all 1x1 items
+ * based on their weights.
+ */
+ @Override
+ public int compareTo(DbEntry another) {
+ if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+ if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+ return another.spanY * another.spanX - spanX * spanY;
+ } else {
+ return -1;
+ }
+ } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
+ return 1;
+ } else {
+ // Place higher weight before lower weight.
+ return Float.compare(another.weight, weight);
+ }
+ }
+
+ public boolean columnsSame(DbEntry org) {
+ return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
+ org.spanY == spanY && org.screenId == screenId;
+ }
+
+ public void addToContentValues(ContentValues values) {
+ values.put(LauncherSettings.Favorites.SCREEN, screenId);
+ values.put(LauncherSettings.Favorites.CELLX, cellX);
+ values.put(LauncherSettings.Favorites.CELLY, cellY);
+ values.put(LauncherSettings.Favorites.SPANX, spanX);
+ values.put(LauncherSettings.Favorites.SPANY, spanY);
+ }
+ }
+
+ private static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
+ ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
+ for (DbEntry e : src) {
+ dup.add(e.copy());
+ }
+ return dup;
+ }
+
+ private static Point parsePoint(String point) {
+ String[] split = point.split(",");
+ return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
+ }
+
+ private static String getPointString(int x, int y) {
+ return String.format(Locale.ENGLISH, "%d,%d", x, y);
+ }
+
+ public static void markForMigration(
+ Context context, HashSet<String> widgets, BackupProtos.DeviceProfieData srcProfile) {
+ Utilities.getPrefs(context).edit()
+ .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE,
+ getPointString((int) srcProfile.desktopCols, (int) srcProfile.desktopRows))
+ .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE,
+ getPointString((int) srcProfile.hotseatCount, srcProfile.allappsRank))
+ .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets)
+ .apply();
+ }
+
+ /**
+ * Migrates the workspace and hotseat in case their sizes changed.
+ * @return false if the migration failed.
+ */
+ public static boolean migrateGridIfNeeded(Context context) {
+ SharedPreferences prefs = Utilities.getPrefs(context);
+ InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+
+ String gridSizeString = getPointString(idp.numColumns, idp.numRows);
+ String hotseatSizeString = getPointString(idp.numHotseatIcons, idp.hotseatAllAppsRank);
+
+ if (gridSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, "")) &&
+ hotseatSizeString.equals(prefs.getString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, ""))) {
+ // Skip if workspace and hotseat sizes have not changed.
+ return true;
+ }
+
+ long migrationStartTime = System.currentTimeMillis();
+ try {
+ boolean dbChanged = false;
+
+ // Initialize list of valid packages. This contain all the packages which are already on
+ // the device and packages which are being installed. Any item which doesn't belong to
+ // 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<>();
+ for (PackageInfo info : context.getPackageManager().getInstalledPackages(0)) {
+ validPackages.add(info.packageName);
+ }
+ validPackages.addAll(PackageInstallerCompat.getInstance(context)
+ .updateAndGetActiveSessionCache().keySet());
+
+ // Hotseat
+ Point srcHotseatSize = parsePoint(prefs.getString(
+ KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString));
+ if (srcHotseatSize.x != idp.numHotseatIcons ||
+ srcHotseatSize.y != idp.hotseatAllAppsRank) {
+ // Migrate hotseat.
+
+ dbChanged = new GridSizeMigrationTask(context,
+ LauncherAppState.getInstance().getInvariantDeviceProfile(),
+ validPackages,
+ srcHotseatSize.x, srcHotseatSize.y,
+ idp.numHotseatIcons, idp.hotseatAllAppsRank).migrateHotseat();
+ }
+
+ // Grid size
+ Point targetSize = new Point(idp.numColumns, idp.numRows);
+ Point sourceSize = parsePoint(prefs.getString(
+ KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString));
+
+ if (!targetSize.equals(sourceSize)) {
+
+ // The following list defines all possible grid sizes (and intermediate steps
+ // during migration). Note that at each step, dx <= 1 && dy <= 1. Any grid size
+ // which is not in this list is not migrated.
+ // Note that the InvariantDeviceProfile defines (rows, cols) but the Points
+ // specified here are defined as (cols, rows).
+ ArrayList<Point> gridSizeSteps = new ArrayList<>();
+ gridSizeSteps.add(new Point(3, 2));
+ gridSizeSteps.add(new Point(3, 3));
+ gridSizeSteps.add(new Point(4, 3));
+ gridSizeSteps.add(new Point(4, 4));
+ gridSizeSteps.add(new Point(5, 5));
+ gridSizeSteps.add(new Point(6, 5));
+ gridSizeSteps.add(new Point(6, 6));
+ gridSizeSteps.add(new Point(7, 7));
+
+ int sourceSizeIndex = gridSizeSteps.indexOf(sourceSize);
+ int targetSizeIndex = gridSizeSteps.indexOf(targetSize);
+
+ if (sourceSizeIndex <= -1 || targetSizeIndex <= -1) {
+ throw new Exception("Unable to migrate grid size from " + sourceSize
+ + " to " + targetSize);
+ }
+
+ // Min widget sizes
+ HashMap<String, Point> widgetMinSize = new HashMap<>();
+ for (String s : Utilities.getPrefs(context).getStringSet(KEY_MIGRATION_WIDGET_MINSIZE,
+ Collections.<String>emptySet())) {
+ String[] parts = s.split("#");
+ widgetMinSize.put(parts[0], parsePoint(parts[1]));
+ }
+
+ // Migrate the workspace grid, step by step.
+ while (targetSizeIndex < sourceSizeIndex ) {
+ // We only need to migrate the grid if source size is greater
+ // than the target size.
+ Point stepTargetSize = gridSizeSteps.get(sourceSizeIndex - 1);
+ Point stepSourceSize = gridSizeSteps.get(sourceSizeIndex);
+
+ if (new GridSizeMigrationTask(context,
+ LauncherAppState.getInstance().getInvariantDeviceProfile(),
+ validPackages, widgetMinSize,
+ stepSourceSize, stepTargetSize).migrateWorkspace()) {
+ dbChanged = true;
+ }
+ sourceSizeIndex--;
+ }
+ }
+
+ if (dbChanged) {
+ // Make sure we haven't removed everything.
+ final Cursor c = context.getContentResolver().query(
+ LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
+ boolean hasData = c.moveToNext();
+ c.close();
+ if (!hasData) {
+ throw new Exception("Removed every thing during grid resize");
+ }
+ }
+
+ return true;
+ } catch (Exception e) {
+ Log.e(TAG, "Error during grid migration", e);
+
+ return false;
+ } finally {
+ Log.v(TAG, "Workspace migration completed in "
+ + (System.currentTimeMillis() - migrationStartTime));
+
+ // Save current configuration, so that the migration does not run again.
+ prefs.edit()
+ .putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
+ .putString(KEY_MIGRATION_SRC_HOTSEAT_SIZE, hotseatSizeString)
+ .remove(KEY_MIGRATION_WIDGET_MINSIZE)
+ .apply();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java
deleted file mode 100644
index 6a529f6..0000000
--- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java
+++ /dev/null
@@ -1,767 +0,0 @@
-package com.android.launcher3.model;
-
-import android.content.ComponentName;
-import android.content.ContentProviderOperation;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.PackageInfo;
-import android.database.Cursor;
-import android.graphics.Point;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.LauncherAppState;
-import com.android.launcher3.LauncherAppWidgetProviderInfo;
-import com.android.launcher3.LauncherModel;
-import com.android.launcher3.LauncherProvider;
-import com.android.launcher3.LauncherSettings;
-import com.android.launcher3.LauncherSettings.Favorites;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.compat.PackageInstallerCompat;
-import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.util.LongArrayMap;
-import com.android.launcher3.util.Thunk;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-
-/**
- * This class takes care of shrinking the workspace (by maximum of one row and one column), as a
- * result of restoring from a larger device.
- */
-public class MigrateFromRestoreTask {
-
- public static boolean ENABLED = false;
-
- private static final String TAG = "MigrateFromRestoreTask";
- private static final boolean DEBUG = true;
-
- private static final String KEY_MIGRATION_SOURCE_SIZE = "migration_restore_src_size";
- private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size";
-
- // These are carefully selected weights for various item types (Math.random?), to allow for
- // the lease absurd migration experience.
- private static final float WT_SHORTCUT = 1;
- private static final float WT_APPLICATION = 0.8f;
- private static final float WT_WIDGET_MIN = 2;
- private static final float WT_WIDGET_FACTOR = 0.6f;
- private static final float WT_FOLDER_FACTOR = 0.5f;
-
- private final Context mContext;
- private final ContentValues mTempValues = new ContentValues();
- private final HashMap<String, Point> mWidgetMinSize;
- private final InvariantDeviceProfile mIdp;
-
- private HashSet<String> mValidPackages;
- public ArrayList<Long> mEntryToRemove;
- private ArrayList<ContentProviderOperation> mUpdateOperations;
-
- private ArrayList<DbEntry> mCarryOver;
-
- private final int mSrcX, mSrcY;
- @Thunk final int mTrgX, mTrgY;
- private final boolean mShouldRemoveX, mShouldRemoveY;
-
- public MigrateFromRestoreTask(Context context) {
- mContext = context;
-
- SharedPreferences prefs = prefs(context);
- Point sourceSize = parsePoint(prefs.getString(KEY_MIGRATION_SOURCE_SIZE, ""));
- mSrcX = sourceSize.x;
- mSrcY = sourceSize.y;
-
- mWidgetMinSize = new HashMap<String, Point>();
- for (String s : prefs.getStringSet(KEY_MIGRATION_WIDGET_MINSIZE,
- Collections.<String>emptySet())) {
- String[] parts = s.split("#");
- mWidgetMinSize.put(parts[0], parsePoint(parts[1]));
- }
-
- mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile();
- mTrgX = mIdp.numColumns;
- mTrgY = mIdp.numRows;
- mShouldRemoveX = mTrgX < mSrcX;
- mShouldRemoveY = mTrgY < mSrcY;
- }
-
- public void execute() throws Exception {
- mEntryToRemove = new ArrayList<>();
- mCarryOver = new ArrayList<>();
- mUpdateOperations = new ArrayList<>();
-
- // Initialize list of valid packages. This contain all the packages which are already on
- // the device and packages which are being installed. Any item which doesn't belong to
- // 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.
- mValidPackages = new HashSet<>();
- for (PackageInfo info : mContext.getPackageManager().getInstalledPackages(0)) {
- mValidPackages.add(info.packageName);
- }
- mValidPackages.addAll(PackageInstallerCompat.getInstance(mContext)
- .updateAndGetActiveSessionCache().keySet());
-
- ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(mContext);
- if (allScreens.isEmpty()) {
- throw new Exception("Unable to get workspace screens");
- }
-
- for (long screenId : allScreens) {
- if (DEBUG) {
- Log.d(TAG, "Migrating " + screenId);
- }
- migrateScreen(screenId);
- }
-
- if (!mCarryOver.isEmpty()) {
- LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
- for (DbEntry e : mCarryOver) {
- itemMap.put(e.id, e);
- }
-
- do {
- // Some items are still remaining. Try adding a few new screens.
-
- // At every iteration, make sure that at least one item is removed from
- // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed,
- // break the loop and abort migration by throwing an exception.
- OptimalPlacementSolution placement = new OptimalPlacementSolution(
- new boolean[mTrgX][mTrgY], deepCopy(mCarryOver), true);
- placement.find();
- if (placement.finalPlacedItems.size() > 0) {
- long newScreenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
- allScreens.add(newScreenId);
- for (DbEntry item : placement.finalPlacedItems) {
- if (!mCarryOver.remove(itemMap.get(item.id))) {
- throw new Exception("Unable to find matching items");
- }
- item.screenId = newScreenId;
- update(item);
- }
- } else {
- throw new Exception("None of the items can be placed on an empty screen");
- }
-
- } while (!mCarryOver.isEmpty());
-
-
- LauncherAppState.getInstance().getModel()
- .updateWorkspaceScreenOrder(mContext, allScreens);
- }
-
- // Update items
- mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations);
-
- if (!mEntryToRemove.isEmpty()) {
- if (DEBUG) {
- Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove));
- }
- mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI,
- Utilities.createDbSelectionQuery(
- LauncherSettings.Favorites._ID, mEntryToRemove), null);
- }
-
- // Make sure we haven't removed everything.
- final Cursor c = mContext.getContentResolver().query(
- LauncherSettings.Favorites.CONTENT_URI, null, null, null, null);
- boolean hasData = c.moveToNext();
- c.close();
- if (!hasData) {
- throw new Exception("Removed every thing during grid resize");
- }
- }
-
- /**
- * Migrate a particular screen id.
- * Strategy:
- * 1) For all possible combinations of row and column, pick the one which causes the least
- * data loss: {@link #tryRemove(int, int, ArrayList, float[])}
- * 2) Maintain a list of all lost items before this screen, and add any new item lost from
- * this screen to that list as well.
- * 3) If all those items from the above list can be placed on this screen, place them
- * (otherwise they are placed on a new screen).
- */
- private void migrateScreen(long screenId) {
- ArrayList<DbEntry> items = loadEntries(screenId);
-
- int removedCol = Integer.MAX_VALUE;
- int removedRow = Integer.MAX_VALUE;
-
- // removeWt represents the cost function for loss of items during migration, and moveWt
- // represents the cost function for repositioning the items. moveWt is only considered if
- // removeWt is same for two different configurations.
- // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least
- // cost.
- float removeWt = Float.MAX_VALUE;
- float moveWt = Float.MAX_VALUE;
- float[] outLoss = new float[2];
- ArrayList<DbEntry> finalItems = null;
-
- // Try removing all possible combinations
- for (int x = 0; x < mSrcX; x++) {
- for (int y = 0; y < mSrcY; y++) {
- // Use a deep copy when trying out a particular combination as it can change
- // the underlying object.
- ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss);
-
- if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) {
- removeWt = outLoss[0];
- moveWt = outLoss[1];
- removedCol = mShouldRemoveX ? x : removedCol;
- removedRow = mShouldRemoveY ? y : removedRow;
- finalItems = itemsOnScreen;
- }
-
- // No need to loop over all rows, if a row removal is not needed.
- if (!mShouldRemoveY) {
- break;
- }
- }
-
- if (!mShouldRemoveX) {
- break;
- }
- }
-
- if (DEBUG) {
- Log.d(TAG, String.format("Removing row %d, column %d on screen %d",
- removedRow, removedCol, screenId));
- }
-
- LongArrayMap<DbEntry> itemMap = new LongArrayMap<>();
- for (DbEntry e : deepCopy(items)) {
- itemMap.put(e.id, e);
- }
-
- for (DbEntry item : finalItems) {
- DbEntry org = itemMap.get(item.id);
- itemMap.remove(item.id);
-
- // Check if update is required
- if (!item.columnsSame(org)) {
- update(item);
- }
- }
-
- // The remaining items in {@link #itemMap} are those which didn't get placed.
- for (DbEntry item : itemMap) {
- mCarryOver.add(item);
- }
-
- if (!mCarryOver.isEmpty() && removeWt == 0) {
- // No new items were removed in this step. Try placing all the items on this screen.
- boolean[][] occupied = new boolean[mTrgX][mTrgY];
- for (DbEntry item : finalItems) {
- markCells(occupied, item, true);
- }
-
- OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied,
- deepCopy(mCarryOver), true);
- placement.find();
- if (placement.lowestWeightLoss == 0) {
- // All items got placed
-
- for (DbEntry item : placement.finalPlacedItems) {
- item.screenId = screenId;
- update(item);
- }
-
- mCarryOver.clear();
- }
- }
- }
-
- /**
- * Updates an item in the DB.
- */
- private void update(DbEntry item) {
- mTempValues.clear();
- item.addToContentValues(mTempValues);
- mUpdateOperations.add(ContentProviderOperation
- .newUpdate(LauncherSettings.Favorites.getContentUri(item.id))
- .withValues(mTempValues).build());
- }
-
- /**
- * Tries the remove the provided row and column.
- * @param items all the items on the screen under operation
- * @param outLoss array of size 2. The first entry is filled with weight loss, and the second
- * with the overall item movement.
- */
- private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items,
- float[] outLoss) {
- boolean[][] occupied = new boolean[mTrgX][mTrgY];
-
- col = mShouldRemoveX ? col : Integer.MAX_VALUE;
- row = mShouldRemoveY ? row : Integer.MAX_VALUE;
-
- ArrayList<DbEntry> finalItems = new ArrayList<>();
- ArrayList<DbEntry> removedItems = new ArrayList<>();
-
- for (DbEntry item : items) {
- if ((item.cellX <= col && (item.spanX + item.cellX) > col)
- || (item.cellY <= row && (item.spanY + item.cellY) > row)) {
- removedItems.add(item);
- if (item.cellX >= col) item.cellX --;
- if (item.cellY >= row) item.cellY --;
- } else {
- if (item.cellX > col) item.cellX --;
- if (item.cellY > row) item.cellY --;
- finalItems.add(item);
- markCells(occupied, item, true);
- }
- }
-
- OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems);
- placement.find();
- finalItems.addAll(placement.finalPlacedItems);
- outLoss[0] = placement.lowestWeightLoss;
- outLoss[1] = placement.lowestMoveCost;
- return finalItems;
- }
-
- @Thunk void markCells(boolean[][] occupied, DbEntry item, boolean val) {
- for (int i = item.cellX; i < (item.cellX + item.spanX); i++) {
- for (int j = item.cellY; j < (item.cellY + item.spanY); j++) {
- occupied[i][j] = val;
- }
- }
- }
-
- @Thunk boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) {
- if (x + w > mTrgX) return false;
- if (y + h > mTrgY) return false;
-
- for (int i = 0; i < w; i++) {
- for (int j = 0; j < h; j++) {
- if (occupied[i + x][j + y]) {
- return false;
- }
- }
- }
- return true;
- }
-
- private class OptimalPlacementSolution {
- private final ArrayList<DbEntry> itemsToPlace;
- private final boolean[][] occupied;
-
- // If set to true, item movement are not considered in move cost, leading to a more
- // linear placement.
- private final boolean ignoreMove;
-
- float lowestWeightLoss = Float.MAX_VALUE;
- float lowestMoveCost = Float.MAX_VALUE;
- ArrayList<DbEntry> finalPlacedItems;
-
- public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace) {
- this(occupied, itemsToPlace, false);
- }
-
- public OptimalPlacementSolution(boolean[][] occupied, ArrayList<DbEntry> itemsToPlace,
- boolean ignoreMove) {
- this.occupied = occupied;
- this.itemsToPlace = itemsToPlace;
- this.ignoreMove = ignoreMove;
-
- // Sort the items such that larger widgets appear first followed by 1x1 items
- Collections.sort(this.itemsToPlace);
- }
-
- public void find() {
- find(0, 0, 0, new ArrayList<DbEntry>());
- }
-
- /**
- * Recursively finds a placement for the provided items.
- * @param index the position in {@link #itemsToPlace} to start looking at.
- * @param weightLoss total weight loss upto this point
- * @param moveCost total move cost upto this point
- * @param itemsPlaced all the items already placed upto this point
- */
- public void find(int index, float weightLoss, float moveCost,
- ArrayList<DbEntry> itemsPlaced) {
- if ((weightLoss >= lowestWeightLoss) ||
- ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) {
- // Abort, as we already have a better solution.
- return;
-
- } else if (index >= itemsToPlace.size()) {
- // End loop.
- lowestWeightLoss = weightLoss;
- lowestMoveCost = moveCost;
-
- // Keep a deep copy of current configuration as it can change during recursion.
- finalPlacedItems = deepCopy(itemsPlaced);
- return;
- }
-
- DbEntry me = itemsToPlace.get(index);
- int myX = me.cellX;
- int myY = me.cellY;
-
- // List of items to pass over if this item was placed.
- ArrayList<DbEntry> itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1);
- itemsIncludingMe.addAll(itemsPlaced);
- itemsIncludingMe.add(me);
-
- if (me.spanX > 1 || me.spanY > 1) {
- // If the current item is a widget (and it greater than 1x1), try to place it at
- // all possible positions. This is because a widget placed at one position can
- // affect the placement of a different widget.
- int myW = me.spanX;
- int myH = me.spanY;
-
- for (int y = 0; y < mTrgY; y++) {
- for (int x = 0; x < mTrgX; x++) {
- float newMoveCost = moveCost;
- if (x != myX) {
- me.cellX = x;
- newMoveCost ++;
- }
- if (y != myY) {
- me.cellY = y;
- newMoveCost ++;
- }
- if (ignoreMove) {
- newMoveCost = moveCost;
- }
-
- if (isVacant(occupied, x, y, myW, myH)) {
- // place at this position and continue search.
- markCells(occupied, me, true);
- find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
- markCells(occupied, me, false);
- }
-
- // Try resizing horizontally
- if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) {
- me.spanX --;
- markCells(occupied, me, true);
- // 1 extra move cost
- find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
- markCells(occupied, me, false);
- me.spanX ++;
- }
-
- // Try resizing vertically
- if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) {
- me.spanY --;
- markCells(occupied, me, true);
- // 1 extra move cost
- find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe);
- markCells(occupied, me, false);
- me.spanY ++;
- }
-
- // Try resizing horizontally & vertically
- if (myH > me.minSpanY && myW > me.minSpanX &&
- isVacant(occupied, x, y, myW - 1, myH - 1)) {
- me.spanX --;
- me.spanY --;
- markCells(occupied, me, true);
- // 2 extra move cost
- find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe);
- markCells(occupied, me, false);
- me.spanX ++;
- me.spanY ++;
- }
- me.cellX = myX;
- me.cellY = myY;
- }
- }
-
- // Finally also try a solution when this item is not included. Trying it in the end
- // causes it to get skipped in most cases due to higher weight loss, and prevents
- // unnecessary deep copies of various configurations.
- find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
- } else {
- // Since this is a 1x1 item and all the following items are also 1x1, just place
- // it at 'the most appropriate position' and hope for the best.
- // The most appropriate position: one with lease straight line distance
- int newDistance = Integer.MAX_VALUE;
- int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE;
-
- for (int y = 0; y < mTrgY; y++) {
- for (int x = 0; x < mTrgX; x++) {
- if (!occupied[x][y]) {
- int dist = ignoreMove ? 0 :
- ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y));
- if (dist < newDistance) {
- newX = x;
- newY = y;
- newDistance = dist;
- }
- }
- }
- }
-
- if (newX < mTrgX && newY < mTrgY) {
- float newMoveCost = moveCost;
- if (newX != myX) {
- me.cellX = newX;
- newMoveCost ++;
- }
- if (newY != myY) {
- me.cellY = newY;
- newMoveCost ++;
- }
- if (ignoreMove) {
- newMoveCost = moveCost;
- }
- markCells(occupied, me, true);
- find(index + 1, weightLoss, newMoveCost, itemsIncludingMe);
- markCells(occupied, me, false);
- me.cellX = myX;
- me.cellY = myY;
-
- // Try to find a solution without this item, only if
- // 1) there was at least one space, i.e., we were able to place this item
- // 2) if the next item has the same weight (all items are already sorted), as
- // if it has lower weight, that solution will automatically get discarded.
- // 3) ignoreMove false otherwise, move cost is ignored and the weight will
- // anyway be same.
- if (index + 1 < itemsToPlace.size()
- && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) {
- find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced);
- }
- } else {
- // No more space. Jump to the end.
- for (int i = index + 1; i < itemsToPlace.size(); i++) {
- weightLoss += itemsToPlace.get(i).weight;
- }
- find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced);
- }
- }
- }
- }
-
- /**
- * Loads entries for a particular screen id.
- */
- public ArrayList<DbEntry> loadEntries(long screen) {
- Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
- new String[] {
- Favorites._ID, // 0
- Favorites.ITEM_TYPE, // 1
- Favorites.CELLX, // 2
- Favorites.CELLY, // 3
- Favorites.SPANX, // 4
- Favorites.SPANY, // 5
- Favorites.INTENT, // 6
- Favorites.APPWIDGET_PROVIDER}, // 7
- Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP
- + " AND " + Favorites.SCREEN + " = " + screen, null, null, null);
-
- final int indexId = c.getColumnIndexOrThrow(Favorites._ID);
- final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE);
- final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX);
- final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY);
- final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX);
- final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY);
- final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT);
- final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER);
-
- ArrayList<DbEntry> entries = new ArrayList<>();
- while (c.moveToNext()) {
- DbEntry entry = new DbEntry();
- entry.id = c.getLong(indexId);
- entry.itemType = c.getInt(indexItemType);
- entry.cellX = c.getInt(indexCellX);
- entry.cellY = c.getInt(indexCellY);
- entry.spanX = c.getInt(indexSpanX);
- entry.spanY = c.getInt(indexSpanY);
- entry.screenId = screen;
-
- try {
- // calculate weight
- switch (entry.itemType) {
- case Favorites.ITEM_TYPE_SHORTCUT:
- case Favorites.ITEM_TYPE_APPLICATION: {
- verifyIntent(c.getString(indexIntent));
- entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT
- ? WT_SHORTCUT : WT_APPLICATION;
- break;
- }
- case Favorites.ITEM_TYPE_APPWIDGET: {
- String provider = c.getString(indexAppWidgetProvider);
- ComponentName cn = ComponentName.unflattenFromString(provider);
- verifyPackage(cn.getPackageName());
- entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR
- * entry.spanX * entry.spanY);
-
- // Migration happens for current user only.
- LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo(
- mContext, cn, UserHandleCompat.myUserHandle());
- Point spans = pInfo == null ?
- mWidgetMinSize.get(provider) : 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;
- } else {
- // Assume that the widget be resized down to 2x2
- entry.minSpanX = entry.minSpanY = 2;
- }
-
- if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) {
- throw new Exception("Widget can't be resized down to fit the grid");
- }
- break;
- }
- case Favorites.ITEM_TYPE_FOLDER: {
- int total = getFolderItemsCount(entry.id);
- if (total == 0) {
- throw new Exception("Folder is empty");
- }
- entry.weight = WT_FOLDER_FACTOR * total;
- break;
- }
- default:
- throw new Exception("Invalid item type");
- }
- } catch (Exception e) {
- if (DEBUG) {
- Log.d(TAG, "Removing item " + entry.id, e);
- }
- mEntryToRemove.add(entry.id);
- continue;
- }
-
- entries.add(entry);
- }
- return entries;
- }
-
- /**
- * @return the number of valid items in the folder.
- */
- private int getFolderItemsCount(long folderId) {
- Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
- new String[] {Favorites._ID, Favorites.INTENT},
- Favorites.CONTAINER + " = " + folderId, null, null, null);
-
- int total = 0;
- while (c.moveToNext()) {
- try {
- verifyIntent(c.getString(1));
- total++;
- } catch (Exception e) {
- mEntryToRemove.add(c.getLong(0));
- }
- }
-
- return total;
- }
-
- /**
- * Verifies if the intent should be restored.
- */
- private void verifyIntent(String intentStr) throws Exception {
- Intent intent = Intent.parseUri(intentStr, 0);
- if (intent.getComponent() != null) {
- verifyPackage(intent.getComponent().getPackageName());
- } else if (intent.getPackage() != null) {
- // Only verify package if the component was null.
- verifyPackage(intent.getPackage());
- }
- }
-
- /**
- * Verifies if the package should be restored
- */
- private void verifyPackage(String packageName) throws Exception {
- if (!mValidPackages.contains(packageName)) {
- throw new Exception("Package not available");
- }
- }
-
- private static class DbEntry extends ItemInfo implements Comparable<DbEntry> {
-
- public float weight;
-
- public DbEntry() { }
-
- public DbEntry copy() {
- DbEntry entry = new DbEntry();
- entry.copyFrom(this);
- entry.weight = weight;
- entry.minSpanX = minSpanX;
- entry.minSpanY = minSpanY;
- return entry;
- }
-
- /**
- * Comparator such that larger widgets come first, followed by all 1x1 items
- * based on their weights.
- */
- @Override
- public int compareTo(DbEntry another) {
- if (itemType == Favorites.ITEM_TYPE_APPWIDGET) {
- if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
- return another.spanY * another.spanX - spanX * spanY;
- } else {
- return -1;
- }
- } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
- return 1;
- } else {
- // Place higher weight before lower weight.
- return Float.compare(another.weight, weight);
- }
- }
-
- public boolean columnsSame(DbEntry org) {
- return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX &&
- org.spanY == spanY && org.screenId == screenId;
- }
-
- public void addToContentValues(ContentValues values) {
- values.put(LauncherSettings.Favorites.SCREEN, screenId);
- values.put(LauncherSettings.Favorites.CELLX, cellX);
- values.put(LauncherSettings.Favorites.CELLY, cellY);
- values.put(LauncherSettings.Favorites.SPANX, spanX);
- values.put(LauncherSettings.Favorites.SPANY, spanY);
- }
- }
-
- @Thunk static ArrayList<DbEntry> deepCopy(ArrayList<DbEntry> src) {
- ArrayList<DbEntry> dup = new ArrayList<DbEntry>(src.size());
- for (DbEntry e : src) {
- dup.add(e.copy());
- }
- return dup;
- }
-
- private static Point parsePoint(String point) {
- String[] split = point.split(",");
- return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1]));
- }
-
- public static void markForMigration(Context context, int srcX, int srcY,
- HashSet<String> widgets) {
- prefs(context).edit()
- .putString(KEY_MIGRATION_SOURCE_SIZE, srcX + "," + srcY)
- .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets)
- .apply();
- }
-
- public static boolean shouldRunTask(Context context) {
- return !TextUtils.isEmpty(prefs(context).getString(KEY_MIGRATION_SOURCE_SIZE, ""));
- }
-
- public static void clearFlags(Context context) {
- prefs(context).edit().remove(KEY_MIGRATION_SOURCE_SIZE)
- .remove(KEY_MIGRATION_WIDGET_MINSIZE).commit();
- }
-
- private static SharedPreferences prefs(Context context) {
- return context.getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
- Context.MODE_PRIVATE);
- }
-}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index eef4f91..53e5213 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -1,9 +1,13 @@
package com.android.launcher3.model;
+import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.os.DeadObjectException;
+import android.os.TransactionTooLargeException;
import android.util.Log;
import com.android.launcher3.AppFilter;
@@ -16,6 +20,7 @@
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.config.ProviderConfig;
import java.util.ArrayList;
import java.util.Collections;
@@ -34,19 +39,19 @@
private static final boolean DEBUG = false;
/* List of packages that is tracked by this model. */
- private ArrayList<PackageItemInfo> mPackageItemInfos = new ArrayList<>();
+ private final ArrayList<PackageItemInfo> mPackageItemInfos;
/* Map of widgets and shortcuts that are tracked per package. */
- private HashMap<PackageItemInfo, ArrayList<Object>> mWidgetsList = new HashMap<>();
-
- private ArrayList<Object> mRawList;
+ private final HashMap<PackageItemInfo, ArrayList<Object>> mWidgetsList;
private final AppWidgetManagerCompat mAppWidgetMgr;
private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
private final Comparator<ItemInfo> mAppNameComparator;
private final IconCache mIconCache;
private final AppFilter mAppFilter;
- private AlphabeticIndexCompat mIndexer;
+ private final AlphabeticIndexCompat mIndexer;
+
+ private ArrayList<Object> mRawList;
public WidgetsModel(Context context, IconCache iconCache, AppFilter appFilter) {
mAppWidgetMgr = AppWidgetManagerCompat.getInstance(context);
@@ -55,6 +60,10 @@
mIconCache = iconCache;
mAppFilter = appFilter;
mIndexer = new AlphabeticIndexCompat(context);
+ mPackageItemInfos = new ArrayList<>();
+ mWidgetsList = new HashMap<>();
+
+ mRawList = new ArrayList<>();
}
@SuppressWarnings("unchecked")
@@ -62,18 +71,16 @@
mAppWidgetMgr = model.mAppWidgetMgr;
mPackageItemInfos = (ArrayList<PackageItemInfo>) model.mPackageItemInfos.clone();
mWidgetsList = (HashMap<PackageItemInfo, ArrayList<Object>>) model.mWidgetsList.clone();
- mRawList = (ArrayList<Object>) model.mRawList.clone();
mWidgetAndShortcutNameComparator = model.mWidgetAndShortcutNameComparator;
mAppNameComparator = model.mAppNameComparator;
mIconCache = model.mIconCache;
mAppFilter = model.mAppFilter;
+ mIndexer = model.mIndexer;
+ mRawList = (ArrayList<Object>) model.mRawList.clone();
}
// Access methods that may be deleted if the private fields are made package-private.
public int getPackageSize() {
- if (mPackageItemInfos == null) {
- return 0;
- }
return mPackageItemInfos.size();
}
@@ -93,8 +100,41 @@
return mRawList;
}
- public void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
+ public boolean isEmpty() {
+ return mRawList.isEmpty();
+ }
+
+ public WidgetsModel updateAndClone(Context context) {
Utilities.assertWorkerThread();
+
+ try {
+ final ArrayList<Object> widgetsAndShortcuts = new ArrayList<>();
+ // Widgets
+ for (AppWidgetProviderInfo widgetInfo :
+ AppWidgetManagerCompat.getInstance(context).getAllProviders()) {
+ widgetsAndShortcuts.add(LauncherAppWidgetProviderInfo
+ .fromProviderInfo(context, widgetInfo));
+ }
+ // Shortcuts
+ widgetsAndShortcuts.addAll(context.getPackageManager().queryIntentActivities(
+ new Intent(Intent.ACTION_CREATE_SHORTCUT), 0));
+ setWidgetsAndShortcuts(widgetsAndShortcuts);
+ } catch (Exception e) {
+ if (!LauncherAppState.isDogfoodBuild() &&
+ (e.getCause() instanceof TransactionTooLargeException ||
+ e.getCause() instanceof DeadObjectException)) {
+ // 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
+ // onResume is called to refresh the widget provider list.
+ } else {
+ throw e;
+ }
+ }
+ return clone();
+ }
+
+ private void setWidgetsAndShortcuts(ArrayList<Object> rawWidgetsShortcuts) {
mRawList = rawWidgetsShortcuts;
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
diff --git a/src/com/android/launcher3/DummyWidget.java b/src/com/android/launcher3/testing/DummyWidget.java
similarity index 82%
rename from src/com/android/launcher3/DummyWidget.java
rename to src/com/android/launcher3/testing/DummyWidget.java
index 59cd805..df887ac 100644
--- a/src/com/android/launcher3/DummyWidget.java
+++ b/src/com/android/launcher3/testing/DummyWidget.java
@@ -1,7 +1,10 @@
-package com.android.launcher3;
+package com.android.launcher3.testing;
import android.appwidget.AppWidgetProviderInfo;
+import com.android.launcher3.CustomAppWidget;
+import com.android.launcher3.R;
+
public class DummyWidget implements CustomAppWidget {
@Override
public String getLabel() {
@@ -20,7 +23,7 @@
@Override
public int getWidgetLayout() {
- return R.layout.dummy_widget;
+ return R.layout.zzz_dummy_widget;
}
@Override
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 8702877..1bb5787 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -5,11 +5,13 @@
import android.animation.ObjectAnimator;
import android.content.ComponentName;
import android.content.Intent;
+import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InsettableFrameLayout;
@@ -210,13 +212,39 @@
public void startVoice() {
}
+ CustomContentCallbacks mCustomContentCallbacks = new 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) {
+ }
+
+ // Custom content is completely hidden
+ public void onHide() {
+ }
+
+ // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
+ public void onScrollProgressChanged(float progress) {
+
+ }
+
+ // Indicates whether the user is allowed to scroll away from the custom content.
+ public boolean isScrollingAllowed() {
+ return true;
+ }
+
+ };
+
@Override
public boolean hasCustomContentToLeft() {
- return false;
+ return true;
}
@Override
public void populateCustomContentContainer() {
+ FrameLayout customContent = new FrameLayout(LauncherExtension.this);
+ customContent.setBackgroundColor(Color.GRAY);
+ addToCustomContentPage(customContent, mCustomContentCallbacks, "");
}
@Override
@@ -225,6 +253,11 @@
}
@Override
+ public Bundle getAdditionalSearchWidgetOptions() {
+ return new Bundle();
+ }
+
+ @Override
public Intent getFirstRunActivity() {
return null;
}
@@ -275,13 +308,18 @@
}
@Override
+ public int getSearchBarHeight() {
+ return SEARCH_BAR_HEIGHT_NORMAL;
+ }
+
+ @Override
public boolean isLauncherPreinstalled() {
return false;
}
@Override
public boolean hasLauncherOverlay() {
- return true;
+ return false;
}
@Override
diff --git a/src/com/android/launcher3/MemoryDumpActivity.java b/src/com/android/launcher3/testing/MemoryDumpActivity.java
similarity index 93%
rename from src/com/android/launcher3/MemoryDumpActivity.java
rename to src/com/android/launcher3/testing/MemoryDumpActivity.java
index d79be80..9bcf92b 100644
--- a/src/com/android/launcher3/MemoryDumpActivity.java
+++ b/src/com/android/launcher3/testing/MemoryDumpActivity.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.testing;
import android.app.Activity;
import android.content.ComponentName;
@@ -23,10 +23,20 @@
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.os.*;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.IBinder;
import android.util.Log;
-import java.io.*;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.zip.ZipEntry;
diff --git a/src/com/android/launcher3/MemoryTracker.java b/src/com/android/launcher3/testing/MemoryTracker.java
similarity index 92%
rename from src/com/android/launcher3/MemoryTracker.java
rename to src/com/android/launcher3/testing/MemoryTracker.java
index 067a50f..ed2a312 100644
--- a/src/com/android/launcher3/MemoryTracker.java
+++ b/src/com/android/launcher3/testing/MemoryTracker.java
@@ -14,22 +14,28 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.testing;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
-import android.os.*;
+import android.os.Binder;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.SystemClock;
import android.util.Log;
import android.util.LongSparseArray;
+import com.android.launcher3.util.TestingUtils;
+
import java.util.ArrayList;
import java.util.List;
public class MemoryTracker extends Service {
public static final String TAG = MemoryTracker.class.getSimpleName();
- public static final String ACTION_START_TRACKING = "com.android.launcher3.action.START_TRACKING";
private static final long UPDATE_RATE = 5000;
@@ -83,14 +89,6 @@
ActivityManager mAm;
- public static void startTrackingMe(Context context, String name) {
- context.startService(new Intent(context, MemoryTracker.class)
- .setAction(MemoryTracker.ACTION_START_TRACKING)
- .putExtra("pid", android.os.Process.myPid())
- .putExtra("name", name)
- );
- }
-
public ProcessMemInfo getMemInfo(int pid) {
return mData.get(pid);
}
@@ -190,7 +188,7 @@
Log.v(TAG, "Received start id " + startId + ": " + intent);
if (intent != null) {
- if (ACTION_START_TRACKING.equals(intent.getAction())) {
+ if (TestingUtils.ACTION_START_TRACKING.equals(intent.getAction())) {
final int pid = intent.getIntExtra("pid", -1);
final String name = intent.getStringExtra("name");
final long start = intent.getLongExtra("start", System.currentTimeMillis());
diff --git a/src/com/android/launcher3/testing/ToggleWeightWatcher.java b/src/com/android/launcher3/testing/ToggleWeightWatcher.java
new file mode 100644
index 0000000..e08ec3a
--- /dev/null
+++ b/src/com/android/launcher3/testing/ToggleWeightWatcher.java
@@ -0,0 +1,31 @@
+package com.android.launcher3.testing;
+
+import android.app.Activity;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.util.TestingUtils;
+
+public class ToggleWeightWatcher extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ SharedPreferences sp = Utilities.getPrefs(this);
+ boolean show = sp.getBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, true);
+
+ show = !show;
+ sp.edit().putBoolean(TestingUtils.SHOW_WEIGHT_WATCHER, show).apply();
+
+ Launcher launcher = (Launcher) LauncherAppState.getInstance().getModel().getCallback();
+ if (launcher != null && launcher.mWeightWatcher != null) {
+ launcher.mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
+ }
+ finish();
+ }
+}
diff --git a/src/com/android/launcher3/WeightWatcher.java b/src/com/android/launcher3/testing/WeightWatcher.java
similarity index 98%
rename from src/com/android/launcher3/WeightWatcher.java
rename to src/com/android/launcher3/testing/WeightWatcher.java
index 7568479..a26a2b6 100644
--- a/src/com/android/launcher3/WeightWatcher.java
+++ b/src/com/android/launcher3/testing/WeightWatcher.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.launcher3;
+package com.android.launcher3.testing;
import android.content.ComponentName;
import android.content.Context;
@@ -116,10 +116,6 @@
}
}
- public WeightWatcher(Context context) {
- this(context, null);
- }
-
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
diff --git a/src/com/android/launcher3/util/ConfigMonitor.java b/src/com/android/launcher3/util/ConfigMonitor.java
new file mode 100644
index 0000000..bdb1639
--- /dev/null
+++ b/src/com/android/launcher3/util/ConfigMonitor.java
@@ -0,0 +1,63 @@
+package com.android.launcher3.util;
+
+/**
+ * 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.
+ */
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.util.Log;
+
+import com.android.launcher3.Utilities;
+
+/**
+ * {@link BroadcastReceiver} which watches configuration changes and
+ * restarts the process in case changes which affect the device profile occur.
+ */
+public class ConfigMonitor extends BroadcastReceiver {
+
+ private final Context mContext;
+ private final float mFontScale;
+ private final int mDensity;
+
+ public ConfigMonitor(Context context) {
+ mContext = context;
+
+ Configuration config = context.getResources().getConfiguration();
+ mFontScale = config.fontScale;
+ mDensity = getDensity(config);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Configuration config = context.getResources().getConfiguration();
+ if (mFontScale != config.fontScale || mDensity != getDensity(config)) {
+ Log.d("ConfigMonitor", "Configuration changed, restarting launcher");
+ mContext.unregisterReceiver(this);
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ }
+
+ public void register() {
+ mContext.registerReceiver(this, new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+ }
+
+ private static int getDensity(Configuration config) {
+ return Utilities.ATLEAST_JB_MR1 ? config.densityDpi : 0;
+ }
+}
diff --git a/src/com/android/launcher3/util/FlagOp.java b/src/com/android/launcher3/util/FlagOp.java
new file mode 100644
index 0000000..5e26ed1
--- /dev/null
+++ b/src/com/android/launcher3/util/FlagOp.java
@@ -0,0 +1,30 @@
+package com.android.launcher3.util;
+
+public abstract class FlagOp {
+
+ public static FlagOp NO_OP = new FlagOp() {};
+
+ private FlagOp() {}
+
+ public int apply(int flags) {
+ return flags;
+ }
+
+ public static FlagOp addFlag(final int flag) {
+ return new FlagOp() {
+ @Override
+ public int apply(int flags) {
+ return flags | flag;
+ }
+ };
+ }
+
+ public static FlagOp removeFlag(final int flag) {
+ return new FlagOp() {
+ @Override
+ public int apply(int flags) {
+ return flags & ~flag;
+ }
+ };
+ }
+}
diff --git a/src/com/android/launcher3/util/FocusLogic.java b/src/com/android/launcher3/util/FocusLogic.java
index 696eabe..a5498f7 100644
--- a/src/com/android/launcher3/util/FocusLogic.java
+++ b/src/com/android/launcher3/util/FocusLogic.java
@@ -22,9 +22,6 @@
import android.view.ViewGroup;
import com.android.launcher3.CellLayout;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.ShortcutAndWidgetContainer;
import java.util.Arrays;
@@ -51,7 +48,7 @@
private static final String TAG = "FocusLogic";
private static final boolean DEBUG = false;
- // Item and page index related constant used by {@link #handleKeyEvent}.
+ /** Item and page index related constant used by {@link #handleKeyEvent}. */
public static final int NOOP = -1;
public static final int PREVIOUS_PAGE_RIGHT_COLUMN = -2;
@@ -66,6 +63,8 @@
public static final int NEXT_PAGE_LEFT_COLUMN = -9;
public static final int NEXT_PAGE_RIGHT_COLUMN = -10;
+ public static final int ALL_APPS_COLUMN = -11;
+
// Matrix related constant.
public static final int EMPTY = -1;
public static final int PIVOT = 100;
@@ -81,8 +80,11 @@
keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_FORWARD_DEL);
}
- public static int handleKeyEvent(int keyCode, int cntX, int cntY,
- int [][] map, int iconIdx, int pageIndex, int pageCount, boolean isRtl) {
+ public static int handleKeyEvent(int keyCode, int [][] map, int iconIdx, int pageIndex,
+ int pageCount, boolean isRtl) {
+
+ int cntX = map == null ? -1 : map.length;
+ int cntY = map == null ? -1 : map[0].length;
if (DEBUG) {
Log.v(TAG, String.format(
@@ -93,16 +95,16 @@
int newIndex = NOOP;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
- newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/);
- if (isRtl && newIndex == NOOP && pageIndex > 0) {
+ newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, -1 /*increment*/, isRtl);
+ if (!isRtl && newIndex == NOOP && pageIndex > 0) {
newIndex = PREVIOUS_PAGE_RIGHT_COLUMN;
} else if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
newIndex = NEXT_PAGE_RIGHT_COLUMN;
}
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
- newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/);
- if (isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
+ newIndex = handleDpadHorizontal(iconIdx, cntX, cntY, map, 1 /*increment*/, isRtl);
+ if (!isRtl && newIndex == NOOP && pageIndex < pageCount - 1) {
newIndex = NEXT_PAGE_LEFT_COLUMN;
} else if (isRtl && newIndex == NOOP && pageIndex > 0) {
newIndex = PREVIOUS_PAGE_LEFT_COLUMN;
@@ -168,8 +170,12 @@
// Iterate thru the children.
for (int i = 0; i < parent.getChildCount(); i++ ) {
- int cx = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellX;
- int cy = ((CellLayout.LayoutParams) parent.getChildAt(i).getLayoutParams()).cellY;
+ View cell = parent.getChildAt(i);
+ if (!cell.isFocusable()) {
+ continue;
+ }
+ int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
+ int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
matrix[invert ? (m - cx - 1) : cx][cy] = i;
}
if (DEBUG) {
@@ -184,51 +190,67 @@
* in portrait orientation. In landscape, [(icon + hotseat) column count x (icon row count)]
*/
// TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrix(CellLayout iconLayout, CellLayout hotseatLayout,
- boolean isHorizontal, int allappsiconRank, boolean includeAllappsicon) {
+ public static int[][] createSparseMatrixWithHotseat(CellLayout iconLayout,
+ CellLayout hotseatLayout, boolean isHotseatHorizontal, int allappsiconRank) {
ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
ViewGroup hotseatParent = hotseatLayout.getShortcutsAndWidgets();
+ boolean moreIconsInHotseatThanWorkspace = isHotseatHorizontal ?
+ hotseatLayout.getCountX() > iconLayout.getCountX() :
+ hotseatLayout.getCountY() > iconLayout.getCountY();
+
int m, n;
- if (isHorizontal) {
- m = iconLayout.getCountX();
+ if (isHotseatHorizontal) {
+ m = hotseatLayout.getCountX();
n = iconLayout.getCountY() + hotseatLayout.getCountY();
} else {
m = iconLayout.getCountX() + hotseatLayout.getCountX();
- n = iconLayout.getCountY();
+ n = hotseatLayout.getCountY();
}
int[][] matrix = createFullMatrix(m, n);
-
- // Iterate thru the children of the top parent.
+ if (moreIconsInHotseatThanWorkspace) {
+ if (isHotseatHorizontal) {
+ for (int j = 0; j < n; j++) {
+ matrix[allappsiconRank][j] = ALL_APPS_COLUMN;
+ }
+ } else {
+ for (int j = 0; j < m; j++) {
+ matrix[j][allappsiconRank] = ALL_APPS_COLUMN;
+ }
+ }
+ }
+ // Iterate thru the children of the workspace.
for (int i = 0; i < iconParent.getChildCount(); i++) {
- int cx = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellX;
- int cy = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellY;
+ View cell = iconParent.getChildAt(i);
+ if (!cell.isFocusable()) {
+ continue;
+ }
+ int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
+ int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
+ if (moreIconsInHotseatThanWorkspace) {
+ if (isHotseatHorizontal && cx >= allappsiconRank) {
+ // Add 1 to account for the All Apps button.
+ cx++;
+ }
+ if (!isHotseatHorizontal && cy >= allappsiconRank) {
+ // Add 1 to account for the All Apps button.
+ cy++;
+ }
+ }
matrix[cx][cy] = i;
}
- // Iterate thru the children of the bottom parent
- // The hotseat view group contains one more item than iconLayout column count.
- // If {@param allappsiconRank} not negative, then the last icon in the hotseat
- // is truncated. If it is negative, then all apps icon index is not inserted.
- for(int i = hotseatParent.getChildCount() - 1; i >= (includeAllappsicon ? 0 : 1); i--) {
- int delta = 0;
- if (isHorizontal) {
+ // Iterate thru the children of the hotseat.
+ for (int i = hotseatParent.getChildCount() - 1; i >= 0; i--) {
+ if (isHotseatHorizontal) {
int cx = ((CellLayout.LayoutParams)
hotseatParent.getChildAt(i).getLayoutParams()).cellX;
- if ((includeAllappsicon && cx >= allappsiconRank) ||
- (!includeAllappsicon && cx > allappsiconRank)) {
- delta = -1;
- }
- matrix[cx + delta][iconLayout.getCountY()] = iconParent.getChildCount() + i;
+ matrix[cx][iconLayout.getCountY()] = iconParent.getChildCount() + i;
} else {
int cy = ((CellLayout.LayoutParams)
hotseatParent.getChildAt(i).getLayoutParams()).cellY;
- if ((includeAllappsicon && cy >= allappsiconRank) ||
- (!includeAllappsicon && cy > allappsiconRank)) {
- delta = -1;
- }
- matrix[iconLayout.getCountX()][cy + delta] = iconParent.getChildCount() + i;
+ matrix[iconLayout.getCountX()][cy] = iconParent.getChildCount() + i;
}
}
if (DEBUG) {
@@ -248,7 +270,8 @@
* @param pivotY y coordinate of the focused item in the current page
*/
// TODO: get rid of the dynamic matrix creation
- public static int[][] createSparseMatrix(CellLayout iconLayout, int pivotX, int pivotY) {
+ public static int[][] createSparseMatrixWithPivotColumn(CellLayout iconLayout,
+ int pivotX, int pivotY) {
ViewGroup iconParent = iconLayout.getShortcutsAndWidgets();
@@ -256,8 +279,12 @@
// Iterate thru the children of the top parent.
for (int i = 0; i < iconParent.getChildCount(); i++) {
- int cx = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellX;
- int cy = ((CellLayout.LayoutParams) iconParent.getChildAt(i).getLayoutParams()).cellY;
+ View cell = iconParent.getChildAt(i);
+ if (!cell.isFocusable()) {
+ continue;
+ }
+ int cx = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellX;
+ int cy = ((CellLayout.LayoutParams) cell.getLayoutParams()).cellY;
if (pivotX < 0) {
matrix[cx - pivotX][cy] = i;
} else {
@@ -291,7 +318,7 @@
*/
// TODO: add unit tests to verify all permutation.
private static int handleDpadHorizontal(int iconIdx, int cntX, int cntY,
- int[][] matrix, int increment) {
+ int[][] matrix, int increment, boolean isRtl) {
if(matrix == null) {
throw new IllegalStateException("Dpad navigation requires a matrix.");
}
@@ -314,8 +341,9 @@
}
// Rule1: check first in the horizontal direction
- for (int i = xPos + increment; 0 <= i && i < cntX; i = i + increment) {
- if ((newIconIndex = inspectMatrix(i, yPos, cntX, cntY, matrix)) != NOOP) {
+ for (int x = xPos + increment; 0 <= x && x < cntX; x += increment) {
+ if ((newIconIndex = inspectMatrix(x, yPos, cntX, cntY, matrix)) != NOOP
+ && newIconIndex != ALL_APPS_COLUMN) {
return newIconIndex;
}
}
@@ -324,19 +352,41 @@
// (x2-n, yPos + 2*increment), (x2-n, yPos - 2*increment)
int nextYPos1;
int nextYPos2;
- int i = -1;
+ boolean haveCrossedAllAppsColumn1 = false;
+ boolean haveCrossedAllAppsColumn2 = false;
+ int x = -1;
for (int coeff = 1; coeff < cntY; coeff++) {
nextYPos1 = yPos + coeff * increment;
nextYPos2 = yPos - coeff * increment;
- for (i = xPos + increment * coeff; 0 <= i && i < cntX; i = i + increment) {
- if ((newIconIndex = inspectMatrix(i, nextYPos1, cntX, cntY, matrix)) != NOOP) {
+ x = xPos + increment * coeff;
+ if (inspectMatrix(x, nextYPos1, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ haveCrossedAllAppsColumn1 = true;
+ }
+ if (inspectMatrix(x, nextYPos2, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ haveCrossedAllAppsColumn2 = true;
+ }
+ for (; 0 <= x && x < cntX; x += increment) {
+ int offset1 = haveCrossedAllAppsColumn1 && x < cntX - 1 ? increment : 0;
+ newIconIndex = inspectMatrix(x, nextYPos1 + offset1, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
- if ((newIconIndex = inspectMatrix(i, nextYPos2, cntX, cntY, matrix)) != NOOP) {
+ int offset2 = haveCrossedAllAppsColumn2 && x < cntX - 1 ? -increment : 0;
+ newIconIndex = inspectMatrix(x, nextYPos2 + offset2, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
}
}
+
+ // Rule3: if switching between pages, do a brute-force search to find an item that was
+ // missed by rules 1 and 2 (such as when going from a bottom right icon to top left)
+ if (iconIdx == PIVOT) {
+ if (isRtl) {
+ return increment < 0 ? NEXT_PAGE_FIRST_ITEM : PREVIOUS_PAGE_LAST_ITEM;
+ }
+ return increment < 0 ? PREVIOUS_PAGE_LAST_ITEM : NEXT_PAGE_FIRST_ITEM;
+ }
return newIconIndex;
}
@@ -375,8 +425,9 @@
}
// Rule1: check first in the dpad direction
- for (int j = yPos + increment; 0 <= j && j <cntY && 0 <= j; j = j + increment) {
- if ((newIconIndex = inspectMatrix(xPos, j, cntX, cntY, matrix)) != NOOP) {
+ for (int y = yPos + increment; 0 <= y && y <cntY && 0 <= y; y += increment) {
+ if ((newIconIndex = inspectMatrix(xPos, y, cntX, cntY, matrix)) != NOOP
+ && newIconIndex != ALL_APPS_COLUMN) {
return newIconIndex;
}
}
@@ -385,15 +436,28 @@
// (xPos + 2*increment, y_(2-n))), (xPos - 2*increment, y_(2-n))
int nextXPos1;
int nextXPos2;
- int j = -1;
+ boolean haveCrossedAllAppsColumn1 = false;
+ boolean haveCrossedAllAppsColumn2 = false;
+ int y = -1;
for (int coeff = 1; coeff < cntX; coeff++) {
nextXPos1 = xPos + coeff * increment;
nextXPos2 = xPos - coeff * increment;
- for (j = yPos + increment * coeff; 0 <= j && j < cntY; j = j + increment) {
- if ((newIconIndex = inspectMatrix(nextXPos1, j, cntX, cntY, matrix)) != NOOP) {
+ y = yPos + increment * coeff;
+ if (inspectMatrix(nextXPos1, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ haveCrossedAllAppsColumn1 = true;
+ }
+ if (inspectMatrix(nextXPos2, y, cntX, cntY, matrix) == ALL_APPS_COLUMN) {
+ haveCrossedAllAppsColumn2 = true;
+ }
+ for (; 0 <= y && y < cntY; y = y + increment) {
+ int offset1 = haveCrossedAllAppsColumn1 && y < cntY - 1 ? increment : 0;
+ newIconIndex = inspectMatrix(nextXPos1 + offset1, y, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
- if ((newIconIndex = inspectMatrix(nextXPos2, j, cntX, cntY, matrix)) != NOOP) {
+ int offset2 = haveCrossedAllAppsColumn2 && y < cntY - 1 ? -increment : 0;
+ newIconIndex = inspectMatrix(nextXPos2 + offset2, y, cntX, cntY, matrix);
+ if (newIconIndex != NOOP) {
return newIconIndex;
}
}
@@ -460,6 +524,7 @@
case CURRENT_PAGE_LAST_ITEM: return "CURRENT_PAGE_LAST";
case NEXT_PAGE_FIRST_ITEM: return "NEXT_PAGE_FIRST";
case NEXT_PAGE_LEFT_COLUMN: return "NEXT_PAGE_LEFT_COLUMN";
+ case ALL_APPS_COLUMN: return "ALL_APPS_COLUMN";
default:
return Integer.toString(index);
}
@@ -485,9 +550,9 @@
/**
* @param edgeColumn the column of the new icon. either {@link #NEXT_PAGE_LEFT_COLUMN} or
* {@link #NEXT_PAGE_RIGHT_COLUMN}
- * @return the view adjacent to {@param oldView} in the {@param nextPage}.
+ * @return the view adjacent to {@param oldView} in the {@param nextPage} of the folder.
*/
- public static View getAdjacentChildInNextPage(
+ public static View getAdjacentChildInNextFolderPage(
ShortcutAndWidgetContainer nextPage, View oldView, int edgeColumn) {
final int newRow = ((CellLayout.LayoutParams) oldView.getLayoutParams()).cellY;
diff --git a/src/com/android/launcher3/util/IconNormalizer.java b/src/com/android/launcher3/util/IconNormalizer.java
new file mode 100644
index 0000000..001cac0
--- /dev/null
+++ b/src/com/android/launcher3/util/IconNormalizer.java
@@ -0,0 +1,239 @@
+/*
+ * 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.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+
+import com.android.launcher3.LauncherAppState;
+
+import java.nio.ByteBuffer;
+
+public class IconNormalizer {
+
+ // Ratio of icon visible area to full icon size for a square shaped icon
+ private static final float MAX_SQUARE_AREA_FACTOR = 359.0f / 576;
+ // Ratio of icon visible area to full icon size for a circular shaped icon
+ private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576;
+
+ private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4;
+
+ // Slope used to calculate icon visible area to full icon size for any generic shaped icon.
+ private static final float LINEAR_SCALE_SLOPE =
+ (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT);
+
+ private static final int MIN_VISIBLE_ALPHA = 40;
+
+ private static final Object LOCK = new Object();
+ private static IconNormalizer sIconNormalizer;
+
+ private final int mMaxSize;
+ private final Bitmap mBitmap;
+ private final Canvas mCanvas;
+ private final byte[] mPixels;
+
+ // for each y, stores the position of the leftmost x and the rightmost x
+ private final float[] mLeftBorder;
+ private final float[] mRightBorder;
+
+ private IconNormalizer() {
+ // Use twice the icon size as maximum size to avoid scaling down twice.
+ mMaxSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize * 2;
+ mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
+ mCanvas = new Canvas(mBitmap);
+ mPixels = new byte[mMaxSize * mMaxSize];
+
+ mLeftBorder = new float[mMaxSize];
+ mRightBorder = new float[mMaxSize];
+ }
+
+ /**
+ * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
+ * matches the design guidelines for a launcher icon.
+ *
+ * We first calculate the convex hull of the visible portion of the icon.
+ * This hull then compared with the bounding rectangle of the hull to find how closely it
+ * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an
+ * ideal solution but it gives satisfactory result without affecting the performance.
+ *
+ * This closeness is used to determine the ratio of hull area to the full icon size.
+ * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR}
+ */
+ public synchronized float getScale(Drawable d) {
+ int width = d.getIntrinsicWidth();
+ int height = d.getIntrinsicHeight();
+ if (width <= 0 || height <= 0) {
+ width = width <= 0 || width > mMaxSize ? mMaxSize : width;
+ height = height <= 0 || height > mMaxSize ? mMaxSize : height;
+ } else if (width > mMaxSize || height > mMaxSize) {
+ int max = Math.max(width, height);
+ width = mMaxSize * width / max;
+ height = mMaxSize * height / max;
+ }
+
+ mBitmap.eraseColor(Color.TRANSPARENT);
+ d.setBounds(0, 0, width, height);
+ d.draw(mCanvas);
+
+ ByteBuffer buffer = ByteBuffer.wrap(mPixels);
+ buffer.rewind();
+ mBitmap.copyPixelsToBuffer(buffer);
+
+ // Overall bounds of the visible icon.
+ int topY = -1;
+ int bottomY = -1;
+ int leftX = mMaxSize + 1;
+ int rightX = -1;
+
+ // Create border by going through all pixels one row at a time and for each row find
+ // the first and the last non-transparent pixel. Set those values to mLeftBorder and
+ // mRightBorder and use -1 if there are no visible pixel in the row.
+
+ // buffer position
+ int index = 0;
+ // buffer shift after every row, width of buffer = mMaxSize
+ int rowSizeDiff = mMaxSize - width;
+ // first and last position for any row.
+ int firstX, lastX;
+
+ for (int y = 0; y < height; y++) {
+ firstX = lastX = -1;
+ for (int x = 0; x < width; x++) {
+ if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
+ if (firstX == -1) {
+ firstX = x;
+ }
+ lastX = x;
+ }
+ index++;
+ }
+ index += rowSizeDiff;
+
+ mLeftBorder[y] = firstX;
+ mRightBorder[y] = lastX;
+
+ // If there is at least one visible pixel, update the overall bounds.
+ if (firstX != -1) {
+ bottomY = y;
+ if (topY == -1) {
+ topY = y;
+ }
+
+ leftX = Math.min(leftX, firstX);
+ rightX = Math.max(rightX, lastX);
+ }
+ }
+
+ if (topY == -1 || rightX == -1) {
+ // No valid pixels found. Do not scale.
+ return 1;
+ }
+
+ convertToConvexArray(mLeftBorder, 1, topY, bottomY);
+ convertToConvexArray(mRightBorder, -1, topY, bottomY);
+
+ // Area of the convex hull
+ float area = 0;
+ for (int y = 0; y < height; y++) {
+ if (mLeftBorder[y] <= -1) {
+ continue;
+ }
+ area += mRightBorder[y] - mLeftBorder[y] + 1;
+ }
+
+ // Area of the rectangle required to fit the convex hull
+ float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
+ float hullByRect = area / rectArea;
+
+ float scaleRequired;
+ if (hullByRect < CIRCLE_AREA_BY_RECT) {
+ scaleRequired = MAX_CIRCLE_AREA_FACTOR;
+ } else {
+ scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect);
+ }
+
+ float areaScale = area / (width * height);
+ // Use sqrt of the final ratio as the images is scaled across both width and height.
+ float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
+ return scale;
+ }
+
+ /**
+ * Modifies {@param xCordinates} to represent a convex border. Fills in all missing values
+ * (except on either ends) with appropriate values.
+ * @param xCordinates map of x coordinate per y.
+ * @param direction 1 for left border and -1 for right border.
+ * @param topY the first Y position (inclusive) with a valid value.
+ * @param bottomY the last Y position (inclusive) with a valid value.
+ */
+ private static void convertToConvexArray(
+ float[] xCordinates, int direction, int topY, int bottomY) {
+ int total = xCordinates.length;
+ // The tangent at each pixel.
+ float[] angles = new float[total - 1];
+
+ int first = topY; // First valid y coordinate
+ int last = -1; // Last valid y coordinate which didn't have a missing value
+
+ float lastAngle = Float.MAX_VALUE;
+
+ for (int i = topY + 1; i <= bottomY; i++) {
+ if (xCordinates[i] <= -1) {
+ continue;
+ }
+ int start;
+
+ if (lastAngle == Float.MAX_VALUE) {
+ start = first;
+ } else {
+ float currentAngle = (xCordinates[i] - xCordinates[last]) / (i - last);
+ start = last;
+ // If this position creates a concave angle, keep moving up until we find a
+ // position which creates a convex angle.
+ if ((currentAngle - lastAngle) * direction < 0) {
+ while (start > first) {
+ start --;
+ currentAngle = (xCordinates[i] - xCordinates[start]) / (i - start);
+ if ((currentAngle - angles[start]) * direction >= 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ // Reset from last check
+ lastAngle = (xCordinates[i] - xCordinates[start]) / (i - start);
+ // Update all the points from start.
+ for (int j = start; j < i; j++) {
+ angles[j] = lastAngle;
+ xCordinates[j] = xCordinates[start] + lastAngle * (j - start);
+ }
+ last = i;
+ }
+ }
+
+ public static IconNormalizer getInstance() {
+ synchronized (LOCK) {
+ if (sIconNormalizer == null) {
+ sIconNormalizer = new IconNormalizer();
+ }
+ }
+ return sIconNormalizer;
+ }
+}
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index 74fc92a..fb9bbb2 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -85,6 +86,7 @@
private ArrayList<ShortcutInfo> mHomescreenApps;
private ArrayList<ShortcutInfo> mWorkFolderApps;
+ private HashMap<ShortcutInfo, Long> mShortcutToInstallTimeMap;
private ManagedProfileHeuristic(Context context, UserHandleCompat user) {
mContext = context;
@@ -100,32 +102,29 @@
Context.MODE_PRIVATE);
}
+ private void initVars() {
+ mHomescreenApps = new ArrayList<>();
+ mWorkFolderApps = new ArrayList<>();
+ mShortcutToInstallTimeMap = new HashMap<>();
+ }
+
/**
* Checks the list of user apps and adds icons for newly installed apps on the homescreen or
* workfolder.
*/
public void processUserApps(List<LauncherActivityInfoCompat> apps) {
- mHomescreenApps = new ArrayList<>();
- mWorkFolderApps = new ArrayList<>();
+ initVars();
HashSet<String> packageSet = new HashSet<>();
final boolean userAppsExisted = getUserApps(packageSet);
boolean newPackageAdded = false;
-
for (LauncherActivityInfoCompat info : apps) {
String packageName = info.getComponentName().getPackageName();
if (!packageSet.contains(packageName)) {
packageSet.add(packageName);
newPackageAdded = true;
-
- try {
- PackageInfo pkgInfo = mContext.getPackageManager()
- .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
- markForAddition(info, pkgInfo.firstInstallTime);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "Unknown package " + packageName, e);
- }
+ markForAddition(info, info.getFirstInstallTime());
}
}
@@ -142,7 +141,22 @@
ArrayList<ShortcutInfo> targetList =
(installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
mWorkFolderApps : mHomescreenApps;
- targetList.add(ShortcutInfo.fromActivityInfo(info, mContext));
+ ShortcutInfo si = ShortcutInfo.fromActivityInfo(info, mContext);
+ mShortcutToInstallTimeMap.put(si, installTime);
+ targetList.add(si);
+ }
+
+ private void sortList(ArrayList<ShortcutInfo> infos) {
+ Collections.sort(infos, new Comparator<ShortcutInfo>() {
+
+ @Override
+ public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
+ Long lhsTime = mShortcutToInstallTimeMap.get(lhs);
+ Long rhsTime = mShortcutToInstallTimeMap.get(rhs);
+ return Utilities.longCompare(lhsTime == null ? 0 : lhsTime,
+ rhsTime == null ? 0 : rhsTime);
+ }
+ });
}
/**
@@ -152,13 +166,7 @@
if (mWorkFolderApps.isEmpty()) {
return;
}
- Collections.sort(mWorkFolderApps, new Comparator<ShortcutInfo>() {
-
- @Override
- public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
- return Long.compare(lhs.firstInstallTime, rhs.firstInstallTime);
- }
- });
+ sortList(mWorkFolderApps);
// Try to get a work folder.
String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
@@ -222,6 +230,7 @@
finalizeWorkFolder();
if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
+ sortList(mHomescreenApps);
mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps);
}
}
@@ -230,9 +239,7 @@
* Updates the list of installed apps and adds any new icons on homescreen or work folder.
*/
public void processPackageAdd(String[] packages) {
- mHomescreenApps = new ArrayList<>();
- mWorkFolderApps = new ArrayList<>();
-
+ initVars();
HashSet<String> packageSet = new HashSet<>();
final boolean userAppsExisted = getUserApps(packageSet);
diff --git a/src/com/android/launcher3/util/PackageManagerHelper.java b/src/com/android/launcher3/util/PackageManagerHelper.java
new file mode 100644
index 0000000..d034572
--- /dev/null
+++ b/src/com/android/launcher3/util/PackageManagerHelper.java
@@ -0,0 +1,62 @@
+/*
+ * 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.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+
+/**
+ * Utility methods using package manager
+ */
+public class PackageManagerHelper {
+
+ private static final int FLAG_SUSPENDED = 1<<30;
+
+ /**
+ * Returns true if the app can possibly be on the SDCard. This is just a workaround and doesn't
+ * guarantee that the app is on SD card.
+ */
+ public static boolean isAppOnSdcard(PackageManager pm, String packageName) {
+ return isAppEnabled(pm, packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
+ }
+
+ public static boolean isAppEnabled(PackageManager pm, String packageName) {
+ return isAppEnabled(pm, packageName, 0);
+ }
+
+ public static boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
+ return info != null && info.enabled;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ public static boolean isAppSuspended(PackageManager pm, String packageName) {
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+ return info != null && isAppSuspended(info);
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ public static boolean isAppSuspended(ApplicationInfo info) {
+ return (info.flags & FLAG_SUSPENDED) != 0;
+ }
+}
diff --git a/src/com/android/launcher3/util/ParcelableSparseArray.java b/src/com/android/launcher3/util/ParcelableSparseArray.java
new file mode 100644
index 0000000..093577e
--- /dev/null
+++ b/src/com/android/launcher3/util/ParcelableSparseArray.java
@@ -0,0 +1,52 @@
+/**
+ * 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.os.Parcel;
+import android.os.Parcelable;
+import android.util.SparseArray;
+
+public class ParcelableSparseArray extends SparseArray<Parcelable> implements Parcelable {
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ final int count = size();
+ dest.writeInt(count);
+ for (int i = 0; i < count; i++) {
+ dest.writeInt(keyAt(i));
+ dest.writeParcelable(valueAt(i), 0);
+ }
+ }
+
+ public static final Parcelable.Creator<ParcelableSparseArray> CREATOR =
+ new Parcelable.Creator<ParcelableSparseArray>() {
+ public ParcelableSparseArray createFromParcel(Parcel source) {
+ final ParcelableSparseArray array = new ParcelableSparseArray();
+ final ClassLoader loader = array.getClass().getClassLoader();
+ final int count = source.readInt();
+ for (int i = 0; i < count; i++) {
+ array.put(source.readInt(), source.readParcelable(loader));
+ }
+ return array;
+ }
+
+ public ParcelableSparseArray[] newArray(int size) {
+ return new ParcelableSparseArray[size];
+ }
+ };
+}
diff --git a/src/com/android/launcher3/util/SQLiteCacheHelper.java b/src/com/android/launcher3/util/SQLiteCacheHelper.java
new file mode 100644
index 0000000..62a30d0
--- /dev/null
+++ b/src/com/android/launcher3/util/SQLiteCacheHelper.java
@@ -0,0 +1,128 @@
+package com.android.launcher3.util;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteException;
+import android.database.sqlite.SQLiteFullException;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+/**
+ * An extension of {@link SQLiteOpenHelper} with utility methods for a single table cache DB.
+ * Any exception during write operations are ignored, and any version change causes a DB reset.
+ */
+public abstract class SQLiteCacheHelper {
+ private static final String TAG = "SQLiteCacheHelper";
+
+ private final String mTableName;
+ private final MySQLiteOpenHelper mOpenHelper;
+
+ private boolean mIgnoreWrites;
+
+ public SQLiteCacheHelper(Context context, String name, int version, String tableName) {
+ mTableName = tableName;
+ mOpenHelper = new MySQLiteOpenHelper(context, name, version);
+
+ mIgnoreWrites = false;
+ }
+
+ /**
+ * @see SQLiteDatabase#update(String, ContentValues, String, String[])
+ */
+ public void update(ContentValues values, String whereClause, String[] whereArgs) {
+ if (mIgnoreWrites) {
+ return;
+ }
+ try {
+ mOpenHelper.getWritableDatabase().update(mTableName, values, whereClause, whereArgs);
+ } catch (SQLiteFullException e) {
+ onDiskFull(e);
+ } catch (SQLiteException e) {
+ Log.d(TAG, "Ignoring sqlite exception", e);
+ }
+ }
+
+ /**
+ * @see SQLiteDatabase#delete(String, String, String[])
+ */
+ public void delete(String whereClause, String[] whereArgs) {
+ if (mIgnoreWrites) {
+ return;
+ }
+ try {
+ mOpenHelper.getWritableDatabase().delete(mTableName, whereClause, whereArgs);
+ } catch (SQLiteFullException e) {
+ onDiskFull(e);
+ } catch (SQLiteException e) {
+ Log.d(TAG, "Ignoring sqlite exception", e);
+ }
+ }
+
+ /**
+ * @see SQLiteDatabase#insertWithOnConflict(String, String, ContentValues, int)
+ */
+ public void insertOrReplace(ContentValues values) {
+ if (mIgnoreWrites) {
+ return;
+ }
+ try {
+ mOpenHelper.getWritableDatabase().insertWithOnConflict(
+ mTableName, null, values, SQLiteDatabase.CONFLICT_REPLACE);
+ } catch (SQLiteFullException e) {
+ onDiskFull(e);
+ } catch (SQLiteException e) {
+ Log.d(TAG, "Ignoring sqlite exception", e);
+ }
+ }
+
+ private void onDiskFull(SQLiteFullException e) {
+ Log.e(TAG, "Disk full, all write operations will be ignored", e);
+ mIgnoreWrites = true;
+ }
+
+ /**
+ * @see SQLiteDatabase#query(String, String[], String, String[], String, String, String)
+ */
+ public Cursor query(String[] columns, String selection, String[] selectionArgs) {
+ return mOpenHelper.getReadableDatabase().query(
+ mTableName, columns, selection, selectionArgs, null, null, null);
+ }
+
+ protected abstract void onCreateTable(SQLiteDatabase db);
+
+ /**
+ * A private inner class to prevent direct DB access.
+ */
+ private class MySQLiteOpenHelper extends SQLiteOpenHelper {
+
+ public MySQLiteOpenHelper(Context context, String name, int version) {
+ super(context, name, null, version);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ onCreateTable(db);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion != newVersion) {
+ clearDB(db);
+ }
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ if (oldVersion != newVersion) {
+ clearDB(db);
+ }
+ }
+
+ private void clearDB(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS " + mTableName);
+ onCreate(db);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/util/StringFilter.java b/src/com/android/launcher3/util/StringFilter.java
new file mode 100644
index 0000000..f539ad1
--- /dev/null
+++ b/src/com/android/launcher3/util/StringFilter.java
@@ -0,0 +1,31 @@
+package com.android.launcher3.util;
+
+import java.util.Set;
+
+/**
+ * Abstract class to filter a set of strings.
+ */
+public abstract class StringFilter {
+
+ private StringFilter() { }
+
+ public abstract boolean matches(String str);
+
+ public static StringFilter matchesAll() {
+ return new StringFilter() {
+ @Override
+ public boolean matches(String str) {
+ return true;
+ }
+ };
+ }
+
+ public static StringFilter of(final Set<String> validEntries) {
+ return new StringFilter() {
+ @Override
+ public boolean matches(String str) {
+ return validEntries.contains(str);
+ }
+ };
+ }
+}
diff --git a/src/com/android/launcher3/util/TestingUtils.java b/src/com/android/launcher3/util/TestingUtils.java
new file mode 100644
index 0000000..665c371
--- /dev/null
+++ b/src/com/android/launcher3/util/TestingUtils.java
@@ -0,0 +1,72 @@
+package com.android.launcher3.util;
+
+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;
+import android.widget.FrameLayout;
+
+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;
+
+import java.util.HashMap;
+
+public class TestingUtils {
+
+ public static final String MEMORY_TRACKER = "com.android.launcher3.testing.MemoryTracker";
+ public static final String ACTION_START_TRACKING = "com.android.launcher3.action.START_TRACKING";
+
+ public static final boolean MEMORY_DUMP_ENABLED = false;
+ public static final String SHOW_WEIGHT_WATCHER = "debug.show_mem";
+
+ public static final boolean ENABLE_CUSTOM_WIDGET_TEST = false;
+ public static final String DUMMY_WIDGET = "com.android.launcher3.testing.DummyWidget";
+
+ public static void startTrackingMemory(Context context) {
+ if (MEMORY_DUMP_ENABLED) {
+ context.startService(new Intent()
+ .setComponent(new ComponentName(context.getPackageName(), MEMORY_TRACKER))
+ .setAction(ACTION_START_TRACKING)
+ .putExtra("pid", android.os.Process.myPid())
+ .putExtra("name", "L"));
+ }
+ }
+
+ public static void addWeightWatcher(Launcher launcher) {
+ if (MEMORY_DUMP_ENABLED) {
+ boolean show = Utilities.getPrefs(launcher).getBoolean(SHOW_WEIGHT_WATCHER, true);
+
+ int id = launcher.getResources().getIdentifier("zzz_weight_watcher", "layout",
+ launcher.getPackageName());
+ View watcher = launcher.getLayoutInflater().inflate(id, null);
+ watcher.setAlpha(0.5f);
+ ((FrameLayout) launcher.findViewById(R.id.launcher)).addView(watcher,
+ new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.BOTTOM)
+ );
+
+ watcher.setVisibility(show ? View.VISIBLE : View.GONE);
+ launcher.mWeightWatcher = watcher;
+ }
+ }
+
+ public static void addDummyWidget(HashMap<String, CustomAppWidget> set) {
+ if (ENABLE_CUSTOM_WIDGET_TEST) {
+ try {
+ Class<?> clazz = Class.forName(DUMMY_WIDGET);
+ CustomAppWidget widget = (CustomAppWidget) clazz.newInstance();
+ set.put(widget.getClass().getName(), widget);
+ } catch (Exception e) {
+ Log.e("TestingUtils", "Error adding dummy widget", e);
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 461aebb..5d3af52 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -4,13 +4,13 @@
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.graphics.Rect;
-import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
+import android.util.Log;
import android.view.View;
import com.android.launcher3.AppWidgetResizeFrame;
-import com.android.launcher3.DragController.DragListener;
+import com.android.launcher3.DragController;
import com.android.launcher3.DragLayer;
import com.android.launcher3.DragSource;
import com.android.launcher3.Launcher;
@@ -19,7 +19,9 @@
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.util.Thunk;
-public class WidgetHostViewLoader implements DragListener {
+public class WidgetHostViewLoader implements DragController.DragListener {
+ private static final String TAG = "WidgetHostViewLoader";
+ private static final boolean LOGD = false;
/* Runnables to handle inflation and binding. */
@Thunk Runnable mInflateWidgetRunnable = null;
@@ -48,6 +50,10 @@
@Override
public void onDragEnd() {
+ if (LOGD) {
+ Log.d(TAG, "Cleaning up in onDragEnd()...");
+ }
+
// Cleanup up preloading state.
mLauncher.getDragController().removeDragListener(this);
@@ -62,6 +68,9 @@
// The widget was inflated and added to the DragLayer -- remove it.
if (mInfo.boundWidget != null) {
+ if (LOGD) {
+ Log.d(TAG, "...removing widget from drag layer");
+ }
mLauncher.getDragLayer().removeView(mInfo.boundWidget);
mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
mInfo.boundWidget = null;
@@ -89,6 +98,9 @@
@Override
public void run() {
mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
+ if (LOGD) {
+ Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId);
+ }
if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
mWidgetLoadingId, pInfo, options)) {
@@ -101,6 +113,9 @@
mInflateWidgetRunnable = new Runnable() {
@Override
public void run() {
+ if (LOGD) {
+ Log.d(TAG, "Inflating widget, id: " + mWidgetLoadingId);
+ }
if (mWidgetLoadingId == -1) {
return;
}
@@ -120,11 +135,17 @@
lp.x = lp.y = 0;
lp.customPosition = true;
hostView.setLayoutParams(lp);
+ if (LOGD) {
+ Log.d(TAG, "Adding host view to drag layer");
+ }
mLauncher.getDragLayer().addView(hostView);
mView.setTag(mInfo);
}
};
+ if (LOGD) {
+ Log.d(TAG, "About to bind/inflate widget");
+ }
mHandler.post(mBindWidgetRunnable);
return true;
}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 0c6ea31..c8e8cf8 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -20,7 +20,6 @@
import android.graphics.Bitmap;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.InsetDrawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView.State;
import android.util.AttributeSet;
@@ -52,13 +51,9 @@
* The widgets list view container.
*/
public class WidgetsContainerView extends BaseContainerView
- implements View.OnLongClickListener, View.OnClickListener, DragSource{
-
+ implements View.OnLongClickListener, View.OnClickListener, DragSource {
private static final String TAG = "WidgetsContainerView";
- private static final boolean DEBUG = false;
-
- /* Coefficient multiplied to the screen height for preloading widgets. */
- private static final int PRELOAD_SCREEN_HEIGHT_MULTIPLE = 1;
+ private static final boolean LOGD = false;
/* Global instances that are used inside this container. */
@Thunk Launcher mLauncher;
@@ -66,8 +61,7 @@
private IconCache mIconCache;
/* Recycler view related member variables */
- private View mContent;
- private WidgetsRecyclerView mView;
+ private WidgetsRecyclerView mRecyclerView;
private WidgetsListAdapter mAdapter;
/* Touch handling related member variables. */
@@ -76,8 +70,6 @@
/* Rendering related. */
private WidgetPreviewLoader mWidgetPreviewLoader;
- private Rect mPadding = new Rect();
-
public WidgetsContainerView(Context context) {
this(context, null);
}
@@ -92,45 +84,25 @@
mDragController = mLauncher.getDragController();
mAdapter = new WidgetsListAdapter(context, this, this, mLauncher);
mIconCache = (LauncherAppState.getInstance()).getIconCache();
- if (DEBUG) {
+ if (LOGD) {
Log.d(TAG, "WidgetsContainerView constructor");
}
}
@Override
protected void onFinishInflate() {
- mContent = findViewById(R.id.content);
- mView = (WidgetsRecyclerView) findViewById(R.id.widgets_list_view);
- mView.setAdapter(mAdapter);
-
- // This extends the layout space so that preloading happen for the {@link RecyclerView}
- mView.setLayoutManager(new LinearLayoutManager(getContext()) {
- @Override
- protected int getExtraLayoutSpace(State state) {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- return super.getExtraLayoutSpace(state)
- + grid.availableHeightPx * PRELOAD_SCREEN_HEIGHT_MULTIPLE;
- }
- });
- mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
- getPaddingBottom());
+ super.onFinishInflate();
+ mRecyclerView = (WidgetsRecyclerView) getContentView().findViewById(R.id.widgets_list_view);
+ mRecyclerView.setAdapter(mAdapter);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
//
// Returns views used for launcher transitions.
//
- public View getContentView() {
- return mView;
- }
-
- public View getRevealView() {
- // TODO(hyunyoungs): temporarily use apps view transition.
- return findViewById(R.id.widgets_reveal_view);
- }
-
public void scrollToTop() {
- mView.scrollToPosition(0);
+ mRecyclerView.scrollToPosition(0);
}
//
@@ -148,14 +120,17 @@
if (mWidgetInstructionToast != null) {
mWidgetInstructionToast.cancel();
}
- mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
- Toast.LENGTH_SHORT);
+
+ CharSequence msg = Utilities.wrapForTts(
+ getContext().getText(R.string.long_press_widget_to_add),
+ getContext().getString(R.string.long_accessible_way_to_add));
+ mWidgetInstructionToast = Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT);
mWidgetInstructionToast.show();
}
@Override
public boolean onLongClick(View v) {
- if (DEBUG) {
+ if (LOGD) {
Log.d(TAG, String.format("onLonglick [v=%s]", v));
}
// Return early if this is not initiated from a touch
@@ -164,14 +139,13 @@
if (!mLauncher.isWidgetsViewVisible() ||
mLauncher.getWorkspace().isSwitchingState()) return false;
// Return if global dragging is not enabled
- Log.d(TAG, String.format("onLonglick dragging enabled?.", v));
if (!mLauncher.isDraggingEnabled()) return false;
boolean status = beginDragging(v);
if (status && v.getTag() instanceof PendingAddWidgetInfo) {
WidgetHostViewLoader hostLoader = new WidgetHostViewLoader(mLauncher, v);
boolean preloadStatus = hostLoader.preloadWidget();
- if (DEBUG) {
+ if (LOGD) {
Log.d(TAG, String.format("preloading widget [status=%s]", preloadStatus));
}
mLauncher.getDragController().addDragListener(hostLoader);
@@ -300,6 +274,9 @@
@Override
public void onDropCompleted(View target, DragObject d, boolean isFlingToDelete,
boolean success) {
+ if (LOGD) {
+ Log.d(TAG, "onDropCompleted");
+ }
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
@@ -333,33 +310,32 @@
//
// Container rendering related.
//
-
@Override
- protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
- // Apply the top-bottom padding to the content itself so that the launcher transition is
- // clipped correctly
- mContent.setPadding(0, padding.top, 0, padding.bottom);
-
- // TODO: Use quantum_panel_dark instead of quantum_panel_shape_dark.
- InsetDrawable background = new InsetDrawable(
- getResources().getDrawable(R.drawable.quantum_panel_shape_dark), padding.left, 0,
- padding.right, 0);
- Rect bgPadding = new Rect();
- background.getPadding(bgPadding);
- mView.setBackground(background);
- getRevealView().setBackground(background.getConstantState().newDrawable());
- mView.updateBackgroundPadding(bgPadding);
+ protected void onUpdateBgPadding(Rect padding, Rect bgPadding) {
+ if (Utilities.isRtl(getResources())) {
+ getContentView().setPadding(0, bgPadding.top,
+ bgPadding.right, bgPadding.bottom);
+ mRecyclerView.updateBackgroundPadding(new Rect(bgPadding.left, 0, 0, 0));
+ } else {
+ getContentView().setPadding(bgPadding.left, bgPadding.top,
+ 0, bgPadding.bottom);
+ mRecyclerView.updateBackgroundPadding(new Rect(0, 0, bgPadding.right, 0));
+ }
}
/**
* Initialize the widget data model.
*/
public void addWidgets(WidgetsModel model) {
- mView.setWidgets(model);
+ mRecyclerView.setWidgets(model);
mAdapter.setWidgetsModel(model);
mAdapter.notifyDataSetChanged();
}
+ public boolean isEmpty() {
+ return mAdapter.getItemCount() == 0;
+ }
+
private WidgetPreviewLoader getWidgetPreviewLoader() {
if (mWidgetPreviewLoader == null) {
mWidgetPreviewLoader = LauncherAppState.getInstance().getWidgetCache();
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index f1cde29..885d96f 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -27,7 +27,6 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
import com.android.launcher3.BubbleTextView;
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 884bdc4..fe9c51c 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -102,9 +102,9 @@
// Stop the scroller if it is scrolling
stopScroll();
- getCurScrollState(mScrollPosState);
+ getCurScrollState(mScrollPosState, -1);
float pos = rowCount * touchFraction;
- int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
+ int availableScrollHeight = getAvailableScrollHeight(rowCount);
LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
@@ -131,7 +131,7 @@
}
// Skip early if, there no child laid out in the container.
- getCurScrollState(mScrollPosState);
+ getCurScrollState(mScrollPosState, -1);
if (mScrollPosState.rowIndex < 0) {
mScrollbar.setThumbOffset(-1, -1);
return;
@@ -143,10 +143,10 @@
/**
* Returns the current scroll state.
*/
- protected void getCurScrollState(ScrollPositionState stateOut) {
+ protected void getCurScrollState(ScrollPositionState stateOut, int viewTypeMask) {
stateOut.rowIndex = -1;
stateOut.rowTopOffset = -1;
- stateOut.rowHeight = -1;
+ stateOut.itemPos = -1;
// Skip early if widgets are not bound.
if (mWidgets == null) {
@@ -163,6 +163,17 @@
stateOut.rowIndex = position;
stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
- stateOut.rowHeight = child.getHeight();
+ stateOut.itemPos = position;
+ }
+
+ @Override
+ protected int getTop(int rowIndex) {
+ if (getChildCount() == 0) {
+ return 0;
+ }
+
+ // All the rows are the same height, return any child height
+ View child = getChildAt(0);
+ return child.getMeasuredHeight() * rowIndex;
}
}
\ No newline at end of file
diff --git a/tests/Android.mk b/tests/Android.mk
index eba4ade..d82f0b3 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -14,21 +14,17 @@
#
LOCAL_PATH := $(call my-dir)
-
-src_dirs := src
-res_dirs := res
-
include $(CLEAR_VARS)
+src_dirs := src
LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
LOCAL_AAPT_FLAGS := --auto-add-overlay
-LOCAL_SDK_VERSION := 21
+LOCAL_SDK_VERSION := 23
LOCAL_PACKAGE_NAME := Launcher3Tests
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 42ae5a3..8acc5e9 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -1,4 +1,4 @@
-<?xml version="2.0" encoding="utf-8"?>
+<?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");
@@ -15,15 +15,17 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
package="com.android.launcher3.tests">
+ <uses-sdk tools:overrideLibrary="android.support.test.uiautomator.v18"/>
+
<application>
<uses-library android:name="android.test.runner" />
</application>
<instrumentation
android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="com.android.launcher3"
- android:label="Unit tests for Launcher3">
+ android:targetPackage="com.android.launcher3" >
</instrumentation>
</manifest>
diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
index 1bc7c11..21df601 100644
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
@@ -15,15 +15,14 @@
*/
package com.android.launcher3;
+import android.content.res.Resources;
+import android.graphics.Point;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.util.FocusLogic;
-
import java.util.ArrayList;
/**
@@ -52,9 +51,12 @@
public void testFindClosestDeviceProfile2() {
for (InvariantDeviceProfile idf: mPredefinedDeviceProfiles) {
+ ArrayList<InvariantDeviceProfile> predefinedProfilesCopy =
+ new ArrayList<>(mPredefinedDeviceProfiles);
ArrayList<InvariantDeviceProfile> closestProfiles =
mInvariantProfile.findClosestDeviceProfiles(
- idf.minWidthDps, idf.minHeightDps, mPredefinedDeviceProfiles);
+ idf.minWidthDps, idf.minHeightDps, predefinedProfilesCopy
+ );
assertTrue(closestProfiles.get(0).equals(idf));
}
}
@@ -106,18 +108,81 @@
if (!android.os.Build.DEVICE.equals("hammerhead")) {
return;
}
- assertEquals(mInvariantProfile.numRows, 4);
- assertEquals(mInvariantProfile.numColumns, 4);
- assertEquals((int) mInvariantProfile.numHotseatIcons, 5);
-
- DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
- DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
-
- assertEquals(portraitProfile.allAppsNumCols, 3);
- assertEquals(landscapeProfile.allAppsNumCols, 5); // not used
+ assertEquals(4, mInvariantProfile.numRows);
+ assertEquals(4, mInvariantProfile.numColumns);
+ assertEquals(5, mInvariantProfile.numHotseatIcons);
}
// Add more tests for other devices, however, running them once on a single device is enough
// for verifying that for a platform version, the WindowManager and DisplayMetrics is
// working as intended.
+
+ /**
+ * Make sure that the height for the QSB is what we expect in normal mode.
+ */
+ public void testQsbNormalHeight() {
+ Resources resources = getContext().getResources();
+ DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
+ DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
+ landscapeProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL);
+ portraitProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL);
+ Rect portraitBounds = portraitProfile.getSearchBarBounds(true); // RTL shouldn't matter.
+ int portraitHeight = (int) Utilities.dpiFromPx(portraitBounds.height(),
+ resources.getDisplayMetrics());
+ Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
+ int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
+ resources.getDisplayMetrics());
+ if (portraitProfile.isTablet) {
+ assertEquals(8 + 48 + 24, portraitHeight);
+ } else {
+ assertEquals(8 + 48 + 12, portraitHeight);
+ }
+ // Make sure the height that we pass in the widget options bundle is the height of the
+ // search bar + 8dps padding top and bottom.
+ Point portraitDimens = portraitProfile.getSearchBarDimensForWidgetOpts(resources);
+ int portraitWidgetOptsHeight = portraitDimens.y;
+ Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
+ int landscapeWidgetOptsHeight = landscapeDimens.y;
+ assertEquals(8 + 48 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
+ resources.getDisplayMetrics()));
+ if (!landscapeProfile.isVerticalBarLayout()) {
+ assertEquals(portraitHeight, landscapeHeight);
+ assertEquals(portraitWidgetOptsHeight, landscapeWidgetOptsHeight);
+ }
+ }
+
+ /**
+ * Make sure that the height for the QSB is what we expect in tall mode.
+ */
+ public void testQsbTallHeight() {
+ Resources resources = getContext().getResources();
+ DeviceProfile landscapeProfile = mInvariantProfile.landscapeProfile;
+ DeviceProfile portraitProfile = mInvariantProfile.portraitProfile;
+ landscapeProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL);
+ portraitProfile.setSearchBarHeight(LauncherCallbacks.SEARCH_BAR_HEIGHT_TALL);
+ Rect portraitBounds = portraitProfile.getSearchBarBounds(true); // RTL shouldn't matter.
+ int portraitHeight = (int) Utilities.dpiFromPx(portraitBounds.height(),
+ resources.getDisplayMetrics());
+ Rect landscapeBounds = landscapeProfile.getSearchBarBounds(true); // RTL shouldn't matter.
+ int landscapeHeight = (int) Utilities.dpiFromPx(landscapeBounds.height(),
+ resources.getDisplayMetrics());
+ if (portraitProfile.isPhone) {
+ // This fails on some devices due to http://b/26884580 (portraitHeight is 101, not 100).
+ assertEquals(4 + 94 + 2, portraitHeight);
+ } else {
+ assertEquals(8 + 94 + 24, portraitHeight);
+ }
+ // Make sure the height that we pass in the widget options bundle is the height of the
+ // search bar + 8dps padding top and bottom.
+ Point portraitDimens = portraitProfile.getSearchBarDimensForWidgetOpts(resources);
+ int portraitWidgetOptsHeight = portraitDimens.y;
+ Point landscapeDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(resources);
+ int landscapeWidgetOptsHeight = landscapeDimens.y;
+ assertEquals(8 + 94 + 8, (int) Utilities.dpiFromPx(portraitWidgetOptsHeight,
+ resources.getDisplayMetrics()));
+ if (!landscapeProfile.isVerticalBarLayout()) {
+ assertEquals(portraitHeight, landscapeHeight);
+ assertEquals(portraitWidgetOptsHeight, landscapeWidgetOptsHeight);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/QuickAddWidgetTest.java b/tests/src/com/android/launcher3/QuickAddWidgetTest.java
new file mode 100644
index 0000000..027c465
--- /dev/null
+++ b/tests/src/com/android/launcher3/QuickAddWidgetTest.java
@@ -0,0 +1,86 @@
+package com.android.launcher3;
+
+import android.content.Intent;
+import android.graphics.Point;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.Direction;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.test.InstrumentationTestCase;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import java.util.List;
+
+/**
+ * Add an arbitrary widget from the widget picker very quickly to test potential race conditions.
+ */
+public class QuickAddWidgetTest extends InstrumentationTestCase {
+ // Disabled because it's flaky and not particularly useful. But this class could still be useful
+ // as an example if we want other UI tests in the future.
+ private static final boolean DISABLED = true;
+
+ private UiDevice mDevice;
+ private String mTargetPackage;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDevice = UiDevice.getInstance(getInstrumentation());
+
+ // Set Launcher3 as home.
+ mTargetPackage = getInstrumentation().getTargetContext().getPackageName();
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(mTargetPackage)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getInstrumentation().getContext().startActivity(homeIntent);
+ mDevice.wait(Until.hasObject(By.pkg(mTargetPackage).depth(0)), 3000);
+ }
+
+ public void testAddWidgetQuickly() throws Exception {
+ if (DISABLED) return;
+ mDevice.pressMenu(); // Enter overview mode.
+ mDevice.wait(Until.findObject(By.text("Widgets")), 3000).click();
+ UiObject2 calendarWidget = getWidgetByName("Clock");
+ Point center = calendarWidget.getVisibleCenter();
+ // Touch widget just long enough to pick it up (longPressTimeout), then let go immediately.
+ getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, center.x, center.y, 0));
+ Thread.sleep(ViewConfiguration.getLongPressTimeout() + 50);
+ getInstrumentation().sendPointerSync(MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, center.x, center.y, 0));
+
+ assertTrue("Drag was never started", isOnHomescreen());
+ }
+
+ private UiObject2 getWidgetByName(String name) {
+ UiObject2 widgetsList = mDevice.wait(Until.findObject(By.res(mTargetPackage,
+ "widgets_list_view")), 3000);
+ do {
+ UiObject2 widget = getVisibleWidgetByName(name);
+ if (widget != null) {
+ return widget;
+ }
+ } while (widgetsList.scroll(Direction.DOWN, 1f));
+ return getVisibleWidgetByName(name);
+ }
+
+ private UiObject2 getVisibleWidgetByName(String name) {
+ List<UiObject2> visibleWidgets = mDevice.wait(Until.findObjects(By.clazz(
+ "android.widget.LinearLayout")), 3000);
+ for (UiObject2 widget : visibleWidgets) {
+ if (widget.hasObject(By.text(name))) {
+ return widget;
+ }
+ }
+ return null;
+ }
+
+ private boolean isOnHomescreen() {
+ return mDevice.wait(Until.hasObject(By.res(mTargetPackage, "hotseat")), 3000);
+ }
+}
diff --git a/tests/src/com/android/launcher3/RotationPreferenceTest.java b/tests/src/com/android/launcher3/RotationPreferenceTest.java
new file mode 100644
index 0000000..0168ee6
--- /dev/null
+++ b/tests/src/com/android/launcher3/RotationPreferenceTest.java
@@ -0,0 +1,88 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.graphics.Rect;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Test for auto rotate preference.
+ */
+public class RotationPreferenceTest extends InstrumentationTestCase {
+
+ private UiDevice mDevice;
+ private Context mTargetContext;
+ private String mTargetPackage;
+
+ private SharedPreferences mPrefs;
+ private boolean mOriginalRotationValue;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mTargetContext = getInstrumentation().getTargetContext();
+ mTargetPackage = mTargetContext.getPackageName();
+ mPrefs = Utilities.getPrefs(mTargetContext);
+ mOriginalRotationValue = mPrefs.getBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, false);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ setRotationEnabled(mOriginalRotationValue);
+ super.tearDown();
+ }
+
+ public void testRotation_disabled() throws Exception {
+ if (mTargetContext.getResources().getBoolean(R.bool.allow_rotation)) {
+ // This is a tablet. The test is only valid to mobile devices.
+ return;
+ }
+
+ setRotationEnabled(false);
+ mDevice.setOrientationRight();
+ goToLauncher();
+
+ Rect hotseat = getHotseatBounds();
+ assertTrue(hotseat.width() > hotseat.height());
+ }
+
+ public void testRotation_enabled() throws Exception {
+ if (mTargetContext.getResources().getBoolean(R.bool.allow_rotation)) {
+ // This is a tablet. The test is only valid to mobile devices.
+ return;
+ }
+
+ setRotationEnabled(true);
+ mDevice.setOrientationRight();
+ goToLauncher();
+
+ Rect hotseat = getHotseatBounds();
+ assertTrue(hotseat.width() < hotseat.height());
+ }
+
+ private void goToLauncher() {
+ Intent homeIntent = new Intent(Intent.ACTION_MAIN)
+ .addCategory(Intent.CATEGORY_HOME)
+ .setPackage(mTargetPackage)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getInstrumentation().getContext().startActivity(homeIntent);
+ mDevice.findObject(new UiSelector().packageName(mTargetPackage)).waitForExists(6000);
+ }
+
+ private void setRotationEnabled(boolean enabled) {
+ mPrefs.edit().putBoolean(Utilities.ALLOW_ROTATION_PREFERENCE_KEY, enabled).commit();
+ }
+
+ private Rect getHotseatBounds() throws Exception {
+ UiObject hotseat = mDevice.findObject(
+ new UiSelector().resourceId(mTargetPackage + ":id/hotseat"));
+ hotseat.waitForExists(6000);
+ return hotseat.getVisibleBounds();
+ }
+}
diff --git a/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
new file mode 100644
index 0000000..ec157bc
--- /dev/null
+++ b/tests/src/com/android/launcher3/model/GridSizeMigrationTaskTest.java
@@ -0,0 +1,317 @@
+package com.android.launcher3.model;
+
+import android.content.ContentValues;
+import android.content.Intent;
+import android.database.Cursor;
+import android.graphics.Point;
+import android.test.ProviderTestCase2;
+
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.config.ProviderConfig;
+import com.android.launcher3.util.TestLauncherProvider;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+/**
+ * Unit tests for {@link GridSizeMigrationTask}
+ */
+public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherProvider> {
+
+ private static final long DESKTOP = LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ private static final long HOTSEAT = LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+
+ private static final int APPLICATION = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ private static final int SHORTCUT = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
+
+ private static final String TEST_PACKAGE = "com.android.launcher3.validpackage";
+ private static final String VALID_INTENT =
+ new Intent(Intent.ACTION_MAIN).setPackage(TEST_PACKAGE).toUri(0);
+
+ private HashSet<String> mValidPackages;
+ private InvariantDeviceProfile mIdp;
+
+ public GridSizeMigrationTaskTest() {
+ super(TestLauncherProvider.class, ProviderConfig.AUTHORITY);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mValidPackages = new HashSet<>();
+ mValidPackages.add(TEST_PACKAGE);
+
+ mIdp = new InvariantDeviceProfile();
+ }
+
+ public void testHotseatMigration_apps_dropped() throws Exception {
+ long[] hotseatItems = {
+ addItem(APPLICATION, 0, HOTSEAT, 0, 0),
+ addItem(SHORTCUT, 1, HOTSEAT, 0, 0),
+ -1,
+ addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
+ addItem(APPLICATION, 4, HOTSEAT, 0, 0),
+ };
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+ .migrateHotseat();
+ // First & last items are dropped as they have the least weight.
+ verifyHotseat(hotseatItems[1], -1, hotseatItems[3]);
+ }
+
+ public void testHotseatMigration_shortcuts_dropped() throws Exception {
+ long[] hotseatItems = {
+ addItem(APPLICATION, 0, HOTSEAT, 0, 0),
+ addItem(30, 1, HOTSEAT, 0, 0),
+ -1,
+ addItem(SHORTCUT, 3, HOTSEAT, 0, 0),
+ addItem(10, 4, HOTSEAT, 0, 0),
+ };
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, 5, 2, 3, 1)
+ .migrateHotseat();
+ // First & third items are dropped as they have the least weight.
+ verifyHotseat(hotseatItems[1], -1, hotseatItems[4]);
+ }
+
+ private void verifyHotseat(long... sortedIds) {
+ int screenId = 0;
+ int total = 0;
+
+ for (long id : sortedIds) {
+ Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{LauncherSettings.Favorites._ID},
+ "container=-101 and screen=" + screenId, null, null, null);
+
+ if (id == -1) {
+ assertEquals(0, c.getCount());
+ } else {
+ assertEquals(1, c.getCount());
+ c.moveToNext();
+ assertEquals(id, c.getLong(0));
+ total ++;
+ }
+ c.close();
+
+ screenId++;
+ }
+
+ // Verify that not other entry exist in the DB.
+ Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{LauncherSettings.Favorites._ID},
+ "container=-101", null, null, null);
+ assertEquals(total, c.getCount());
+ c.close();
+ }
+
+ public void testWorkspace_empty_row_column_removed() throws Exception {
+ long[][][] ids = createGrid(new int[][][]{{
+ { 0, 0, -1, 1},
+ { 3, 1, -1, 4},
+ { -1, -1, -1, -1},
+ { 5, 2, -1, 6},
+ }});
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+ new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+ // Column 2 and row 2 got removed.
+ verifyWorkspace(new long[][][] {{
+ {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+ {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+ {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+ }});
+ }
+
+ public void testWorkspace_new_screen_created() throws Exception {
+ long[][][] ids = createGrid(new int[][][]{{
+ { 0, 0, 0, 1},
+ { 3, 1, 0, 4},
+ { -1, -1, -1, -1},
+ { 5, 2, -1, 6},
+ }});
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+ new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+ // Items in the second column get moved to new screen
+ verifyWorkspace(new long[][][] {{
+ {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+ {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+ {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+ }, {
+ {ids[0][0][2], ids[0][1][2], -1},
+ }});
+ }
+
+ public void testWorkspace_items_merged_in_next_screen() throws Exception {
+ long[][][] ids = createGrid(new int[][][]{{
+ { 0, 0, 0, 1},
+ { 3, 1, 0, 4},
+ { -1, -1, -1, -1},
+ { 5, 2, -1, 6},
+ },{
+ { 0, 0, -1, 1},
+ { 3, 1, -1, 4},
+ }});
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+ new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+ // Items in the second column of the first screen should get placed on the 3rd
+ // row of the second screen
+ verifyWorkspace(new long[][][] {{
+ {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+ {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+ {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+ }, {
+ {ids[1][0][0], ids[1][0][1], ids[1][0][3]},
+ {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
+ {ids[0][0][2], ids[0][1][2], -1},
+ }});
+ }
+
+ public void testWorkspace_items_not_merged_in_next_screen() throws Exception {
+ // First screen has 2 items that need to be moved, but second screen has only one
+ // empty space after migration (top-left corner)
+ long[][][] ids = createGrid(new int[][][]{{
+ { 0, 0, 0, 1},
+ { 3, 1, 0, 4},
+ { -1, -1, -1, -1},
+ { 5, 2, -1, 6},
+ },{
+ { -1, 0, -1, 1},
+ { 3, 1, -1, 4},
+ { -1, -1, -1, -1},
+ { 5, 2, -1, 6},
+ }});
+
+ new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
+ new Point(4, 4), new Point(3, 3)).migrateWorkspace();
+
+ // Items in the second column of the first screen should get placed on a new screen.
+ verifyWorkspace(new long[][][] {{
+ {ids[0][0][0], ids[0][0][1], ids[0][0][3]},
+ {ids[0][1][0], ids[0][1][1], ids[0][1][3]},
+ {ids[0][3][0], ids[0][3][1], ids[0][3][3]},
+ }, {
+ { -1, ids[1][0][1], ids[1][0][3]},
+ {ids[1][1][0], ids[1][1][1], ids[1][1][3]},
+ {ids[1][3][0], ids[1][3][1], ids[1][3][3]},
+ }, {
+ {ids[0][0][2], ids[0][1][2], -1},
+ }});
+ }
+
+ /**
+ * Initializes the DB with dummy elements to represent the provided grid structure.
+ * @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
+ * type definitions. The first dimension represents the screens and the next
+ * two represent the workspace grid.
+ * @return the same grid representation where each entry is the corresponding item id.
+ */
+ private long[][][] createGrid(int[][][] typeArray) throws Exception {
+ long[][][] ids = new long[typeArray.length][][];
+
+ for (int i = 0; i < typeArray.length; i++) {
+ // Add screen to DB
+ long screenId = LauncherAppState.getLauncherProvider().generateNewScreenId();
+ ContentValues v = new ContentValues();
+ v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+ v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+ getMockContentResolver().insert(LauncherSettings.WorkspaceScreens.CONTENT_URI, v);
+
+ ids[i] = new long[typeArray[i].length][];
+ for (int y = 0; y < typeArray[i].length; y++) {
+ ids[i][y] = new long[typeArray[i][y].length];
+ for (int x = 0; x < typeArray[i][y].length; x++) {
+ if (typeArray[i][y][x] < 0) {
+ // Empty cell
+ ids[i][y][x] = -1;
+ } else {
+ ids[i][y][x] = addItem(typeArray[i][y][x], screenId, DESKTOP, x, y);
+ }
+ }
+ }
+ }
+ return ids;
+ }
+
+ /**
+ * Verifies that the workspace items are arranged in the provided order.
+ * @param ids A 3d array where the first dimension represents the screen, and the rest two
+ * represent the workspace grid.
+ */
+ private void verifyWorkspace(long[][][] ids) {
+ ArrayList<Long> allScreens = LauncherModel.loadWorkspaceScreensDb(getMockContext());
+ assertEquals(ids.length, allScreens.size());
+ int total = 0;
+
+ for (int i = 0; i < ids.length; i++) {
+ long screenId = allScreens.get(i);
+ for (int y = 0; y < ids[i].length; y++) {
+ for (int x = 0; x < ids[i][y].length; x++) {
+ long id = ids[i][y][x];
+
+ Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{LauncherSettings.Favorites._ID},
+ "container=-100 and screen=" + screenId +
+ " and cellX=" + x + " and cellY=" + y, null, null, null);
+ if (id == -1) {
+ assertEquals(0, c.getCount());
+ } else {
+ assertEquals(1, c.getCount());
+ c.moveToNext();
+ assertEquals(id, c.getLong(0));
+ total++;
+ }
+ c.close();
+ }
+ }
+ }
+
+ // Verify that not other entry exist in the DB.
+ Cursor c = getMockContentResolver().query(LauncherSettings.Favorites.CONTENT_URI,
+ new String[]{LauncherSettings.Favorites._ID},
+ "container=-100", null, null, null);
+ assertEquals(total, c.getCount());
+ c.close();
+ }
+
+ /**
+ * Adds a dummy item in the DB.
+ * @param type {@link #APPLICATION} or {@link #SHORTCUT} or >= 2 for
+ * folder (where the type represents the number of items in the folder).
+ */
+ private long addItem(int type, long screen, long container, int x, int y) throws Exception {
+ long id = LauncherAppState.getLauncherProvider().generateNewItemId();
+
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites._ID, id);
+ values.put(LauncherSettings.Favorites.CONTAINER, container);
+ values.put(LauncherSettings.Favorites.SCREEN, screen);
+ values.put(LauncherSettings.Favorites.CELLX, x);
+ values.put(LauncherSettings.Favorites.CELLY, y);
+ values.put(LauncherSettings.Favorites.SPANX, 1);
+ values.put(LauncherSettings.Favorites.SPANY, 1);
+
+ if (type == APPLICATION || type == SHORTCUT) {
+ values.put(LauncherSettings.Favorites.ITEM_TYPE, type);
+ values.put(LauncherSettings.Favorites.INTENT, VALID_INTENT);
+ } else {
+ values.put(LauncherSettings.Favorites.ITEM_TYPE,
+ LauncherSettings.Favorites.ITEM_TYPE_FOLDER);
+ // Add folder items.
+ for (int i = 0; i < type; i++) {
+ addItem(APPLICATION, 0, id, 0, 0);
+ }
+ }
+
+ getMockContentResolver().insert(LauncherSettings.Favorites.CONTENT_URI, values);
+ return id;
+ }
+}
diff --git a/tests/src/com/android/launcher3/util/FocusLogicTest.java b/tests/src/com/android/launcher3/util/FocusLogicTest.java
index ea87014..386f7dd 100644
--- a/tests/src/com/android/launcher3/util/FocusLogicTest.java
+++ b/tests/src/com/android/launcher3/util/FocusLogicTest.java
@@ -19,6 +19,7 @@
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
+import android.view.View;
import com.android.launcher3.util.FocusLogic;
@@ -57,4 +58,200 @@
// may get created in real world to test this method. OR 2) Move all the matrix
// management routine to celllayout and write tests for them.
}
+
+ public void testMoveFromBottomRightToBottomLeft() {
+ int[][] map = transpose(new int[][] {
+ {-1, 0, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1},
+ {100, 1, -1, -1, -1, -1},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 100, 1, 2, false);
+ assertEquals(1, i);
+ }
+
+ public void testMoveFromBottomRightToTopLeft() {
+ int[][] map = transpose(new int[][] {
+ {-1, 0, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1, -1},
+ {100, -1, -1, -1, -1, -1},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 100, 1, 2, false);
+ assertEquals(FocusLogic.NEXT_PAGE_FIRST_ITEM, i);
+ }
+
+ public void testMoveIntoHotseatWithEqualHotseatAndWorkspaceColumns() {
+ // Test going from an icon right above the All Apps button to the All Apps button.
+ int[][] map = transpose(new int[][] {
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, 0, -1, -1},
+ { 2, 3, 1, 4, 5},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from an icon above and to the right of the All Apps
+ // button to an icon to the right of the All Apps button.
+ map = transpose(new int[][] {
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, -1, -1},
+ {-1, -1, -1, 0, -1},
+ { 2, 3, 1, 4, 5},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(4, i);
+ }
+
+ public void testMoveIntoHotseatWithExtraColumnForAllApps() {
+ // Test going from an icon above and to the left
+ // of the All Apps button to the All Apps button.
+ int[][] map = transpose(new int[][] {
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, 0,-11, -1, -1, -1},
+ {-1, -1, -1, 1, 1, -1, -1},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from an icon above and to the right
+ // of the All Apps button to the All Apps button.
+ map = transpose(new int[][] {
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, 0, -1, -1},
+ {-1, -1, -1, 1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from the All Apps button to an icon
+ // above and to the right of the All Apps button.
+ map = transpose(new int[][] {
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, 0, -1, -1},
+ {-1, -1, -1, 1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_UP, map, 1, 1, 1, true);
+ assertEquals(0, i);
+ // Test going from an icon above and to the left of the
+ // All Apps button in landscape to the All Apps button.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, 0, -1},
+ {-11,-11,-11,-11, 1},
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test going from the All Apps button in landscape to
+ // an icon above and to the left of the All Apps button.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, 0, -1},
+ {-11,-11,-11,-11, 1},
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT, map, 1, 1, 1, true);
+ assertEquals(0, i);
+ // Test that going to the hotseat always goes to the same row as the original icon.
+ map = transpose(new int[][]{
+ { 0, 1, 2,-11, 3, 4, 5},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ {-1, -1, -1,-11, -1, -1, -1},
+ { 7, 8, 9, 6, 10, 11, 12},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(7, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 1, 1, 1, true);
+ assertEquals(8, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 2, 1, 1, true);
+ assertEquals(9, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 3, 1, 1, true);
+ assertEquals(10, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 4, 1, 1, true);
+ assertEquals(11, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 5, 1, 1, true);
+ assertEquals(12, i);
+ }
+
+ public void testCrossingAllAppsColumn() {
+ // Test crossing from left to right in portrait.
+ int[][] map = transpose(new int[][] {
+ {-1, -1,-11, -1, -1},
+ {-1, 0,-11, -1, -1},
+ {-1, -1,-11, 1, -1},
+ {-1, -1,-11, -1, -1},
+ {-1, -1, 2, -1, -1},
+ });
+ int i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test crossing from right to left in portrait.
+ map = transpose(new int[][] {
+ {-1, -1,-11, -1, -1},
+ {-1, -1,-11, 0, -1},
+ {-1, 1,-11, -1, -1},
+ {-1, -1,-11, -1, -1},
+ {-1, -1, 2, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test crossing from left to right in landscape.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, -1, -1, 0, -1},
+ {-11,-11,-11,-11, 2},
+ { -1, 1, -1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test crossing from right to left in landscape.
+ map = transpose(new int[][] {
+ { -1, -1, -1, -1, -1},
+ { -1, 0, -1, -1, -1},
+ {-11,-11,-11,-11, 2},
+ { -1, -1, 1, -1, -1},
+ { -1, -1, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 0, 1, 1, true);
+ assertEquals(1, i);
+ // Test NOT crossing it, if the All Apps button is the only suitable candidate.
+ map = transpose(new int[][]{
+ {-1, 0, -1, -1, -1},
+ {-1, 1, -1, -1, -1},
+ {-11, -11, -11, -11, 4},
+ {-1, 2, -1, -1, -1},
+ {-1, 3, -1, -1, -1},
+ });
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 1, 1, 1, true);
+ assertEquals(4, i);
+ i = FocusLogic.handleKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT, map, 2, 1, 1, true);
+ assertEquals(4, i);
+ }
+
+ /** Transposes the matrix so that we can write it in human-readable format in the tests. */
+ private int[][] transpose(int[][] m) {
+ int[][] t = new int[m[0].length][m.length];
+ for (int i = 0; i < m.length; i++) {
+ for (int j = 0; j < m[0].length; j++) {
+ t[j][i] = m[i][j];
+ }
+ }
+ return t;
+ }
}
diff --git a/tests/src/com/android/launcher3/util/TestLauncherProvider.java b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
new file mode 100644
index 0000000..8758f55
--- /dev/null
+++ b/tests/src/com/android/launcher3/util/TestLauncherProvider.java
@@ -0,0 +1,41 @@
+package com.android.launcher3.util;
+
+import android.content.Context;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherProvider;
+
+/**
+ * An extension of LauncherProvider backed up by in-memory database.
+ */
+public class TestLauncherProvider extends LauncherProvider {
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ protected synchronized void createDbIfNotExists() {
+ if (mOpenHelper == null) {
+ mOpenHelper = new MyDatabaseHelper(getContext());
+ }
+ }
+
+ @Override
+ protected void notifyListeners() { }
+
+ private static class MyDatabaseHelper extends DatabaseHelper {
+ public MyDatabaseHelper(Context context) {
+ super(context, null);
+ }
+
+ @Override
+ protected long getDefaultUserSerial() {
+ return 0;
+ }
+
+ @Override
+ protected void onEmptyDbCreated() { }
+ }
+}
\ No newline at end of file
diff --git a/util/com/android/launcher3/DecoderRing.java b/util/com/android/launcher3/DecoderRing.java
deleted file mode 100644
index 0732fe8..0000000
--- a/util/com/android/launcher3/DecoderRing.java
+++ /dev/null
@@ -1,338 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import com.android.launcher3.backup.BackupProtos.CheckedMessage;
-import com.android.launcher3.backup.BackupProtos.Favorite;
-import com.android.launcher3.backup.BackupProtos.Key;
-import com.android.launcher3.backup.BackupProtos.Journal;
-import com.android.launcher3.backup.BackupProtos.Resource;
-import com.android.launcher3.backup.BackupProtos.Screen;
-import com.android.launcher3.backup.BackupProtos.Widget;
-
-import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
-import com.google.protobuf.nano.MessageNano;
-
-import java.io.BufferedInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.lang.System;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.zip.CRC32;
-
-import javax.xml.bind.DatatypeConverter;
-
-
-/**
- * Commandline utility for decoding Launcher3 backup protocol buffers.
- *
- * <P>When using com.android.internal.backup.LocalTransport, the file names are base64-encoded Key
- * protocol buffers with a prefix, that have been base64-encoded again by the transport:
- * <pre>
- * echo "TDpDQUlnL0pxVTVnOD0=" | launcher_protoutil -k
- * </pre>
- *
- * <P>This tool understands these file names and will use the embedded Key to detect the type and
- * extract the payload automatically:
- * <pre>
- * launcher_protoutil /tmp/TDpDQUlnL0pxVTVnOD0=
- * </pre>
- *
- * <P>With payload debugging enabled, base64-encoded protocol buffers will be written to the logs.
- * Copy the encoded snippet from the log, and specify the type explicitly, with the Logs flags:
- * <pre>
- * echo "CAEYLiCJ9JKsDw==" | launcher_protoutil -L -k
- * </pre>
- * For backup payloads it is more convenient to copy the log snippet to a file:
- * <pre>
- * launcher_protoutil -L -f favorite.log
- * </pre>
- */
-class DecoderRing {
-
- public static final String STANDARD_IN = "**stdin**";
-
- private static Class[] TYPES = {
- Key.class,
- Favorite.class,
- Screen.class,
- Resource.class,
- Widget.class
- };
- static final int ICON_TYPE_BITMAP = 1;
-
- public static void main(String[ ] args)
- throws Exception {
- Class defaultType = null;
- boolean extractImages = false;
- boolean fromLogs = false;
- int skip = 0;
- List<File> files = new LinkedList<File>();
- boolean verbose = false;
-
- for (int i = 0; i < args.length; i++) {
- if ("-k".equals(args[i])) {
- defaultType = Key.class;
- } else if ("-f".equals(args[i])) {
- defaultType = Favorite.class;
- } else if ("-j".equals(args[i])) {
- defaultType = Journal.class;
- } else if ("-i".equals(args[i])) {
- defaultType = Resource.class;
- } else if ("-s".equals(args[i])) {
- defaultType = Screen.class;
- } else if ("-w".equals(args[i])) {
- defaultType = Widget.class;
- } else if ("-S".equals(args[i])) {
- if ((i + 1) < args.length) {
- skip = Integer.valueOf(args[++i]);
- } else {
- usage(args);
- }
- } else if ("-x".equals(args[i])) {
- extractImages = true;
- } else if ("-v".equals(args[i])) {
- verbose = true;
- } else if ("-L".equals(args[i])) {
- fromLogs = true;
- } else if (args[i] != null && !args[i].startsWith("-")) {
- files.add(new File(args[i]));
- } else {
- System.err.println("Unsupported flag: " + args[i]);
- usage(args);
- }
- }
-
- if (defaultType == null && files.isEmpty()) {
- // can't infer file type without the key
- usage(args);
- }
-
- if (files.size() > 1 && defaultType != null) {
- System.err.println("Explicit type ignored for multiple files.");
- defaultType = null;
- }
-
- if (files.isEmpty()) {
- files.add(new File(STANDARD_IN));
- }
-
- for (File source : files) {
- Class type = null;
- if (defaultType == null) {
- Key key = decodeKey(source.getName().getBytes(), fromLogs);
- if (key != null) {
- type = TYPES[key.type];
- if (verbose) {
- System.err.println(source.getName() + " is a " + type.getSimpleName());
- System.out.println(key.toString());
- }
- }
- } else {
- type = defaultType;
- }
-
- // read in the bytes
- ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
- BufferedInputStream input = null;
- if (source.getName() == STANDARD_IN) {
- input = new BufferedInputStream(System.in);
- } else {
- try {
- input = new BufferedInputStream(new FileInputStream(source));
- } catch (FileNotFoundException e) {
- System.err.println("failed to open file: " + source + ", " + e);
- System.exit(1);
- }
- }
- byte[] buffer = new byte[1024];
- try {
- while (input.available() > 0) {
- int n = input.read(buffer);
- int offset = 0;
- if (skip > 0) {
- offset = Math.min(skip, n);
- n -= offset;
- skip -= offset;
- }
- if (n > 0) {
- byteStream.write(buffer, offset, n);
- }
- }
- } catch (IOException e) {
- System.err.println("failed to read input: " + e);
- System.exit(1);
- }
-
- MessageNano proto = null;
- byte[] payload = byteStream.toByteArray();
- if (type == Key.class) {
- proto = decodeKey(payload, fromLogs);
- } else if (type != null) {
- proto = decodeBackupData(payload, type, fromLogs);
- }
-
- // Generic string output
- if (proto != null) {
- System.out.println(proto.toString());
- }
-
- if (extractImages) {
- String prefix = "stdin";
- if (source != null) {
- prefix = source.getName();
- }
- // save off the icon bits in a file for inspection
- if (proto instanceof Resource) {
- Resource icon = (Resource) proto;
- writeImageData(icon.data, prefix + ".png");
- }
-
- // save off the icon bits in a file for inspection
- if (proto instanceof Favorite) {
- Favorite favorite = (Favorite) proto;
- if (favorite.iconType == ICON_TYPE_BITMAP) {
- writeImageData(favorite.icon, prefix + ".png");
- }
- }
-
- // save off the widget icon and preview bits in files for inspection
- if (proto instanceof Widget) {
- Widget widget = (Widget) proto;
- if (widget.icon != null) {
- writeImageData(widget.icon.data, prefix + "_icon.png");
- }
- }
- }
- }
- System.exit(0);
- }
-
- // In logcat, backup data is base64 encoded, but in localtransport files it is raw
- private static MessageNano decodeBackupData(byte[] payload, Class type, boolean fromLogs)
- throws InstantiationException, IllegalAccessException {
- MessageNano proto;// other types are wrapped in a checksum message
- CheckedMessage wrapper = new CheckedMessage();
- try {
- if (fromLogs) {
- payload = DatatypeConverter.parseBase64Binary(new String(payload));
- }
- MessageNano.mergeFrom(wrapper, payload);
- } catch (InvalidProtocolBufferNanoException e) {
- System.err.println("failed to parse wrapper: " + e);
- System.exit(1);
- }
-
- CRC32 checksum = new CRC32();
- checksum.update(wrapper.payload);
- if (wrapper.checksum != checksum.getValue()) {
- System.err.println("wrapper checksum failed");
- System.exit(1);
- }
-
- // decode the actual message
- proto = (MessageNano) type.newInstance();
- try {
- MessageNano.mergeFrom(proto, wrapper.payload);
- } catch (InvalidProtocolBufferNanoException e) {
- System.err.println("failed to parse proto: " + e);
- System.exit(1);
- }
- return proto;
- }
-
- // In logcat, keys are base64 encoded with no prefix.
- // The localtransport adds a prefix and the base64 encodes the whole thing again.
- private static Key decodeKey(byte[] payload, boolean fromLogs) {
- Key key = new Key();
- try {
- String encodedKey = new String(payload);
- if (!fromLogs) {
- byte[] rawKey = DatatypeConverter.parseBase64Binary(encodedKey);
- if (rawKey[0] != 'L' || rawKey[1] != ':') {
- System.err.println(encodedKey + " is not a launcher backup key.");
- return null;
- }
- encodedKey = new String(rawKey, 2, rawKey.length - 2);
- }
- byte[] keyProtoData = DatatypeConverter.parseBase64Binary(encodedKey);
- key = Key.parseFrom(keyProtoData);
- } catch (InvalidProtocolBufferNanoException protoException) {
- System.err.println("failed to extract key from filename: " + protoException);
- return null;
- } catch (IllegalArgumentException base64Exception) {
- System.err.println("failed to extract key from filename: " + base64Exception);
- return null;
- }
-
- // keys are self-checked
- if (key.checksum != checkKey(key)) {
- System.err.println("key ckecksum failed");
- return null;
- }
- return key;
- }
-
- private static void writeImageData(byte[] data, String path) {
- FileOutputStream iconFile = null;
- try {
- iconFile = new FileOutputStream(path);
- iconFile.write(data);
- System.err.println("wrote " + path);
- } catch (IOException e) {
- System.err.println("failed to write image file: " + e);
- } finally {
- if (iconFile != null) {
- try {
- iconFile.close();
- } catch (IOException e) {
- System.err.println("failed to close the image file: " + e);
- }
- }
- }
- }
-
- private static long checkKey(Key key) {
- CRC32 checksum = new CRC32();
- checksum.update(key.type);
- checksum.update((int) (key.id & 0xffff));
- checksum.update((int) ((key.id >> 32) & 0xffff));
- if (key.name != null && key.name.length() > 0) {
- checksum.update(key.name.getBytes());
- }
- return checksum.getValue();
- }
-
- private static void usage(String[] args) {
- System.err.println("launcher_protoutil [-x] [-S b] [-k|-f|-i|-s|-w] [filename]");
- System.err.println("\t-k\tdecode a key");
- System.err.println("\t-f\tdecode a favorite");
- System.err.println("\t-i\tdecode a icon");
- System.err.println("\t-s\tdecode a screen");
- System.err.println("\t-w\tdecode a widget");
- System.err.println("\t-S b\tskip b bytes");
- System.err.println("\t-x\textract image data to files");
- System.err.println("\t-v\tprint key type data, as well as payload");
- System.err.println("\t-l\texpect data from logcat, instead of the local transport");
- System.err.println("\tfilename\tread from filename, not stdin");
- System.exit(1);
- }
-}
\ No newline at end of file
diff --git a/util/etc/launcher_protoutil b/util/etc/launcher_protoutil
deleted file mode 100644
index 833b583..0000000
--- a/util/etc/launcher_protoutil
+++ /dev/null
@@ -1,83 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Set up prog to be the path of this script, including following symlinks,
-# and set up progdir to be the fully-qualified pathname of its directory.
-prog="$0"
-while [ -h "${prog}" ]; do
- newProg=`/bin/ls -ld "${prog}"`
- newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
- if expr "x${newProg}" : 'x/' >/dev/null; then
- prog="${newProg}"
- else
- progdir=`dirname "${prog}"`
- prog="${progdir}/${newProg}"
- fi
-done
-oldwd=`pwd`
-progdir=`dirname "${prog}"`
-cd "${progdir}"
-progdir=`pwd`
-prog="${progdir}"/`basename "${prog}"`
-cd "${oldwd}"
-
-jarfile=launcher_protoutil_lib.jar
-libdir="$progdir"
-
-if [ ! -r "$libdir/$jarfile" ]; then
- # set jar location for the Android tree case
- libdir=`dirname "$progdir"`/framework
-fi
-
-if [ ! -r "$libdir/$jarfile" ]; then
- echo `basename "$prog"`": can't find $jarfile"
- exit 1
-fi
-
-# By default, give decoder a max heap size of 1 gig. This can be overridden
-# by using a "-J" option (see below).
-defaultMx="-Xmx1024M"
-
-# The following will extract any initial parameters of the form
-# "-J<stuff>" from the command line and pass them to the Java
-# invocation (instead of to the decoder). This makes it possible for
-# you to add a command-line parameter such as "-JXmx256M" in your
-# scripts, for example. "java" (with no args) and "java -X" give a
-# summary of available options.
-
-javaOpts=""
-
-while expr "x$1" : 'x-J' >/dev/null; do
- opt=`expr "x$1" : 'x-J\(.*\)'`
- javaOpts="${javaOpts} -${opt}"
- if expr "x${opt}" : "xXmx[0-9]" >/dev/null; then
- defaultMx="no"
- fi
- shift
-done
-
-if [ "${defaultMx}" != "no" ]; then
- javaOpts="${javaOpts} ${defaultMx}"
-fi
-
-if [ "$OSTYPE" = "cygwin" ]; then
- # For Cygwin, convert the jarfile path into native Windows style.
- jarpath=`cygpath -w "$libdir/$jarfile"`
-else
- jarpath="$libdir/$jarfile"
-fi
-
-exec java $javaOpts -jar "$jarpath" "$@"
diff --git a/util/etc/manifest.txt b/util/etc/manifest.txt
deleted file mode 100644
index 84842ed..0000000
--- a/util/etc/manifest.txt
+++ /dev/null
@@ -1 +0,0 @@
-Main-Class: com.android.launcher3.DecoderRing