summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2015-09-14 21:40:15 +0000
committerAndroid Git Automerger <android-git-automerger@android.com>2015-09-14 21:40:15 +0000
commit5fcaab43e603ceabd1d71ec355c8a0b59aac8cf0 (patch)
treefcf23a25aadc660bd2dbb8b5178da454e8a00f08
parente40f5ce4af3c743bd4dbc3abeba938559c31732c (diff)
parentea9ad5cead9ad894fb670476bb5381198cdcf2de (diff)
downloadandroid_packages_apps_Trebuchet-5fcaab43e603ceabd1d71ec355c8a0b59aac8cf0.tar.gz
android_packages_apps_Trebuchet-5fcaab43e603ceabd1d71ec355c8a0b59aac8cf0.tar.bz2
android_packages_apps_Trebuchet-5fcaab43e603ceabd1d71ec355c8a0b59aac8cf0.zip
am ea9ad5ce: Merge remote-tracking branch \'origin/ub-launcher3-burnaby\' into mnc-dev
* commit 'ea9ad5cead9ad894fb670476bb5381198cdcf2de': (76 commits) Restoring provider behavior for reloading app on old devices > For older devices, launcher will only reload in case of inserts with specific query parameters > For older devices, launcehr will notify content observers of any internal inserts > Chaning TAG for Launcher provider as max logging tag is only 23 characters Removing items which are on invalid screen Preventing null pointer crash when opening a folder Revert workaround for move to default screen on home intent. Fixing NPE in recycler view scroll bar. Adding workaround for regression caused by ag/752175 Adding gradle script for Android Studio Override the overscroll color for the widget rows. Adding graphic for all apps empty search screen. Using GET_UNINSTALLED_PACKAGES flag when getting packageInfo for a managed profile app Revert "Adding viewId for the QSB" Adding viewId for the QSB Fixing issue with missing scroll bar after fast-scrolling and searching. Fixing an issue where you would inadvertently start fastscrolling. Pending bind callbacks should be cleared before starting the loader, similar to startBinding Fixing widgets container inactive scroll bar color. Making the detached scrollbar catch up faster to the actual scroll position. Updating theme to use the light theme by default, instead of wallpaper theme > This allows us to use all the goodness of material theme > Cursor in folder edit text is no longer 1px wide Updating the target sdk to launcher Using the usermanager api to get creation time ...
-rw-r--r--.gitignore6
-rw-r--r--Android.mk15
-rw-r--r--AndroidManifest.xml3
-rw-r--r--WallpaperPicker/res/values-v19/styles.xml2
-rw-r--r--WallpaperPicker/res/values-v21/styles.xml7
-rw-r--r--WallpaperPicker/res/values/colors.xml2
-rw-r--r--WallpaperPicker/res/values/styles.xml8
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java6
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java33
-rw-r--r--build.gradle55
-rw-r--r--proguard.flags15
-rw-r--r--protos/backup.proto4
-rw-r--r--res/drawable-hdpi/ic_all_apps_bg_hand.pngbin0 -> 28031 bytes
-rw-r--r--res/drawable-hdpi/ic_all_apps_bg_icon_1.pngbin0 -> 3712 bytes
-rw-r--r--res/drawable-hdpi/ic_all_apps_bg_icon_2.pngbin0 -> 3974 bytes
-rw-r--r--res/drawable-hdpi/ic_all_apps_bg_icon_3.pngbin0 -> 5629 bytes
-rw-r--r--res/drawable-hdpi/ic_all_apps_bg_icon_4.pngbin0 -> 4108 bytes
-rwxr-xr-xres/drawable-hdpi/ic_arrow_back_grey.pngbin190 -> 152 bytes
-rwxr-xr-xres/drawable-hdpi/ic_search_grey.pngbin743 -> 419 bytes
-rw-r--r--res/drawable-mdpi/ic_all_apps_bg_hand.pngbin0 -> 16483 bytes
-rw-r--r--res/drawable-mdpi/ic_all_apps_bg_icon_1.pngbin0 -> 2390 bytes
-rw-r--r--res/drawable-mdpi/ic_all_apps_bg_icon_2.pngbin0 -> 2553 bytes
-rw-r--r--res/drawable-mdpi/ic_all_apps_bg_icon_3.pngbin0 -> 3450 bytes
-rw-r--r--res/drawable-mdpi/ic_all_apps_bg_icon_4.pngbin0 -> 2619 bytes
-rwxr-xr-xres/drawable-mdpi/ic_arrow_back_grey.pngbin151 -> 117 bytes
-rwxr-xr-xres/drawable-mdpi/ic_search_grey.pngbin497 -> 263 bytes
-rw-r--r--res/drawable-v21/all_apps_search_market_bg.xml19
-rw-r--r--res/drawable-xhdpi/ic_all_apps_bg_hand.pngbin0 -> 40314 bytes
-rw-r--r--res/drawable-xhdpi/ic_all_apps_bg_icon_1.pngbin0 -> 5139 bytes
-rw-r--r--res/drawable-xhdpi/ic_all_apps_bg_icon_2.pngbin0 -> 5482 bytes
-rw-r--r--res/drawable-xhdpi/ic_all_apps_bg_icon_3.pngbin0 -> 8186 bytes
-rw-r--r--res/drawable-xhdpi/ic_all_apps_bg_icon_4.pngbin0 -> 5680 bytes
-rwxr-xr-xres/drawable-xhdpi/ic_arrow_back_grey.pngbin234 -> 151 bytes
-rwxr-xr-xres/drawable-xhdpi/ic_search_grey.pngbin972 -> 497 bytes
-rw-r--r--res/drawable-xxhdpi/ic_all_apps_bg_hand.pngbin0 -> 70350 bytes
-rw-r--r--res/drawable-xxhdpi/ic_all_apps_bg_icon_1.pngbin0 -> 8385 bytes
-rw-r--r--res/drawable-xxhdpi/ic_all_apps_bg_icon_2.pngbin0 -> 9194 bytes
-rw-r--r--res/drawable-xxhdpi/ic_all_apps_bg_icon_3.pngbin0 -> 13880 bytes
-rw-r--r--res/drawable-xxhdpi/ic_all_apps_bg_icon_4.pngbin0 -> 9256 bytes
-rwxr-xr-xres/drawable-xxhdpi/ic_arrow_back_grey.pngbin308 -> 190 bytes
-rwxr-xr-xres/drawable-xxhdpi/ic_search_grey.pngbin1473 -> 743 bytes
-rw-r--r--res/drawable-xxxhdpi/ic_all_apps_bg_hand.pngbin0 -> 82201 bytes
-rw-r--r--res/drawable-xxxhdpi/ic_all_apps_bg_icon_1.pngbin0 -> 29432 bytes
-rw-r--r--res/drawable-xxxhdpi/ic_all_apps_bg_icon_2.pngbin0 -> 10936 bytes
-rw-r--r--res/drawable-xxxhdpi/ic_all_apps_bg_icon_3.pngbin0 -> 14554 bytes
-rw-r--r--res/drawable-xxxhdpi/ic_all_apps_bg_icon_4.pngbin0 -> 10317 bytes
-rwxr-xr-xres/drawable-xxxhdpi/ic_arrow_back_grey.pngbin359 -> 234 bytes
-rwxr-xr-xres/drawable-xxxhdpi/ic_search_grey.pngbin1996 -> 972 bytes
-rw-r--r--res/drawable/all_apps_search_market_bg.xml20
-rw-r--r--res/drawable/horizontal_line.xml21
-rw-r--r--res/layout/all_apps_empty_search.xml15
-rw-r--r--res/layout/all_apps_search_bar.xml17
-rw-r--r--res/layout/all_apps_search_market.xml29
-rw-r--r--res/layout/all_apps_search_market_divider.xml27
-rw-r--r--res/layout/overview_panel.xml3
-rw-r--r--res/layout/user_folder.xml3
-rw-r--r--res/layout/widgets_list_row_view.xml1
-rw-r--r--res/values-af/strings.xml1
-rw-r--r--res/values-am/strings.xml1
-rw-r--r--res/values-ar/strings.xml3
-rw-r--r--res/values-az-rAZ/strings.xml1
-rw-r--r--res/values-bg/strings.xml1
-rw-r--r--res/values-bn-rBD/strings.xml1
-rw-r--r--res/values-ca/strings.xml1
-rw-r--r--res/values-cs/strings.xml1
-rw-r--r--res/values-da/strings.xml1
-rw-r--r--res/values-de/strings.xml1
-rw-r--r--res/values-el/strings.xml1
-rw-r--r--res/values-en-rAU/strings.xml1
-rw-r--r--res/values-en-rGB/strings.xml1
-rw-r--r--res/values-en-rIN/strings.xml1
-rw-r--r--res/values-es-rUS/strings.xml1
-rw-r--r--res/values-es/strings.xml1
-rw-r--r--res/values-et-rEE/strings.xml1
-rw-r--r--res/values-eu-rES/strings.xml1
-rw-r--r--res/values-fa/strings.xml1
-rw-r--r--res/values-fi/strings.xml1
-rw-r--r--res/values-fr-rCA/strings.xml1
-rw-r--r--res/values-fr/strings.xml1
-rw-r--r--res/values-gl-rES/strings.xml1
-rw-r--r--res/values-gu-rIN/strings.xml1
-rw-r--r--res/values-hi/strings.xml1
-rw-r--r--res/values-hr/strings.xml1
-rw-r--r--res/values-hu/strings.xml1
-rw-r--r--res/values-hy-rAM/strings.xml9
-rw-r--r--res/values-in/strings.xml1
-rw-r--r--res/values-is-rIS/strings.xml1
-rw-r--r--res/values-it/strings.xml1
-rw-r--r--res/values-iw/strings.xml1
-rw-r--r--res/values-ja/strings.xml3
-rw-r--r--res/values-ka-rGE/strings.xml1
-rw-r--r--res/values-kk-rKZ/strings.xml1
-rw-r--r--res/values-km-rKH/strings.xml1
-rw-r--r--res/values-kn-rIN/strings.xml1
-rw-r--r--res/values-ko/strings.xml1
-rw-r--r--res/values-ky-rKG/strings.xml1
-rw-r--r--res/values-lo-rLA/strings.xml1
-rw-r--r--res/values-lt/strings.xml1
-rw-r--r--res/values-lv/strings.xml1
-rw-r--r--res/values-mk-rMK/strings.xml1
-rw-r--r--res/values-ml-rIN/strings.xml1
-rw-r--r--res/values-mn-rMN/strings.xml1
-rw-r--r--res/values-mr-rIN/strings.xml1
-rw-r--r--res/values-ms-rMY/strings.xml1
-rw-r--r--res/values-my-rMM/strings.xml1
-rw-r--r--res/values-nb/strings.xml1
-rw-r--r--res/values-ne-rNP/strings.xml1
-rw-r--r--res/values-nl/strings.xml1
-rw-r--r--res/values-pa-rIN/strings.xml1
-rw-r--r--res/values-pl/strings.xml1
-rw-r--r--res/values-pt-rPT/strings.xml1
-rw-r--r--res/values-pt/strings.xml1
-rw-r--r--res/values-ro/strings.xml1
-rw-r--r--res/values-ru/strings.xml1
-rw-r--r--res/values-si-rLK/strings.xml1
-rw-r--r--res/values-sk/strings.xml1
-rw-r--r--res/values-sl/strings.xml1
-rw-r--r--res/values-sq-rAL/strings.xml1
-rw-r--r--res/values-sr/strings.xml1
-rw-r--r--res/values-sv/strings.xml1
-rw-r--r--res/values-sw/strings.xml1
-rw-r--r--res/values-sw600dp/dimens.xml2
-rw-r--r--res/values-sw720dp/dimens.xml2
-rw-r--r--res/values-ta-rIN/strings.xml1
-rw-r--r--res/values-te-rIN/strings.xml1
-rw-r--r--res/values-th/strings.xml1
-rw-r--r--res/values-tl/strings.xml1
-rw-r--r--res/values-tr/strings.xml1
-rw-r--r--res/values-uk/strings.xml1
-rw-r--r--res/values-ur-rPK/strings.xml1
-rw-r--r--res/values-uz-rUZ/strings.xml1
-rw-r--r--res/values-vi/strings.xml1
-rw-r--r--res/values-zh-rCN/strings.xml1
-rw-r--r--res/values-zh-rHK/strings.xml1
-rw-r--r--res/values-zh-rTW/strings.xml1
-rw-r--r--res/values-zu/strings.xml1
-rw-r--r--res/values/colors.xml4
-rw-r--r--res/values/config.xml7
-rw-r--r--res/values/dimens.xml14
-rw-r--r--res/values/strings.xml7
-rw-r--r--res/xml/default_workspace_4x4.xml101
-rw-r--r--res/xml/default_workspace_5x5.xml100
-rw-r--r--res/xml/default_workspace_5x6.xml100
-rw-r--r--res/xml/dw_phone_hotseat.xml63
-rw-r--r--res/xml/dw_tablet_hotseat.xml78
-rw-r--r--src/com/android/launcher3/AppWidgetResizeFrame.java4
-rw-r--r--src/com/android/launcher3/AppWidgetsRestoredReceiver.java4
-rw-r--r--src/com/android/launcher3/AutoInstallsLayout.java4
-rw-r--r--src/com/android/launcher3/BaseContainerView.java22
-rw-r--r--src/com/android/launcher3/BaseRecyclerView.java40
-rw-r--r--src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java145
-rw-r--r--src/com/android/launcher3/BubbleTextView.java11
-rw-r--r--src/com/android/launcher3/ButtonDropTarget.java9
-rw-r--r--src/com/android/launcher3/CellLayout.java67
-rw-r--r--src/com/android/launcher3/DeviceProfile.java20
-rw-r--r--src/com/android/launcher3/DragLayer.java4
-rw-r--r--src/com/android/launcher3/DragView.java6
-rw-r--r--src/com/android/launcher3/ExtendedEditText.java (renamed from src/com/android/launcher3/allapps/AllAppsSearchEditView.java)20
-rw-r--r--src/com/android/launcher3/Folder.java21
-rw-r--r--src/com/android/launcher3/FolderEditText.java36
-rw-r--r--src/com/android/launcher3/FolderPagedView.java4
-rw-r--r--src/com/android/launcher3/IconCache.java47
-rw-r--r--src/com/android/launcher3/Launcher.java308
-rw-r--r--src/com/android/launcher3/LauncherAppState.java9
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHost.java2
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetInfo.java4
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetProviderInfo.java81
-rw-r--r--src/com/android/launcher3/LauncherBackupAgentHelper.java16
-rw-r--r--src/com/android/launcher3/LauncherBackupHelper.java98
-rw-r--r--src/com/android/launcher3/LauncherCallbacks.java1
-rw-r--r--src/com/android/launcher3/LauncherClings.java22
-rw-r--r--src/com/android/launcher3/LauncherModel.java223
-rw-r--r--src/com/android/launcher3/LauncherProvider.java41
-rw-r--r--src/com/android/launcher3/LauncherSettings.java2
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java224
-rw-r--r--src/com/android/launcher3/LauncherViewPropertyAnimator.java4
-rw-r--r--src/com/android/launcher3/PagedView.java2
-rw-r--r--src/com/android/launcher3/PendingAppWidgetHostView.java200
-rw-r--r--src/com/android/launcher3/PreloadIconDrawable.java4
-rw-r--r--src/com/android/launcher3/SearchDropTargetBar.java216
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java8
-rw-r--r--src/com/android/launcher3/StylusEventHelper.java8
-rw-r--r--src/com/android/launcher3/UninstallDropTarget.java2
-rw-r--r--src/com/android/launcher3/Utilities.java41
-rw-r--r--src/com/android/launcher3/WidgetPreviewLoader.java21
-rw-r--r--src/com/android/launcher3/Workspace.java87
-rw-r--r--src/com/android/launcher3/WorkspaceStateTransitionAnimation.java80
-rw-r--r--src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java194
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java36
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java160
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java154
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java98
-rw-r--r--src/com/android/launcher3/allapps/DefaultAppSearchController.java32
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompat.java2
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java6
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompat.java2
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompatV16.java3
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompat.java2
-rw-r--r--src/com/android/launcher3/compat/UserHandleCompat.java10
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompat.java25
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatV16.java4
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatV17.java33
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatVL.java34
-rw-r--r--src/com/android/launcher3/model/AbstractUserComparator.java22
-rw-r--r--src/com/android/launcher3/model/AppNameComparator.java2
-rw-r--r--src/com/android/launcher3/model/MigrateFromRestoreTask.java767
-rw-r--r--src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java85
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java33
-rw-r--r--src/com/android/launcher3/testing/LauncherExtension.java5
-rw-r--r--src/com/android/launcher3/util/ComponentKey.java7
-rw-r--r--src/com/android/launcher3/util/ManagedProfileHeuristic.java4
-rw-r--r--src/com/android/launcher3/util/UiThreadCircularReveal.java2
-rw-r--r--src/com/android/launcher3/util/WallpaperUtils.java4
-rw-r--r--src/com/android/launcher3/widget/PendingAddWidgetInfo.java16
-rw-r--r--src/com/android/launcher3/widget/WidgetCell.java4
-rw-r--r--src/com/android/launcher3/widget/WidgetHostViewLoader.java3
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java1
-rw-r--r--src/com/android/launcher3/widget/WidgetsListAdapter.java5
-rw-r--r--src/com/android/launcher3/widget/WidgetsRecyclerView.java36
219 files changed, 3346 insertions, 1491 deletions
diff --git a/.gitignore b/.gitignore
index aea5d6102..7240e4877 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,3 +8,9 @@ tests/stress/gen/
WallpaperPicker/gen/
WallpaperPicker/.project.properties
bin/
+.idea/
+.gradle/
+local.properties
+gradle/
+build/
+gradlew* \ No newline at end of file
diff --git a/Android.mk b/Android.mk
index aade48a82..3a6442aee 100644
--- a/Android.mk
+++ b/Android.mk
@@ -23,16 +23,25 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-v4 \
+ android-support-v7-recyclerview
+
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-java-files-under, WallpaperPicker/src) \
$(call all-proto-files-under, protos)
-LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res $(LOCAL_PATH)/res
+
+LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res \
+ $(LOCAL_PATH)/res \
+ $(LOCAL_PATH)/../../../prebuilts/sdk/current/support/v7/recyclerview/res
+
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
-LOCAL_AAPT_FLAGS := --auto-add-overlay
+LOCAL_AAPT_FLAGS := \
+ --auto-add-overlay \
+ --extra-packages android.support.v7.recyclerview
LOCAL_SDK_VERSION := current
LOCAL_PACKAGE_NAME := Launcher3
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 1fb8e8d01..99a6ad9aa 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="21" android:minSdkVersion="16"/>
+ <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="16"/>
<permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -53,7 +53,6 @@
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BIND_APPWIDGET" />
- <uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
diff --git a/WallpaperPicker/res/values-v19/styles.xml b/WallpaperPicker/res/values-v19/styles.xml
index 136cf012c..15fb0ea2a 100644
--- a/WallpaperPicker/res/values-v19/styles.xml
+++ b/WallpaperPicker/res/values-v19/styles.xml
@@ -25,7 +25,7 @@
<item name="android:windowTranslucentNavigation">true</item>
</style>
- <style name="Theme" parent="@android:style/Theme.DeviceDefault.Wallpaper.NoTitleBar">
+ <style name="Theme" parent="@style/BaseWallpaperTheme">
<item name="android:windowTranslucentStatus">true</item>
<item name="android:windowTranslucentNavigation">true</item>
</style>
diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml
index 582ab8fed..70220edb9 100644
--- a/WallpaperPicker/res/values-v21/styles.xml
+++ b/WallpaperPicker/res/values-v21/styles.xml
@@ -33,4 +33,11 @@
<item name="android:background">?android:attr/selectableItemBackgroundBorderless</item>
</style>
+ <style name="Theme" parent="@style/BaseWallpaperTheme">
+ <item name="android:windowTranslucentStatus">true</item>
+ <item name="android:windowTranslucentNavigation">true</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>
+ </style>
</resources> \ No newline at end of file
diff --git a/WallpaperPicker/res/values/colors.xml b/WallpaperPicker/res/values/colors.xml
index adae7cff6..6ba32f06d 100644
--- a/WallpaperPicker/res/values/colors.xml
+++ b/WallpaperPicker/res/values/colors.xml
@@ -19,4 +19,6 @@
-->
<resources>
<color name="wallpaper_picker_translucent_gray">#66000000</color>
+
+ <color name="launcher_accent_color">#ff009688</color>
</resources>
diff --git a/WallpaperPicker/res/values/styles.xml b/WallpaperPicker/res/values/styles.xml
index 74aeab903..d1c945a32 100644
--- a/WallpaperPicker/res/values/styles.xml
+++ b/WallpaperPicker/res/values/styles.xml
@@ -35,9 +35,15 @@
<item name="android:background">#88000000</item>
</style>
- <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>
</style>
+ <style name="Theme" parent="@style/BaseWallpaperTheme"></style>
+
<style name="ActionBarSetWallpaperStyle" parent="@android:style/Widget.DeviceDefault.ActionButton">
<item name="android:textColor">#ffffffff</item>
<item name="android:background">?android:attr/selectableItemBackground</item>
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index f2bb50944..f2459dd3e 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -223,14 +223,12 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
protected boolean isActivityDestroyed() {
- return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
- && isDestroyed();
+ return Utilities.ATLEAST_JB_MR1 && isDestroyed();
}
@Thunk void addReusableBitmap(TileSource src) {
synchronized (mReusableBitmaps) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
- && src instanceof BitmapRegionTileSource) {
+ if (Utilities.ATLEAST_KITKAT && src instanceof BitmapRegionTileSource) {
Bitmap preview = ((BitmapRegionTileSource) src).getBitmap();
if (preview != null && preview.isMutable()) {
mReusableBitmaps.add(preview);
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 88dc3e22b..59858501a 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -100,6 +100,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
@Thunk LinearLayout mWallpapersView;
@Thunk HorizontalScrollView mWallpaperScrollContainer;
+ @Thunk View mWallpaperStrip;
@Thunk ActionMode.Callback mActionModeCallback;
@Thunk ActionMode mActionMode;
@@ -379,6 +380,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
mProgressView = findViewById(R.id.loading);
mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
+ mWallpaperStrip = findViewById(R.id.wallpaper_strip);
mCropView.setTouchCallback(new CropView.TouchCallback() {
ViewPropertyAnimator mAnim;
@Override
@@ -386,15 +388,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
if (mAnim != null) {
mAnim.cancel();
}
- if (mWallpaperScrollContainer.getAlpha() == 1f) {
+ if (mWallpaperStrip.getAlpha() == 1f) {
mIgnoreNextTap = true;
}
- mAnim = mWallpaperScrollContainer.animate();
+ mAnim = mWallpaperStrip.animate();
mAnim.alpha(0f)
.setDuration(150)
.withEndAction(new Runnable() {
public void run() {
- mWallpaperScrollContainer.setVisibility(View.INVISIBLE);
+ mWallpaperStrip.setVisibility(View.INVISIBLE);
}
});
mAnim.setInterpolator(new AccelerateInterpolator(0.75f));
@@ -412,8 +414,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
if (mAnim != null) {
mAnim.cancel();
}
- mWallpaperScrollContainer.setVisibility(View.VISIBLE);
- mAnim = mWallpaperScrollContainer.animate();
+ mWallpaperStrip.setVisibility(View.VISIBLE);
+ mAnim = mWallpaperStrip.animate();
mAnim.alpha(1f)
.setDuration(150)
.setInterpolator(new DecelerateInterpolator(0.75f));
@@ -548,7 +550,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (mSelectedTile != null) {
+ // Ensure that a tile is slelected and loaded.
+ if (mSelectedTile != null && mCropView.getTileSource() != null) {
+ // Prevent user from selecting any new tile.
+ mWallpaperStrip.setVisibility(View.GONE);
+ actionBar.hide();
+
WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
info.onSave(WallpaperPickerActivity.this);
} else {
@@ -713,10 +720,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
public void onStop() {
super.onStop();
- mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
- if (mWallpaperScrollContainer.getAlpha() < 1f) {
- mWallpaperScrollContainer.setAlpha(1f);
- mWallpaperScrollContainer.setVisibility(View.VISIBLE);
+ mWallpaperStrip = findViewById(R.id.wallpaper_strip);
+ if (mWallpaperStrip.getAlpha() < 1f) {
+ mWallpaperStrip.setAlpha(1f);
+ mWallpaperStrip.setVisibility(View.VISIBLE);
}
}
@@ -970,10 +977,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
if (partner == null || !partner.hideDefaultWallpaper()) {
// Add an entry for the default wallpaper (stored in system resources)
- WallpaperTileInfo defaultWallpaperInfo =
- (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
- ? getPreKKDefaultWallpaperInfo()
- : getDefaultWallpaper();
+ WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT
+ ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo();
if (defaultWallpaperInfo != null) {
bundled.add(0, defaultWallpaperInfo);
}
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 000000000..d97175568
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,55 @@
+buildscript {
+ repositories {
+ mavenCentral()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:1.3.0'
+ classpath 'com.google.protobuf:protobuf-gradle-plugin:0.7.0'
+ }
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'com.google.protobuf'
+
+android {
+ compileSdkVersion 23
+ buildToolsVersion "22.0.1"
+
+ defaultConfig {
+ applicationId "com.android.launcher3"
+ minSdkVersion 16
+ targetSdkVersion 23
+ versionCode 1
+ versionName "1.0"
+ }
+ buildTypes {
+ debug {
+ minifyEnabled false
+ }
+ }
+ sourceSets {
+ main {
+ res.srcDirs = ['res', 'WallpaperPicker/res']
+ main.java.srcDirs = ['src', 'WallpaperPicker/src']
+ manifest.srcFile 'AndroidManifest.xml'
+ proto.srcDirs 'protos/'
+ }
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile 'com.android.support:support-v4:+'
+ compile 'com.android.support:recyclerview-v7:+'
+ compile 'com.google.protobuf.nano:protobuf-javanano:3.0.0-alpha-2'
+}
+
+protobuf {
+ // Configure the protoc executable
+ protoc {
+ artifact = 'com.google.protobuf:protoc:3.0.0-alpha-3'
+ }
+}
diff --git a/proguard.flags b/proguard.flags
index 6a9d6f345..d6b9ba3af 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,8 +1,13 @@
+-keep class com.android.launcher3.allapps.AllAppsBackgroundDrawable {
+ public void setAlpha(int);
+ public int getAlpha();
+}
+
-keep class com.android.launcher3.BaseRecyclerViewFastScrollBar {
- public void setWidth(int);
- public int getWidth();
- public void setTrackAlpha(int);
- public int getTrackAlpha();
+ public void setThumbWidth(int);
+ public int getThumbWidth();
+ public void setTrackWidth(int);
+ public int getTrackWidth();
}
-keep class com.android.launcher3.BaseRecyclerViewFastScrollPopup {
@@ -63,4 +68,4 @@
-keep class com.android.launcher3.Workspace {
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
-} \ No newline at end of file
+}
diff --git a/protos/backup.proto b/protos/backup.proto
index d8d94e8cb..6704e08ad 100644
--- a/protos/backup.proto
+++ b/protos/backup.proto
@@ -101,7 +101,10 @@ message Favorite {
optional string iconPackage = 16;
optional string iconResource = 17;
optional bytes icon = 18;
+
+ // Added in backup version 4
optional TargetType targetType = 19 [default = TARGET_NONE];
+ optional int32 rank = 20;
}
message Screen {
@@ -121,6 +124,7 @@ message Widget {
optional Resource icon = 4;
optional Resource preview = 5;
+ // Added in backup version 3
// Assume that a widget is resizable upto 2x2 if no data is available
optional int32 minSpanX = 6 [default = 2];
optional int32 minSpanY = 7 [default = 2];
diff --git a/res/drawable-hdpi/ic_all_apps_bg_hand.png b/res/drawable-hdpi/ic_all_apps_bg_hand.png
new file mode 100644
index 000000000..64f50df00
--- /dev/null
+++ 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
new file mode 100644
index 000000000..df3e2de81
--- /dev/null
+++ 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
new file mode 100644
index 000000000..7138ee8e2
--- /dev/null
+++ 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
new file mode 100644
index 000000000..ed8819990
--- /dev/null
+++ 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
new file mode 100644
index 000000000..0ff9453a6
--- /dev/null
+++ b/res/drawable-hdpi/ic_all_apps_bg_icon_4.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 ccd3900dd..c7c00886f 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_search_grey.png b/res/drawable-hdpi/ic_search_grey.png
index f4c5e27d2..bd20ba062 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-mdpi/ic_all_apps_bg_hand.png b/res/drawable-mdpi/ic_all_apps_bg_hand.png
new file mode 100644
index 000000000..d94bb7aa1
--- /dev/null
+++ 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
new file mode 100644
index 000000000..76d973fa6
--- /dev/null
+++ 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
new file mode 100644
index 000000000..0257f8cd8
--- /dev/null
+++ 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
new file mode 100644
index 000000000..67545f5c0
--- /dev/null
+++ 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
new file mode 100644
index 000000000..3e36e27b1
--- /dev/null
+++ b/res/drawable-mdpi/ic_all_apps_bg_icon_4.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 11996efe3..5892c77d5 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_search_grey.png b/res/drawable-mdpi/ic_search_grey.png
index e83891c11..c386dbb38 100755
--- a/res/drawable-mdpi/ic_search_grey.png
+++ b/res/drawable-mdpi/ic_search_grey.png
Binary files differ
diff --git a/res/drawable-v21/all_apps_search_market_bg.xml b/res/drawable-v21/all_apps_search_market_bg.xml
new file mode 100644
index 000000000..7bd2f8816
--- /dev/null
+++ b/res/drawable-v21/all_apps_search_market_bg.xml
@@ -0,0 +1,19 @@
+<?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.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/all_apps_search_market_button_focused_bg_color">
+ <item android:drawable="@color/quantum_panel_bg_color" />
+</ripple>
diff --git a/res/drawable-xhdpi/ic_all_apps_bg_hand.png b/res/drawable-xhdpi/ic_all_apps_bg_hand.png
new file mode 100644
index 000000000..5dde7f343
--- /dev/null
+++ 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
new file mode 100644
index 000000000..f5bd32a18
--- /dev/null
+++ 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
new file mode 100644
index 000000000..fb0795662
--- /dev/null
+++ 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
new file mode 100644
index 000000000..c7d687eb3
--- /dev/null
+++ 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
new file mode 100644
index 000000000..e22b96299
--- /dev/null
+++ b/res/drawable-xhdpi/ic_all_apps_bg_icon_4.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 79b9b486c..11996efe3 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_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png
index bd5fdf444..e83891c11 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-xxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxhdpi/ic_all_apps_bg_hand.png
new file mode 100644
index 000000000..e107c2e88
--- /dev/null
+++ 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
new file mode 100644
index 000000000..748283042
--- /dev/null
+++ 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
new file mode 100644
index 000000000..028c7f44d
--- /dev/null
+++ 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
new file mode 100644
index 000000000..dce7b6715
--- /dev/null
+++ 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
new file mode 100644
index 000000000..811a6b3fe
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_all_apps_bg_icon_4.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 8e42e091d..ccd3900dd 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_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png
index 1d5c91361..f4c5e27d2 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-xxxhdpi/ic_all_apps_bg_hand.png b/res/drawable-xxxhdpi/ic_all_apps_bg_hand.png
new file mode 100644
index 000000000..c638456b5
--- /dev/null
+++ 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
new file mode 100644
index 000000000..511a02a20
--- /dev/null
+++ 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
new file mode 100644
index 000000000..2cc18f852
--- /dev/null
+++ 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
new file mode 100644
index 000000000..c32f8ff66
--- /dev/null
+++ 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
new file mode 100644
index 000000000..7bead8a32
--- /dev/null
+++ 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 854a9bd1a..79b9b486c 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_search_grey.png b/res/drawable-xxxhdpi/ic_search_grey.png
index 28519fda6..bd5fdf444 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/all_apps_search_market_bg.xml b/res/drawable/all_apps_search_market_bg.xml
new file mode 100644
index 000000000..5278e00a6
--- /dev/null
+++ b/res/drawable/all_apps_search_market_bg.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_focused="true" android:drawable="@color/all_apps_search_market_button_focused_bg_color" />
+ <item android:state_pressed="true" android:drawable="@color/all_apps_search_market_button_focused_bg_color" />
+ <item android:drawable="@android:color/transparent" />
+</selector>
diff --git a/res/drawable/horizontal_line.xml b/res/drawable/horizontal_line.xml
new file mode 100644
index 000000000..3f3f17e35
--- /dev/null
+++ b/res/drawable/horizontal_line.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <size android:height="1dp" />
+ <solid android:color="#ddd" />
+</shape>
diff --git a/res/layout/all_apps_empty_search.xml b/res/layout/all_apps_empty_search.xml
index f60c4a09a..5439111a2 100644
--- a/res/layout/all_apps_empty_search.xml
+++ b/res/layout/all_apps_empty_search.xml
@@ -18,11 +18,14 @@
android:id="@+id/empty_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:gravity="center"
- android:paddingTop="24dp"
- android:paddingBottom="24dp"
- android:paddingRight="@dimen/all_apps_grid_view_start_margin"
- android:textSize="16sp"
- android:textColor="#4c4c4c"
+ android:gravity="start"
+ android:paddingTop="@dimen/all_apps_empty_search_message_top_offset"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:fontFamily="sans-serif-medium"
+ android:textSize="14sp"
+ android:textColor="#212121"
+ android:alpha="0.56"
android:focusable="false" />
diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml
index cf30eac36..69a66c817 100644
--- a/res/layout/all_apps_search_bar.xml
+++ b/res/layout/all_apps_search_bar.xml
@@ -32,14 +32,13 @@
android:id="@+id/dismiss_search_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:layout_marginStart="4dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:contentDescription="@string/all_apps_button_label"
- android:paddingBottom="13dp"
- android:paddingTop="13dp"
android:src="@drawable/ic_arrow_back_grey" />
- <com.android.launcher3.allapps.AllAppsSearchEditView
+ <com.android.launcher3.ExtendedEditText
android:id="@+id/search_box_input"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -48,7 +47,7 @@
android:gravity="fill_horizontal|center_vertical"
android:hint="@string/all_apps_search_bar_hint"
android:inputType="text|textNoSuggestions|textCapWords"
- android:imeOptions="actionDone|flagNoExtractUi"
+ android:imeOptions="actionSearch|flagNoExtractUi"
android:maxLines="1"
android:paddingLeft="8dp"
android:scrollHorizontally="true"
@@ -63,10 +62,8 @@
android:layout_width="wrap_content"
android:layout_height="@dimen/all_apps_search_bar_height"
android:layout_gravity="end|center_vertical"
- android:layout_marginEnd="4dp"
- android:layout_marginRight="4dp"
+ android:layout_marginEnd="16dp"
+ android:layout_marginRight="16dp"
android:contentDescription="@string/all_apps_search_bar_hint"
- android:paddingBottom="13dp"
- android:paddingTop="13dp"
android:src="@drawable/ic_search_grey" />
</FrameLayout> \ No newline at end of file
diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml
new file mode 100644
index 000000000..1ed508890
--- /dev/null
+++ b/res/layout/all_apps_search_market.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/search_market_text"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:gravity="start|center_vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:fontFamily="sans-serif-medium"
+ android:textSize="14sp"
+ android:textColor="@color/launcher_accent_color"
+ android:textAllCaps="true"
+ android:focusable="false"
+ android:background="@drawable/all_apps_search_market_bg" />
diff --git a/res/layout/all_apps_search_market_divider.xml b/res/layout/all_apps_search_market_divider.xml
new file mode 100644
index 000000000..39097818f
--- /dev/null
+++ b/res/layout/all_apps_search_market_divider.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:paddingTop="16dp"
+ android:paddingBottom="8dp"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:focusable="false"
+ android:scaleType="matrix"
+ android:src="@drawable/horizontal_line" /> \ No newline at end of file
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 4b7423eba..1f02dce3c 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -32,6 +32,7 @@
android:gravity="center_horizontal"
android:text="@string/wallpaper_button_text"
android:textAllCaps="true"
+ android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
@@ -45,6 +46,7 @@
android:gravity="center_horizontal"
android:text="@string/widget_button_text"
android:textAllCaps="true"
+ android:textColor="@android:color/white"
android:textSize="12sp" />
<TextView
@@ -58,6 +60,7 @@
android:gravity="center_horizontal"
android:text="@string/settings_button_text"
android:textAllCaps="true"
+ android:textColor="@android:color/white"
android:textSize="12sp" />
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index ecf7def48..252ebf01e 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -53,7 +53,7 @@
android:paddingLeft="8dp"
android:paddingRight="8dp" >
- <com.android.launcher3.FolderEditText
+ <com.android.launcher3.ExtendedEditText
android:id="@+id/folder_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
@@ -70,7 +70,6 @@
android:textColor="#ff777777"
android:textColorHighlight="#ffCCCCCC"
android:textColorHint="#ff808080"
- android:textCursorDrawable="@null"
android:textSize="14sp" />
<include
diff --git a/res/layout/widgets_list_row_view.xml b/res/layout/widgets_list_row_view.xml
index ced564801..1d593dfd1 100644
--- a/res/layout/widgets_list_row_view.xml
+++ b/res/layout/widgets_list_row_view.xml
@@ -51,6 +51,7 @@
<HorizontalScrollView
android:id="@+id/widgets_scroll_container"
+ android:theme="@style/Theme.Dark.CustomOverscroll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scrollbars="none">
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 700452463..78be3f421 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Niks meer spasie op die tuisskerm nie."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen plek meer in die Gunstelinge-laai nie"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programme"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index d7fed9de4..b5d982ee4 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"መተግበሪያዎችን ይፈልጉ"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"መተግበሪያዎችን በመጫን ላይ..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"ከ«<xliff:g id="QUERY">%1$s</xliff:g>» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"ወደ <xliff:g id="QUERY">%1$s</xliff:g> ሂድ"</string>
<string name="out_of_space" msgid="4691004494942118364">"በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"መተግበሪያዎች"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index b7cd901de..619e11d67 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -32,12 +32,13 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"البحث في التطبيقات"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"جارٍ تحميل التطبيقات…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"لم يتم العثور على أية تطبيقات تتطابق مع \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"الانتقال إلى <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"ليس هناك مساحة أخرى في هذه الشاشة الرئيسية."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"لا يوجد المزيد من الحقول في علبة المفضلة"</string>
<string name="all_apps_button_label" msgid="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>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 099a2208d..b9ea83f26 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Tətbiq Axtarın"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Tətbiqlər endirilir..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" sorğusuna uyğun Tətbiqlər tapılmadı"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> daxil olun"</string>
<string name="out_of_space" msgid="4691004494942118364">"Bu Əsas ekranda boş yer yoxdur."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritlər-də yer yoxdur"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Tətbiqlər"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index eeaa1ede4..214c64b2d 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Търсене в приложенията"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Приложенията се зареждат…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Няма намерени приложения, съответстващи на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Отваряне на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"На този начален екран няма повече място."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Няма повече място в областта с любимите"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 57f7b511d..129d2507a 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"অ্যাপ্লিকেশানগুলি লোড হচ্ছে..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> এ যান"</string>
<string name="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"অ্যাপ্লিকেশানগুলি"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 83f284a9d..cf874a451 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Ja no queda espai en aquesta pantalla d\'inici."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No hi ha més espai a la safata Preferits."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacions"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index c99a7ed84..c2f9f421b 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Hledat aplikace"</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>
<string name="out_of_space" msgid="4691004494942118364">"Na této ploše již není místo."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na panelu Oblíbené položky již není místo."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikace"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index eeab92e6e..a9487050e 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Der er ikke mere plads på denne startskærm."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Der er ikke mere plads i bakken Foretrukne"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index ac6f76b0c..96ae74b11 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"In 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>
<string name="out_of_space" msgid="4691004494942118364">"Auf diesem Startbildschirm ist kein Platz mehr vorhanden."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ablage \"Favoriten\" ist voll."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index a60458a97..81b43e2e9 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Αναζήτηση εφαρμογών"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Φόρτωση εφαρμογών…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Δεν βρέθηκαν εφαρμογές για το ερώτημα \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Μετάβαση σε <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Εφαρμογές"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 09963e422..f2a6eab16 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 09963e422..f2a6eab16 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 09963e422..f2a6eab16 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"No more room on this Home screen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"No more room in the Favourites tray"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 636674293..c754493c8 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Buscar aplicaciones"</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>
<string name="out_of_space" msgid="4691004494942118364">"No hay más espacio en esta pantalla principal."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está llena."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index 34331fce9..16cd84fb2 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Busca 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>
<string name="out_of_space" msgid="4691004494942118364">"No queda espacio en la pantalla de inicio."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"La bandeja de favoritos está completa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicaciones"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index c4ba823cc..84ca45af4 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Otsige rakendustest"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Rakenduste laadimine ..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Päringule „<xliff:g id="QUERY">%1$s</xliff:g>” ei vastanud ükski rakendus"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Mine: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Sellel avaekraanil pole enam ruumi."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Salves Lemmikud pole rohkem ruumi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Rakendused"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 8bfca6cd1..096a4f95e 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Bilatu aplikazioetan"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Aplikazioak kargatzen…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Ez da aurkitu \"<xliff:g id="QUERY">%1$s</xliff:g>\" bilaketarekin bat datorren aplikaziorik"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Joan hona: <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikazioak"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 701d59d50..3b6d01ae6 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"جستجوی برنامه‌ها"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"در حال بارگیری برنامه‌ها..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"هیچ برنامه‌ای مطابق با «<xliff:g id="QUERY">%1$s</xliff:g>» پیدا نشد"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"رفتن به <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"فضای بیشتری در این صفحه اصلی موجود نیست."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"فضای بیشتری در سینی موارد دلخواه وجود ندارد"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"برنامه‌ها"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index f0766d68a..47b580e3b 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Sovellushaku"</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>
<string name="out_of_space" msgid="4691004494942118364">"Tässä aloitusruudussa ei ole enää tilaa."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Suosikit-valikossa ei ole enää tilaa"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Sovellukset"</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 0c19ef268..91271f7e9 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur l\'écran d\'accueil."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Il n\'y a plus d\'espace dans la zone des favoris"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 662bcf14e..84e1594b4 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Rechercher dans les 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>
<string name="out_of_space" msgid="4691004494942118364">"Pas d\'espace libre sur cet écran d\'accueil."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Plus d\'espace disponible dans la zone de favoris."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Applications"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 6cf76a7c0..ccfe21ed1 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Aplicacións de busca"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Cargando aplicacións..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Non se atoparon aplicacións que coincidan con \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Ir a <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacións"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 4a2274f1b..db056a0e8 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"શોધ એપ્લિકેશનો"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"એપ્લિકેશનો લોડ કરી રહ્યું છે…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" થી મેળ ખાતી કોઈ એપ્લિકેશનો મળી નથી"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> પર જાઓ"</string>
<string name="out_of_space" msgid="4691004494942118364">"આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"મનપસંદ ટ્રે પર વધુ જગ્યા નથી"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"એપ્લિકેશનો"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 6fdbf2441..7cf4f33ef 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ऐप्‍स खोजें"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"ऐप्स लोड हो रहे हैं..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" से मिलान करने वाला कोई ऐप नहीं मिला"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> पर जाएं"</string>
<string name="out_of_space" msgid="4691004494942118364">"इस होम स्‍क्रीन पर स्थान शेष नहीं है."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"पसंदीदा ट्रे में और स्थान नहीं है"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ऐप्लिकेशन"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index a54a7e4b0..2679570dc 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Pretraži aplikacije"</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>
<string name="out_of_space" msgid="4691004494942118364">"Na ovom početnom zaslonu više nema mjesta."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nema više prostora na traci Favoriti"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index e9fc5747f..41c38b2de 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Nincs több hely ezen a kezdőképernyőn."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nincs több hely a Kedvencek tálcán"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Alkalmazások"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index de4aed158..49a9d18ab 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Հավելվածների որոնում"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Հավելվածների բեռնում…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» հարցմանը համապատասխանող հավելվածներ չեն գտնվել"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Գնալ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Այլևս տեղ չկա այս հիմնական էկրանին:"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ընտրյալների ցուցակում այլևս ազատ տեղ չկա"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ծրագրեր"</string>
@@ -59,11 +60,11 @@
<string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Պաստառներ, վիջեթներ և կարգավորումներ"</string>
<string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ՀԱՍԿԱՆԱԼԻ Է"</string>
- <string name="folder_opened" msgid="94695026776264709">"Թղթապանակը բաց է, <xliff:g id="WIDTH">%1$d</xliff:g>-ից <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Պանակը բաց է, <xliff:g id="WIDTH">%1$d</xliff:g>-ից <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="1884479294466410023">"Հպեք՝ պանակը փակելու համար"</string>
<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_closed" msgid="4100806530910930934">"Պանակը փակ է"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Պանակը վերանվանվեց <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="folder_name_format" msgid="6629239338071103179">"Թղթապանակ՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
@@ -87,7 +88,7 @@
<string name="add_to_folder_with_app" msgid="4534929978967147231">"Ավելացնել «<xliff:g id="NAME">%1$s</xliff:g>» պանակին"</string>
<string name="added_to_folder" msgid="4793259502305558003">"Տարրն ավելացվեց թղթապանակում"</string>
<string name="create_folder_with" msgid="4050141361160214248">"Ստեղծել թղթապանակ, օգտագործելով՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
- <string name="folder_created" msgid="6409794597405184510">"Թղթապանակը ստեղծվեց"</string>
+ <string name="folder_created" msgid="6409794597405184510">"Պանակը ստեղծվեց"</string>
<string name="action_move_to_workspace" msgid="1603837886334246317">"Տեղափոխել Հիմնական էկրան"</string>
<string name="action_move_screen_left" msgid="8854216831569401665">"Տեղափոխել էկրանը ձախ"</string>
<string name="action_move_screen_right" msgid="329334910274311123">"Տեղափոխել էկրանը աջ"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index c65254a2a..21c208056 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Telusuri Apps"</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>
<string name="out_of_space" msgid="4691004494942118364">"Tidak ada ruang lagi pada layar Utama ini."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tidak ada ruang tersisa di baki Favorit"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikasi"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 1d46cdc93..7422eaf58 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Leita í forritum"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Hleður forrit…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Ekki fundust forrit sem samsvara „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Fara í <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Forrit"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 02057a19c..e1e75b37f 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cerca 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>
<string name="out_of_space" msgid="4691004494942118364">"Spazio nella schermata Home esaurito."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spazio esaurito nella barra dei Preferiti"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"App"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index a8039f7c1..3686abdbb 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"חפש אפליקציות"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"טוען אפליקציות…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"לא נמצאו אפליקציות התואמות ל-\"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"עבור אל <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"אין עוד מקום במסך דף הבית הזה."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"אין עוד מקום במגש המועדפים"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"אפליקציות"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ba58d88ef..87b3fa499 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"アプリを検索"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"アプリを読み込んでいます…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"「<xliff:g id="QUERY">%1$s</xliff:g>」に一致するアプリは見つかりませんでした"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>にアクセス"</string>
<string name="out_of_space" msgid="4691004494942118364">"このホーム画面に空きスペースがありません。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"お気に入りトレイに空きスペースがありません"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"アプリ"</string>
@@ -57,7 +58,7 @@
<string name="migration_cling_copy_apps" msgid="946331230090919440">"アイコンをコピー"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"初期状態にリセットする"</string>
<string name="workspace_cling_longpress_title" msgid="9173998993909018310">"壁紙、ウィジェット、設定"</string>
- <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"カスタマイズするにはバックグラウンドを押し続けます"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"カスタマイズするには背景を押し続けます"</string>
<string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_opened" msgid="94695026776264709">"フォルダが開いています。<xliff:g id="WIDTH">%1$d</xliff:g>x<xliff:g id="HEIGHT">%2$d</xliff:g>の大きさです"</string>
<string name="folder_tap_to_close" msgid="1884479294466410023">"タップしてフォルダを閉じます"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 07030d866..290033088 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"აპების ძიება"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"აპები იტვირთება..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"„<xliff:g id="QUERY">%1$s</xliff:g>“-ის თანხვედრი აპები არ მოიძებნა"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"გადადი <xliff:g id="QUERY">%1$s</xliff:g>-ში"</string>
<string name="out_of_space" msgid="4691004494942118364">"ამ მთავარ ეკრანზე ადგილი აღარ არის."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"რჩეულების თაროზე ადგილი არ არის"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"აპები"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index e79c9ce98..cae5e5edf 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Қолданбаларды іздеу"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Қолданбалар жүктелуде…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"«<xliff:g id="QUERY">%1$s</xliff:g>» сұрауына сәйкес келетін қолданбалар жоқ"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сұрауына өту"</string>
<string name="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Қолданбалар"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 9f7f53717..5ffbf7616 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ស្វែងរកកម្មវិធី"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"កំពុងដំណើរការកម្មវិធី..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"គ្មានកម្មវិធីដែលត្រូវជាមួយ \"<xliff:g id="QUERY">%1$s</xliff:g>\" ទេ"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"ចូលទៅកាន់ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"កម្មវិធី"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index 5cd8792dd..a1aab7b5c 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ಗೆ ಹೋಗಿ"</string>
<string name="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index fa445acca..e0697121a 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"앱 검색"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"앱 로드 중..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\'<xliff:g id="QUERY">%1$s</xliff:g>\'와(과) 일치하는 앱이 없습니다."</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>(으)로 이동"</string>
<string name="out_of_space" msgid="4691004494942118364">"홈 화면에 더 이상 공간이 없습니다."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"즐겨찾기 트레이에 더 이상 공간이 없습니다."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"앱"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index 20787489e..c530b5c9f 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Колдонмолорду издөө"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Колдонмолор жүктөлүүдө…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" дал келген колдонмолор табылган жок"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> сурамына өтүңүз"</string>
<string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Колдонмолор"</string>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index dff2612ba..005f0265f 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ຊອກຫາແອັບ"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"​ກຳ​ລັງ​ໂຫລດ​ແອັບ..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"ໄປ​ທີ່ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ແອັບຯ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index b5c0832dc..442874dbe 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Šiame pagrindiniame ekrane vietos nebėra."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Mėgstamiausių dėkle nebėra vietos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programos"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 432bda443..1bef22e03 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Šajā sākuma ekrānā vairs nav vietas."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Izlases joslā vairs nav vietas."</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Lietotnes"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index 8c276e3f0..0a6704b50 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пребарување апликации"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Се вчитуваат апликации…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Не се најдени апликации што одговараат на „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Оди на <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апликации"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index 59ad153a8..2bd80d358 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ആപ്പ്‌സ് തിരയുക"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"ആപ്പ്‌സ് ലോഡുചെയ്യുന്നു..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> എന്നതിലേക്ക് പോവുക"</string>
<string name="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"അപ്ലിക്കേഷനുകൾ"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index fafb37b2c..19ba66fdb 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Апп хайх"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Аппликейшныг ачаалж байна..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"-тай нийцэх апп олдсонгүй"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> руу очих"</string>
<string name="out_of_space" msgid="4691004494942118364">"Энэ Нүүр дэлгэц зайгүй."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"\"Дуртай\" трей дээр өөр зай байхгүй байна"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апп"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index 657010d6c..dc29b5462 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अॅप्स शोधा"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"अॅप्स लोड करीत आहे..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> वर जा"</string>
<string name="out_of_space" msgid="4691004494942118364">"या मुख्य स्क्रीनवर आणखी जागा नाही."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"अॅप्स"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 0c6a5f501..26f27488c 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Cari Apl"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Memuatkan Apl…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Tiada Apl yang ditemui sepadan dengan \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Pergi ke <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Tiada lagi ruang pada skrin Laman Utama ini."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Tiada ruang dalam dulang Kegemaran lagi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apl"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index 19ec3a6ff..c13ff7e0d 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ရှာဖွေမှု Appများ"</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>
<string name="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"အပ်ပလီကေးရှင်းများ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index 21b87c894..198d56c21 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Denne startsiden er full."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritter-skuffen er full"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apper"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index dd3b3f310..472bd4f5d 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"अनुप्रयोगहरू खोज्नुहोस्"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"अनुप्रयोगहरू लोड गरिँदै..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> मा जानुहोस्"</string>
<string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index fcbe375e7..6f4981087 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Er is geen ruimte meer op dit startscherm."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Geen ruimte meer in het vak \'Favorieten\'"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index d510dc7fa..30c44280e 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ਐਪਸ ਖੋਜੋ"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"ਐਪਸ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" ਨਾਲ ਮਿਲਦੀ ਕੋਈ ਵੀ ਐਪਸ ਨਹੀਂ ਲੱਭੀਆਂ"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> ਤੇ ਜਾਓ"</string>
<string name="out_of_space" msgid="4691004494942118364">"ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ਐਪਸ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b3b761d9c..a1fd29f56 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Brak miejsca na tym ekranie głównym."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Brak miejsca w Ulubionych"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacje"</string>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 9140b62f2..0d8a43179 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Sem espaço suficiente neste Ecrã principal."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Não existe mais espaço no tabuleiro de Favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicações"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index c8e645849..41e5c021e 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Não há mais espaço na tela inicial."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Sem espaço na bandeja de favoritos"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 88cbc79fc..ffaca7856 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Nu mai este loc pe acest Ecran de pornire."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Spațiu epuizat în bara Preferate"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplicații"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 5b492fe12..6e69e4dee 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Поиск приложений"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Загрузка…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"По запросу \"<xliff:g id="QUERY">%1$s</xliff:g>\" ничего не найдено"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"На этом экране все занято"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В разделе \"Избранное\" больше нет места"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Приложения"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 68e3178ae..476fff816 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"යෙදුම් සෙවීම"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"යෙදුම් පූරණය වෙමින්…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" සමග ගැළපෙන යෙදුම් හමු නොවිණි"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> වෙත යන්න"</string>
<string name="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"යෙදුම්"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 1cb76e3a8..703b46cb4 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Na tejto ploche už nie je miesto"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Na paneli Obľúbené položky už nie je miesto"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikácie"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 92c69a562..497378b78 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Na tem začetnem zaslonu ni več prostora."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"V vrstici za priljubljene ni več prostora"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacije"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 65aabf689..2b535a3a5 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Kërko për aplikacione"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Po ngarkon aplikacionet..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Nuk u gjet asnjë aplikacion që përputhet me \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Shko te <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Nuk ka më hapësirë në këtë ekran bazë."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Nuk ka më hapësirë në tabakanë \"Të preferuarat\""</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Aplikacionet"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 0f93bf3ea..f21aae019 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Претражите апликације"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Апликације се учитавају..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Није пронађена ниједна апликација за „<xliff:g id="QUERY">%1$s</xliff:g>“"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Иди на апликацију <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"Нема више простора на овом почетном екрану."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема више простора на траци Омиљено"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Апликације"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 93fce80ca..385d7a6f6 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Det finns inte plats för mer på den här startskärmen."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoritfältet är fullt"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Appar"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 297b731bf..b86fb7325 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Hakuna nafasi katika skrini hii ya Mwanzo."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Hakuna nafasi zaidi katika treya ya Vipendeleo"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Programu"</string>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 2651fbb3f..fb54f12a2 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -19,6 +19,8 @@
<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>
<!-- Cling -->
<dimen name="cling_migration_logo_height">400dp</dimen>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index d48f9eed0..807fab919 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -18,6 +18,8 @@
<!-- All Apps -->
<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>
+ <dimen name="all_apps_empty_search_bg_top_offset">180dp</dimen>
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">8dip</dimen>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 1f2a6f959..9dac6d011 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"பயன்பாடுகளில் தேடுக"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"பயன்பாடுகளை ஏற்றுகிறது..."</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>க்குச் செல்லவும்"</string>
<string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"பயன்பாடுகள்"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index bd5fd70db..32cb292a1 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"అనువర్తనాలను శోధించండి"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"అనువర్తనాలను లోడ్ చేస్తోంది…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g>కి వెళ్లు"</string>
<string name="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"అనువర్తనాలు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 85919af9a..53ae5896b 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ค้นหาแอป"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"กำลังโหลดแอป…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"ไม่พบแอปที่ตรงกับ \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"ไปที่ <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"ไม่มีที่ว่างในหน้าจอหลักนี้"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"ไม่มีพื้นที่เหลือในถาดรายการโปรด"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"แอป"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 69a5bb7d9..43afeab53 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Mga App sa Paghahanap"</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>
<string name="out_of_space" msgid="4691004494942118364">"Wala nang lugar sa Home screen na ito."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Wala nang lugar sa tray ng Mga Paborito"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index d933c8770..064d0496a 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Bu Ana ekranda yer kalmadı."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Favoriler tepsisinde başka yer kalmadı"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Uygulamalar"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index b06177e71..ff9364e5f 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"Пошук додатків"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"Завантаження додатків…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"Немає додатків для запиту \"<xliff:g id="QUERY">%1$s</xliff:g>\""</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"Перейти в додаток <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"На цьому головному екрані більше немає місця."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"В області \"Вибране\" немає місця"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Додатки"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index ba189c87d..f3d631173 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"ایپس تلاش کریں"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"ایپس لوڈ ہو رہی ہیں…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" سے مماثل کوئی ایپس نہیں ملیں"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"<xliff:g id="QUERY">%1$s</xliff:g> پر جائیں"</string>
<string name="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"ایپس"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index c3bffeb3c..24e4c4d14 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index f57f491ae..6eff50760 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Không còn chỗ trên Màn hình chính này."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Không còn chỗ trong khay Mục yêu thích"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Ứng dụng"</string>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 82274ba2c..77f07c955 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜索应用"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"正在加载应用…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"未找到与“<xliff:g id="QUERY">%1$s</xliff:g>”相符的应用"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"转到 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"此主屏幕上已没有空间。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"收藏栏已满"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"应用"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index f7d240e05..62032ecaa 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"無法找到與「<xliff:g id="QUERY">%1$s</xliff:g>」相符的應用程式"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"主畫面已無空間。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"我的收藏寄存區沒有足夠空間"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 39a0ec77a..0b3f4d6bc 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"搜尋應用程式"</string>
<string name="all_apps_loading_message" msgid="7557140873644765180">"正在載入應用程式…"</string>
<string name="all_apps_no_search_results" msgid="6332185285860416787">"找不到符合「<xliff:g id="QUERY">%1$s</xliff:g>」的應用程式"</string>
+ <string name="all_apps_search_market_message" msgid="5470761048755751471">"前往 <xliff:g id="QUERY">%1$s</xliff:g>"</string>
<string name="out_of_space" msgid="4691004494942118364">"這個主螢幕已無空間。"</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"「我的最愛」匣已無可用空間"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"應用程式"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 83ba089df..bb49f1f0a 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -32,6 +32,7 @@
<string name="all_apps_search_bar_hint" msgid="7084713969757597256">"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>
<string name="out_of_space" msgid="4691004494942118364">"Asisekho isikhala kulesi sikrini Sasekhaya."</string>
<string name="hotseat_out_of_space" msgid="7448809638125333693">"Asisekho isikhala kwitreyi lezintandokazi"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"Izinhlelo zokusebenza"</string>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 51e4d40a5..8a7f62743 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -39,14 +39,14 @@
<color name="outline_color">#FFFFFFFF</color>
<!-- Containers -->
- <color name="container_fastscroll_thumb_inactive_color">#42000000</color>
+ <color name="container_fastscroll_thumb_inactive_color">#009688</color>
<color name="container_fastscroll_thumb_active_color">#009688</color>
<!-- All Apps -->
<color name="all_apps_grid_section_text_color">#009688</color>
+ <color name="all_apps_search_market_button_focused_bg_color">#DDDDDD</color>
<!-- Widgets view -->
- <color name="widgets_view_fastscroll_thumb_inactive_color">#42FFFFFF</color>
<color name="widgets_view_section_text_color">#FFFFFF</color>
<color name="widgets_view_item_text_color">#C4C4C4</color>
<color name="widgets_cell_color">#263238</color>
diff --git a/res/values/config.xml b/res/values/config.xml
index 73de79417..93c6d14f4 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -2,8 +2,6 @@
<!-- Dynamic Grid -->
<!-- Out of 100, the percent of space the overview bar should try and take vertically. -->
<integer name="config_dynamic_grid_overview_icon_zone_percentage">20</integer>
- <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
- <integer name="config_dynamic_grid_overview_scale_percentage">80</integer>
<!-- Miscellaneous -->
<bool name="config_largeHeap">false</bool>
@@ -30,6 +28,8 @@
<!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
<integer name="config_workspaceSpringLoadShrinkPercentage">80</integer>
+ <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
+ <integer name="config_workspaceOverviewShrinkPercentage">70</integer>
<!-- Fade/zoom in/out duration & scale in a Launcher overlay transition.
Note: This should be less than the config_overlayTransitionTime as they happen together. -->
@@ -78,6 +78,9 @@
get build information. Can be empty. -->
<string name="build_info_class" translatable="false"></string>
+ <!-- View ID to use for QSB widget -->
+ <item type="id" name="qsb_widget" />
+
<!-- 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 589d69666..36721797e 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -55,9 +55,9 @@
<!-- Notes: container_bounds_inset - quantum_panel_outer_padding -->
<dimen name="container_bounds_minus_quantum_panel_padding_inset">4dp</dimen>
- <dimen name="container_fastscroll_thumb_min_width">4dp</dimen>
- <dimen name="container_fastscroll_thumb_max_width">8dp</dimen>
- <dimen name="container_fastscroll_thumb_height">64dp</dimen>
+ <dimen name="container_fastscroll_thumb_min_width">5dp</dimen>
+ <dimen name="container_fastscroll_thumb_max_width">9dp</dimen>
+ <dimen name="container_fastscroll_thumb_height">72dp</dimen>
<dimen name="container_fastscroll_thumb_touch_inset">-24dp</dimen>
<dimen name="container_fastscroll_popup_size">72dp</dimen>
<dimen name="container_fastscroll_popup_text_size">48dp</dimen>
@@ -74,6 +74,10 @@
<dimen name="all_apps_prediction_icon_top_padding">8dp</dimen>
<dimen name="all_apps_prediction_icon_bottom_padding">18dp</dimen>
<dimen name="all_apps_list_top_bottom_padding">8dp</dimen>
+ <dimen name="all_apps_empty_search_message_top_offset">40dp</dimen>
+ <dimen name="all_apps_empty_search_bg_top_offset">144dp</dimen>
+ <dimen name="all_apps_background_canvas_width">700dp</dimen>
+ <dimen name="all_apps_background_canvas_height">475dp</dimen>
<!-- Widget tray -->
<dimen name="widget_container_inset">8dp</dimen>
@@ -129,4 +133,8 @@
<dimen name="blur_size_click_shadow">4dp</dimen>
<dimen name="click_shadow_high_shift">2dp</dimen>
+<!-- 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 88f149bd0..fefadef28 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -24,10 +24,10 @@
<!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
<string name="old_launcher_provider_uri" translatable="false">content://com.android.launcher2.settings/favorites?notify=true</string>
- <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
<string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
- <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
<string name="receive_first_load_broadcast_permission" translatable="false">com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST</string>
<!-- Application name -->
@@ -61,6 +61,9 @@
<string name="all_apps_loading_message">Loading Apps&#8230;</string>
<!-- No-search-results text. [CHAR_LIMIT=50] -->
<string name="all_apps_no_search_results">No Apps found matching \"<xliff:g id="query" example="Android">%1$s</xliff:g>\"</string>
+ <!-- Search market text. This is a format string where the first argument is the name of the activity
+ handling the search. The format string does not need to handle both of these arguments. [CHAR_LIMIT=50] -->
+ <string name="all_apps_search_market_message">Go to <xliff:g id="query" example="Play Store">%1$s</xliff:g></string>
<!-- Drag and drop -->
<skip />
diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml
index 9bec86aa8..060a1f880 100644
--- a/res/xml/default_workspace_4x4.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -15,102 +15,33 @@
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
- <!-- Far-left screen [0] -->
- <!-- Left screen [1] -->
- <appwidget
- launcher:packageName="com.android.settings"
- launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
- launcher:screen="1"
- launcher:x="0"
- launcher:y="3"
- launcher:spanX="4"
- launcher:spanY="1" />
-
- <!-- Middle screen [2] -->
- <appwidget
- launcher:packageName="com.android.deskclock"
- launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
- launcher:screen="2"
- launcher:x="1"
- launcher:y="0"
- launcher:spanX="2"
- launcher:spanY="2" />
- <favorite
- launcher:packageName="com.android.camera"
- launcher:className="com.android.camera.Camera"
- launcher:screen="2"
- launcher:x="0"
- launcher:y="3" />
+ <!-- Hotseat -->
+ <include launcher:workspace="@xml/dw_phone_hotseat" />
- <!-- Right screen [3] -->
- <favorite
- launcher:packageName="com.android.gallery3d"
- launcher:className="com.android.gallery3d.app.Gallery"
- launcher:screen="3"
- launcher:x="1"
- launcher:y="3" />
- <favorite
- launcher:packageName="com.android.settings"
- launcher:className="com.android.settings.Settings"
- launcher:screen="3"
- launcher:x="2"
- launcher:y="3" />
-
- <!-- Far-right screen [4] -->
-
- <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
- <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+ <!-- Bottom row -->
<resolve
- launcher:container="-101"
launcher:screen="0"
launcher:x="0"
- 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" />
-
- <favorite
- launcher:packageName="com.android.dialer"
- launcher:className="com.android.dialer.DialtactsActivity" />
+ launcher:y="3" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+ <favorite launcher:uri="mailto:" />
</resolve>
- <favorite
- launcher:packageName="com.android.contacts"
- launcher:className="com.android.contacts.activities.PeopleActivity"
- launcher:container="-101"
- launcher:screen="1"
+ <resolve
+ launcher:screen="0"
launcher:x="1"
- launcher:y="0" />
+ launcher:y="3" >
+ <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:container="-101"
- launcher:screen="3"
+ launcher:screen="0"
launcher:x="3"
- 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:" />
-
- <favorite
- launcher:packageName="com.android.mms"
- launcher:className="com.android.mms.ui.ConversationList" />
- </resolve>
- <resolve
- launcher:container="-101"
- launcher:screen="4"
- launcher:x="4"
- launcher:y="0" >
- <favorite
- launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
- <favorite launcher:uri="http://www.example.com/" />
-
- <favorite
- launcher:packageName="com.android.browser"
- launcher:className="com.android.browser.BrowserActivity" />
+ launcher:y="3" >
+ <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>
</favorites>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index 9bec86aa8..322661720 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -15,102 +15,34 @@
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
- <!-- Far-left screen [0] -->
- <!-- Left screen [1] -->
- <appwidget
- launcher:packageName="com.android.settings"
- launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
- launcher:screen="1"
- launcher:x="0"
- launcher:y="3"
- launcher:spanX="4"
- launcher:spanY="1" />
-
- <!-- Middle screen [2] -->
- <appwidget
- launcher:packageName="com.android.deskclock"
- launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
- launcher:screen="2"
- launcher:x="1"
- launcher:y="0"
- launcher:spanX="2"
- launcher:spanY="2" />
- <favorite
- launcher:packageName="com.android.camera"
- launcher:className="com.android.camera.Camera"
- launcher:screen="2"
- launcher:x="0"
- launcher:y="3" />
+ <!-- Hotseat -->
+ <include launcher:workspace="@xml/dw_phone_hotseat" />
- <!-- Right screen [3] -->
- <favorite
- launcher:packageName="com.android.gallery3d"
- launcher:className="com.android.gallery3d.app.Gallery"
- launcher:screen="3"
- launcher:x="1"
- launcher:y="3" />
- <favorite
- launcher:packageName="com.android.settings"
- launcher:className="com.android.settings.Settings"
- launcher:screen="3"
- launcher:x="2"
- launcher:y="3" />
-
- <!-- Far-right screen [4] -->
-
- <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
- <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+ <!-- Bottom row -->
<resolve
- launcher:container="-101"
launcher:screen="0"
launcher:x="0"
- 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" />
+ launcher:y="4" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+ <favorite launcher:uri="mailto:" />
- <favorite
- launcher:packageName="com.android.dialer"
- launcher:className="com.android.dialer.DialtactsActivity" />
</resolve>
- <favorite
- launcher:packageName="com.android.contacts"
- launcher:className="com.android.contacts.activities.PeopleActivity"
- launcher:container="-101"
- launcher:screen="1"
- launcher:x="1"
- launcher:y="0" />
-
<resolve
- launcher:container="-101"
- launcher:screen="3"
- launcher:x="3"
- 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:" />
+ launcher:screen="0"
+ launcher:x="1"
+ launcher:y="4" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_GALLERY;end" />
+ <favorite launcher:uri="#Intent;type=images/*;end" />
- <favorite
- launcher:packageName="com.android.mms"
- launcher:className="com.android.mms.ui.ConversationList" />
</resolve>
+
<resolve
- launcher:container="-101"
- launcher:screen="4"
+ launcher:screen="0"
launcher:x="4"
- launcher:y="0" >
- <favorite
- launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
- <favorite launcher:uri="http://www.example.com/" />
-
- <favorite
- launcher:packageName="com.android.browser"
- launcher:className="com.android.browser.BrowserActivity" />
+ launcher:y="4" >
+ <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>
-
</favorites>
diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml
index d42a93a6a..bc236fb14 100644
--- a/res/xml/default_workspace_5x6.xml
+++ b/res/xml/default_workspace_5x6.xml
@@ -15,101 +15,23 @@
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
- <!-- Far-left screen [0] -->
- <!-- Left screen [1] -->
- <appwidget
- launcher:packageName="com.android.settings"
- launcher:className="com.android.settings.widget.SettingsAppWidgetProvider"
- launcher:screen="1"
- launcher:x="0"
- launcher:y="3"
- launcher:spanX="4"
- launcher:spanY="1" />
+ <!-- Hotseat -->
+ <include launcher:workspace="@xml/dw_tablet_hotseat" />
- <!-- Middle screen [2] -->
- <appwidget
- launcher:packageName="com.android.deskclock"
- launcher:className="com.android.alarmclock.AnalogAppWidgetProvider"
- launcher:screen="2"
- launcher:x="1"
- launcher:y="0"
- launcher:spanX="2"
- launcher:spanY="2" />
+ <!-- Bottom row -->
<favorite
- launcher:packageName="com.android.camera"
- launcher:className="com.android.camera.Camera"
- launcher:screen="2"
+ launcher:screen="0"
launcher:x="0"
- launcher:y="3" />
-
- <!-- Right screen [3] -->
- <favorite
- launcher:packageName="com.android.gallery3d"
- launcher:className="com.android.gallery3d.app.Gallery"
- launcher:screen="3"
- launcher:x="1"
- launcher:y="3" />
- <favorite
- launcher:packageName="com.android.settings"
- launcher:className="com.android.settings.Settings"
- launcher:screen="3"
- launcher:x="2"
- launcher:y="3" />
-
- <!-- Far-right screen [4] -->
-
- <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
- <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
- <resolve
- launcher:container="-101"
- launcher:screen="1"
- launcher:x="1"
- 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" />
-
- <favorite
- launcher:packageName="com.android.dialer"
- launcher:className="com.android.dialer.DialtactsActivity" />
- </resolve>
-
- <favorite
- launcher:packageName="com.android.contacts"
- launcher:className="com.android.contacts.activities.PeopleActivity"
- launcher:container="-101"
- launcher:screen="2"
- launcher:x="2"
- launcher:y="0" />
-
- <resolve
- launcher:container="-101"
- launcher:screen="4"
- launcher:x="4"
- 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:" />
+ launcher:y="4"
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CONTACTS;end" />
- <favorite
- launcher:packageName="com.android.mms"
- launcher:className="com.android.mms.ui.ConversationList" />
- </resolve>
<resolve
- launcher:container="-101"
- launcher:screen="5"
+ launcher:screen="0"
launcher:x="5"
- launcher:y="0" >
- <favorite
- launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
- <favorite launcher:uri="http://www.example.com/" />
-
- <favorite
- launcher:packageName="com.android.browser"
- launcher:className="com.android.browser.BrowserActivity" />
+ launcher:y="4" >
+ <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>
+
</favorites>
diff --git a/res/xml/dw_phone_hotseat.xml b/res/xml/dw_phone_hotseat.xml
new file mode 100644
index 000000000..b58994d1d
--- /dev/null
+++ b/res/xml/dw_phone_hotseat.xml
@@ -0,0 +1,63 @@
+<?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) -->
+ <!-- Dialer, Messaging, [All Apps], Browser, Camera -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ 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>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ 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="3"
+ launcher:x="3"
+ launcher:y="0" >
+ <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:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <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/dw_tablet_hotseat.xml b/res/xml/dw_tablet_hotseat.xml
new file mode 100644
index 000000000..671ccba3c
--- /dev/null
+++ b/res/xml/dw_tablet_hotseat.xml
@@ -0,0 +1,78 @@
+<?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, Email, Browser, [All Apps], Music, Gallery, Camera -->
+ <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>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" >
+ <favorite launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_EMAIL;end" />
+ <favorite launcher:uri="mailto:" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+ <favorite launcher:uri="http://www.example.com/" />
+ </resolve>
+
+ <!-- All Apps -->
+
+ <favorite
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0"
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MUSIC;end" />
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="5"
+ launcher:x="5"
+ launcher:y="0" >
+ <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:container="-101"
+ launcher:screen="6"
+ launcher:x="6"
+ launcher:y="0" >
+ <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/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index ea7c22189..e6bf52531 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -75,8 +75,8 @@ public class AppWidgetResizeFrame extends FrameLayout {
mResizeMode = info.resizeMode;
mDragLayer = dragLayer;
- mMinHSpan = info.getMinSpanX(mLauncher);
- mMinVSpan = info.getMinSpanY(mLauncher);
+ mMinHSpan = info.minSpanX;
+ mMinVSpan = info.minSpanY;
setBackgroundResource(R.drawable.widget_resize_shadow);
setForeground(getResources().getDrawable(R.drawable.widget_resize_frame));
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
index 5e7a012d2..b1d51ece0 100644
--- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -13,6 +13,7 @@ import android.os.AsyncTask;
import android.util.Log;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
import java.util.ArrayList;
import java.util.List;
@@ -48,7 +49,8 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
final int state;
if (LauncherModel.isValidProvider(provider)) {
- state = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ // This will ensure that we show 'Click to setup' UI if required.
+ state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
} else {
state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
}
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
index 99a98ddac..440e4e7b9 100644
--- a/src/com/android/launcher3/AutoInstallsLayout.java
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -355,7 +355,7 @@ public class AutoInstallsLayout {
return addShortcut(info.loadLabel(mPackageManager).toString(),
intent, Favorites.ITEM_TYPE_APPLICATION);
} catch (PackageManager.NameNotFoundException e) {
- if (LOGD) Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
+ Log.e(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
}
return -1;
} else {
@@ -367,7 +367,7 @@ public class AutoInstallsLayout {
* Helper method to allow extending the parser capabilities
*/
protected long invalidPackageOrClass(XmlResourceParser parser) {
- if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+ Log.w(TAG, "Skipping invalid <favorite> with no component");
return -1;
}
}
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index c8de9df10..c11824054 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -34,10 +34,12 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
// 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 bounds of the container
+ // The computed bounds of the search bar
+ private Rect mSearchBarBounds = new Rect();
+ // The computed bounds of the container
protected Rect mContentBounds = new Rect();
- // The padding to apply to the container to achieve the bounds
- protected Rect mContentPadding = new Rect();
+ // The computed padding to apply to the container to achieve the container bounds
+ private 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;
@@ -90,7 +92,7 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
*/
protected void updateBackgroundAndPaddings() {
Rect padding;
- Rect searchBarBounds = new Rect(mFixedSearchBarBounds);
+ Rect searchBarBounds = new Rect();
if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
// Use the default bounds
padding = new Rect(mInsets.left + mContainerBoundsInset,
@@ -110,14 +112,20 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
(mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)),
getMeasuredWidth() - mFixedSearchBarBounds.right,
mInsets.bottom + mContainerBoundsInset);
+
+ // Use the search bounds
+ searchBarBounds.set(mFixedSearchBarBounds);
}
- if (!padding.equals(mContentPadding) || !searchBarBounds.equals(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)) {
mContentPadding.set(padding);
mContentBounds.set(padding.left, padding.top,
getMeasuredWidth() - padding.right,
getMeasuredHeight() - padding.bottom);
- mFixedSearchBarBounds.set(searchBarBounds);
- onUpdateBackgroundAndPaddings(mFixedSearchBarBounds, padding);
+ mSearchBarBounds.set(searchBarBounds);
+ onUpdateBackgroundAndPaddings(mSearchBarBounds, padding);
}
}
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index 0fae427e8..f0d8b3b3d 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -92,9 +92,15 @@ public abstract class BaseRecyclerView extends RecyclerView
// TODO(winsonc): If we want to animate the section heads while scrolling, we can
// initiate that here if the recycler view scroll state is not
// RecyclerView.SCROLL_STATE_IDLE.
+
+ onUpdateScrollbar(dy);
}
}
+ public void reset() {
+ mScrollbar.reattachThumbToScroll();
+ }
+
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -143,7 +149,7 @@ public abstract class BaseRecyclerView extends RecyclerView
mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
break;
}
- return mScrollbar.isDragging();
+ return mScrollbar.isDraggingThumb();
}
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -185,12 +191,10 @@ public abstract class BaseRecyclerView extends RecyclerView
* AvailableScrollHeight = Total height of the all items - last page height
*
* This assumes that all rows are the same height.
- *
- * @param yOffset the offset from the top of the recycler view to start tracking.
*/
- protected int getAvailableScrollHeight(int rowCount, int rowHeight, int yOffset) {
+ protected int getAvailableScrollHeight(int rowCount, int rowHeight) {
int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
- int scrollHeight = getPaddingTop() + yOffset + rowCount * rowHeight + getPaddingBottom();
+ int scrollHeight = getPaddingTop() + rowCount * rowHeight + getPaddingBottom();
int availableScrollHeight = scrollHeight - visibleHeight;
return availableScrollHeight;
}
@@ -222,7 +226,7 @@ public abstract class BaseRecyclerView extends RecyclerView
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
- onUpdateScrollbar();
+ onUpdateScrollbar(0);
mScrollbar.draw(canvas);
}
@@ -234,24 +238,21 @@ public abstract class BaseRecyclerView extends RecyclerView
* @param scrollPosState the current scroll position
* @param rowCount the number of rows, used to calculate the total scroll height (assumes that
* all rows are the same height)
- * @param yOffset the offset to start tracking in the recycler view (only used for all apps)
*/
protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState,
- int rowCount, int yOffset) {
- int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight,
- yOffset);
- int availableScrollBarHeight = getAvailableScrollBarHeight();
-
+ int rowCount) {
// Only show the scrollbar if there is height to be scrolled
+ int availableScrollBarHeight = getAvailableScrollBarHeight();
+ int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight);
if (availableScrollHeight <= 0) {
- mScrollbar.setScrollbarThumbOffset(-1, -1);
+ mScrollbar.setThumbOffset(-1, -1);
return;
}
// 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() + yOffset +
+ int scrollY = getPaddingTop() +
(scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -261,9 +262,9 @@ public abstract class BaseRecyclerView extends RecyclerView
if (Utilities.isRtl(getResources())) {
scrollBarX = mBackgroundPadding.left;
} else {
- scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getWidth();
+ scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth();
}
- mScrollbar.setScrollbarThumbOffset(scrollBarX, scrollBarY);
+ mScrollbar.setThumbOffset(scrollBarX, scrollBarY);
}
/**
@@ -276,10 +277,15 @@ public abstract class BaseRecyclerView extends RecyclerView
* Updates the bounds for the scrollbar.
* <p>Override in each subclass of this base class.
*/
- public abstract void onUpdateScrollbar();
+ public 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);
} \ No newline at end of file
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index 2c4184dc4..fcee7e8dd 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -23,6 +23,7 @@ import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
@@ -51,14 +52,21 @@ public class BaseRecyclerViewFastScrollBar {
private int mThumbActiveColor;
@Thunk Point mThumbOffset = new Point(-1, -1);
@Thunk Paint mThumbPaint;
- private Paint mTrackPaint;
private int mThumbMinWidth;
private int mThumbMaxWidth;
@Thunk int mThumbWidth;
@Thunk int mThumbHeight;
+ private int mThumbCurvature;
+ private Path mThumbPath = new Path();
+ private Paint mTrackPaint;
+ private int mTrackWidth;
+ private float mLastTouchY;
// The inset is the buffer around which a point will still register as a click on the scrollbar
private int mTouchInset;
private boolean mIsDragging;
+ private boolean mIsThumbDetached;
+ private boolean mCanThumbDetach;
+ private boolean mIgnoreDragGesture;
// This is the offset from the top of the scrollbar when the user first starts touching. To
// prevent jumping, this offset is applied as the user scrolls.
@@ -72,51 +80,74 @@ public class BaseRecyclerViewFastScrollBar {
mPopup = new BaseRecyclerViewFastScrollPopup(rv, res);
mTrackPaint = new Paint();
mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK));
- mTrackPaint.setAlpha(0);
+ mTrackPaint.setAlpha(MAX_TRACK_ALPHA);
mThumbInactiveColor = rv.getFastScrollerThumbInactiveColor(
res.getColor(R.color.container_fastscroll_thumb_inactive_color));
mThumbActiveColor = res.getColor(R.color.container_fastscroll_thumb_active_color);
mThumbPaint = new Paint();
+ mThumbPaint.setAntiAlias(true);
mThumbPaint.setColor(mThumbInactiveColor);
+ mThumbPaint.setStyle(Paint.Style.FILL);
mThumbWidth = mThumbMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width);
mThumbMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width);
mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height);
+ mThumbCurvature = mThumbMaxWidth - mThumbMinWidth;
mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset);
}
- public void setScrollbarThumbOffset(int x, int y) {
+ public void setDetachThumbOnFastScroll() {
+ mCanThumbDetach = true;
+ }
+
+ public void reattachThumbToScroll() {
+ mIsThumbDetached = false;
+ }
+
+ public void setThumbOffset(int x, int y) {
if (mThumbOffset.x == x && mThumbOffset.y == y) {
return;
}
- mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight());
+ mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
+ mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
mThumbOffset.set(x, y);
- mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth,
- mRv.getHeight()));
+ updateThumbPath();
+ mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
+ mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
mRv.invalidate(mInvalidateRect);
}
- // Setter/getter for the search bar width for animations
- public void setWidth(int width) {
- mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight());
+ public Point getThumbOffset() {
+ return mThumbOffset;
+ }
+
+ // Setter/getter for the thumb bar width for animations
+ public void setThumbWidth(int width) {
+ mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
+ mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
mThumbWidth = width;
- mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth,
- mRv.getHeight()));
+ updateThumbPath();
+ mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y,
+ mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight);
mRv.invalidate(mInvalidateRect);
}
- public int getWidth() {
+ public int getThumbWidth() {
return mThumbWidth;
}
- // Setter/getter for the track background alpha for animations
- public void setTrackAlpha(int alpha) {
- mTrackPaint.setAlpha(alpha);
- mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight());
+ // Setter/getter for the track bar width for animations
+ public void setTrackWidth(int width) {
+ mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth,
+ mRv.getHeight());
+ mTrackWidth = width;
+ updateThumbPath();
+ mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth,
+ mRv.getHeight());
mRv.invalidate(mInvalidateRect);
}
- public int getTrackAlpha() {
- return mTrackPaint.getAlpha();
+ public int getTrackWidth() {
+ return mTrackWidth;
}
public int getThumbHeight() {
@@ -127,10 +158,18 @@ public class BaseRecyclerViewFastScrollBar {
return mThumbMaxWidth;
}
- public boolean isDragging() {
+ public float getLastTouchY() {
+ return mLastTouchY;
+ }
+
+ public boolean isDraggingThumb() {
return mIsDragging;
}
+ public boolean isThumbDetached() {
+ return mIsThumbDetached;
+ }
+
/**
* Handles the touch event and determines whether to show the fast scroller (or updates it if
* it is already showing).
@@ -142,16 +181,21 @@ public class BaseRecyclerViewFastScrollBar {
int y = (int) ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
- if (isNearPoint(downX, downY)) {
+ if (isNearThumb(downX, downY)) {
mTouchOffset = downY - mThumbOffset.y;
}
break;
case MotionEvent.ACTION_MOVE:
- // Check if we should start scrolling
- if (!mIsDragging && isNearPoint(downX, downY) &&
+ // 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) &&
Math.abs(y - downY) > config.getScaledTouchSlop()) {
mRv.getParent().requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
+ if (mCanThumbDetach) {
+ mIsThumbDetached = true;
+ }
mTouchOffset += (lastY - downY);
mPopup.animateVisibility(true);
animateScrollbar(true);
@@ -166,11 +210,14 @@ public class BaseRecyclerViewFastScrollBar {
mPopup.setSectionName(sectionName);
mPopup.animateVisibility(!sectionName.isEmpty());
mRv.invalidate(mPopup.updateFastScrollerBounds(mRv, lastY));
+ mLastTouchY = boundedY;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
mTouchOffset = 0;
+ mLastTouchY = 0;
+ mIgnoreDragGesture = false;
if (mIsDragging) {
mIsDragging = false;
mPopup.animateVisibility(false);
@@ -189,8 +236,7 @@ public class BaseRecyclerViewFastScrollBar {
if (mTrackPaint.getAlpha() > 0) {
canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight(), mTrackPaint);
}
- canvas.drawRect(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
- mThumbOffset.y + mThumbHeight, mThumbPaint);
+ canvas.drawPath(mThumbPath, mThumbPaint);
// Draw the popup
mPopup.draw(canvas);
@@ -203,30 +249,49 @@ public class BaseRecyclerViewFastScrollBar {
if (mScrollbarAnimator != null) {
mScrollbarAnimator.cancel();
}
- ObjectAnimator trackAlphaAnim = ObjectAnimator.ofInt(this, "trackAlpha",
- isScrolling ? MAX_TRACK_ALPHA : 0);
- ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "width",
- isScrolling ? mThumbMaxWidth : mThumbMinWidth);
- ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(),
- mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor);
- colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animator) {
- mThumbPaint.setColor((Integer) animator.getAnimatedValue());
- mRv.invalidate(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
- mThumbOffset.y + mThumbHeight);
- }
- });
+
mScrollbarAnimator = new AnimatorSet();
- mScrollbarAnimator.playTogether(trackAlphaAnim, thumbWidthAnim, colorAnimation);
+ ObjectAnimator trackWidthAnim = ObjectAnimator.ofInt(this, "trackWidth",
+ isScrolling ? mThumbMaxWidth : mThumbMinWidth);
+ ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "thumbWidth",
+ isScrolling ? mThumbMaxWidth : mThumbMinWidth);
+ mScrollbarAnimator.playTogether(trackWidthAnim, thumbWidthAnim);
+ if (mThumbActiveColor != mThumbInactiveColor) {
+ ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(),
+ mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor);
+ colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animator) {
+ mThumbPaint.setColor((Integer) animator.getAnimatedValue());
+ mRv.invalidate(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
+ mThumbOffset.y + mThumbHeight);
+ }
+ });
+ mScrollbarAnimator.play(colorAnimation);
+ }
mScrollbarAnimator.setDuration(SCROLL_BAR_VIS_DURATION);
mScrollbarAnimator.start();
}
/**
+ * Updates the path for the thumb drawable.
+ */
+ private void updateThumbPath() {
+ mThumbCurvature = mThumbMaxWidth - mThumbWidth;
+ mThumbPath.reset();
+ mThumbPath.moveTo(mThumbOffset.x + mThumbWidth, mThumbOffset.y); // tr
+ mThumbPath.lineTo(mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); // br
+ mThumbPath.lineTo(mThumbOffset.x, mThumbOffset.y + mThumbHeight); // bl
+ mThumbPath.cubicTo(mThumbOffset.x, mThumbOffset.y + mThumbHeight,
+ mThumbOffset.x - mThumbCurvature, mThumbOffset.y + mThumbHeight / 2,
+ mThumbOffset.x, mThumbOffset.y); // bl2tl
+ mThumbPath.close();
+ }
+
+ /**
* Returns whether the specified points are near the scroll bar bounds.
*/
- private boolean isNearPoint(int x, int y) {
+ private boolean isNearThumb(int x, int y) {
mTmpRect.set(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth,
mThumbOffset.y + mThumbHeight);
mTmpRect.inset(mTouchInset, mTouchInset);
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index a0be8ea2b..507087824 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -34,11 +34,13 @@ import android.util.SparseArray;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
+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;
@@ -508,7 +510,7 @@ public class BubbleTextView extends TextView
mIcon.setBounds(0, 0, iconSize, iconSize);
}
if (mLayoutHorizontal) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
setCompoundDrawablesRelative(mIcon, null, null, null);
} else {
setCompoundDrawables(mIcon, null, null, null);
@@ -538,6 +540,13 @@ public class BubbleTextView extends TextView
} else if (info instanceof ShortcutInfo) {
applyFromShortcutInfo((ShortcutInfo) info,
LauncherAppState.getInstance().getIconCache());
+ if ((info.rank < FolderIcon.NUM_ITEMS_IN_PREVIEW) && (info.container >= 0)) {
+ View folderIcon =
+ mLauncher.getWorkspace().getHomescreenIconByItemId(info.container);
+ if (folderIcon != null) {
+ folderIcon.invalidate();
+ }
+ }
} else if (info instanceof PackageItemInfo) {
applyFromPackageItemInfo((PackageItemInfo) info);
}
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index b7f89d02a..2baa6d8ee 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -24,7 +24,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
-import android.content.res.Configuration;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.PointF;
@@ -93,7 +92,7 @@ public abstract class ButtonDropTarget extends TextView
// drawableLeft and drawableStart.
mDrawable = getResources().getDrawable(resId);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
} else {
setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null);
@@ -114,7 +113,7 @@ public abstract class ButtonDropTarget extends TextView
@Override
public final void onDragEnter(DragObject d) {
d.dragView.setColor(mHoverColor);
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
animateTextColor(mHoverColor);
} else {
if (mCurrentFilter == null) {
@@ -131,8 +130,8 @@ public abstract class ButtonDropTarget extends TextView
// Do nothing
}
- protected void resetHoverColor() {
- if (Utilities.isLmpOrAbove()) {
+ protected void resetHoverColor() {
+ if (Utilities.ATLEAST_LOLLIPOP) {
animateTextColor(mOriginalTextColor.getDefaultColor());
} else {
mDrawable.setColorFilter(null);
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 809688712..84e2d49c2 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -54,7 +54,6 @@ import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate;
import com.android.launcher3.accessibility.FolderAccessibilityHelper;
import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper;
import com.android.launcher3.util.Thunk;
-import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
import java.util.Arrays;
@@ -211,6 +210,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel);
mBackground.setCallback(this);
+ mBackground.setAlpha((int) (mBackgroundAlpha * 255));
mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE *
grid.iconSizePx);
@@ -414,7 +414,11 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
if (mIsDragOverlapping) {
mBackground.startTransition(BACKGROUND_ACTIVATE_DURATION);
} else {
- mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION);
+ if (mBackgroundAlpha > 0f) {
+ mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION);
+ } else {
+ mBackground.resetTransition();
+ }
}
invalidate();
}
@@ -2681,65 +2685,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
resultRect.set(x, y, x + width, y + height);
}
- /**
- * Computes the required horizontal and vertical cell spans to always
- * fit the given rectangle.
- *
- * @param width Width in pixels
- * @param height Height in pixels
- * @param result An array of length 2 in which to store the result (may be null).
- */
- public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) {
- return rectToCell(launcher.getDeviceProfile(), launcher, width, height, result);
- }
-
- public static int[] rectToCell(DeviceProfile grid, Context context, int width, int height,
- int[] result) {
- Rect padding = grid.getWorkspacePadding(Utilities.isRtl(context.getResources()));
-
- // Always assume we're working with the smallest span to make sure we
- // reserve enough space in both orientations.
- int parentWidth = DeviceProfile.calculateCellWidth(grid.widthPx
- - padding.left - padding.right, (int) grid.inv.numColumns);
- int parentHeight = DeviceProfile.calculateCellHeight(grid.heightPx
- - padding.top - padding.bottom, (int) grid.inv.numRows);
- int smallerSize = Math.min(parentWidth, parentHeight);
-
- // Always round up to next largest cell
- int spanX = (int) Math.ceil(width / (float) smallerSize);
- int spanY = (int) Math.ceil(height / (float) smallerSize);
-
- if (result == null) {
- return new int[] { spanX, spanY };
- }
- result[0] = spanX;
- result[1] = spanY;
- return result;
- }
-
- /**
- * Calculate the grid spans needed to fit given item
- */
- public void calculateSpans(ItemInfo info) {
- final int minWidth;
- final int minHeight;
-
- if (info instanceof LauncherAppWidgetInfo) {
- minWidth = ((LauncherAppWidgetInfo) info).minWidth;
- minHeight = ((LauncherAppWidgetInfo) info).minHeight;
- } else if (info instanceof PendingAddWidgetInfo) {
- minWidth = ((PendingAddWidgetInfo) info).minWidth;
- minHeight = ((PendingAddWidgetInfo) info).minHeight;
- } else {
- // It's not a widget, so it must be 1x1
- info.spanX = info.spanY = 1;
- return;
- }
- int[] spans = rectToCell(mLauncher, minWidth, minHeight, null);
- info.spanX = spans[0];
- info.spanY = spans[1];
- }
-
private void clearOccupiedCells() {
for (int x = 0; x < mCountX; x++) {
for (int y = 0; y < mCountY; y++) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 62b05b0d6..774594fe2 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -56,7 +56,6 @@ public class DeviceProfile {
private final int overviewModeBarItemWidthPx;
private final int overviewModeBarSpacerWidthPx;
private final float overviewModeIconZoneRatio;
- private final float overviewModeScaleFactor;
// Workspace
private int desiredWorkspaceLeftRightMarginPx;
@@ -136,8 +135,6 @@ public class DeviceProfile {
res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width);
overviewModeIconZoneRatio =
res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f;
- overviewModeScaleFactor =
- res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f;
iconDrawablePaddingOriginalPx =
res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
@@ -334,18 +331,11 @@ public class DeviceProfile {
}
}
- Rect getOverviewModeButtonBarRect() {
+ int getOverviewModeButtonBarHeight() {
int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx,
Math.max(overviewModeMinIconZoneHeightPx, zoneHeight));
- return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx);
- }
-
- public float getOverviewModeScale(boolean isLayoutRtl) {
- Rect workspacePadding = getWorkspacePadding(isLayoutRtl);
- Rect overviewBar = getOverviewModeButtonBarRect();
- int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom;
- return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace;
+ return zoneHeight;
}
// The rect returned will be extended to below the system ui that covers the workspace
@@ -394,7 +384,7 @@ public class DeviceProfile {
final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
// Layout the search bar space
- View searchBar = launcher.getSearchBar();
+ View searchBar = launcher.getSearchDropTargetBar();
lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
if (hasVerticalBarLayout) {
// Vertical search bar space -- The search bar is fixed in the layout to be on the left
@@ -476,7 +466,7 @@ public class DeviceProfile {
// Layout the Overview Mode
ViewGroup overviewMode = launcher.getOverviewPanel();
if (overviewMode != null) {
- Rect r = getOverviewModeButtonBarRect();
+ int overviewButtonBarHeight = getOverviewModeButtonBarHeight();
lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
@@ -485,7 +475,7 @@ public class DeviceProfile {
int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx;
lp.width = Math.min(availableWidthPx, maxWidth);
- lp.height = r.height();
+ lp.height = overviewButtonBarHeight;
overviewMode.setLayoutParams(lp);
if (lp.width > totalItemWidth && visibleChildCount > 1) {
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index aaa14e6a6..1c18747c1 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -159,7 +159,7 @@ public class DragLayer extends InsettableFrameLayout {
}
private boolean isEventOverDropTargetBar(MotionEvent ev) {
- getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect);
+ getDescendantRectRelativeToSelf(mLauncher.getSearchDropTargetBar(), mHitRect);
if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
return true;
}
@@ -321,7 +321,7 @@ public class DragLayer extends InsettableFrameLayout {
childrenForAccessibility.add(currentFolder);
if (isInAccessibleDrag()) {
- childrenForAccessibility.add(mLauncher.getSearchBar());
+ childrenForAccessibility.add(mLauncher.getSearchDropTargetBar());
}
} else {
super.addChildrenForAccessibility(childrenForAccessibility);
diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java
index dfa8202a7..2acfc6140 100644
--- a/src/com/android/launcher3/DragView.java
+++ b/src/com/android/launcher3/DragView.java
@@ -131,7 +131,7 @@ public class DragView extends View {
measure(ms, ms);
mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
setElevation(getResources().getDimension(R.dimen.drag_elevation));
}
}
@@ -252,14 +252,14 @@ public class DragView extends View {
setColorScale(color, m2);
m1.postConcat(m2);
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
animateFilterTo(m1.getArray());
} else {
mPaint.setColorFilter(new ColorMatrixColorFilter(m1));
invalidate();
}
} else {
- if (!Utilities.isLmpOrAbove() || mCurrentFilter == null) {
+ if (!Utilities.ATLEAST_LOLLIPOP || mCurrentFilter == null) {
mPaint.setColorFilter(null);
invalidate();
} else {
diff --git a/src/com/android/launcher3/allapps/AllAppsSearchEditView.java b/src/com/android/launcher3/ExtendedEditText.java
index b7dcd66ed..c7b64ec7d 100644
--- a/src/com/android/launcher3/allapps/AllAppsSearchEditView.java
+++ b/src/com/android/launcher3/ExtendedEditText.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.launcher3.allapps;
+package com.android.launcher3;
import android.content.Context;
import android.util.AttributeSet;
@@ -22,28 +22,28 @@ import android.widget.EditText;
/**
- * The edit text for the search container
+ * The edit text that reports back when the back key has been pressed.
*/
-public class AllAppsSearchEditView extends EditText {
+public class ExtendedEditText extends EditText {
/**
* Implemented by listeners of the back key.
*/
public interface OnBackKeyListener {
- public void onBackKey();
+ public boolean onBackKey();
}
private OnBackKeyListener mBackKeyListener;
- public AllAppsSearchEditView(Context context) {
- this(context, null);
+ public ExtendedEditText(Context context) {
+ super(context);
}
- public AllAppsSearchEditView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
+ public ExtendedEditText(Context context, AttributeSet attrs) {
+ super(context, attrs);
}
- public AllAppsSearchEditView(Context context, AttributeSet attrs, int defStyleAttr) {
+ public ExtendedEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@@ -56,7 +56,7 @@ public class AllAppsSearchEditView extends EditText {
// If this is a back key, propagate the key back to the listener
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (mBackKeyListener != null) {
- mBackKeyListener.onBackKey();
+ return mBackKeyListener.onBackKey();
}
return false;
}
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 2e19f6eba..c1aa35669 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -124,7 +124,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Thunk FolderPagedView mContent;
@Thunk View mContentWrapper;
- FolderEditText mFolderName;
+ ExtendedEditText mFolderName;
private View mFooter;
private int mFooterHeight;
@@ -196,8 +196,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mContent = (FolderPagedView) findViewById(R.id.folder_content);
mContent.setFolder(this);
- mFolderName = (FolderEditText) findViewById(R.id.folder_name);
- mFolderName.setFolder(this);
+ mFolderName = (ExtendedEditText) findViewById(R.id.folder_name);
+ mFolderName.setOnBackKeyListener(new ExtendedEditText.OnBackKeyListener() {
+ @Override
+ public boolean onBackKey() {
+ // Close the activity on back key press
+ doneEditingFolderName(true);
+ return false;
+ }
+ });
mFolderName.setOnFocusChangeListener(this);
// We disable action mode for now since it messes up the view on phones
@@ -275,7 +282,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public void enableAccessibleDrag(boolean enable) {
- mLauncher.getSearchBar().enableAccessibleDrag(enable);
+ mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
for (int i = 0; i < mContent.getChildCount(); i++) {
mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG);
}
@@ -446,7 +453,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
Animator openFolderAnim = null;
final Runnable onCompleteRunnable;
- if (!Utilities.isLmpOrAbove()) {
+ if (!Utilities.ATLEAST_LOLLIPOP) {
positionAndSizeAsIcon();
centerAboutIcon();
@@ -561,7 +568,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void onAnimationEnd(Animator animation) {
mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION)
.translationX(0)
- .setInterpolator(Utilities.isLmpOrAbove() ?
+ .setInterpolator(Utilities.ATLEAST_LOLLIPOP ?
AnimationUtils.loadInterpolator(mLauncher,
android.R.interpolator.fast_out_slow_in)
: new LogDecelerateInterpolator(100, 0));
@@ -1389,7 +1396,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
// Compares item position based on rank and position giving priority to the rank.
- private static final Comparator<ItemInfo> ITEM_POS_COMPARATOR = new Comparator<ItemInfo>() {
+ public static final Comparator<ItemInfo> ITEM_POS_COMPARATOR = new Comparator<ItemInfo>() {
@Override
public int compare(ItemInfo lhs, ItemInfo rhs) {
diff --git a/src/com/android/launcher3/FolderEditText.java b/src/com/android/launcher3/FolderEditText.java
deleted file mode 100644
index c31100899..000000000
--- a/src/com/android/launcher3/FolderEditText.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.android.launcher3;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.widget.EditText;
-
-public class FolderEditText extends EditText {
-
- private Folder mFolder;
-
- public FolderEditText(Context context) {
- super(context);
- }
-
- public FolderEditText(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public FolderEditText(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void setFolder(Folder folder) {
- mFolder = folder;
- }
-
- @Override
- public boolean onKeyPreIme(int keyCode, KeyEvent event) {
- // Catch the back button on the soft keyboard so that we can just close the activity
- if (event.getKeyCode() == android.view.KeyEvent.KEYCODE_BACK) {
- mFolder.doneEditingFolderName(true);
- }
- return super.onKeyPreIme(keyCode, event);
- }
-}
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index f2ec1b68c..cc9c5738a 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -329,6 +329,10 @@ public class FolderPagedView extends PagedView {
lp.cellY = info.cellY;
currentPage.addViewToCellLayout(
v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true);
+
+ if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) {
+ ((BubbleTextView) v).verifyHighRes();
+ }
}
rank ++;
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 916418f18..59ab8397d 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -540,7 +540,7 @@ public class IconCache {
mCache.put(cacheKey, entry);
// Check the DB first.
- if (!getEntryFromDB(componentName, user, entry, useLowResIcon)) {
+ if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
if (info != null) {
entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext);
} else {
@@ -579,7 +579,14 @@ public class IconCache {
Bitmap icon, CharSequence title) {
removeFromMemCacheLocked(packageName, user);
- CacheEntry entry = getEntryForPackageLocked(packageName, user, false);
+ ComponentKey cacheKey = getPackageKey(packageName, user);
+ CacheEntry entry = mCache.get(cacheKey);
+
+ // For icon caching, do not go through DB. Just update the in-memory entry.
+ if (entry == null) {
+ entry = new CacheEntry();
+ mCache.put(cacheKey, entry);
+ }
if (!TextUtils.isEmpty(title)) {
entry.title = title;
}
@@ -588,15 +595,18 @@ public class IconCache {
}
}
+ private static ComponentKey getPackageKey(String packageName, UserHandleCompat user) {
+ ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
+ return new ComponentKey(cn, user);
+ }
+
/**
* Gets an entry for the package, which can be used as a fallback entry for various components.
* This method is not thread safe, it must be called from a synchronized method.
- *
*/
private CacheEntry getEntryForPackageLocked(String packageName, UserHandleCompat user,
boolean useLowResIcon) {
- ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME);
- ComponentKey cacheKey = new ComponentKey(cn, user);
+ ComponentKey cacheKey = getPackageKey(packageName, user);
CacheEntry entry = mCache.get(cacheKey);
if (entry == null || (entry.isLowResIcon && !useLowResIcon)) {
@@ -604,9 +614,11 @@ public class IconCache {
boolean entryUpdated = true;
// Check the DB first.
- if (!getEntryFromDB(cn, user, entry, useLowResIcon)) {
+ if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) {
try {
- PackageInfo info = mPackageManager.getPackageInfo(packageName, 0);
+ int flags = UserHandleCompat.myUserHandle().equals(user) ? 0 :
+ PackageManager.GET_UNINSTALLED_PACKAGES;
+ PackageInfo info = mPackageManager.getPackageInfo(packageName, flags);
ApplicationInfo appInfo = info.applicationInfo;
if (appInfo == null) {
throw new NameNotFoundException("ApplicationInfo is null");
@@ -622,7 +634,8 @@ public class IconCache {
// package updates.
ContentValues values =
newContentValues(entry.icon, entry.title.toString(), mPackageBgColor);
- addIconToDB(values, cn, info, mUserManager.getSerialNumberForUser(user));
+ addIconToDB(values, cacheKey.componentName, info,
+ mUserManager.getSerialNumberForUser(user));
} catch (NameNotFoundException e) {
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
@@ -649,7 +662,7 @@ public class IconCache {
* @param dpi the native density of the icon
*/
public void preloadIcon(ComponentName componentName, Bitmap icon, int dpi, String label,
- long userSerial) {
+ long userSerial, InvariantDeviceProfile idp) {
// TODO rescale to the correct native DPI
try {
PackageManager packageManager = mContext.getPackageManager();
@@ -660,21 +673,22 @@ public class IconCache {
// pass
}
- ContentValues values = newContentValues(icon, label, Color.TRANSPARENT);
+ ContentValues values = newContentValues(
+ Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true),
+ 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);
}
- private boolean getEntryFromDB(ComponentName component, UserHandleCompat user,
- CacheEntry entry, boolean lowRes) {
+ 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,
IconDB.COLUMN_LABEL},
IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?",
- new String[] {component.flattenToString(),
- Long.toString(mUserManager.getSerialNumberForUser(user))},
+ new String[] {cacheKey.componentName.flattenToString(),
+ Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))},
null, null, null);
try {
if (c.moveToNext()) {
@@ -685,7 +699,8 @@ public class IconCache {
entry.title = "";
entry.contentDescription = "";
} else {
- entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
+ entry.contentDescription = mUserManager.getBadgedLabelForUser(
+ entry.title, cacheKey.user);
}
return true;
}
@@ -774,7 +789,7 @@ public class IconCache {
}
private static final class IconDB extends SQLiteOpenHelper {
- private final static int DB_VERSION = 6;
+ private final static int DB_VERSION = 7;
private final static String TABLE_NAME = "icons";
private final static String COLUMN_ROWID = "rowid";
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 6648b6e74..1f843cb70 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -116,8 +116,6 @@ import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -132,8 +130,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
- View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener,
- LauncherStateTransitionAnimation.Callbacks {
+ View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -304,6 +301,8 @@ public class Launcher extends Activity
private boolean mHasFocus = false;
private boolean mAttached = false;
+ private LauncherClings mClings;
+
private static LongArrayMap<FolderInfo> sFolders = new LongArrayMap<>();
private View.OnTouchListener mHapticFeedbackTouchListener;
@@ -361,18 +360,6 @@ public class Launcher extends Activity
}
}
- // TODO: remove this field and call method directly when Launcher3 can depend on M APIs
- private static Method sClipRevealMethod = null;
- static {
- Class<?> activityOptionsClass = ActivityOptions.class;
- try {
- sClipRevealMethod = activityOptionsClass.getDeclaredMethod("makeClipRevealAnimation",
- View.class, int.class, int.class, int.class, int.class);
- } catch (Exception e) {
- // Earlier version
- }
- }
-
@Thunk Runnable mBuildLayersRunnable = new Runnable() {
public void run() {
if (mWorkspace != null) {
@@ -452,7 +439,7 @@ public class Launcher extends Activity
mDragController = new DragController(this);
mInflater = getLayoutInflater();
- mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this);
+ mStateTransitionAnimation = new LauncherStateTransitionAnimation(this);
mStats = new Stats(this);
@@ -663,12 +650,12 @@ public class Launcher extends Activity
public boolean isDraggingEnabled() {
// We prevent dragging when we are loading the workspace as it is possible to pick up a view
// that is subsequently removed from the workspace in startBinding().
- return !mModel.isLoadingWorkspace();
+ return !isWorkspaceLoading();
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static int generateViewId() {
- if (Build.VERSION.SDK_INT >= 17) {
+ if (Utilities.ATLEAST_JB_MR1) {
return View.generateViewId();
} else {
// View.generateViewId() is not available. The following fallback logic is a copy
@@ -743,6 +730,7 @@ public class Launcher extends Activity
};
if (requestCode == REQUEST_BIND_APPWIDGET) {
+ // This is called only if the user did not previously have permissions to bind widgets
final int appWidgetId = data != null ?
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
@@ -752,6 +740,10 @@ public class Launcher extends Activity
} else if (resultCode == RESULT_OK) {
addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
+
+ // When the user has granted permission to bind widgets, we should check to see if
+ // we can inflate the default search bar widget.
+ getOrCreateQsbBar();
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -1004,6 +996,12 @@ public class Launcher extends Activity
mPaused = false;
if (mRestoring || mOnResumeNeedsLoad) {
setWorkspaceLoading(true);
+
+ // If we're starting binding all over again, clear any bind calls we'd postponed in
+ // the past (see waitUntilResume) -- we don't need them since we're starting binding
+ // from scratch again
+ mBindOnResumeCallbacks.clear();
+
mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
mRestoring = false;
mOnResumeNeedsLoad = false;
@@ -1553,23 +1551,6 @@ public class Launcher extends Activity
}
}
- private int[] getSpanForWidget(ComponentName component, int minWidth, int minHeight) {
- Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(this, component, null);
- // We want to account for the extra amount of padding that we are adding to the widget
- // to ensure that it gets the full amount of space that it has requested
- int requiredWidth = minWidth + padding.left + padding.right;
- int requiredHeight = minHeight + padding.top + padding.bottom;
- return CellLayout.rectToCell(this, requiredWidth, requiredHeight, null);
- }
-
- public int[] getSpanForWidget(AppWidgetProviderInfo info) {
- return getSpanForWidget(info.provider, info.minWidth, info.minHeight);
- }
-
- public int[] getMinSpanForWidget(AppWidgetProviderInfo info) {
- return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight);
- }
-
/**
* Add a widget to the workspace.
*
@@ -1666,18 +1647,18 @@ public class Launcher extends Activity
}
registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
- setupTransparentSystemBarsForLmp();
+ setupTransparentSystemBarsForLollipop();
mAttached = true;
mVisible = true;
}
/**
- * Sets up transparent navigation and status bars in LMP.
+ * 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 setupTransparentSystemBarsForLmp() {
- if (Utilities.isLmpOrAbove()) {
+ private void setupTransparentSystemBarsForLollipop() {
+ if (Utilities.ATLEAST_LOLLIPOP) {
Window window = getWindow();
window.getAttributes().systemUiVisibility |=
(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
@@ -1844,7 +1825,7 @@ public class Launcher extends Activity
return mOverviewPanel;
}
- public SearchDropTargetBar getSearchBar() {
+ public SearchDropTargetBar getSearchDropTargetBar() {
return mSearchDropTargetBar;
}
@@ -1880,29 +1861,22 @@ public class Launcher extends Activity
super.onNewIntent(intent);
// Close the menu
- if (Intent.ACTION_MAIN.equals(intent.getAction())) {
+ Folder openFolder = mWorkspace.getOpenFolder();
+ boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
+ Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
+ != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
+ boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
+ if (isActionMain) {
// also will cancel mWaitingForResult.
closeSystemDialogs();
- final boolean alreadyOnHome = mHasFocus && ((intent.getFlags() &
- Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT)
- != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
-
if (mWorkspace == null) {
// Can be cases where mWorkspace is null, this prevents a NPE
return;
}
- Folder openFolder = mWorkspace.getOpenFolder();
// In all these cases, only animate if we're already on home
mWorkspace.exitWidgetResizeMode();
- boolean moveToDefaultScreen = mLauncherCallbacks != null ?
- mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
- if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
- openFolder == null && moveToDefaultScreen) {
- mWorkspace.moveToDefaultScreen(true);
- }
-
closeFolder();
exitSpringLoadedDragMode();
@@ -1936,13 +1910,30 @@ public class Launcher extends Activity
}
}
- if (DEBUG_RESUME_TIME) {
- Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
- }
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onNewIntent(intent);
}
+
+ // Defer moving to the default screen until after we callback to the LauncherCallbacks
+ // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
+ // animation.
+ if (isActionMain) {
+ boolean moveToDefaultScreen = mLauncherCallbacks != null ?
+ mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
+ if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() &&
+ openFolder == null && moveToDefaultScreen) {
+ mWorkspace.post(new Runnable() {
+ @Override
+ public void run() {
+ mWorkspace.moveToDefaultScreen(true);
+ }
+ });
+ }
+ }
+
+ if (DEBUG_RESUME_TIME) {
+ Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime));
+ }
}
@Override
@@ -2150,6 +2141,15 @@ public class Launcher extends Activity
}
}
+ 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();
}
@@ -2218,7 +2218,7 @@ public class Launcher extends Activity
mPendingAddInfo.screenId = -1;
mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1;
mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1;
- mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1;
+ mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1;
mPendingAddInfo.dropPos = null;
}
@@ -2551,6 +2551,10 @@ public class Launcher extends Activity
if (!isAppsViewVisible()) {
showAppsView(true /* animated */, false /* resetListToTop */,
true /* updatePredictedApps */, false /* focusSearchBar */);
+
+ if (mLauncherCallbacks != null) {
+ mLauncherCallbacks.onClickAllAppsButton(v);
+ }
}
}
@@ -2679,6 +2683,7 @@ public class Launcher extends Activity
throw new IllegalArgumentException("Input must be a FolderIcon");
}
+ // TODO(sunnygoyal): Re-evaluate this code.
FolderIcon folderIcon = (FolderIcon) v;
final FolderInfo info = folderIcon.getFolderInfo();
Folder openFolder = mWorkspace.getFolderForTag(info);
@@ -2882,8 +2887,7 @@ public class Launcher extends Activity
Bundle optsBundle = null;
if (useLaunchAnimation) {
ActivityOptions opts = null;
- if (sClipRevealMethod != null) {
- // TODO: call method directly when Launcher3 can depend on M APIs
+ if (Utilities.ATLEAST_MARSHMALLOW) {
int left = 0, top = 0;
int width = v.getMeasuredWidth(), height = v.getMeasuredHeight();
if (v instanceof TextView) {
@@ -2897,22 +2901,12 @@ public class Launcher extends Activity
height = bounds.height();
}
}
- try {
- opts = (ActivityOptions) sClipRevealMethod.invoke(null, v,
- left, top, width, height);
- } catch (IllegalAccessException e) {
- Log.d(TAG, "Could not call makeClipRevealAnimation: " + e);
- sClipRevealMethod = null;
- } catch (InvocationTargetException e) {
- Log.d(TAG, "Could not call makeClipRevealAnimation: " + e);
- sClipRevealMethod = null;
- }
- }
- if (opts == null && !Utilities.isLmpOrAbove()) {
+ opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height);
+ } else if (!Utilities.ATLEAST_LOLLIPOP) {
// Below L, we use a scale up animation
opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
v.getMeasuredWidth(), v.getMeasuredHeight());
- } else if (opts == null && Utilities.isLmpMR1()) {
+ } else if (Utilities.ATLEAST_LOLLIPOP_MR1) {
// On L devices, we use the device default slide-up transition.
// On L MR1 devices, we a custom version of the slide-up transition which
// doesn't have the delay present in the device default.
@@ -2941,7 +2935,7 @@ public class Launcher extends Activity
return false;
}
- @Thunk boolean startActivitySafely(View v, Intent intent, Object tag) {
+ public boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
@@ -3027,7 +3021,7 @@ public class Launcher extends Activity
ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
scaleX, scaleY);
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
}
oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
@@ -3265,14 +3259,6 @@ public class Launcher extends Activity
}
}
- @Override
- public void onStateTransitionHideSearchBar() {
- // Hide the search bar
- if (mSearchDropTargetBar != null) {
- mSearchDropTargetBar.hideSearchBar(false /* animated */);
- }
- }
-
public void showWorkspace(boolean animated) {
showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null);
}
@@ -3290,16 +3276,9 @@ public class Launcher extends Activity
boolean changed = mState != State.WORKSPACE ||
mWorkspace.getState() != Workspace.State.NORMAL;
if (changed) {
- boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
mWorkspace.setVisibility(View.VISIBLE);
- mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL,
- snapToPage, animated, onCompleteRunnable);
-
- // Show the search bar (only animate if we were showing the drop target bar in spring
- // loaded mode)
- if (mSearchDropTargetBar != null) {
- mSearchDropTargetBar.showSearchBar(animated && wasInSpringLoadedMode);
- }
+ mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
+ Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable);
// Set focus to the AppsCustomize button
if (mAllAppsButton != null) {
@@ -3323,7 +3302,8 @@ public class Launcher extends Activity
void showOverviewMode(boolean animated) {
mWorkspace.setVisibility(View.VISIBLE);
- mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW,
+ mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
+ Workspace.State.OVERVIEW,
WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated,
null /* onCompleteRunnable */);
mState = State.WORKSPACE;
@@ -3378,9 +3358,10 @@ public class Launcher extends Activity
}
if (toState == State.APPS) {
- mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar);
+ mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated,
+ focusSearchBar);
} else {
- mStateTransitionAnimation.startAnimationToWidgets(animated);
+ mStateTransitionAnimation.startAnimationToWidgets(mWorkspace.getState(), animated);
}
// Change the state *after* we've called all the transition code
@@ -3402,10 +3383,9 @@ public class Launcher extends Activity
* new state.
*/
public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage,
- boolean animated, boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
+ boolean animated, HashMap<View, Integer> layerViews) {
Workspace.State fromState = mWorkspace.getState();
- Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated,
- hasOverlaySearchBar, layerViews);
+ Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews);
updateInteraction(fromState, toState);
return anim;
}
@@ -3417,7 +3397,8 @@ public class Launcher extends Activity
return;
}
- mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED,
+ mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
+ Workspace.State.SPRING_LOADED,
WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */,
null /* onCompleteRunnable */);
mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED;
@@ -3526,6 +3507,7 @@ public class Launcher extends Activity
mAppWidgetHost.setQsbWidgetId(widgetId);
if (widgetId != -1) {
mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider);
+ mQsb.setId(R.id.qsb_widget);
mQsb.updateAppWidgetOptions(opts);
mQsb.setPadding(0, 0, 0, 0);
mSearchDropTargetBar.addView(mQsb);
@@ -3782,11 +3764,12 @@ public class Launcher extends Activity
continue;
}
+ final View view;
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
ShortcutInfo info = (ShortcutInfo) item;
- View shortcut = createShortcut(info);
+ view = createShortcut(info);
/*
* TODO: FIX collision case
@@ -3805,28 +3788,26 @@ public class Launcher extends Activity
}
}
}
-
- workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX,
- item.cellY, 1, 1);
- if (animateIcons) {
- // Animate all the applications up now
- shortcut.setAlpha(0f);
- shortcut.setScaleX(0f);
- shortcut.setScaleY(0f);
- bounceAnims.add(createNewAppBounceAnimation(shortcut, i));
- newShortcutsScreenId = item.screenId;
- }
break;
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this,
+ view = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);
- workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX,
- item.cellY, 1, 1);
break;
default:
throw new RuntimeException("Invalid Item Type");
}
+
+ workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX,
+ item.cellY, 1, 1);
+ if (animateIcons) {
+ // Animate all the applications up now
+ view.setAlpha(0f);
+ view.setScaleX(0f);
+ view.setScaleY(0f);
+ bounceAnims.add(createNewAppBounceAnimation(view, i));
+ newShortcutsScreenId = item.screenId;
+ }
}
if (animateIcons) {
@@ -3901,7 +3882,8 @@ public class Launcher extends Activity
if (!mIsSafeModeEnabled
&& ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0)
- && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
+ && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) {
+
if (appWidgetInfo == null) {
if (DEBUG_WIDGETS) {
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
@@ -3911,42 +3893,51 @@ public class Launcher extends Activity
LauncherModel.deleteItemFromDatabase(this, item);
return;
}
- // 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.
- PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
- pendingInfo.spanX = item.spanX;
- pendingInfo.spanY = item.spanY;
- pendingInfo.minSpanX = item.minSpanX;
- pendingInfo.minSpanY = item.minSpanY;
- Bundle options = null;
- WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
-
- int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
- boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
- newWidgetId, appWidgetInfo, options);
- // TODO consider showing a permission dialog when the widget is clicked.
- if (!success) {
- mAppWidgetHost.deleteAppWidgetId(newWidgetId);
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
- + " belongs to component " + item.providerName
- + ", as the launcher is unable to bing a new widget id");
+ // If we do not have a valid id, try to bind an id.
+ if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) {
+ // 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.
+ PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null);
+ pendingInfo.spanX = item.spanX;
+ pendingInfo.spanY = item.spanY;
+ pendingInfo.minSpanX = item.minSpanX;
+ pendingInfo.minSpanY = item.minSpanY;
+ Bundle options = null;
+ WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo);
+
+ int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ newWidgetId, appWidgetInfo, options);
+
+ // TODO consider showing a permission dialog when the widget is clicked.
+ if (!success) {
+ mAppWidgetHost.deleteAppWidgetId(newWidgetId);
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the launcher is unable to bing a new widget id");
+ }
+ LauncherModel.deleteItemFromDatabase(this, item);
+ return;
}
- LauncherModel.deleteItemFromDatabase(this, item);
- return;
- }
- item.appWidgetId = newWidgetId;
+ item.appWidgetId = newWidgetId;
- // If the widget has a configure activity, it is still needs to set it up, otherwise
- // the widget is ready to go.
- item.restoreStatus = (appWidgetInfo.configure == null)
- ? LauncherAppWidgetInfo.RESTORE_COMPLETED
- : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+ // If the widget has a configure activity, it is still needs to set it up, otherwise
+ // the widget is ready to go.
+ item.restoreStatus = (appWidgetInfo.configure == null)
+ ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+ : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
- LauncherModel.updateItemInDatabase(this, item);
+ LauncherModel.updateItemInDatabase(this, item);
+ } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0)
+ && (appWidgetInfo.configure == null)) {
+ // If the ID is already valid, verify if we need to configure or not.
+ item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ LauncherModel.updateItemInDatabase(this, item);
+ }
}
if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
@@ -3957,6 +3948,8 @@ public class Launcher extends Activity
}
item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ item.minSpanX = appWidgetInfo.minSpanX;
+ item.minSpanY = appWidgetInfo.minSpanY;
} else {
appWidgetInfo = null;
PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item,
@@ -4079,7 +4072,8 @@ public class Launcher extends Activity
private boolean canRunNewAppsAnimation() {
long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime();
- return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000);
+ return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000)
+ && (mClings == null || !mClings.isVisible());
}
private ValueAnimator createNewAppBounceAnimation(View v, int i) {
@@ -4101,7 +4095,7 @@ public class Launcher extends Activity
return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources()));
}
- public void bindSearchablesChanged() {
+ public void bindSearchProviderChanged() {
if (mSearchDropTargetBar == null) {
return;
}
@@ -4324,13 +4318,14 @@ public class Launcher extends Activity
return oriMap[(d.getRotation() + indexOffset) % 4];
}
+ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void lockScreenOrientation() {
if (mRotationEnabled) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ if (Utilities.ATLEAST_JB_MR2) {
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+ } else {
setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources()
.getConfiguration().orientation));
- } else {
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
}
}
}
@@ -4505,6 +4500,7 @@ public class Launcher extends Activity
// launcher2). Otherwise, we prompt the user upon started for migration
LauncherClings launcherClings = new LauncherClings(this);
if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
+ mClings = launcherClings;
if (mModel.canMigrateFromOldLauncherDb(this)) {
launcherClings.showMigrationCling();
} else {
@@ -4517,14 +4513,16 @@ public class Launcher extends Activity
if (mWorkspace != null) mWorkspace.setAlpha(1f);
if (mHotseat != null) mHotseat.setAlpha(1f);
if (mPageIndicators != null) mPageIndicators.setAlpha(1f);
- if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false);
+ if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
+ SearchDropTargetBar.State.SEARCH_BAR, 0);
}
void hideWorkspaceSearchAndHotseat() {
if (mWorkspace != null) mWorkspace.setAlpha(0f);
if (mHotseat != null) mHotseat.setAlpha(0f);
if (mPageIndicators != null) mPageIndicators.setAlpha(0f);
- if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
+ if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState(
+ SearchDropTargetBar.State.INVISIBLE, 0);
}
// TODO: These method should be a part of LauncherSearchCallback
@@ -4532,7 +4530,7 @@ public class Launcher extends Activity
public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
// Called from search suggestion
UserHandleCompat user = null;
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
UserHandle userHandle = appLaunchIntent.getParcelableExtra(Intent.EXTRA_USER);
if (userHandle != null) {
user = UserHandleCompat.fromUser(userHandle);
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 0b7b1fdc4..d87ad67e5 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,15 +17,16 @@
package com.android.launcher3;
import android.app.SearchManager;
-import android.content.ComponentName;
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.util.Thunk;
import java.lang.ref.WeakReference;
@@ -96,12 +97,12 @@ public class LauncherAppState {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED);
- filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED);
// For handling managed profiles
filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
sContext.registerReceiver(mModel, filter);
+ UserManagerCompat.getInstance(sContext).enableAndResetCache();
}
/**
@@ -126,7 +127,7 @@ public class LauncherAppState {
LauncherModel setLauncher(Launcher launcher) {
getLauncherProvider().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
- mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ?
+ mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ?
new LauncherAccessibilityDelegate(launcher) : null;
return mModel;
}
@@ -147,7 +148,7 @@ public class LauncherAppState {
sLauncherProvider = new WeakReference<LauncherProvider>(provider);
}
- static LauncherProvider getLauncherProvider() {
+ public static LauncherProvider getLauncherProvider() {
return sLauncherProvider.get();
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index de7c61000..6c3a1e896 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -95,8 +95,6 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
protected void onProvidersChanged() {
- mLauncher.getModel().loadAndBindWidgetsAndShortcuts(mLauncher, mLauncher,
- true /* refresh */);
if (!mProviderChangeListeners.isEmpty()) {
for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) {
callback.run();
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index aad18b578..882f7e202 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -68,10 +68,6 @@ public class LauncherAppWidgetInfo extends ItemInfo {
ComponentName providerName;
- // TODO: Are these necessary here?
- int minWidth = -1;
- int minHeight = -1;
-
/**
* Indicates the restore status of the widget.
*/
diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
index 85af92f30..71a08a853 100644
--- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java
@@ -1,10 +1,13 @@
package com.android.launcher3;
import android.annotation.TargetApi;
+import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Parcel;
@@ -19,10 +22,10 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
public boolean isCustomWidget = false;
- private int mSpanX = -1;
- private int mSpanY = -1;
- private int mMinSpanX = -1;
- private int mMinSpanY = -1;
+ public int spanX;
+ public int spanY;
+ public int minSpanX;
+ public int minSpanY;
public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context,
AppWidgetProviderInfo info) {
@@ -41,6 +44,7 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
public LauncherAppWidgetProviderInfo(Parcel in) {
super(in);
+ initSpans();
}
public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) {
@@ -52,6 +56,41 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
previewImage = widget.getPreviewImage();
initialLayout = widget.getWidgetLayout();
resizeMode = widget.getResizeMode();
+ initSpans();
+ }
+
+ private void initSpans() {
+ LauncherAppState app = LauncherAppState.getInstance();
+ InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
+
+ // We only care out the cell size, which is independent of the the layout direction.
+ Rect paddingLand = idp.landscapeProfile.getWorkspacePadding(false /* isLayoutRtl */);
+ Rect paddingPort = idp.portraitProfile.getWorkspacePadding(false /* isLayoutRtl */);
+
+ // Always assume we're working with the smallest span to make sure we
+ // reserve enough space in both orientations.
+ float smallestCellWidth = DeviceProfile.calculateCellWidth(Math.min(
+ idp.landscapeProfile.widthPx - paddingLand.left - paddingLand.right,
+ idp.portraitProfile.widthPx - paddingPort.left - paddingPort.right),
+ idp.numColumns);
+ float smallestCellHeight = DeviceProfile.calculateCellWidth(Math.min(
+ idp.landscapeProfile.heightPx - paddingLand.top - paddingLand.bottom,
+ idp.portraitProfile.heightPx - paddingPort.top - paddingPort.bottom),
+ idp.numRows);
+
+ // We want to account for the extra amount of padding that we are adding to the widget
+ // to ensure that it gets the full amount of space that it has requested.
+ Rect widgetPadding = AppWidgetHostView.getDefaultPaddingForWidget(
+ app.getContext(), provider, null);
+ spanX = Math.max(1, (int) Math.ceil(
+ (minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
+ spanY = Math.max(1, (int) Math.ceil(
+ (minHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
+
+ minSpanX = Math.max(1, (int) Math.ceil(
+ (minResizeWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth));
+ minSpanY = Math.max(1, (int) Math.ceil(
+ (minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight));
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -79,35 +118,9 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo {
provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm));
}
- public int getSpanX(Launcher launcher) {
- lazyLoadSpans(launcher);
- return mSpanX;
- }
-
- public int getSpanY(Launcher launcher) {
- lazyLoadSpans(launcher);
- return mSpanY;
- }
-
- public int getMinSpanX(Launcher launcher) {
- lazyLoadSpans(launcher);
- return mMinSpanX;
- }
-
- public int getMinSpanY(Launcher launcher) {
- lazyLoadSpans(launcher);
- return mMinSpanY;
- }
-
- private void lazyLoadSpans(Launcher launcher) {
- if (mSpanX < 0 || mSpanY < 0 || mMinSpanX < 0 || mMinSpanY < 0) {
- int[] minResizeSpan = launcher.getMinSpanForWidget(this);
- int[] span = launcher.getSpanForWidget(this);
-
- mSpanX = span[0];
- mSpanY = span[1];
- mMinSpanX = minResizeSpan[0];
- mMinSpanY = minResizeSpan[1];
- }
+ public Point getMinSpans(InvariantDeviceProfile idp, Context context) {
+ return new Point(
+ (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1,
+ (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1);
}
}
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index a92a889f9..8eb4e6369 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -24,6 +24,8 @@ import android.database.Cursor;
import android.os.ParcelFileDescriptor;
import android.util.Log;
+import com.android.launcher3.model.MigrateFromRestoreTask;
+
import java.io.IOException;
public class LauncherBackupAgentHelper extends BackupAgentHelper {
@@ -63,7 +65,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
throws IOException {
- if (!Utilities.isLmpOrAbove()) {
+ if (!Utilities.ATLEAST_LOLLIPOP) {
// No restore for old devices.
Log.i(TAG, "You shall not pass!!!");
Log.d(TAG, "Restore is only supported on devices running Lollipop and above.");
@@ -91,11 +93,19 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated();
LauncherClings.synchonouslyMarkFirstRunClingDismissed(this);
- // TODO: Update the backup set to include rank.
+ // Rank was added in v4.
if (mHelper.restoredBackupVersion <= 3) {
LauncherAppState.getLauncherProvider().updateFolderItemsRank();
- LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
}
+
+ if (MigrateFromRestoreTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) {
+ MigrateFromRestoreTask.markForMigration(getApplicationContext(),
+ (int) mHelper.migrationCompatibleProfileData.desktopCols,
+ (int) mHelper.migrationCompatibleProfileData.desktopRows,
+ mHelper.widgetSizes);
+ }
+
+ LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities();
} else {
if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
LauncherAppState.getLauncherProvider().createEmptyDB();
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 8c6fedbdc..2d11d3af1 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -32,6 +32,7 @@ import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
+import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
@@ -51,6 +52,7 @@ import com.android.launcher3.backup.BackupProtos.Screen;
import com.android.launcher3.backup.BackupProtos.Widget;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.model.MigrateFromRestoreTask;
import com.android.launcher3.util.Thunk;
import com.google.protobuf.nano.InvalidProtocolBufferNanoException;
import com.google.protobuf.nano.MessageNano;
@@ -75,7 +77,7 @@ public class LauncherBackupHelper implements BackupHelper {
private static final boolean VERBOSE = LauncherBackupAgentHelper.VERBOSE;
private static final boolean DEBUG = LauncherBackupAgentHelper.DEBUG;
- private static final int BACKUP_VERSION = 3;
+ private static final int BACKUP_VERSION = 4;
private static final int MAX_JOURNAL_SIZE = 1000000;
// Journal key is such that it is always smaller than any dynamically generated
@@ -107,6 +109,7 @@ public class LauncherBackupHelper implements BackupHelper {
Favorites.SPANY, // 15
Favorites.TITLE, // 16
Favorites.PROFILE_ID, // 17
+ Favorites.RANK, // 18
};
private static final int ID_INDEX = 0;
@@ -126,6 +129,7 @@ public class LauncherBackupHelper implements BackupHelper {
private static final int SPANX_INDEX = 14;
private static final int SPANY_INDEX = 15;
private static final int TITLE_INDEX = 16;
+ private static final int RANK_INDEX = 18;
private static final String[] SCREEN_PROJECTION = {
WorkspaceScreens._ID, // 0
@@ -150,9 +154,16 @@ public class LauncherBackupHelper implements BackupHelper {
private DeviceProfieData mDeviceProfileData;
private InvariantDeviceProfile mIdp;
+ DeviceProfieData migrationCompatibleProfileData;
+ HashSet<String> widgetSizes = new HashSet<>();
+
boolean restoreSuccessful;
int restoredBackupVersion = 1;
+ // When migrating from a device which different hotseat configuration, the icons are shifted
+ // to center along the new all-apps icon.
+ private int mHotseatShift = 0;
+
public LauncherBackupHelper(Context context) {
mContext = context;
mExistingKeys = new HashSet<String>();
@@ -282,17 +293,39 @@ public class LauncherBackupHelper implements BackupHelper {
return true;
}
- boolean isHotsetCompatible = false;
+ boolean isHotseatCompatible = false;
if (currentProfile.allappsRank >= oldProfile.hotseatCount) {
- isHotsetCompatible = true;
+ isHotseatCompatible = true;
+ mHotseatShift = 0;
+ }
+
+ if ((currentProfile.allappsRank >= oldProfile.allappsRank)
+ && ((currentProfile.hotseatCount - currentProfile.allappsRank) >=
+ (oldProfile.hotseatCount - oldProfile.allappsRank))) {
+ // There is enough space on both sides of the hotseat.
+ isHotseatCompatible = true;
+ mHotseatShift = currentProfile.allappsRank - oldProfile.allappsRank;
+ }
+
+ if (!isHotseatCompatible) {
+ return false;
}
- if ((currentProfile.hotseatCount >= oldProfile.hotseatCount) &&
- (currentProfile.allappsRank == oldProfile.allappsRank)) {
- isHotsetCompatible = true;
+ if ((currentProfile.desktopCols >= oldProfile.desktopCols)
+ && (currentProfile.desktopRows >= oldProfile.desktopRows)) {
+ return true;
}
- return isHotsetCompatible && (currentProfile.desktopCols >= oldProfile.desktopCols)
- && (currentProfile.desktopRows >= oldProfile.desktopRows);
+ 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.
+
+ migrationCompatibleProfileData = initDeviceProfileData(mIdp);
+ migrationCompatibleProfileData.desktopCols = oldProfile.desktopCols;
+ migrationCompatibleProfileData.desktopRows = oldProfile.desktopRows;
+ return true;
+ }
+ return false;
}
/**
@@ -432,7 +465,10 @@ public class LauncherBackupHelper implements BackupHelper {
Key key = getKey(Key.FAVORITE, id);
mKeys.add(key);
final String backupKey = keyToBackupKey(key);
- if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime) {
+
+ // Favorite proto changed in v4. Backup again if the version is old.
+ if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime
+ || restoredBackupVersion < 4) {
writeRowToBackup(key, packFavorite(cursor), data);
} else {
if (DEBUG) Log.d(TAG, "favorite already backup up: " + id);
@@ -601,10 +637,11 @@ public class LauncherBackupHelper implements BackupHelper {
Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length);
if (icon == null) {
Log.w(TAG, "failed to unpack icon for " + key.name);
+ } else {
+ if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name);
+ mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi,
+ "" /* label */, mUserSerial, mIdp);
}
- if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name);
- mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi,
- "" /* label */, mUserSerial);
}
/**
@@ -638,7 +675,9 @@ public class LauncherBackupHelper implements BackupHelper {
} else {
Log.w(TAG, "empty intent on appwidget: " + id);
}
- if (mExistingKeys.contains(backupKey) && restoredBackupVersion >= BACKUP_VERSION) {
+
+ // Widget backup proto changed in v3. So add it again if the original backup is old.
+ if (mExistingKeys.contains(backupKey) && restoredBackupVersion >= 3) {
if (DEBUG) Log.d(TAG, "already saved widget " + backupKey);
// remember that we already backed this up previously
@@ -685,11 +724,12 @@ public class LauncherBackupHelper implements BackupHelper {
Log.w(TAG, "failed to unpack widget icon for " + key.name);
} else {
mIconCache.preloadIcon(ComponentName.unflattenFromString(widget.provider),
- icon, widget.icon.dpi, widget.label, mUserSerial);
+ icon, widget.icon.dpi, widget.label, mUserSerial, mIdp);
}
}
- // future site of widget table mutation
+ // Cache widget min sizes incase migration is required.
+ widgetSizes.add(widget.provider + "#" + widget.minSpanX + "," + widget.minSpanY);
}
/** create a new key, with an integer ID.
@@ -773,6 +813,7 @@ public class LauncherBackupHelper implements BackupHelper {
favorite.spanX = c.getInt(SPANX_INDEX);
favorite.spanY = c.getInt(SPANY_INDEX);
favorite.iconType = c.getInt(ICON_TYPE_INDEX);
+ favorite.rank = c.getInt(RANK_INDEX);
String title = c.getString(TITLE_INDEX);
if (!TextUtils.isEmpty(title)) {
@@ -847,6 +888,11 @@ public class LauncherBackupHelper implements BackupHelper {
throws IOException {
Favorite favorite = unpackProto(new Favorite(), buffer, dataSize);
+ // If it is a hotseat item, move it accordingly.
+ if (favorite.container == Favorites.CONTAINER_HOTSEAT) {
+ favorite.screen += mHotseatShift;
+ }
+
ContentValues values = new ContentValues();
values.put(Favorites._ID, favorite.id);
values.put(Favorites.SCREEN, favorite.screen);
@@ -855,6 +901,7 @@ public class LauncherBackupHelper implements BackupHelper {
values.put(Favorites.CELLY, favorite.cellY);
values.put(Favorites.SPANX, favorite.spanX);
values.put(Favorites.SPANY, favorite.spanY);
+ values.put(Favorites.RANK, favorite.rank);
if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) {
values.put(Favorites.ICON_TYPE, favorite.iconType);
@@ -880,7 +927,11 @@ public class LauncherBackupHelper implements BackupHelper {
UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
- DeviceProfieData currentProfile = mDeviceProfileData;
+ // If we will attempt grid resize, use the original profile to validate grid size, as
+ // anything which fits in the original grid should fit in the current grid after
+ // grid migration.
+ DeviceProfieData currentProfile = migrationCompatibleProfileData == null
+ ? mDeviceProfileData : migrationCompatibleProfileData;
if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
if (!TextUtils.isEmpty(favorite.appWidgetProvider)) {
@@ -972,14 +1023,9 @@ public class LauncherBackupHelper implements BackupHelper {
widget.icon.dpi = dpi;
}
- // Calculate the spans corresponding to any one of the orientations as it should not change
- // based on orientation.
- int[] minSpans = CellLayout.rectToCell(
- mIdp.portraitProfile, mContext, info.minResizeWidth, info.minResizeHeight, null);
- widget.minSpanX = (info.resizeMode & LauncherAppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0
- ? minSpans[0] : -1;
- widget.minSpanY = (info.resizeMode & LauncherAppWidgetProviderInfo.RESIZE_VERTICAL) != 0
- ? minSpans[1] : -1;
+ Point spans = info.getMinSpans(mIdp, mContext);
+ widget.minSpanX = spans.x;
+ widget.minSpanY = spans.y;
return widget;
}
@@ -1164,6 +1210,10 @@ public class LauncherBackupHelper implements BackupHelper {
}
}
+ public boolean shouldAttemptWorkspaceMigration() {
+ return migrationCompatibleProfileData != null;
+ }
+
/**
* A class to check if an activity can handle one of the intents from a list of
* predefined intents.
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 6618cca78..e34bd57fd 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -77,6 +77,7 @@ public interface LauncherCallbacks {
public boolean providesSearch();
public boolean startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, Rect sourceBounds);
+ public boolean startSearchFromAllApps(String query);
@Deprecated
public void startVoice();
public boolean hasCustomContentToLeft();
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index c13752cb1..18fe8ef86 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -16,8 +16,6 @@
package com.android.launcher3;
-import android.accounts.Account;
-import android.accounts.AccountManager;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.TargetApi;
@@ -36,6 +34,7 @@ import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityManager;
+
import com.android.launcher3.util.Thunk;
class LauncherClings implements OnClickListener {
@@ -44,8 +43,6 @@ class LauncherClings implements OnClickListener {
private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
- private static final boolean DISABLE_CLINGS = false;
-
private static final int SHOW_CLING_DURATION = 250;
private static final int DISMISS_CLING_DURATION = 200;
@@ -54,6 +51,7 @@ class LauncherClings implements OnClickListener {
@Thunk Launcher mLauncher;
private LayoutInflater mInflater;
+ @Thunk boolean mIsVisible;
/** Ctor */
public LauncherClings(Launcher launcher) {
@@ -94,6 +92,7 @@ class LauncherClings implements OnClickListener {
* package was not preinstalled and there exists a db to migrate from.
*/
public void showMigrationCling() {
+ mIsVisible = true;
mLauncher.hideWorkspaceSearchAndHotseat();
ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
@@ -120,6 +119,7 @@ class LauncherClings implements OnClickListener {
}
public void showLongPressCling(boolean showWelcome) {
+ mIsVisible = true;
ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
@@ -199,6 +199,7 @@ class LauncherClings implements OnClickListener {
mLauncher.getSharedPrefs().edit()
.putBoolean(flag, true)
.apply();
+ mIsVisible = false;
if (postAnimationCb != null) {
postAnimationCb.run();
}
@@ -212,13 +213,13 @@ class LauncherClings implements OnClickListener {
}
}
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+
/** Returns whether the clings are enabled or should be shown */
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private boolean areClingsEnabled() {
- if (DISABLE_CLINGS) {
- return false;
- }
-
// disable clings when running in a test harness
if(ActivityManager.isRunningInTestHarness()) return false;
@@ -231,10 +232,7 @@ class LauncherClings implements OnClickListener {
// Restricted secondary users (child mode) will potentially have very few apps
// seeded when they start up for the first time. Clings won't work well with that
- boolean supportsLimitedUsers =
- android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
- Account[] accounts = AccountManager.get(mLauncher).getAccounts();
- if (supportsLimitedUsers && accounts.length == 0) {
+ if (Utilities.ATLEAST_JB_MR2) {
UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
Bundle restrictions = um.getUserRestrictions();
if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b60477fa0..b5922c6a3 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import android.app.SearchManager;
+import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -33,7 +34,6 @@ import android.content.pm.ResolveInfo;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
-import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -55,6 +55,7 @@ import com.android.launcher3.compat.PackageInstallerCompat;
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.WidgetsModel;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.CursorIconInfo;
@@ -203,7 +204,7 @@ public class LauncherModel extends BroadcastReceiver
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
public void bindAllPackages(WidgetsModel model);
- public void bindSearchablesChanged();
+ public void bindSearchProviderChanged();
public boolean isAllAppsButtonRank(int rank);
public void onPageBoundSynchronously(int page);
public void dumpLogsToLocalData();
@@ -257,7 +258,7 @@ public class LauncherModel extends BroadcastReceiver
/** Runs the specified runnable immediately if called from the worker thread, otherwise it is
* posted on the worker thread handler. */
- private static void runOnWorkerThread(Runnable r) {
+ @Thunk static void runOnWorkerThread(Runnable r) {
if (sWorkerThread.getThreadId() == Process.myTid()) {
r.run();
} else {
@@ -266,19 +267,6 @@ public class LauncherModel extends BroadcastReceiver
}
}
- /**
- * Runs the specified runnable after the loader is complete
- */
- @Thunk void runAfterBindCompletes(Runnable r) {
- if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) {
- synchronized (mBindCompleteRunnables) {
- mBindCompleteRunnables.add(r);
- }
- } else {
- runOnWorkerThread(r);
- }
- }
-
boolean canMigrateFromOldLauncherDb(Launcher launcher) {
return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ;
}
@@ -892,8 +880,13 @@ public class LauncherModel extends BroadcastReceiver
}
private void assertWorkspaceLoaded() {
- if (LauncherAppState.isDogfoodBuild() && (isLoadingWorkspace() || !mHasLoaderCompletedOnce)) {
- throw new RuntimeException("Trying to add shortcut while loader is running");
+ if (LauncherAppState.isDogfoodBuild()) {
+ synchronized (mLock) {
+ if (!mHasLoaderCompletedOnce ||
+ (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) {
+ throw new RuntimeException("Trying to add shortcut while loader is running");
+ }
+ }
}
}
@@ -1133,7 +1126,7 @@ public class LauncherModel extends BroadcastReceiver
* Update the order of the workspace screens in the database. The array list contains
* a list of screen ids in the order that they should appear.
*/
- void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
+ public void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
final ArrayList<Long> screensCopy = new ArrayList<Long>(screens);
final ContentResolver cr = context.getContentResolver();
final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -1280,14 +1273,14 @@ public class LauncherModel extends BroadcastReceiver
if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
- } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) ||
- SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) {
+ } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) {
Callbacks callbacks = getCallback();
if (callbacks != null) {
- callbacks.bindSearchablesChanged();
+ callbacks.bindSearchProviderChanged();
}
} else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
|| LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ UserManagerCompat.getInstance(context).enableAndResetCache();
forceReload();
}
}
@@ -1388,16 +1381,6 @@ public class LauncherModel extends BroadcastReceiver
mHandler.post(r);
}
}
-
- // Run all the bind complete runnables after workspace is bound.
- if (!mBindCompleteRunnables.isEmpty()) {
- synchronized (mBindCompleteRunnables) {
- for (final Runnable r : mBindCompleteRunnables) {
- runOnWorkerThread(r);
- }
- mBindCompleteRunnables.clear();
- }
- }
}
public void stopLoader() {
@@ -1411,7 +1394,7 @@ public class LauncherModel extends BroadcastReceiver
/**
* Loads the workspace screen ids in an ordered list.
*/
- @Thunk static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
+ public static ArrayList<Long> loadWorkspaceScreensDb(Context context) {
final ContentResolver contentResolver = context.getContentResolver();
final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI;
@@ -1439,15 +1422,6 @@ public class LauncherModel extends BroadcastReceiver
return mAllAppsLoaded;
}
- boolean isLoadingWorkspace() {
- synchronized (mLock) {
- if (mLoaderTask != null) {
- return mLoaderTask.isLoadingWorkspace();
- }
- }
- return false;
- }
-
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -1466,10 +1440,6 @@ public class LauncherModel extends BroadcastReceiver
mFlags = flags;
}
- boolean isLoadingWorkspace() {
- return mIsLoadingAndBindingWorkspace;
- }
-
private void loadAndBindWorkspace() {
mIsLoadingAndBindingWorkspace = true;
@@ -1641,11 +1611,12 @@ public class LauncherModel extends BroadcastReceiver
}
// check & update map of what's occupied; used to discard overlapping/invalid items
- private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item) {
+ private boolean checkItemPlacement(LongArrayMap<ItemInfo[][]> occupied, ItemInfo item,
+ ArrayList<Long> workspaceScreens) {
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
- final int countX = (int) profile.numColumns;
- final int countY = (int) profile.numRows;
+ final int countX = profile.numColumns;
+ final int countY = profile.numRows;
long containerIndex = item.screenId;
if (item.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
@@ -1687,7 +1658,12 @@ public class LauncherModel extends BroadcastReceiver
occupied.put((long) LauncherSettings.Favorites.CONTAINER_HOTSEAT, items);
return true;
}
- } else if (item.container != LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ } else if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ if (!workspaceScreens.contains((Long) item.screenId)) {
+ // The item has an invalid screen id.
+ return false;
+ }
+ } else {
// Skip further checking if it is not the hotseat or workspace container
return true;
}
@@ -1754,8 +1730,27 @@ public class LauncherModel extends BroadcastReceiver
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
- int countX = (int) profile.numColumns;
- int countY = (int) profile.numRows;
+ 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 ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) {
Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true);
@@ -1776,6 +1771,7 @@ public class LauncherModel extends BroadcastReceiver
clearSBgDataStructures();
final HashMap<String, Integer> installingPkgs = PackageInstallerCompat
.getInstance(mContext).updateAndGetActiveSessionCache();
+ sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final ArrayList<Long> restoredRows = new ArrayList<Long>();
@@ -1977,6 +1973,7 @@ public class LauncherModel extends BroadcastReceiver
} catch (URISyntaxException e) {
Launcher.addDumpLog(TAG,
"Invalid uri: " + intentDescription, true);
+ itemsToRemove.add(id);
continue;
}
@@ -2047,7 +2044,7 @@ public class LauncherModel extends BroadcastReceiver
}
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, info)) {
+ if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
itemsToRemove.add(id);
break;
}
@@ -2098,7 +2095,7 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.options = c.getInt(optionsIndex);
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, folderInfo)) {
+ if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) {
itemsToRemove.add(id);
break;
}
@@ -2163,14 +2160,18 @@ public class LauncherModel extends BroadcastReceiver
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
provider.provider);
- int status = restoreStatus;
+ // The provider is available. So the widget is either
+ // available or not available. We do not need to track
+ // any future restore updates.
+ int status = restoreStatus &
+ ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
if (!wasProviderReady) {
// If provider was not previously ready, update the
// status and UI flag.
// Id would be valid only if the widget restore broadcast was received.
if (isIdValid) {
- status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
} else {
status &= ~LauncherAppWidgetInfo
.FLAG_PROVIDER_NOT_READY;
@@ -2214,13 +2215,14 @@ public class LauncherModel extends BroadcastReceiver
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
container != LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
Log.e(TAG, "Widget found where container != " +
- "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+ "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
+ itemsToRemove.add(id);
continue;
}
appWidgetInfo.container = container;
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, appWidgetInfo)) {
+ if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) {
itemsToRemove.add(id);
break;
}
@@ -2279,6 +2281,21 @@ public class LauncherModel extends BroadcastReceiver
}
}
+ // Sort all the folder items and make sure the first 3 items are high resolution.
+ for (FolderInfo folder : sBgFolders) {
+ Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR);
+ int pos = 0;
+ for (ShortcutInfo info : folder.contents) {
+ if (info.usingLowResIcon) {
+ info.updateIcon(mIconCache, false);
+ }
+ pos ++;
+ if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) {
+ break;
+ }
+ }
+ }
+
if (restoredRows.size() > 0) {
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
@@ -2294,8 +2311,6 @@ public class LauncherModel extends BroadcastReceiver
null, sWorker);
}
- sBgWorkspaceScreens.addAll(loadWorkspaceScreensDb(mContext));
-
// Remove any empty screens
ArrayList<Long> unusedScreens = new ArrayList<Long>(sBgWorkspaceScreens);
for (ItemInfo item: sBgItemsIdMap) {
@@ -2657,13 +2672,24 @@ public class LauncherModel extends BroadcastReceiver
callbacks.finishBindingItems();
}
+ mIsLoadingAndBindingWorkspace = false;
+
+ // Run all the bind complete runnables after workspace is bound.
+ if (!mBindCompleteRunnables.isEmpty()) {
+ synchronized (mBindCompleteRunnables) {
+ for (final Runnable r : mBindCompleteRunnables) {
+ runOnWorkerThread(r);
+ }
+ mBindCompleteRunnables.clear();
+ }
+ }
+
// If we're profiling, ensure this is the last thing in the queue.
if (DEBUG_LOADERS) {
Log.d(TAG, "bound workspace in "
+ (SystemClock.uptimeMillis()-t) + "ms");
}
- mIsLoadingAndBindingWorkspace = false;
}
};
if (isLoadingSynchronously) {
@@ -2792,12 +2818,27 @@ public class LauncherModel extends BroadcastReceiver
final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user);
if (heuristic != null) {
- runAfterBindCompletes(new Runnable() {
+ final Runnable r = new Runnable() {
@Override
public void run() {
heuristic.processUserApps(apps);
}
+ };
+ runOnMainThread(new Runnable() {
+
+ @Override
+ public void run() {
+ // Check isLoadingWorkspace on the UI thread, as it is updated on
+ // the UI thread.
+ if (mIsLoadingAndBindingWorkspace) {
+ synchronized (mBindCompleteRunnables) {
+ mBindCompleteRunnables.add(r);
+ }
+ } else {
+ runOnWorkerThread(r);
+ }
+ }
});
}
}
@@ -2825,8 +2866,7 @@ public class LauncherModel extends BroadcastReceiver
// Cleanup any data stored for a deleted user.
ManagedProfileHeuristic.processAllUsers(profiles, mContext);
- loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks),
- true /* refresh */);
+ loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */);
if (DEBUG_LOADERS) {
Log.d(TAG, "Icons processed in "
+ (SystemClock.uptimeMillis() - loadTime) + "ms");
@@ -2895,7 +2935,7 @@ public class LauncherModel extends BroadcastReceiver
}
// Reload widget list. No need to refresh, as we only want to update the icons and labels.
- loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, false);
+ loadAndBindWidgetsAndShortcuts(callbacks, false);
}
void enqueuePackageUpdated(PackageUpdatedTask task) {
@@ -3155,7 +3195,15 @@ public class LauncherModel extends BroadcastReceiver
if (mUser.equals(widgetInfo.user)
&& widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
&& packageSet.contains(widgetInfo.providerName.getPackageName())) {
- widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+ widgetInfo.restoreStatus &=
+ ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY &
+ ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+
+ // adding this flag ensures that launcher shows 'click to setup'
+ // if the widget has a config activity. In case there is no config
+ // activity, it will be marked as 'restored' during bind.
+ widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
widgets.add(widgetInfo);
updateItemInDatabase(context, widgetInfo);
}
@@ -3235,8 +3283,36 @@ public class LauncherModel extends BroadcastReceiver
});
}
- // onProvidersChanged method (API >= 17) already refreshed the widget list
- loadAndBindWidgetsAndShortcuts(context, callbacks, Build.VERSION.SDK_INT < 17);
+ // 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 (!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() {
@@ -3310,13 +3386,12 @@ public class LauncherModel extends BroadcastReceiver
}
}
- public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks,
- final boolean refresh) {
+ public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) {
runOnWorkerThread(new Runnable() {
@Override
public void run() {
- updateWidgetsModel(context, refresh);
+ updateWidgetsModel(refresh);
final WidgetsModel model = mBgWidgetsModel.clone();
mHandler.post(new Runnable() {
@@ -3340,10 +3415,10 @@ public class LauncherModel extends BroadcastReceiver
*
* @see #loadAndBindWidgetsAndShortcuts
*/
- @Thunk void updateWidgetsModel(Context context, boolean refresh) {
- PackageManager packageManager = context.getPackageManager();
+ @Thunk void updateWidgetsModel(boolean refresh) {
+ PackageManager packageManager = mApp.getContext().getPackageManager();
final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
- widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh));
+ widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh));
Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index cc5e18bc1..8791e9e57 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -65,20 +65,17 @@ import java.util.HashSet;
import java.util.List;
public class LauncherProvider extends ContentProvider {
- private static final String TAG = "Launcher.LauncherProvider";
+ private static final String TAG = "LauncherProvider";
private static final boolean LOGD = false;
private static final int DATABASE_VERSION = 26;
- static final String OLD_AUTHORITY = "com.android.launcher2.settings";
- static final String AUTHORITY = ProviderConfig.AUTHORITY;
+ public static final String AUTHORITY = ProviderConfig.AUTHORITY;
static final String TABLE_FAVORITES = LauncherSettings.Favorites.TABLE_NAME;
static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME;
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
- private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd";
-
private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
@Thunk LauncherProviderChangeListener mListener;
@@ -140,14 +137,21 @@ public class LauncherProvider extends ContentProvider {
return db.insert(table, nullColumnHack, values);
}
+ private void reloadLauncherIfExternal() {
+ if (Utilities.ATLEAST_MARSHMALLOW && Binder.getCallingPid() != Process.myPid()) {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app != null) {
+ app.reloadWorkspace();
+ }
+ }
+ }
+
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
SqlArguments args = new SqlArguments(uri);
- // In very limited cases, we support system|signature permission apps to add to the db
- String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD);
- final boolean isExternalAll = externalAdd != null && "true".equals(externalAdd);
- if (isExternalAll) {
+ // In very limited cases, we support system|signature permission apps to modify the db.
+ if (Binder.getCallingPid() != Process.myPid()) {
if (!mOpenHelper.initializeExternalAdd(initialValues)) {
return null;
}
@@ -161,13 +165,20 @@ public class LauncherProvider extends ContentProvider {
uri = ContentUris.withAppendedId(uri, rowId);
notifyListeners();
- if (isExternalAll) {
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ reloadLauncherIfExternal();
+ } else {
+ // Deprecated behavior to support legacy devices which rely on provider callbacks.
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app != null) {
+ if (app != null && "true".equals(uri.getQueryParameter("isExternalAdd"))) {
app.reloadWorkspace();
}
- }
+ String notify = uri.getQueryParameter("notify");
+ if (notify == null || "true".equals(notify)) {
+ getContext().getContentResolver().notifyChange(uri, null);
+ }
+ }
return uri;
}
@@ -192,6 +203,7 @@ public class LauncherProvider extends ContentProvider {
}
notifyListeners();
+ reloadLauncherIfExternal();
return values.length;
}
@@ -203,6 +215,7 @@ public class LauncherProvider extends ContentProvider {
try {
ContentProviderResult[] result = super.applyBatch(operations);
db.setTransactionSuccessful();
+ reloadLauncherIfExternal();
return result;
} finally {
db.endTransaction();
@@ -217,6 +230,7 @@ public class LauncherProvider extends ContentProvider {
int count = db.delete(args.table, args.where, args.args);
if (count > 0) notifyListeners();
+ reloadLauncherIfExternal();
return count;
}
@@ -229,6 +243,7 @@ public class LauncherProvider extends ContentProvider {
int count = db.update(args.table, values, args.where, args.args);
if (count > 0) notifyListeners();
+ reloadLauncherIfExternal();
return count;
}
@@ -399,7 +414,7 @@ public class LauncherProvider extends ContentProvider {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction() {
// UserManager.getApplicationRestrictions() requires minSdkVersion >= 18
- if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ if (!Utilities.ATLEAST_JB_MR2) {
return null;
}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index f2c85a195..8a5804f34 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -143,7 +143,7 @@ public class LauncherSettings {
*
* @return The unique content URL for the specified row.
*/
- static Uri getContentUri(long id) {
+ public static Uri getContentUri(long id) {
return Uri.parse("content://" + ProviderConfig.AUTHORITY +
"/" + TABLE_NAME + "/" + id);
}
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index d69b7432d..cdde8c13f 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -80,13 +80,6 @@ import java.util.HashMap;
public class LauncherStateTransitionAnimation {
/**
- * Callbacks made during the state transition
- */
- interface Callbacks {
- public void onStateTransitionHideSearchBar();
- }
-
- /**
* Private callbacks made during transition setup.
*/
static abstract class PrivateTransitionCallbacks {
@@ -111,12 +104,10 @@ public class LauncherStateTransitionAnimation {
public static final int SINGLE_FRAME_DELAY = 16;
@Thunk Launcher mLauncher;
- @Thunk Callbacks mCb;
- @Thunk AnimatorSet mStateAnimation;
+ @Thunk AnimatorSet mCurrentAnimation;
- public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) {
+ public LauncherStateTransitionAnimation(Launcher l) {
mLauncher = l;
- mCb = cb;
}
/**
@@ -125,8 +116,8 @@ public class LauncherStateTransitionAnimation {
* @param startSearchAfterTransition Immediately starts app search after the transition to
* All Apps is completed.
*/
- public void startAnimationToAllApps(final boolean animated,
- final boolean startSearchAfterTransition) {
+ public void startAnimationToAllApps(final Workspace.State fromWorkspaceState,
+ final boolean animated, final boolean startSearchAfterTransition) {
final AllAppsContainerView toView = mLauncher.getAppsView();
final View buttonView = mLauncher.getAllAppsButton();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@@ -159,15 +150,16 @@ public class LauncherStateTransitionAnimation {
}
};
// Only animate the search bar if animating from spring loaded mode back to all apps
- startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, buttonView, toView,
- toView.getContentView(), toView.getRevealView(), toView.getSearchBarView(),
- animated, true /* hideSearchBar */, cb);
+ mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
+ Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(),
+ toView.getRevealView(), toView.getSearchBarView(), animated, cb);
}
/**
* Starts an animation to the widgets view.
*/
- public void startAnimationToWidgets(final boolean animated) {
+ public void startAnimationToWidgets(final Workspace.State fromWorkspaceState,
+ final boolean animated) {
final WidgetsContainerView toView = mLauncher.getWidgetsView();
final View buttonView = mLauncher.getWidgetsButton();
@@ -177,17 +169,17 @@ public class LauncherStateTransitionAnimation {
return 0.3f;
}
};
- startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, buttonView, toView,
- toView.getContentView(), toView.getRevealView(), null, animated,
- true /* hideSearchBar */, cb);
+ mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
+ Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
+ toView.getRevealView(), null, animated, cb);
}
/**
* Starts and animation to the workspace from the current overlay view.
*/
public void startAnimationToWorkspace(final Launcher.State fromState,
- final Workspace.State toWorkspaceState, final int toWorkspacePage,
- final boolean animated, final Runnable onCompleteRunnable) {
+ final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState,
+ final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
if (toWorkspaceState != Workspace.State.NORMAL &&
toWorkspaceState != Workspace.State.SPRING_LOADED &&
toWorkspaceState != Workspace.State.OVERVIEW) {
@@ -195,10 +187,10 @@ public class LauncherStateTransitionAnimation {
}
if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) {
- startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage,
+ startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
animated, onCompleteRunnable);
} else {
- startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage,
+ startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage,
animated, onCompleteRunnable);
}
}
@@ -207,12 +199,13 @@ public class LauncherStateTransitionAnimation {
* Creates and starts a new animation to a particular overlay view.
*/
@SuppressLint("NewApi")
- private void startAnimationToOverlay(final Workspace.State toWorkspaceState,
- final View buttonView, final View toView, final View contentView, final View revealView,
- final View overlaySearchBarView, final boolean animated, final boolean hideSearchBar,
- final PrivateTransitionCallbacks pCb) {
+ 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,
+ final boolean animated, final PrivateTransitionCallbacks pCb) {
+ final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
- final boolean material = Utilities.isLmpOrAbove();
+ final boolean material = Utilities.ATLEAST_LOLLIPOP;
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
@@ -230,11 +223,13 @@ public class LauncherStateTransitionAnimation {
// Create the workspace animation.
// NOTE: this call apparently also sets the state for the workspace if !animated
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1,
- animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews);
+ animated, layerViews);
- if (animated && initialized) {
- mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+ // Animate the search bar
+ startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
+ animated ? revealDuration : 0, overlaySearchBarView);
+ if (animated && initialized) {
// Setup the reveal view animation
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
@@ -274,7 +269,7 @@ public class LauncherStateTransitionAnimation {
// Play the animation
layerViews.put(revealView, BUILD_AND_SET_LAYER);
- mStateAnimation.play(panelAlphaAndDrift);
+ animation.play(panelAlphaAndDrift);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(0f);
@@ -282,7 +277,7 @@ public class LauncherStateTransitionAnimation {
searchBarAlpha.setDuration(100);
searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
- mStateAnimation.play(searchBarAlpha);
+ animation.play(searchBarAlpha);
}
// Setup the animation for the content view
@@ -297,13 +292,13 @@ public class LauncherStateTransitionAnimation {
pageDrift.setDuration(revealDuration);
pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
pageDrift.setStartDelay(itemsAlphaStagger);
- mStateAnimation.play(pageDrift);
+ animation.play(pageDrift);
ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f);
itemsAlpha.setDuration(revealDuration);
itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
itemsAlpha.setStartDelay(itemsAlphaStagger);
- mStateAnimation.play(itemsAlpha);
+ animation.play(itemsAlpha);
if (material) {
float startRadius = pCb.getMaterialRevealViewStartFinalRadius();
@@ -316,10 +311,10 @@ public class LauncherStateTransitionAnimation {
if (listener != null) {
reveal.addListener(listener);
}
- mStateAnimation.play(reveal);
+ animation.play(reveal);
}
- mStateAnimation.addListener(new AnimatorListenerAdapter() {
+ animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchOnLauncherTransitionEnd(fromView, animated, false);
@@ -335,12 +330,8 @@ public class LauncherStateTransitionAnimation {
}
}
- if (hideSearchBar) {
- mCb.onStateTransitionHideSearchBar();
- }
-
// This can hold unnecessary references to views.
- mStateAnimation = null;
+ cleanupAnimation();
pCb.onTransitionComplete();
}
@@ -348,7 +339,7 @@ public class LauncherStateTransitionAnimation {
// Play the workspace animation
if (workspaceAnim != null) {
- mStateAnimation.play(workspaceAnim);
+ animation.play(workspaceAnim);
}
// Dispatch the prepare transition signal
@@ -356,23 +347,22 @@ public class LauncherStateTransitionAnimation {
dispatchOnLauncherTransitionPrepare(toView, animated, false);
- final AnimatorSet stateAnimation = mStateAnimation;
+ final AnimatorSet stateAnimation = animation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
- // Check that mStateAnimation hasn't changed while
+ // Check that mCurrentAnimation hasn't changed while
// we waited for a layout/draw pass
- if (mStateAnimation != stateAnimation)
+ if (mCurrentAnimation != stateAnimation)
return;
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
// Enable all necessary layers
- boolean isLmpOrAbove = Utilities.isLmpOrAbove();
for (View v : layerViews.keySet()) {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
- if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
+ if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
v.buildLayer();
}
}
@@ -380,12 +370,14 @@ public class LauncherStateTransitionAnimation {
// Focus the new view
toView.requestFocus();
- mStateAnimation.start();
+ stateAnimation.start();
}
};
toView.bringToFront();
toView.setVisibility(View.VISIBLE);
toView.post(startAnimRunnable);
+
+ return animation;
} else {
toView.setTranslationX(0.0f);
toView.setTranslationY(0.0f);
@@ -397,10 +389,6 @@ public class LauncherStateTransitionAnimation {
// Show the content view
contentView.setVisibility(View.VISIBLE);
- if (hideSearchBar) {
- mCb.onStateTransitionHideSearchBar();
- }
-
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionEnd(fromView, animated, false);
@@ -408,18 +396,19 @@ public class LauncherStateTransitionAnimation {
dispatchOnLauncherTransitionStart(toView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
pCb.onTransitionComplete();
+
+ return null;
}
}
/**
* Starts and animation to the workspace from the apps view.
*/
- private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState,
- final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
+ 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() {
- int[] mAllAppsToPanelDelta;
-
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
// No alpha anim from all apps
@@ -451,8 +440,8 @@ public class LauncherStateTransitionAnimation {
}
};
// Only animate the search bar if animating to spring loaded mode from all apps
- startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage,
- mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
+ mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState,
+ toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(),
appsView.getRevealView(), appsView.getSearchBarView(), animated,
onCompleteRunnable, cb);
}
@@ -460,8 +449,9 @@ public class LauncherStateTransitionAnimation {
/**
* Starts and animation to the workspace from the widgets view.
*/
- private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState,
- final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) {
+ private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState,
+ final Workspace.State toWorkspaceState, final int toWorkspacePage,
+ final boolean animated, final Runnable onCompleteRunnable) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
@@ -479,21 +469,23 @@ public class LauncherStateTransitionAnimation {
};
}
};
- startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage,
- mLauncher.getWidgetsButton(), widgetsView, widgetsView.getContentView(),
- widgetsView.getRevealView(), null, animated, onCompleteRunnable, cb);
+ mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState,
+ toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView,
+ widgetsView.getContentView(), widgetsView.getRevealView(), null, animated,
+ onCompleteRunnable, cb);
}
/**
* Creates and starts a new animation to the workspace.
*/
- private void startAnimationToWorkspaceFromOverlay(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,
- final PrivateTransitionCallbacks pCb) {
+ 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,
+ final PrivateTransitionCallbacks pCb) {
+ final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
- final boolean material = Utilities.isLmpOrAbove();
+ final boolean material = Utilities.ATLEAST_LOLLIPOP;
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
@@ -511,15 +503,16 @@ public class LauncherStateTransitionAnimation {
// Create the workspace animation.
// NOTE: this call apparently also sets the state for the workspace if !animated
Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState,
- toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */,
- layerViews);
+ toWorkspacePage, animated, layerViews);
- if (animated && initialized) {
- mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+ // Animate the search bar
+ startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState,
+ animated ? revealDuration : 0, overlaySearchBarView);
+ if (animated && initialized) {
// Play the workspace animation
if (workspaceAnim != null) {
- mStateAnimation.play(workspaceAnim);
+ animation.play(workspaceAnim);
}
// hideAppsCustomizeHelper is called in some cases when it is already hidden
@@ -558,14 +551,14 @@ public class LauncherStateTransitionAnimation {
panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelDriftY.setInterpolator(decelerateInterpolator);
- mStateAnimation.play(panelDriftY);
+ animation.play(panelDriftY);
ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX",
0, revealViewToXDrift);
panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelDriftX.setInterpolator(decelerateInterpolator);
- mStateAnimation.play(panelDriftX);
+ animation.play(panelDriftX);
// Setup animation for the reveal panel alpha
final float revealViewToAlpha = !material ? 0f :
@@ -576,7 +569,7 @@ public class LauncherStateTransitionAnimation {
panelAlpha.setDuration(material ? revealDuration : 150);
panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
panelAlpha.setInterpolator(decelerateInterpolator);
- mStateAnimation.play(panelAlpha);
+ animation.play(panelAlpha);
}
// Setup the animation for the content view
@@ -589,13 +582,13 @@ public class LauncherStateTransitionAnimation {
pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
pageDrift.setInterpolator(decelerateInterpolator);
pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
- mStateAnimation.play(pageDrift);
+ animation.play(pageDrift);
contentView.setAlpha(1f);
ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f);
itemsAlpha.setDuration(100);
itemsAlpha.setInterpolator(decelerateInterpolator);
- mStateAnimation.play(itemsAlpha);
+ animation.play(itemsAlpha);
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
@@ -604,7 +597,7 @@ public class LauncherStateTransitionAnimation {
searchAlpha.setInterpolator(decelerateInterpolator);
searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY);
layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER);
- mStateAnimation.play(searchAlpha);
+ animation.play(searchAlpha);
}
if (material) {
@@ -620,14 +613,14 @@ public class LauncherStateTransitionAnimation {
if (listener != null) {
reveal.addListener(listener);
}
- mStateAnimation.play(reveal);
+ animation.play(reveal);
}
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionPrepare(toView, animated, true);
}
- mStateAnimation.addListener(new AnimatorListenerAdapter() {
+ animation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
fromView.setVisibility(View.GONE);
@@ -657,35 +650,37 @@ public class LauncherStateTransitionAnimation {
}
// This can hold unnecessary references to views.
- mStateAnimation = null;
+ cleanupAnimation();
pCb.onTransitionComplete();
}
});
- final AnimatorSet stateAnimation = mStateAnimation;
+ final AnimatorSet stateAnimation = animation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
- // Check that mStateAnimation hasn't changed while
+ // Check that mCurrentAnimation hasn't changed while
// we waited for a layout/draw pass
- if (mStateAnimation != stateAnimation)
+ if (mCurrentAnimation != stateAnimation)
return;
+
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
// Enable all necessary layers
- boolean isLmpOrAbove = Utilities.isLmpOrAbove();
for (View v : layerViews.keySet()) {
if (layerViews.get(v) == BUILD_AND_SET_LAYER) {
v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
}
- if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) {
+ if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) {
v.buildLayer();
}
}
- mStateAnimation.start();
+ stateAnimation.start();
}
};
fromView.post(startAnimRunnable);
+
+ return animation;
} else {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
@@ -700,9 +695,44 @@ public class LauncherStateTransitionAnimation {
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
+
+ return null;
}
}
+ /**
+ * 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) {
+ 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);
+ }
+ }
/**
* Dispatches the prepare-transition event to suitable views.
@@ -753,10 +783,14 @@ public class LauncherStateTransitionAnimation {
* Cancels the current animation.
*/
private void cancelAnimation() {
- if (mStateAnimation != null) {
- mStateAnimation.setDuration(0);
- mStateAnimation.cancel();
- mStateAnimation = null;
+ if (mCurrentAnimation != null) {
+ mCurrentAnimation.setDuration(0);
+ mCurrentAnimation.cancel();
+ mCurrentAnimation = null;
}
}
+
+ @Thunk void cleanupAnimation() {
+ mCurrentAnimation = null;
+ }
}
diff --git a/src/com/android/launcher3/LauncherViewPropertyAnimator.java b/src/com/android/launcher3/LauncherViewPropertyAnimator.java
index 4cafbbfa6..4406a2c5c 100644
--- a/src/com/android/launcher3/LauncherViewPropertyAnimator.java
+++ b/src/com/android/launcher3/LauncherViewPropertyAnimator.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
import java.util.EnumSet;
public class LauncherViewPropertyAnimator extends Animator implements AnimatorListener {
+
enum Properties {
TRANSLATION_X,
TRANSLATION_Y,
@@ -51,13 +52,12 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi
long mStartDelay;
long mDuration;
TimeInterpolator mInterpolator;
- ArrayList<Animator.AnimatorListener> mListeners;
+ ArrayList<Animator.AnimatorListener> mListeners = new ArrayList<>();
boolean mRunning = false;
FirstFrameAnimatorHelper mFirstFrameHelper;
public LauncherViewPropertyAnimator(View target) {
mTarget = target;
- mListeners = new ArrayList<Animator.AnimatorListener>();
}
@Override
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 218c1a36f..05f0a0553 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -2293,7 +2293,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Besides disabling the accessibility long-click, this also prevents this view from getting
// accessibility focus.
info.setLongClickable(false);
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK);
}
}
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 08f8e5601..40eadabd5 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -16,13 +16,17 @@
package com.android.launcher3;
+import android.annotation.TargetApi;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources.Theme;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.text.Layout;
import android.text.StaticLayout;
@@ -32,6 +36,8 @@ import android.view.View;
import android.view.View.OnClickListener;
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
+ private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5;
+ private static final float MIN_SATUNATION = 0.7f;
private static Theme sPreloaderTheme;
@@ -47,13 +53,14 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
private Bitmap mIcon;
private Drawable mCenterDrawable;
- private Drawable mTopCornerDrawable;
+ private Drawable mSettingIconDrawable;
private boolean mDrawableSizeChanged;
private final TextPaint mPaint;
private Layout mSetupTextLayout;
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info,
boolean disabledForSafeMode) {
super(context);
@@ -70,6 +77,10 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
setBackgroundResource(R.drawable.quantum_panel_dark);
setWillNotDraw(false);
+
+ if (Utilities.ATLEAST_LOLLIPOP) {
+ setElevation(getResources().getDimension(R.dimen.pending_widget_elevation));
+ }
}
@Override
@@ -124,10 +135,12 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon);
disabledIcon.setGhostModeEnabled(true);
mCenterDrawable = disabledIcon;
- mTopCornerDrawable = null;
+ mSettingIconDrawable = null;
} else if (isReadyForClickSetup()) {
- mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting);
- mTopCornerDrawable = new FastBitmapDrawable(mIcon);
+ mCenterDrawable = new FastBitmapDrawable(mIcon);
+ mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate();
+
+ updateSettingColor();
} else {
if (sPreloaderTheme == null) {
sPreloaderTheme = getResources().newTheme();
@@ -137,13 +150,25 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon);
mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
mCenterDrawable.setCallback(this);
- mTopCornerDrawable = null;
+ mSettingIconDrawable = null;
applyState();
}
mDrawableSizeChanged = true;
}
}
+ private void updateSettingColor() {
+ int color = Utilities.findDominantColorByHue(mIcon, 20);
+ // Make the dominant color bright.
+ float[] hsv = new float[3];
+ Color.colorToHSV(color, hsv);
+ hsv[1] = Math.min(hsv[1], MIN_SATUNATION);
+ hsv[2] = 1;
+ color = Color.HSVToColor(hsv);
+
+ mSettingIconDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
+ }
+
@Override
protected boolean verifyDrawable(Drawable who) {
return (who == mCenterDrawable) || super.verifyDrawable(who);
@@ -169,6 +194,83 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
&& (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
}
+ private void updateDrawableBounds() {
+ DeviceProfile grid = mLauncher.getDeviceProfile();
+ int paddingTop = getPaddingTop();
+ int paddingBottom = getPaddingBottom();
+ int paddingLeft = getPaddingLeft();
+ int paddingRight = getPaddingRight();
+
+ int minPadding = getResources()
+ .getDimensionPixelSize(R.dimen.pending_widget_min_padding);
+
+ int availableWidth = getWidth() - paddingLeft - paddingRight - 2 * minPadding;
+ int availableHeight = getHeight() - paddingTop - paddingBottom - 2 * minPadding;
+
+ if (mSettingIconDrawable == null) {
+ int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
+ ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
+ int maxSize = grid.iconSizePx + 2 * outset;
+ int size = Math.min(maxSize, Math.min(availableWidth, availableHeight));
+
+ mRect.set(0, 0, size, size);
+ mRect.inset(outset, outset);
+ mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
+ mCenterDrawable.setBounds(mRect);
+ } else {
+ float iconSize = 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.
+ float settingIconScaleFactor = 1 + SETUP_ICON_SIZE_FACTOR * 2;
+ int maxSize = Math.max(availableWidth, availableHeight);
+ if (iconSize * settingIconScaleFactor > maxSize) {
+ // There is an overlap
+ iconSize = maxSize / settingIconScaleFactor;
+ }
+
+ 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();
+
+ // Extra icon size due to the setting icon
+ float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor
+ + grid.iconDrawablePaddingPx;
+
+ int iconTop;
+ if (minHeightWithText < availableHeight) {
+ // We can draw the text as well
+ iconTop = (getHeight() - textHeight -
+ grid.iconDrawablePaddingPx - actualIconSize) / 2;
+
+ } else {
+ // The text will not fit. Only draw the icons.
+ iconTop = (getHeight() - actualIconSize) / 2;
+ mSetupTextLayout = null;
+ }
+
+ mRect.set(0, 0, actualIconSize, actualIconSize);
+ mRect.offset((getWidth() - actualIconSize) / 2, iconTop);
+ mCenterDrawable.setBounds(mRect);
+
+ mRect.left = paddingLeft + minPadding;
+ mRect.right = mRect.left + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize);
+ mRect.top = paddingTop + minPadding;
+ mRect.bottom = mRect.top + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize);
+ mSettingIconDrawable.setBounds(mRect);
+
+ if (mSetupTextLayout != null) {
+ // Set up position for dragging the text
+ mRect.left = paddingLeft + minPadding;
+ mRect.top = mCenterDrawable.getBounds().bottom + grid.iconDrawablePaddingPx;
+ }
+ }
+ }
+
@Override
protected void onDraw(Canvas canvas) {
if (mCenterDrawable == null) {
@@ -176,81 +278,21 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
return;
}
- DeviceProfile grid = mLauncher.getDeviceProfile();
- if (mTopCornerDrawable == null) {
- if (mDrawableSizeChanged) {
- int outset = (mCenterDrawable instanceof PreloadIconDrawable) ?
- ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0;
- int maxSize = grid.iconSizePx + 2 * outset;
- int size = Math.min(maxSize, Math.min(
- getWidth() - getPaddingLeft() - getPaddingRight(),
- getHeight() - getPaddingTop() - getPaddingBottom()));
-
- mRect.set(0, 0, size, size);
- mRect.inset(outset, outset);
- mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
- mCenterDrawable.setBounds(mRect);
- mDrawableSizeChanged = false;
- }
- mCenterDrawable.draw(canvas);
- } else {
- // Draw the top corner icon and "Setup" text is possible
- if (mDrawableSizeChanged) {
- int iconSize = grid.iconSizePx;
- int paddingTop = getPaddingTop();
- int paddingBottom = getPaddingBottom();
- int paddingLeft = getPaddingLeft();
- int paddingRight = getPaddingRight();
-
- int availableWidth = getWidth() - paddingLeft - paddingRight;
- int availableHeight = getHeight() - paddingTop - paddingBottom;
-
- // Recreate the setup text.
- mSetupTextLayout = new StaticLayout(
- getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
- Layout.Alignment.ALIGN_CENTER, 1, 0, true);
- if (mSetupTextLayout.getLineCount() == 1) {
- // The text fits in a single line. No need to draw the setup icon.
- int size = Math.min(iconSize, Math.min(availableWidth,
- availableHeight - mSetupTextLayout.getHeight()));
- mRect.set(0, 0, size, size);
- mRect.offsetTo((getWidth() - mRect.width()) / 2,
- (getHeight() - mRect.height() - mSetupTextLayout.getHeight()
- - grid.iconDrawablePaddingPx) / 2);
-
- mTopCornerDrawable.setBounds(mRect);
-
- // Update left and top to indicate the position where the text will be drawn.
- mRect.left = paddingLeft;
- mRect.top = mRect.bottom + grid.iconDrawablePaddingPx;
- } else {
- // The text can't be drawn in a single line. Draw a setup icon instead.
- mSetupTextLayout = null;
- int size = Math.min(iconSize, Math.min(
- getWidth() - paddingLeft - paddingRight,
- getHeight() - paddingTop - paddingBottom));
- mRect.set(0, 0, size, size);
- mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
- mCenterDrawable.setBounds(mRect);
-
- size = Math.min(size / 2,
- Math.max(mRect.top - paddingTop, mRect.left - paddingLeft));
- mTopCornerDrawable.setBounds(paddingLeft, paddingTop,
- paddingLeft + size, paddingTop + size);
- }
- mDrawableSizeChanged = false;
- }
+ if (mDrawableSizeChanged) {
+ updateDrawableBounds();
+ mDrawableSizeChanged = false;
+ }
- if (mSetupTextLayout == null) {
- mCenterDrawable.draw(canvas);
- mTopCornerDrawable.draw(canvas);
- } else {
- canvas.save();
- canvas.translate(mRect.left, mRect.top);
- mSetupTextLayout.draw(canvas);
- canvas.restore();
- mTopCornerDrawable.draw(canvas);
- }
+ mCenterDrawable.draw(canvas);
+ if (mSettingIconDrawable != null) {
+ mSettingIconDrawable.draw(canvas);
}
+ if (mSetupTextLayout != null) {
+ canvas.save();
+ canvas.translate(mRect.left, mRect.top);
+ mSetupTextLayout.draw(canvas);
+ canvas.restore();
+ }
+
}
}
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
index bcb59c448..45e4b2c4a 100644
--- a/src/com/android/launcher3/PreloadIconDrawable.java
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -213,6 +213,10 @@ class PreloadIconDrawable extends Drawable {
return mAnimationProgress;
}
+ public boolean hasNotCompleted() {
+ return mAnimationProgress < ANIMATION_PROGRESS_COMPLETED;
+ }
+
@Override
public int getIntrinsicHeight() {
return mIcon.getIntrinsicHeight();
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 4cdf1cac9..772a334b9 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -18,32 +18,57 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
-import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.View;
+import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.widget.FrameLayout;
+import com.android.launcher3.util.Thunk;
+
/*
* Ths bar will manage the transition between the QSB search bar and the delete drop
* targets so that each of the individual IconDropTargets don't have to.
*/
public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener {
- private static final int TRANSITION_DURATION = 200;
+ /** 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);
+
+ private final float mSearchBarAlpha;
+ private final float mDropTargetBarAlpha;
+
+ State(float sbAlpha, float dtbAlpha) {
+ mSearchBarAlpha = sbAlpha;
+ mDropTargetBarAlpha = dtbAlpha;
+ }
+
+ float getSearchBarAlpha() {
+ return mSearchBarAlpha;
+ }
+
+ float getDropTargetBarAlpha() {
+ return mDropTargetBarAlpha;
+ }
+ }
- private ObjectAnimator mShowDropTargetBarAnim;
- private ValueAnimator mHideSearchBarAnim;
+ private static int DEFAULT_DRAG_FADE_DURATION = 175;
+
+ private LauncherViewPropertyAnimator mDropTargetBarAnimator;
+ private LauncherViewPropertyAnimator mQSBSearchBarAnimator;
private static final AccelerateInterpolator sAccelerateInterpolator =
new AccelerateInterpolator();
- private boolean mIsSearchBarHidden;
- private View mQSBSearchBar;
- private View mDropTargetBar;
+ private State mState = State.SEARCH_BAR;
+ @Thunk View mQSB;
+ @Thunk View mDropTargetBar;
private boolean mDeferOnDragEnd = false;
+ @Thunk boolean mAccessibilityEnabled = false;
// Drop targets
private ButtonDropTarget mInfoDropTarget;
@@ -75,39 +100,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
mUninstallDropTarget.setLauncher(launcher);
}
- public void setQsbSearchBar(View qsb) {
- mQSBSearchBar = qsb;
- if (mQSBSearchBar != null) {
- mHideSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f);
- setupAnimation(mHideSearchBarAnim, mQSBSearchBar);
- } else {
- // Create a no-op animation of the search bar is null
- mHideSearchBarAnim = ValueAnimator.ofFloat(0, 0);
- mHideSearchBarAnim.setDuration(TRANSITION_DURATION);
- }
- }
-
- private void prepareStartAnimation(View v) {
- // Enable the hw layers before the animation starts (will be disabled in the onAnimationEnd
- // callback below)
- if (v != null) {
- v.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- }
- }
-
- private void setupAnimation(ValueAnimator anim, final View v) {
- anim.setInterpolator(sAccelerateInterpolator);
- anim.setDuration(TRANSITION_DURATION);
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- if (v != null) {
- v.setLayerType(View.LAYER_TYPE_NONE, null);
- }
- }
- });
- }
-
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -124,72 +116,89 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
// Create the various fade animations
mDropTargetBar.setAlpha(0f);
- mShowDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f);
- setupAnimation(mShowDropTargetBarAnim, mDropTargetBar);
- }
+ 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);
+ }
- /**
- * Finishes all the animations on the search and drop target bars.
- */
- public void finishAnimations() {
- prepareStartAnimation(mDropTargetBar);
- mShowDropTargetBarAnim.reverse();
- prepareStartAnimation(mQSBSearchBar);
- mHideSearchBarAnim.reverse();
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mDropTargetBar != null) {
+ AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled);
+ }
+ }
+ });
}
- /**
- * Shows the search bar.
- */
- public void showSearchBar(boolean animated) {
- if (!mIsSearchBarHidden) return;
- if (animated) {
- prepareStartAnimation(mQSBSearchBar);
- mHideSearchBarAnim.reverse();
+ 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 {
- mHideSearchBarAnim.cancel();
- if (mQSBSearchBar != null) {
- mQSBSearchBar.setAlpha(1f);
- }
+ mQSBSearchBarAnimator = null;
}
- mIsSearchBarHidden = false;
}
/**
- * Hides the search bar. We only use this for clings.
+ * Animates the current search bar state to a new state. If the {@param duration} is 0, then
+ * the state is applied immediately.
*/
- public void hideSearchBar(boolean animated) {
- if (mIsSearchBarHidden) return;
- if (animated) {
- prepareStartAnimation(mQSBSearchBar);
- mHideSearchBarAnim.start();
- } else {
- mHideSearchBarAnim.cancel();
- if (mQSBSearchBar != null) {
- mQSBSearchBar.setAlpha(0f);
- }
+ public void animateToState(State newState, int duration) {
+ if (mState != newState) {
+ mState = newState;
+
+ // Update the accessibility state
+ AccessibilityManager am = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ mAccessibilityEnabled = am.isEnabled();
+
+ animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
+ duration);
+ animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
+ duration);
}
- mIsSearchBarHidden = true;
}
/**
- * Shows the drop target bar.
+ * Convenience method to animate the alpha of a view using hardware layers.
*/
- public void showDeleteTarget() {
- // Animate out the QSB search bar, and animate in the drop target bar
- prepareStartAnimation(mDropTargetBar);
- mShowDropTargetBarAnim.start();
- hideSearchBar(true);
- }
+ private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha,
+ int duration) {
+ if (v == null) {
+ return;
+ }
- /**
- * Hides the drop target bar.
- */
- public void hideDeleteTarget() {
- // Restore the QSB search bar, and animate out the drop target bar
- prepareStartAnimation(mDropTargetBar);
- mShowDropTargetBarAnim.reverse();
- showSearchBar(true);
+ animator.cancel();
+ 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);
+ }
+ }
}
/*
@@ -197,9 +206,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
*/
@Override
public void onDragStart(DragSource source, Object info, int dragAction) {
- showDeleteTarget();
+ animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION);
}
+ /**
+ * This is called to defer hiding the delete drop target until the drop animation has completed,
+ * instead of hiding immediately when the drag has ended.
+ */
public void deferOnDragEnd() {
mDeferOnDragEnd = true;
}
@@ -207,22 +220,25 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
@Override
public void onDragEnd() {
if (!mDeferOnDragEnd) {
- hideDeleteTarget();
+ animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION);
} else {
mDeferOnDragEnd = false;
}
}
+ /**
+ * @return the bounds of the QSB search bar.
+ */
public Rect getSearchBarBounds() {
- if (mQSBSearchBar != null) {
+ if (mQSB != null) {
final int[] pos = new int[2];
- mQSBSearchBar.getLocationOnScreen(pos);
+ mQSB.getLocationOnScreen(pos);
final Rect rect = new Rect();
rect.left = pos[0];
rect.top = pos[1];
- rect.right = pos[0] + mQSBSearchBar.getWidth();
- rect.bottom = pos[1] + mQSBSearchBar.getHeight();
+ rect.right = pos[0] + mQSB.getWidth();
+ rect.bottom = pos[1] + mQSB.getHeight();
return rect;
} else {
return null;
@@ -230,8 +246,8 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
public void enableAccessibleDrag(boolean enable) {
- if (mQSBSearchBar != null) {
- mQSBSearchBar.setVisibility(enable ? View.GONE : View.VISIBLE);
+ if (mQSB != null) {
+ mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
}
mInfoDropTarget.enableAccessibleDrag(enable);
mDeleteDropTarget.enableAccessibleDrag(enable);
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 56c0b9d2f..5766cf2f2 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -198,13 +198,17 @@ public class ShortcutInfo extends ItemInfo {
return mIcon;
}
- public void updateIcon(IconCache iconCache) {
+ public void updateIcon(IconCache iconCache, boolean useLowRes) {
if (itemType == Favorites.ITEM_TYPE_APPLICATION) {
iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user,
- shouldUseLowResIcon());
+ useLowRes);
}
}
+ public void updateIcon(IconCache iconCache) {
+ updateIcon(iconCache, shouldUseLowResIcon());
+ }
+
@Override
void onAddToDatabase(Context context, ContentValues values) {
super.onAddToDatabase(context, values);
diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java
index da46e6a54..e03273a6e 100644
--- a/src/com/android/launcher3/StylusEventHelper.java
+++ b/src/com/android/launcher3/StylusEventHelper.java
@@ -1,8 +1,5 @@
package com.android.launcher3;
-import com.android.launcher3.Utilities;
-
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@@ -77,8 +74,9 @@ public class StylusEventHelper {
* @param event The event to check.
* @return Whether a stylus button press occurred.
*/
- public static boolean isStylusButtonPressed(MotionEvent event) {
+ private static boolean isStylusButtonPressed(MotionEvent event) {
return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
- && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
+ && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY)
+ == MotionEvent.BUTTON_SECONDARY);
}
} \ No newline at end of file
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 0819f8ce0..955d4013c 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -38,7 +38,7 @@ public class UninstallDropTarget extends ButtonDropTarget {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public static boolean supportsDrop(Context context, Object info) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ if (Utilities.ATLEAST_JB_MR2) {
UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
Bundle restrictions = userManager.getUserRestrictions();
if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 8fd298df7..adedd33b2 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -87,6 +87,24 @@ public final class Utilities {
private static final int[] sLoc0 = new int[2];
private static final int[] sLoc1 = new int[2];
+ // TODO: use Build.VERSION_CODES when available
+ public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= 23;
+
+ public static final boolean ATLEAST_LOLLIPOP_MR1 =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1;
+
+ public static final boolean ATLEAST_LOLLIPOP =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
+
+ public static final boolean ATLEAST_KITKAT =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
+
+ public static final boolean ATLEAST_JB_MR1 =
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1;
+
+ 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";
@@ -110,23 +128,6 @@ public final class Utilities {
return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation);
}
- /**
- * Indicates if the device is running LMP or higher.
- */
- public static boolean isLmpOrAbove() {
- return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP;
- }
-
- public static boolean isLmpMR1OrAbove() {
- // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22;
- return Build.VERSION.SDK_INT >= 22;
- }
-
- public static boolean isLmpMR1() {
- // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22;
- return Build.VERSION.SDK_INT == 22;
- }
-
public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
byte[] data = c.getBlob(iconIndex);
try {
@@ -517,7 +518,7 @@ public final class Utilities {
@TargetApi(Build.VERSION_CODES.KITKAT)
public static boolean isViewAttachedToWindow(View v) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ if (ATLEAST_KITKAT) {
return v.isAttachedToWindow();
} else {
// A proxy call which returns null, if the view is not attached to the window.
@@ -544,7 +545,7 @@ public final class Utilities {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
if (info.provider.getPackageName().equals(providerPkg)) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (ATLEAST_JB_MR1) {
if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
return info;
} else if (defaultWidgetForSearchPackage == null) {
@@ -655,7 +656,7 @@ public final class Utilities {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean isRtl(Resources res) {
- return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) &&
+ return ATLEAST_JB_MR1 &&
(res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL);
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 629387ed0..346055566 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -105,7 +105,7 @@ public class WidgetPreviewLoader {
* sizes (landscape vs portrait).
*/
private static class CacheDb extends SQLiteOpenHelper {
- private static final int DB_VERSION = 3;
+ private static final int DB_VERSION = 4;
private static final String TABLE_NAME = "shortcut_and_widget_previews";
private static final String COLUMN_COMPONENT = "componentName";
@@ -212,7 +212,6 @@ public class WidgetPreviewLoader {
public void removeObsoletePreviews(ArrayList<Object> list) {
Utilities.assertWorkerThread();
- LongSparseArray<UserHandleCompat> userIdCache = new LongSparseArray<>();
LongSparseArray<HashSet<String>> validPackages = new LongSparseArray<>();
for (Object obj : list) {
@@ -227,15 +226,7 @@ public class WidgetPreviewLoader {
pkg = info.provider.getPackageName();
}
- int userIdIndex = userIdCache.indexOfValue(user);
- final long userId;
- if (userIdIndex < 0) {
- userId = mUserManager.getSerialNumberForUser(user);
- userIdCache.put(userId, user);
- } else {
- userId = userIdCache.keyAt(userIdIndex);
- }
-
+ final long userId = mUserManager.getSerialNumberForUser(user);
HashSet<String> packages = validPackages.get(userId);
if (packages == null) {
packages = new HashSet<>();
@@ -361,8 +352,8 @@ public class WidgetPreviewLoader {
}
final boolean widgetPreviewExists = (drawable != null);
- final int spanX = info.getSpanX(launcher) < 1 ? 1 : info.getSpanX(launcher);
- final int spanY = info.getSpanY(launcher) < 1 ? 1 : info.getSpanY(launcher);
+ final int spanX = info.spanX;
+ final int spanY = info.spanY;
int previewWidth;
int previewHeight;
@@ -386,7 +377,7 @@ public class WidgetPreviewLoader {
preScaledWidthOut[0] = previewWidth;
}
if (previewWidth > maxPreviewWidth) {
- scale = maxPreviewWidth / (float) previewWidth;
+ scale = (maxPreviewWidth - 2 * mProfileBadgeMargin) / (float) (previewWidth);
}
if (scale != 1f) {
previewWidth = (int) (scale * previewWidth);
@@ -405,7 +396,7 @@ public class WidgetPreviewLoader {
}
// Draw the scaled preview into the final bitmap
- int x = (preview.getWidth() - previewWidth - mProfileBadgeMargin) / 2;
+ int x = (preview.getWidth() - previewWidth) / 2;
if (widgetPreviewExists) {
drawable.setBounds(x, 0, x + previewWidth, previewHeight);
drawable.draw(c);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4a6b90afe..856e3b88a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -178,7 +178,24 @@ public class Workspace extends PagedView
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
- enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
+ 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);
+
+ private final SearchDropTargetBar.State mBarState;
+
+ State(SearchDropTargetBar.State searchBarState) {
+ mBarState = searchBarState;
+ }
+
+ public SearchDropTargetBar.State getSearchDropTargetBarState() {
+ return mBarState;
+ }
+ };
+
private State mState = State.NORMAL;
private boolean mIsSwitchingState = false;
@@ -306,8 +323,9 @@ public class Workspace extends PagedView
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.Workspace, defStyle, 0);
mSpringLoadedShrinkFactor =
- res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
- mOverviewModeShrinkFactor = grid.getOverviewModeScale(mIsRtl);
+ res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f;
+ mOverviewModeShrinkFactor =
+ res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1);
a.recycle();
@@ -869,6 +887,9 @@ public class Workspace extends PagedView
}
}
+ LauncherAccessibilityDelegate delegate =
+ LauncherAppState.getInstance().getAccessibilityDelegate();
+
// We enforce at least one page to add new items to. In the case that we remove the last
// such screen, we convert the last screen to the empty screen
int minScreens = 1 + numCustomPages();
@@ -883,6 +904,11 @@ public class Workspace extends PagedView
if (indexOfChild(cl) < currentPage) {
pageShift++;
}
+
+ if (delegate != null && delegate.isInAccessibleDrag()) {
+ cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
+ }
+
removeView(cl);
} else {
// if this is the last non-custom content screen, convert it to the empty screen
@@ -1566,7 +1592,7 @@ public class Workspace extends PagedView
// Reset our click listener
setOnClickListener(mLauncher);
}
- mLauncher.getSearchBar().enableAccessibleDrag(enable);
+ mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable);
mLauncher.getHotseat().getLayout()
.enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG);
}
@@ -1952,15 +1978,17 @@ public class Workspace extends PagedView
int getOverviewModeTranslationY() {
DeviceProfile grid = mLauncher.getDeviceProfile();
- Rect overviewBar = grid.getOverviewModeButtonBarRect();
+ Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
+ int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
- int availableHeight = getViewportHeight();
int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
- int offsetFromTopEdge = (availableHeight - scaledHeight) / 2;
- int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height()
- - scaledHeight) / 2;
-
- return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview;
+ int workspaceTop = mInsets.top + workspacePadding.top;
+ int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
+ int overviewTop = mInsets.top;
+ int overviewBottom = getViewportHeight() - mInsets.bottom - overviewButtonBarHeight;
+ int workspaceOffsetTopEdge = workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
+ int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
+ return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
}
/**
@@ -1968,10 +1996,10 @@ public class Workspace extends PagedView
* to that new state.
*/
public Animator setStateWithAnimation(State toState, int toPage, boolean animated,
- boolean hasOverlaySearchBar, HashMap<View, Integer> layerViews) {
+ HashMap<View, Integer> layerViews) {
// Create the animation to the new state
Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState,
- toState, toPage, animated, hasOverlaySearchBar, layerViews);
+ toState, toPage, animated, layerViews);
// Update the current state
mState = toState;
@@ -1985,7 +2013,7 @@ public class Workspace extends PagedView
}
public void updateAccessibilityFlags() {
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
int total = getPageCount();
for (int i = numCustomPages(); i < total; i++) {
updateAccessibilityFlags((CellLayout) getPageAt(i), i);
@@ -2273,6 +2301,8 @@ public class Workspace extends PagedView
dragRect = new Rect(left, top, right, bottom);
} else if (child instanceof FolderIcon) {
int previewSize = grid.folderIconSizePx;
+ dragVisualizeOffset = new Point(-padding.get() / 2,
+ padding.get() / 2 - child.getPaddingTop());
dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize);
}
@@ -2289,14 +2319,14 @@ public class Workspace extends PagedView
throw new IllegalStateException(msg);
}
- DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
- DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible);
- dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
-
if (child.getParent() instanceof ShortcutAndWidgetContainer) {
mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent();
}
+ DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
+ DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible);
+ dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
+
b.recycle();
}
@@ -3763,7 +3793,11 @@ public class Workspace extends PagedView
if (parentCell != null) {
parentCell.removeView(v);
} else if (LauncherAppState.isDogfoodBuild()) {
- throw new NullPointerException("mDragInfo.cell has null parent");
+ // When an app is uninstalled using the drop target, we wait until resume to remove
+ // the icon. We also remove all the corresponding items from the workspace at
+ // {@link Launcher#bindComponentsRemoved}. That call can come before or after
+ // {@link Launcher#mOnResumeCallbacks} depending on how busy the worker thread is.
+ Log.e(TAG, "mDragInfo.cell has null parent");
}
if (v instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) v);
@@ -4050,6 +4084,16 @@ public class Workspace extends PagedView
});
}
+ public View getHomescreenIconByItemId(final long id) {
+ return getFirstMatch(new ItemOperator() {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ return info != null && info.id == id;
+ }
+ });
+ }
+
public View getViewForTag(final Object tag) {
return getFirstMatch(new ItemOperator() {
@@ -4298,8 +4342,9 @@ public class Workspace extends PagedView
updates.contains(info)) {
ShortcutInfo si = (ShortcutInfo) info;
BubbleTextView shortcut = (BubbleTextView) v;
- boolean oldPromiseState = getTextViewIcon(shortcut)
- instanceof PreloadIconDrawable;
+ Drawable oldIcon = getTextViewIcon(shortcut);
+ boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable)
+ && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
shortcut.applyFromShortcutInfo(si, mIconCache,
si.isPromise() != oldPromiseState);
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index b8916a72b..54f63bbd8 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -211,22 +211,20 @@ public class WorkspaceStateTransitionAnimation {
mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime);
mSpringLoadedShrinkFactor =
res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f;
+ mOverviewModeShrinkFactor =
+ res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f;
- mOverviewModeShrinkFactor = grid.getOverviewModeScale(Utilities.isRtl(res));
mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
}
public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState,
- int toPage, boolean animated, boolean hasOverlaySearchBar,
- HashMap<View, Integer> layerViews) {
+ int toPage, boolean animated, HashMap<View, Integer> layerViews) {
AccessibilityManager am = (AccessibilityManager)
mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
final boolean accessibilityEnabled = am.isEnabled();
TransitionStates states = new TransitionStates(fromState, toState);
- int duration = getAnimationDuration(states);
- animateWorkspace(states, toPage, animated, duration, layerViews,
- accessibilityEnabled);
- animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews,
+ int workspaceDuration = getAnimationDuration(states);
+ animateWorkspace(states, toPage, animated, workspaceDuration, layerViews,
accessibilityEnabled);
animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION);
return mStateAnimator;
@@ -473,75 +471,9 @@ public class WorkspaceStateTransitionAnimation {
}
/**
- * Coordinates with the workspace animation to animate the search bar.
- *
- * TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the
- * bar has no idea that it is hidden, and this has no idea what state the bar is
- * actually in.
- */
- private void animateSearchBar(TransitionStates states, boolean animated, int duration,
- boolean hasOverlaySearchBar, final HashMap<View, Integer> layerViews,
- final boolean accessibilityEnabled) {
-
- // The search bar is only visible in the workspace
- final View searchBar = mLauncher.getOrCreateQsbBar();
- if (searchBar != null) {
- final boolean searchBarWillBeShown = states.stateIsNormal;
- final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f;
- if (animated) {
- if (hasOverlaySearchBar) {
- // If there is an overlay search bar, then we will coordinate with it.
- mStateAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- // If we are transitioning to a visible search bar, show it immediately
- // and let the overlay search bar has faded out
- if (searchBarWillBeShown) {
- searchBar.setAlpha(finalSearchBarAlpha);
- AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
- }
- }
-
- @Override
- public void onAnimationEnd(Animator animation) {
- // If we are transitioning to a hidden search bar, hide it only after
- // the overlay search bar has faded in
- if (!searchBarWillBeShown) {
- searchBar.setAlpha(finalSearchBarAlpha);
- AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
- }
- }
- });
- } else {
- // Otherwise, we can just do the normal animation
- LauncherViewPropertyAnimator searchBarAlpha =
- new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha);
- searchBarAlpha.addListener(new AlphaUpdateListener(searchBar,
- accessibilityEnabled));
- searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- if (layerViews != null) {
- // If layerViews is not null, we add these views, and indicate that
- // the caller can manage layer state.
- layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER);
- } else {
- // Otherwise let the animator handle layer management.
- searchBarAlpha.withLayer();
- }
- searchBarAlpha.setDuration(duration);
- mStateAnimator.play(searchBarAlpha);
- }
- } else {
- // Set the search bar state immediately
- searchBar.setAlpha(finalSearchBarAlpha);
- AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled);
- }
- }
- }
-
- /**
* Animates the background scrim. Add to the state animator to prevent jankiness.
*
- * @param finalAlpha the final alpha for the background scrim
+ * @param states the current and final workspace states
* @param animated whether or not to set the background alpha immediately
* @duration duration of the animation
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
new file mode 100644
index 000000000..117aca921
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsBackgroundDrawable.java
@@ -0,0 +1,194 @@
+/*
+ * 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.animation.ObjectAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import android.view.Gravity;
+import com.android.launcher3.R;
+
+/**
+ * A helper class to positon and orient a drawable to be drawn.
+ */
+class TransformedImageDrawable {
+ private Drawable mImage;
+ private float mXPercent;
+ private float mYPercent;
+ private int mGravity;
+
+ /**
+ * @param gravity If one of the Gravity center values, the x and y offset will take the width
+ * and height of the image into account to center the image to the offset.
+ */
+ public TransformedImageDrawable(Resources res, int resourceId, float xPct, float yPct,
+ int gravity) {
+ mImage = res.getDrawable(resourceId);
+ mXPercent = xPct;
+ mYPercent = yPct;
+ mGravity = gravity;
+ }
+
+ public void setAlpha(int alpha) {
+ mImage.setAlpha(alpha);
+ }
+
+ public int getAlpha() {
+ return mImage.getAlpha();
+ }
+
+ public void updateBounds(Rect bounds) {
+ int width = mImage.getIntrinsicWidth();
+ int height = mImage.getIntrinsicHeight();
+ int left = bounds.left + (int) (mXPercent * bounds.width());
+ int top = bounds.top + (int) (mYPercent * bounds.height());
+ if ((mGravity & Gravity.CENTER_HORIZONTAL) == Gravity.CENTER_HORIZONTAL) {
+ left -= (width / 2);
+ }
+ if ((mGravity & Gravity.CENTER_VERTICAL) == Gravity.CENTER_VERTICAL) {
+ top -= (height / 2);
+ }
+ mImage.setBounds(left, top, left + width, top + height);
+ }
+
+ public void draw(Canvas canvas) {
+ int c = canvas.save(Canvas.MATRIX_SAVE_FLAG);
+ mImage.draw(canvas);
+ canvas.restoreToCount(c);
+ }
+}
+
+/**
+ * This is a custom composite drawable that has a fixed virtual size and dynamically lays out its
+ * children images relatively within its bounds. This way, we can reduce the memory usage of a
+ * single, large sparsely populated image.
+ */
+public class AllAppsBackgroundDrawable extends Drawable {
+
+ private final TransformedImageDrawable mHand;
+ private final TransformedImageDrawable[] mIcons;
+ private final int mWidth;
+ private final int mHeight;
+
+ private ObjectAnimator mBackgroundAnim;
+
+ public AllAppsBackgroundDrawable(Context context) {
+ Resources res = context.getResources();
+ mHand = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_hand,
+ 0.575f, 0.1f, Gravity.CENTER_HORIZONTAL);
+ mIcons = new TransformedImageDrawable[4];
+ mIcons[0] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_1,
+ 0.375f, 0, Gravity.CENTER_HORIZONTAL);
+ mIcons[1] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_2,
+ 0.3125f, 0.25f, Gravity.CENTER_HORIZONTAL);
+ mIcons[2] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_3,
+ 0.475f, 0.4f, Gravity.CENTER_HORIZONTAL);
+ mIcons[3] = new TransformedImageDrawable(res, R.drawable.ic_all_apps_bg_icon_4,
+ 0.7f, 0.125f, Gravity.CENTER_HORIZONTAL);
+ mWidth = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_width);
+ mHeight = res.getDimensionPixelSize(R.dimen.all_apps_background_canvas_height);
+ }
+
+ /**
+ * Animates the background alpha.
+ */
+ public void animateBgAlpha(float finalAlpha, int duration) {
+ int finalAlphaI = (int) (finalAlpha * 255f);
+ if (getAlpha() != finalAlphaI) {
+ mBackgroundAnim = cancelAnimator(mBackgroundAnim);
+ mBackgroundAnim = ObjectAnimator.ofInt(this, "alpha", finalAlphaI);
+ mBackgroundAnim.setDuration(duration);
+ mBackgroundAnim.start();
+ }
+ }
+
+ /**
+ * Sets the background alpha immediately.
+ */
+ public void setBgAlpha(float finalAlpha) {
+ int finalAlphaI = (int) (finalAlpha * 255f);
+ if (getAlpha() != finalAlphaI) {
+ mBackgroundAnim = cancelAnimator(mBackgroundAnim);
+ setAlpha(finalAlphaI);
+ }
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mWidth;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mHeight;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mHand.draw(canvas);
+ for (int i = 0; i < mIcons.length; i++) {
+ mIcons[i].draw(canvas);
+ }
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ super.onBoundsChange(bounds);
+ mHand.updateBounds(bounds);
+ for (int i = 0; i < mIcons.length; i++) {
+ mIcons[i].updateBounds(bounds);
+ }
+ invalidateSelf();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mHand.setAlpha(alpha);
+ for (int i = 0; i < mIcons.length; i++) {
+ mIcons[i].setAlpha(alpha);
+ }
+ invalidateSelf();
+ }
+
+ @Override
+ public int getAlpha() {
+ return mHand.getAlpha();
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // Do nothing
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ private ObjectAnimator cancelAnimator(ObjectAnimator animator) {
+ if (animator != null) {
+ animator.removeAllListeners();
+ animator.cancel();
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 67d572819..88c6acada 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -16,34 +16,26 @@
package com.android.launcher3.allapps;
import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
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.os.Build;
-import android.os.Bundle;
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.view.ViewTreeObserver;
-import android.widget.FrameLayout;
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.CheckLongPressHelper;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DragSource;
@@ -53,7 +45,6 @@ import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherTransitionable;
import com.android.launcher3.R;
-import com.android.launcher3.Stats;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.util.ComponentKey;
@@ -155,6 +146,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Thunk AllAppsSearchBarController mSearchBarController;
private ViewGroup mSearchBarContainerView;
private View mSearchBarView;
+ private SpannableStringBuilder mSearchQueryBuilder = null;
private int mSectionNamesMargin;
private int mNumAppsPerRow;
@@ -165,7 +157,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// This coordinate is relative to its parent
private final Point mIconLastTouchPos = new Point();
- private SpannableStringBuilder mSearchQueryBuilder = null;
+ 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);
@@ -182,8 +180,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mLauncher = (Launcher) context;
mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
mApps = new AlphabeticalAppsList(context);
- mAdapter = new AllAppsGridAdapter(context, mApps, this, mLauncher, this);
- mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message));
+ mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this);
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
@@ -528,7 +525,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
ItemInfo itemInfo = (ItemInfo) d.dragInfo;
if (layout != null) {
- layout.calculateSpans(itemInfo);
showOutOfSpaceMessage =
!layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
}
@@ -559,8 +555,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
if (toWorkspace) {
- // Reset the search bar after transitioning home
+ // Reset the search bar and base recycler view after transitioning home
mSearchBarController.reset();
+ mAppsRecyclerView.reset();
}
}
@@ -616,19 +613,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
@Override
public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
if (apps != null) {
- if (apps.isEmpty()) {
- String formatStr = getResources().getString(R.string.all_apps_no_search_results);
- mAdapter.setEmptySearchText(String.format(formatStr, query));
- } else {
- mAppsRecyclerView.scrollToTop();
- }
mApps.setOrderedFilter(apps);
+ mAdapter.setLastSearchQuery(query);
+ mAppsRecyclerView.onSearchResultsChanged();
}
}
@Override
public void clearSearchResult() {
mApps.setOrderedFilter(null);
+ mAppsRecyclerView.onSearchResultsChanged();
// Clear the search query
mSearchQueryBuilder.clear();
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 057883cab..1f95133d4 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -16,21 +16,30 @@
package com.android.launcher3.allapps;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.os.Handler;
+import android.graphics.drawable.Drawable;
+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.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Thunk;
@@ -42,7 +51,7 @@ import java.util.List;
/**
* The grid view adapter of all the apps.
*/
-class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
+public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {
public static final String TAG = "AppsGridAdapter";
private static final boolean DEBUG = false;
@@ -55,6 +64,10 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
public static final int PREDICTION_ICON_VIEW_TYPE = 2;
// The message shown when there are no filtered results
public static final int EMPTY_SEARCH_VIEW_TYPE = 3;
+ // A divider that separates the apps list and the search market button
+ public static final int SEARCH_MARKET_DIVIDER_VIEW_TYPE = 4;
+ // The message to continue to a market search when there are no filtered results
+ public static final int SEARCH_MARKET_VIEW_TYPE = 5;
/**
* ViewHolder for each icon.
@@ -69,6 +82,38 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
}
/**
+ * A subclass of GridLayoutManager that overrides accessibility values during app search.
+ */
+ public class AppsGridLayoutManager extends GridLayoutManager {
+
+ public AppsGridLayoutManager(Context context) {
+ super(context, 1, GridLayoutManager.VERTICAL, false);
+ }
+
+ @Override
+ public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+ super.onInitializeAccessibilityEvent(event);
+
+ // Ensure that we only report the number apps for accessibility not including other
+ // adapter views
+ final AccessibilityRecordCompat record = AccessibilityEventCompat
+ .asRecord(event);
+ record.setItemCount(mApps.getNumFilteredApps());
+ }
+
+ @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;
+ } else {
+ return super.getRowCountForAccessibility(recycler, state);
+ }
+ }
+ }
+
+ /**
* Helper class to size the grid items.
*/
public class GridSpanSizer extends GridLayoutManager.SpanSizeLookup {
@@ -80,11 +125,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Override
public int getSpanSize(int position) {
- if (mApps.hasNoFilteredResults()) {
- // Empty view spans full width
- return mAppsPerRow;
- }
-
switch (mApps.getAdapterItems().get(position).viewType) {
case AllAppsGridAdapter.ICON_VIEW_TYPE:
case AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE:
@@ -279,6 +319,7 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
}
}
+ private Launcher mLauncher;
private LayoutInflater mLayoutInflater;
@Thunk AlphabeticalAppsList mApps;
private GridLayoutManager mGridLayoutMgr;
@@ -291,7 +332,19 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Thunk int mPredictionBarDividerOffset;
@Thunk int mAppsPerRow;
@Thunk boolean mIsRtl;
- private String mEmptySearchText;
+
+ // The text to show when there are no search results and no market search handler.
+ private String mEmptySearchMessage;
+ // The name of the market app which handles searches, to be used in the format str
+ // below when updating the search-market view. Only needs to be loaded once.
+ private String mMarketAppName;
+ // The text to show when there is a market app which can handle a specific query, updated
+ // each time the search query changes.
+ 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;
@@ -299,16 +352,18 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Thunk Paint mSectionTextPaint;
@Thunk Paint mPredictedAppsDividerPaint;
- public AllAppsGridAdapter(Context context, AlphabeticalAppsList apps,
+ public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps,
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener) {
- Resources res = context.getResources();
+ Resources res = launcher.getResources();
+ mLauncher = launcher;
mApps = apps;
+ mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
mGridSizer = new GridSpanSizer();
- mGridLayoutMgr = new GridLayoutManager(context, 1, GridLayoutManager.VERTICAL, false);
+ mGridLayoutMgr = new AppsGridLayoutManager(launcher);
mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
mItemDecoration = new GridItemDecoration();
- mLayoutInflater = LayoutInflater.from(context);
+ mLayoutInflater = LayoutInflater.from(launcher);
mTouchListener = touchListener;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
@@ -328,6 +383,14 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
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();
+ }
}
/**
@@ -346,10 +409,19 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
}
/**
- * Sets the text to show when there are no apps.
+ * Sets the last search query that was made, used to show when there are no results and to also
+ * seed the intent for searching the market.
*/
- public void setEmptySearchText(String query) {
- mEmptySearchText = query;
+ 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);
+ }
}
/**
@@ -378,9 +450,6 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
- case EMPTY_SEARCH_VIEW_TYPE:
- return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search, parent,
- false));
case SECTION_BREAK_VIEW_TYPE:
return new ViewHolder(new View(parent.getContext()));
case ICON_VIEW_TYPE: {
@@ -405,6 +474,22 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
icon.setFocusable(true);
return new ViewHolder(icon);
}
+ case EMPTY_SEARCH_VIEW_TYPE:
+ return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
+ parent, false));
+ case SEARCH_MARKET_DIVIDER_VIEW_TYPE:
+ return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_search_market_divider,
+ parent, false));
+ case SEARCH_MARKET_VIEW_TYPE:
+ View searchMarketView = mLayoutInflater.inflate(R.layout.all_apps_search_market,
+ parent, false);
+ searchMarketView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mLauncher.startSearchFromAllApps(v, mMarketSearchIntent, mLastSearchQuery);
+ }
+ });
+ return new ViewHolder(searchMarketView);
default:
throw new RuntimeException("Unexpected view type");
}
@@ -426,28 +511,47 @@ class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHol
break;
}
case EMPTY_SEARCH_VIEW_TYPE:
- TextView emptyViewText = (TextView) holder.mContent.findViewById(R.id.empty_text);
- emptyViewText.setText(mEmptySearchText);
+ TextView emptyViewText = (TextView) holder.mContent;
+ emptyViewText.setText(mEmptySearchMessage);
+ emptyViewText.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
+ Gravity.START | Gravity.CENTER_VERTICAL);
+ break;
+ case SEARCH_MARKET_VIEW_TYPE:
+ TextView searchView = (TextView) holder.mContent;
+ if (mMarketSearchIntent != null) {
+ searchView.setVisibility(View.VISIBLE);
+ searchView.setContentDescription(mMarketSearchMessage);
+ searchView.setGravity(mApps.hasNoFilteredResults() ? Gravity.CENTER :
+ Gravity.START | Gravity.CENTER_VERTICAL);
+ searchView.setText(mMarketSearchMessage);
+ } else {
+ searchView.setVisibility(View.GONE);
+ }
break;
}
}
@Override
public int getItemCount() {
- if (mApps.hasNoFilteredResults()) {
- // For the empty view
- return 1;
- }
return mApps.getAdapterItems().size();
}
@Override
public int getItemViewType(int position) {
- if (mApps.hasNoFilteredResults()) {
- return EMPTY_SEARCH_VIEW_TYPE;
- }
-
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 730c8d15a..2f66e2cad 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,8 +15,11 @@
*/
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;
@@ -26,6 +29,7 @@ import android.view.View;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.BaseRecyclerViewFastScrollBar;
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;
@@ -57,6 +61,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private ScrollPositionState mScrollPosState = new ScrollPositionState();
+ private AllAppsBackgroundDrawable mEmptySearchBackground;
+ private int mEmptySearchBackgroundTopOffset;
+
public AllAppsRecyclerView(Context context) {
this(context, null);
}
@@ -72,6 +79,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView
public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr);
+
+ Resources res = getResources();
+ mScrollbar.setDetachThumbOnFastScroll();
+ mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
+ R.dimen.all_apps_empty_search_bg_top_offset);
}
/**
@@ -90,6 +102,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
RecyclerView.RecycledViewPool pool = getRecycledViewPool();
int approxRows = (int) Math.ceil(grid.availableHeightPx / grid.allAppsIconSizePx);
pool.setMaxRecycledViews(AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE, 1);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE, 1);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE, 1);
pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
@@ -99,6 +113,10 @@ public class AllAppsRecyclerView extends BaseRecyclerView
* Scrolls this recycler view to the top.
*/
public void scrollToTop() {
+ // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
+ if (mScrollbar.isThumbDetached()) {
+ mScrollbar.reattachThumbToScroll();
+ }
scrollToPosition(0);
}
@@ -115,6 +133,30 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
@Override
+ public void onDraw(Canvas c) {
+ // Draw the background
+ if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
+ c.clipRect(mBackgroundPadding.left, mBackgroundPadding.top,
+ getWidth() - mBackgroundPadding.right,
+ getHeight() - mBackgroundPadding.bottom);
+
+ mEmptySearchBackground.draw(c);
+ }
+
+ super.onDraw(c);
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return who == mEmptySearchBackground || super.verifyDrawable(who);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ updateEmptySearchBackgroundBounds();
+ }
+
+ @Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -134,6 +176,25 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
}
+ public void onSearchResultsChanged() {
+ // Always scroll the view to the top so the user can see the changed results
+ scrollToTop();
+
+ if (mApps.hasNoFilteredResults()) {
+ if (mEmptySearchBackground == null) {
+ mEmptySearchBackground = new AllAppsBackgroundDrawable(getContext());
+ mEmptySearchBackground.setAlpha(0);
+ mEmptySearchBackground.setCallback(this);
+ updateEmptySearchBackgroundBounds();
+ }
+ mEmptySearchBackground.animateBgAlpha(1f, 150);
+ } else if (mEmptySearchBackground != null) {
+ // For the time being, we just immediately hide the background to ensure that it does
+ // not overlap with the results
+ mEmptySearchBackground.setBgAlpha(0f);
+ }
+ }
+
/**
* Maps the touch (from 0..1) to the adapter position that should be visible.
*/
@@ -166,8 +227,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
// Map the touch position back to the scroll of the recycler view
- getCurScrollState(mScrollPosState, mApps.getAdapterItems());
- int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 0);
+ 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));
@@ -214,24 +275,83 @@ public class AllAppsRecyclerView extends BaseRecyclerView
* Updates the bounds for the scrollbar.
*/
@Override
- public void onUpdateScrollbar() {
+ public void onUpdateScrollbar(int dy) {
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
if (items.isEmpty() || mNumAppsPerRow == 0) {
- mScrollbar.setScrollbarThumbOffset(-1, -1);
+ mScrollbar.setThumbOffset(-1, -1);
return;
}
// Find the index and height of the first visible row (all rows have the same height)
int rowCount = mApps.getNumAppRows();
- getCurScrollState(mScrollPosState, items);
+ getCurScrollState(mScrollPosState);
if (mScrollPosState.rowIndex < 0) {
- mScrollbar.setScrollbarThumbOffset(-1, -1);
+ mScrollbar.setThumbOffset(-1, -1);
return;
}
- synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, 0);
+ // Only show the scrollbar if there is height to be scrolled
+ int availableScrollBarHeight = getAvailableScrollBarHeight();
+ int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows(), mScrollPosState.rowHeight);
+ if (availableScrollHeight <= 0) {
+ mScrollbar.setThumbOffset(-1, -1);
+ return;
+ }
+
+ // 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 scrollBarY = mBackgroundPadding.top +
+ (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
+
+ if (mScrollbar.isThumbDetached()) {
+ int scrollBarX;
+ if (Utilities.isRtl(getResources())) {
+ scrollBarX = mBackgroundPadding.left;
+ } else {
+ scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth();
+ }
+
+ if (mScrollbar.isDraggingThumb()) {
+ // If the thumb is detached, then just update the thumb to the current
+ // touch position
+ mScrollbar.setThumbOffset(scrollBarX, (int) mScrollbar.getLastTouchY());
+ } else {
+ int thumbScrollY = mScrollbar.getThumbOffset().y;
+ int diffScrollY = scrollBarY - thumbScrollY;
+ if (diffScrollY * dy > 0f) {
+ // User is scrolling in the same direction the thumb needs to catch up to the
+ // current scroll position. We do this by mapping the difference in movement
+ // from the original scroll bar position to the difference in movement necessary
+ // in the detached thumb position to ensure that both speed towards the same
+ // position at either end of the list.
+ if (dy < 0) {
+ int offset = (int) ((dy * thumbScrollY) / (float) scrollBarY);
+ thumbScrollY += Math.max(offset, diffScrollY);
+ } else {
+ int offset = (int) ((dy * (availableScrollBarHeight - thumbScrollY)) /
+ (float) (availableScrollBarHeight - scrollBarY));
+ thumbScrollY += Math.min(offset, diffScrollY);
+ }
+ thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY));
+ mScrollbar.setThumbOffset(scrollBarX, thumbScrollY);
+ if (scrollBarY == thumbScrollY) {
+ mScrollbar.reattachThumbToScroll();
+ }
+ } else {
+ // User is scrolling in an opposite direction to the direction that the thumb
+ // needs to catch up to the scroll position. Do nothing except for updating
+ // the scroll bar x to match the thumb width.
+ mScrollbar.setThumbOffset(scrollBarX, thumbScrollY);
+ }
+ }
+ } else {
+ synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount);
+ }
}
/**
@@ -283,13 +403,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView
/**
* Returns the current scroll state of the apps rows.
*/
- private void getCurScrollState(ScrollPositionState stateOut,
- List<AlphabeticalAppsList.AdapterItem> items) {
+ protected void getCurScrollState(ScrollPositionState stateOut) {
stateOut.rowIndex = -1;
stateOut.rowTopOffset = -1;
stateOut.rowHeight = -1;
// Return early if there are no items or we haven't been measured
+ List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
if (items.isEmpty() || mNumAppsPerRow == 0) {
return;
}
@@ -324,4 +444,20 @@ public class AllAppsRecyclerView extends BaseRecyclerView
return 0;
}
}
+
+ /**
+ * Updates the bounds of the empty search background.
+ */
+ private void updateEmptySearchBackgroundBounds() {
+ if (mEmptySearchBackground == null) {
+ return;
+ }
+
+ // Center the empty search background on this new view bounds
+ int x = (getMeasuredWidth() - mEmptySearchBackground.getIntrinsicWidth()) / 2;
+ int y = mEmptySearchBackgroundTopOffset;
+ mEmptySearchBackground.setBounds(x, y,
+ x + mEmptySearchBackground.getIntrinsicWidth(),
+ y + mEmptySearchBackground.getIntrinsicHeight());
+ }
}
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 47241ce5d..dac0df12a 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -43,6 +43,11 @@ public class AlphabeticalAppsList {
private static final boolean DEBUG = false;
private static final boolean DEBUG_PREDICTIONS = false;
+ private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0;
+ private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1;
+
+ private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
+
/**
* Info about a section in the alphabetic list
*/
@@ -81,8 +86,6 @@ public class AlphabeticalAppsList {
public int position;
// The type of this item
public int viewType;
- // The row that this item shows up on
- public int rowIndex;
/** Section & App properties */
// The section for this item
@@ -94,6 +97,8 @@ public class AlphabeticalAppsList {
public String sectionName = null;
// The index of this app in the section
public int sectionAppIndex = -1;
+ // The row that this item shows up on
+ public int rowIndex;
// The index of this app in the row
public int rowAppIndex;
// The associated AppInfo for the app
@@ -111,14 +116,14 @@ public class AlphabeticalAppsList {
}
public static AdapterItem asPredictedApp(int pos, SectionInfo section, String sectionName,
- int sectionAppIndex, AppInfo appInfo, int appIndex) {
+ int sectionAppIndex, AppInfo appInfo, int appIndex) {
AdapterItem item = asApp(pos, section, sectionName, sectionAppIndex, appInfo, appIndex);
item.viewType = AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE;
return item;
}
public static AdapterItem asApp(int pos, SectionInfo section, String sectionName,
- int sectionAppIndex, AppInfo appInfo, int appIndex) {
+ int sectionAppIndex, AppInfo appInfo, int appIndex) {
AdapterItem item = new AdapterItem();
item.viewType = AllAppsGridAdapter.ICON_VIEW_TYPE;
item.position = pos;
@@ -129,6 +134,27 @@ public class AlphabeticalAppsList {
item.appIndex = appIndex;
return item;
}
+
+ public static AdapterItem asEmptySearch(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.EMPTY_SEARCH_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
+
+ public static AdapterItem asDivider(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.SEARCH_MARKET_DIVIDER_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
+
+ public static AdapterItem asMarketSearch(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
}
/**
@@ -222,17 +248,17 @@ public class AlphabeticalAppsList {
}
/**
- * Returns the number of applications in this list.
+ * Returns the number of rows of applications (not including predictions)
*/
- public int getSize() {
- return mFilteredApps.size();
+ public int getNumAppRows() {
+ return mNumAppRowsInAdapter;
}
/**
- * Returns the number of rows of applications (not including predictions)
+ * Returns the number of applications in this list.
*/
- public int getNumAppRows() {
- return mNumAppRowsInAdapter;
+ public int getNumFilteredApps() {
+ return mFilteredApps.size();
}
/**
@@ -457,6 +483,16 @@ public class AlphabeticalAppsList {
mFilteredApps.add(info);
}
+ // Append the search market item if we are currently searching
+ if (hasFilter()) {
+ if (hasNoFilteredResults()) {
+ mAdapterItems.add(AdapterItem.asEmptySearch(position++));
+ } else {
+ mAdapterItems.add(AdapterItem.asDivider(position++));
+ }
+ mAdapterItems.add(AdapterItem.asMarketSearch(position++));
+ }
+
// Merge multiple sections together as requested by the merge strategy for this device
mergeSections();
@@ -484,18 +520,36 @@ public class AlphabeticalAppsList {
}
mNumAppRowsInAdapter = rowIndex + 1;
- // Pre-calculate all the fast scroller fractions based on the number of rows
- float rowFraction = 1f / mNumAppRowsInAdapter;
- for (FastScrollSectionInfo info : mFastScrollerSections) {
- AdapterItem item = info.fastScrollToItem;
- if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
- item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
- info.touchFraction = 0f;
- continue;
- }
-
- float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow);
- info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
+ // Pre-calculate all the fast scroller fractions
+ switch (mFastScrollDistributionMode) {
+ case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION:
+ float rowFraction = 1f / mNumAppRowsInAdapter;
+ for (FastScrollSectionInfo info : mFastScrollerSections) {
+ AdapterItem item = info.fastScrollToItem;
+ if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
+ item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ info.touchFraction = 0f;
+ continue;
+ }
+
+ float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow);
+ info.touchFraction = item.rowIndex * rowFraction + subRowFraction;
+ }
+ break;
+ case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS:
+ float perSectionTouchFraction = 1f / mFastScrollerSections.size();
+ float cumulativeTouchFraction = 0f;
+ for (FastScrollSectionInfo info : mFastScrollerSections) {
+ AdapterItem item = info.fastScrollToItem;
+ if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
+ item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ info.touchFraction = 0f;
+ continue;
+ }
+ info.touchFraction = cumulativeTouchFraction;
+ cumulativeTouchFraction += perSectionTouchFraction;
+ }
+ break;
}
}
diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
index 83b920589..3169f842a 100644
--- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java
+++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java
@@ -25,6 +25,7 @@ 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;
@@ -54,7 +55,8 @@ final class DefaultAppSearchController extends AllAppsSearchBarController
@Thunk View mSearchBarContainerView;
private View mSearchButtonView;
private View mDismissSearchButtonView;
- @Thunk AllAppsSearchEditView mSearchBarEditView;
+ @Thunk
+ ExtendedEditText mSearchBarEditView;
@Thunk AllAppsRecyclerView mAppsRecyclerView;
@Thunk Runnable mFocusRecyclerViewRunnable = new Runnable() {
@Override
@@ -82,21 +84,23 @@ final class DefaultAppSearchController extends AllAppsSearchBarController
mSearchBarContainerView = mSearchView.findViewById(R.id.search_container);
mDismissSearchButtonView = mSearchBarContainerView.findViewById(R.id.dismiss_search_button);
mDismissSearchButtonView.setOnClickListener(this);
- mSearchBarEditView = (AllAppsSearchEditView)
+ mSearchBarEditView = (ExtendedEditText)
mSearchBarContainerView.findViewById(R.id.search_box_input);
mSearchBarEditView.addTextChangedListener(this);
mSearchBarEditView.setOnEditorActionListener(this);
mSearchBarEditView.setOnBackKeyListener(
- new AllAppsSearchEditView.OnBackKeyListener() {
+ new ExtendedEditText.OnBackKeyListener() {
@Override
- public void onBackKey() {
+ 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;
@@ -166,22 +170,24 @@ final class DefaultAppSearchController extends AllAppsSearchBarController
return false;
}
// Skip if it's not the right action
- if (actionId != EditorInfo.IME_ACTION_DONE) {
+ if (actionId != EditorInfo.IME_ACTION_SEARCH) {
return false;
}
- // Skip if there isn't exactly one item
- if (mApps.getSize() != 1) {
+ // Skip if there are more than one icon
+ if (mApps.getNumFilteredApps() > 1) {
return false;
}
- // If there is exactly one icon, then quick-launch it
+ // 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);
- if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) {
- mAppsRecyclerView.getChildAt(i).performClick();
- mInputMethodManager.hideSoftInputFromWindow(
- mContainerView.getWindowToken(), 0);
- return true;
+ 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;
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
index 7aa36d447..434f13dcc 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -40,7 +40,7 @@ public abstract class AppWidgetManagerCompat {
public static AppWidgetManagerCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
} else {
sInstance = new AppWidgetManagerCompatV16(context.getApplicationContext());
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
index f7f4b7e4f..463cf902d 100644
--- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -54,10 +54,10 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
@Override
public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
Bundle options) {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
- return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
- } else {
+ if (Utilities.ATLEAST_JB_MR1) {
return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider, options);
+ } else {
+ return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
}
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 5858bc8b9..95e3ba902 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -53,7 +53,7 @@ public abstract class LauncherAppsCompat {
public static LauncherAppsCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
} else {
sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
index ac3d252f5..339c457e1 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -31,6 +31,7 @@ import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import com.android.launcher3.Utilities;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -188,7 +189,7 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat {
// when moving a package or mounting/un-mounting external storage. Assume that
// it is a replacing operation.
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
- Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT);
+ !Utilities.ATLEAST_KITKAT);
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
callback.onPackagesAvailable(packages, user, replacing);
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
index c49908328..ec5014d7c 100644
--- a/src/com/android/launcher3/compat/PackageInstallerCompat.java
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -34,7 +34,7 @@ public abstract class PackageInstallerCompat {
public static PackageInstallerCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
- if (Utilities.isLmpOrAbove()) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
sInstance = new PackageInstallerCompatVL(context);
} else {
sInstance = new PackageInstallerCompatV16();
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
index ab4b7216b..567022b43 100644
--- a/src/com/android/launcher3/compat/UserHandleCompat.java
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -34,7 +34,7 @@ public class UserHandleCompat {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static UserHandleCompat myUserHandle() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
return new UserHandleCompat(android.os.Process.myUserHandle());
} else {
return new UserHandleCompat();
@@ -55,7 +55,7 @@ public class UserHandleCompat {
@Override
public String toString() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
return mUser.toString();
} else {
return "";
@@ -67,7 +67,7 @@ public class UserHandleCompat {
if (!(other instanceof UserHandleCompat)) {
return false;
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
return mUser.equals(((UserHandleCompat) other).mUser);
} else {
return true;
@@ -76,7 +76,7 @@ public class UserHandleCompat {
@Override
public int hashCode() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
return mUser.hashCode();
} else {
return 0;
@@ -89,7 +89,7 @@ public class UserHandleCompat {
* profiles so this is a no-op.
*/
public void addToIntent(Intent intent, String name) {
- if (Utilities.isLmpOrAbove() && mUser != null) {
+ if (Utilities.ATLEAST_LOLLIPOP && mUser != null) {
intent.putExtra(name, mUser);
}
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
index a79d94646..f708004a3 100644
--- a/src/com/android/launcher3/compat/UserManagerCompat.java
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -28,16 +28,29 @@ public abstract class UserManagerCompat {
protected UserManagerCompat() {
}
+ private static final Object sInstanceLock = new Object();
+ private static UserManagerCompat sInstance;
+
public static UserManagerCompat getInstance(Context context) {
- if (Utilities.isLmpOrAbove()) {
- return new UserManagerCompatVL(context);
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- return new UserManagerCompatV17(context);
- } else {
- return new UserManagerCompatV16();
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ if (Utilities.ATLEAST_LOLLIPOP) {
+ sInstance = new UserManagerCompatVL(context.getApplicationContext());
+ } else if (Utilities.ATLEAST_JB_MR1) {
+ sInstance = new UserManagerCompatV17(context.getApplicationContext());
+ } else {
+ sInstance = new UserManagerCompatV16();
+ }
+ }
+ return sInstance;
}
}
+ /**
+ * Creates a cache for users.
+ */
+ public abstract void enableAndResetCache();
+
public abstract List<UserHandleCompat> getUserProfiles();
public abstract long getSerialNumberForUser(UserHandleCompat user);
public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
index ffe698c8b..85aee57e8 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV16.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -53,4 +53,8 @@ public class UserManagerCompatV16 extends UserManagerCompat {
public long getUserCreationTime(UserHandleCompat user) {
return 0;
}
+
+ @Override
+ public void enableAndResetCache() {
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java
index c42c00c7d..75203b7f3 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatV17.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java
@@ -21,8 +21,18 @@ import android.content.Context;
import android.os.Build;
import android.os.UserManager;
+import com.android.launcher3.util.LongArrayMap;
+
+import java.util.HashMap;
+
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class UserManagerCompatV17 extends UserManagerCompatV16 {
+
+ protected LongArrayMap<UserHandleCompat> mUsers;
+ // Create a separate reverse map as LongArrayMap.indexOfValue checks if objects are same
+ // and not {@link Object#equals}
+ protected HashMap<UserHandleCompat, Long> mUserToSerialMap;
+
protected UserManager mUserManager;
UserManagerCompatV17(Context context) {
@@ -30,11 +40,34 @@ public class UserManagerCompatV17 extends UserManagerCompatV16 {
}
public long getSerialNumberForUser(UserHandleCompat user) {
+ synchronized (this) {
+ if (mUserToSerialMap != null) {
+ Long serial = mUserToSerialMap.get(user);
+ return serial == null ? 0 : serial;
+ }
+ }
return mUserManager.getSerialNumberForUser(user.getUser());
}
public UserHandleCompat getUserForSerialNumber(long serialNumber) {
+ synchronized (this) {
+ if (mUsers != null) {
+ return mUsers.get(serialNumber);
+ }
+ }
return UserHandleCompat.fromUser(mUserManager.getUserForSerialNumber(serialNumber));
}
+
+ @Override
+ public void enableAndResetCache() {
+ synchronized (this) {
+ mUsers = new LongArrayMap<>();
+ mUserToSerialMap = new HashMap<>();
+ UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+ long serial = mUserManager.getSerialNumberForUser(myUser.getUser());
+ mUsers.put(serial, myUser);
+ mUserToSerialMap.put(myUser, serial);
+ }
+ }
}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
index dd7a72617..dc3ec3cd8 100644
--- a/src/com/android/launcher3/compat/UserManagerCompatVL.java
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -24,9 +24,14 @@ 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;
+
import java.util.ArrayList;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
@@ -43,7 +48,32 @@ public class UserManagerCompatVL extends UserManagerCompatV17 {
}
@Override
+ public void enableAndResetCache() {
+ synchronized (this) {
+ mUsers = new LongArrayMap<>();
+ mUserToSerialMap = new HashMap<>();
+ List<UserHandle> users = mUserManager.getUserProfiles();
+ if (users != null) {
+ for (UserHandle user : users) {
+ long serial = mUserManager.getSerialNumberForUser(user);
+ UserHandleCompat userCompat = UserHandleCompat.fromUser(user);
+ mUsers.put(serial, userCompat);
+ mUserToSerialMap.put(userCompat, serial);
+ }
+ }
+ }
+ }
+
+ @Override
public List<UserHandleCompat> getUserProfiles() {
+ synchronized (this) {
+ if (mUsers != null) {
+ List<UserHandleCompat> users = new ArrayList<>();
+ users.addAll(mUserToSerialMap.keySet());
+ return users;
+ }
+ }
+
List<UserHandle> users = mUserManager.getUserProfiles();
if (users == null) {
return Collections.emptyList();
@@ -71,7 +101,9 @@ public class UserManagerCompatVL extends UserManagerCompatV17 {
@Override
public long getUserCreationTime(UserHandleCompat user) {
- // TODO: Use system API once available.
+ if (Utilities.ATLEAST_MARSHMALLOW) {
+ return mUserManager.getUserCreationTime(user.getUser());
+ }
SharedPreferences prefs = mContext.getSharedPreferences(
LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user);
diff --git a/src/com/android/launcher3/model/AbstractUserComparator.java b/src/com/android/launcher3/model/AbstractUserComparator.java
index cf47ce648..bd28560f3 100644
--- a/src/com/android/launcher3/model/AbstractUserComparator.java
+++ b/src/com/android/launcher3/model/AbstractUserComparator.java
@@ -22,14 +22,12 @@ import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
import java.util.Comparator;
-import java.util.HashMap;
/**
* A comparator to arrange items based on user profiles.
*/
public abstract class AbstractUserComparator<T extends ItemInfo> implements Comparator<T> {
- private HashMap<UserHandleCompat, Long> mUserSerialCache = new HashMap<>();
private final UserManagerCompat mUserManager;
private final UserHandleCompat mMyUser;
@@ -43,25 +41,9 @@ public abstract class AbstractUserComparator<T extends ItemInfo> implements Comp
if (mMyUser.equals(lhs.user)) {
return -1;
} else {
- Long aUserSerial = getAndCacheUserSerial(lhs.user);
- Long bUserSerial = getAndCacheUserSerial(rhs.user);
+ Long aUserSerial = mUserManager.getSerialNumberForUser(lhs.user);
+ Long bUserSerial = mUserManager.getSerialNumberForUser(rhs.user);
return aUserSerial.compareTo(bUserSerial);
}
}
-
- /**
- * Returns the user serial for this user, using a cached serial if possible.
- */
- private Long getAndCacheUserSerial(UserHandleCompat user) {
- Long userSerial = mUserSerialCache.get(user);
- if (userSerial == null) {
- userSerial = mUserManager.getSerialNumberForUser(user);
- mUserSerialCache.put(user, userSerial);
- }
- return userSerial;
- }
-
- public void clearUserCache() {
- mUserSerialCache.clear();
- }
}
diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java
index c4b74d4dc..5f80037dc 100644
--- a/src/com/android/launcher3/model/AppNameComparator.java
+++ b/src/com/android/launcher3/model/AppNameComparator.java
@@ -68,8 +68,6 @@ public class AppNameComparator {
* Returns a locale-aware comparator that will alphabetically order a list of applications.
*/
public Comparator<ItemInfo> getAppInfoComparator() {
- // Clear the user serial cache so that we get serials as needed in the comparator
- mAppInfoComparator.clearUserCache();
return mAppInfoComparator;
}
diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java
new file mode 100644
index 000000000..6a529f61f
--- /dev/null
+++ b/src/com/android/launcher3/model/MigrateFromRestoreTask.java
@@ -0,0 +1,767 @@
+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/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
index 61e895283..b99056023 100644
--- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
+++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java
@@ -1,13 +1,14 @@
package com.android.launcher3.model;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.util.ComponentKey;
import java.text.Collator;
import java.util.Comparator;
@@ -16,53 +17,81 @@ import java.util.HashMap;
public class WidgetsAndShortcutNameComparator implements Comparator<Object> {
private final AppWidgetManagerCompat mManager;
private final PackageManager mPackageManager;
- private final HashMap<Object, String> mLabelCache;
+ private final HashMap<ComponentKey, String> mLabelCache;
private final Collator mCollator;
private final UserHandleCompat mMainHandle;
public WidgetsAndShortcutNameComparator(Context context) {
mManager = AppWidgetManagerCompat.getInstance(context);
mPackageManager = context.getPackageManager();
- mLabelCache = new HashMap<Object, String>();
+ mLabelCache = new HashMap<>();
mCollator = Collator.getInstance();
mMainHandle = UserHandleCompat.myUserHandle();
}
- @Override
- public final int compare(Object a, Object b) {
- String labelA, labelB;
- if (mLabelCache.containsKey(a)) {
- labelA = mLabelCache.get(a);
- } else {
- labelA = (a instanceof LauncherAppWidgetProviderInfo)
- ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a))
- : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager));
- mLabelCache.put(a, labelA);
- }
- if (mLabelCache.containsKey(b)) {
- labelB = mLabelCache.get(b);
- } else {
- labelB = (b instanceof LauncherAppWidgetProviderInfo)
- ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b))
- : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager));
- mLabelCache.put(b, labelB);
- }
-
- // Currently, there is no work profile shortcuts, hence only considering the widget cases.
+ /**
+ * Resets any stored state.
+ */
+ public void reset() {
+ mLabelCache.clear();
+ }
- boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) &&
- !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a));
- boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) &&
- !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b));
+ @Override
+ public final int compare(Object objA, Object objB) {
+ ComponentKey keyA = getComponentKey(objA);
+ ComponentKey keyB = getComponentKey(objB);
// Independent of how the labels compare, if only one of the two widget info belongs to
// work profile, put that one in the back.
+ boolean aWorkProfile = !mMainHandle.equals(keyA.user);
+ boolean bWorkProfile = !mMainHandle.equals(keyB.user);
if (aWorkProfile && !bWorkProfile) {
return 1;
}
if (!aWorkProfile && bWorkProfile) {
return -1;
}
+
+ // Get the labels for comparison
+ String labelA = mLabelCache.get(keyA);
+ String labelB = mLabelCache.get(keyB);
+ if (labelA == null) {
+ labelA = getLabel(objA);
+ mLabelCache.put(keyA, labelA);
+ }
+ if (labelB == null) {
+ labelB = getLabel(objB);
+ mLabelCache.put(keyB, labelB);
+ }
return mCollator.compare(labelA, labelB);
}
+
+ /**
+ * @return a component key for the given widget or shortcut info.
+ */
+ private ComponentKey getComponentKey(Object o) {
+ if (o instanceof LauncherAppWidgetProviderInfo) {
+ LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
+ return new ComponentKey(widgetInfo.provider, mManager.getUser(widgetInfo));
+ } else {
+ ResolveInfo shortcutInfo = (ResolveInfo) o;
+ ComponentName cn = new ComponentName(shortcutInfo.activityInfo.packageName,
+ shortcutInfo.activityInfo.name);
+ // Currently, there are no work profile shortcuts
+ return new ComponentKey(cn, UserHandleCompat.myUserHandle());
+ }
+ }
+
+ /**
+ * @return the label for the given widget or shortcut info. This may be an expensive call.
+ */
+ private String getLabel(Object o) {
+ if (o instanceof LauncherAppWidgetProviderInfo) {
+ LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
+ return Utilities.trim(mManager.loadLabel(widgetInfo));
+ } else {
+ ResolveInfo shortcutInfo = (ResolveInfo) o;
+ return Utilities.trim(shortcutInfo.loadLabel(mPackageManager));
+ }
+ }
};
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 185dfcae3..eef4f9173 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -8,6 +8,9 @@ import android.util.Log;
import com.android.launcher3.AppFilter;
import com.android.launcher3.IconCache;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
@@ -39,8 +42,8 @@ public class WidgetsModel {
private ArrayList<Object> mRawList;
private final AppWidgetManagerCompat mAppWidgetMgr;
- private final Comparator mWidgetAndShortcutNameComparator;
- private final Comparator mAppNameComparator;
+ private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
+ private final Comparator<ItemInfo> mAppNameComparator;
private final IconCache mIconCache;
private final AppFilter mAppFilter;
private AlphabeticIndexCompat mIndexer;
@@ -54,6 +57,7 @@ public class WidgetsModel {
mIndexer = new AlphabeticIndexCompat(context);
}
+ @SuppressWarnings("unchecked")
private WidgetsModel(WidgetsModel model) {
mAppWidgetMgr = model.mAppWidgetMgr;
mPackageItemInfos = (ArrayList<PackageItemInfo>) model.mPackageItemInfos.clone();
@@ -103,6 +107,9 @@ public class WidgetsModel {
// clear the lists.
mWidgetsList.clear();
mPackageItemInfos.clear();
+ mWidgetAndShortcutNameComparator.reset();
+
+ InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
// add and update.
for (Object o: rawWidgetsShortcuts) {
@@ -111,9 +118,23 @@ public class WidgetsModel {
ComponentName componentName = null;
if (o instanceof LauncherAppWidgetProviderInfo) {
LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o;
- componentName = widgetInfo.provider;
- packageName = widgetInfo.provider.getPackageName();
- userHandle = mAppWidgetMgr.getUser(widgetInfo);
+
+ // Ensure that all widgets we show can be added on a workspace of this size
+ int minSpanX = Math.min(widgetInfo.spanX, widgetInfo.minSpanX);
+ int minSpanY = Math.min(widgetInfo.spanY, widgetInfo.minSpanY);
+ if (minSpanX <= (int) idp.numColumns &&
+ minSpanY <= (int) idp.numRows) {
+ componentName = widgetInfo.provider;
+ packageName = widgetInfo.provider.getPackageName();
+ userHandle = mAppWidgetMgr.getUser(widgetInfo);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "Widget %s : (%d X %d) can't fit on this device",
+ widgetInfo.provider, minSpanX, minSpanY));
+ }
+ continue;
+ }
} else if (o instanceof ResolveInfo) {
ResolveInfo resolveInfo = (ResolveInfo) o;
componentName = new ComponentName(resolveInfo.activityInfo.packageName,
@@ -139,7 +160,7 @@ public class WidgetsModel {
if (widgetsShortcutsList != null) {
widgetsShortcutsList.add(o);
} else {
- widgetsShortcutsList = new ArrayList<Object>();
+ widgetsShortcutsList = new ArrayList<>();
widgetsShortcutsList.add(o);
pInfo = new PackageItemInfo(packageName);
mIconCache.getTitleAndIconForApp(packageName, userHandle,
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 34492e4ca..8702877bf 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -202,6 +202,11 @@ public class LauncherExtension extends Launcher {
}
@Override
+ public boolean startSearchFromAllApps(String query) {
+ return false;
+ }
+
+ @Override
public void startVoice() {
}
diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java
index 6a7df4318..b7aafaea9 100644
--- a/src/com/android/launcher3/util/ComponentKey.java
+++ b/src/com/android/launcher3/util/ComponentKey.java
@@ -64,8 +64,11 @@ public class ComponentKey {
* Encodes a component key as a string of the form [flattenedComponentString#userId].
*/
public String flattenToString(Context context) {
- return componentName.flattenToString() + "#" +
- UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
+ String flattened = componentName.flattenToString();
+ if (user != null) {
+ flattened += "#" + UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
+ }
+ return flattened;
}
@Override
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index b37f44719..74fc92a04 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -68,7 +68,7 @@ public class ManagedProfileHeuristic {
private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000;
public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) {
- if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) {
+ if (Utilities.ATLEAST_LOLLIPOP && !UserHandleCompat.myUserHandle().equals(user)) {
return new ManagedProfileHeuristic(context, user);
}
return null;
@@ -296,7 +296,7 @@ public class ManagedProfileHeuristic {
* Verifies that entries corresponding to {@param users} exist and removes all invalid entries.
*/
public static void processAllUsers(List<UserHandleCompat> users, Context context) {
- if (!Utilities.isLmpOrAbove()) {
+ if (!Utilities.ATLEAST_LOLLIPOP) {
return;
}
UserManagerCompat userManager = UserManagerCompat.getInstance(context);
diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java
index 3ca3aeeee..f2b5e5e15 100644
--- a/src/com/android/launcher3/util/UiThreadCircularReveal.java
+++ b/src/com/android/launcher3/util/UiThreadCircularReveal.java
@@ -47,7 +47,7 @@ public class UiThreadCircularReveal {
float progress = arg0.getAnimatedFraction();
outlineProvider.setProgress(progress);
revealView.invalidateOutline();
- if (!Utilities.isLmpMR1OrAbove()) {
+ if (!Utilities.ATLEAST_LOLLIPOP_MR1) {
revealView.invalidate();
}
}
diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java
index 53b2acd84..b9fccbcfd 100644
--- a/src/com/android/launcher3/util/WallpaperUtils.java
+++ b/src/com/android/launcher3/util/WallpaperUtils.java
@@ -24,6 +24,8 @@ import android.graphics.Point;
import android.os.Build;
import android.view.WindowManager;
+import com.android.launcher3.Utilities;
+
/**
* Utility methods for wallpaper management.
*/
@@ -99,7 +101,7 @@ public final class WallpaperUtils {
int maxDim = Math.max(maxDims.x, maxDims.y);
int minDim = Math.max(minDims.x, minDims.y);
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
Point realSize = new Point();
windowManager.getDefaultDisplay().getRealSize(realSize);
maxDim = Math.max(realSize.x, realSize.y);
diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
index 758287af3..fcb714ff1 100644
--- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
+++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java
@@ -31,10 +31,6 @@ import com.android.launcher3.compat.AppWidgetManagerCompat;
* @see {@link PendingAddItemInfo}
*/
public class PendingAddWidgetInfo extends PendingAddItemInfo {
- public int minWidth;
- public int minHeight;
- public int minResizeWidth;
- public int minResizeHeight;
public int previewImage;
public int icon;
public LauncherAppWidgetProviderInfo info;
@@ -50,17 +46,13 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo {
this.info = i;
user = AppWidgetManagerCompat.getInstance(launcher).getUser(i);
componentName = i.provider;
- minWidth = i.minWidth;
- minHeight = i.minHeight;
- minResizeWidth = i.minResizeWidth;
- minResizeHeight = i.minResizeHeight;
previewImage = i.previewImage;
icon = i.icon;
- spanX = i.getSpanX(launcher);
- spanY = i.getSpanY(launcher);
- minSpanX = i.getMinSpanX(launcher);
- minSpanY = i.getMinSpanY(launcher);
+ spanX = i.spanX;
+ spanY = i.spanY;
+ minSpanX = i.minSpanX;
+ minSpanY = i.minSpanY;
}
public boolean isCustomWidget() {
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index 7496ea2ef..94bbd929f 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -146,8 +146,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener {
mInfo = info;
// TODO(hyunyoungs): setup a cache for these labels.
mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
- int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns);
- int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows);
+ int hSpan = Math.min(info.spanX, profile.numColumns);
+ int vSpan = Math.min(info.spanY, profile.numRows);
mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan));
mWidgetPreviewLoader = loader;
}
diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
index 30b3d581a..461aebb6b 100644
--- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java
+++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java
@@ -15,6 +15,7 @@ import com.android.launcher3.DragLayer;
import com.android.launcher3.DragSource;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
+import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.util.Thunk;
@@ -131,7 +132,7 @@ public class WidgetHostViewLoader implements DragListener {
public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
Bundle options = null;
Rect rect = new Rect();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect);
Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
info.componentName, null);
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 5afd7c493..0c6ea31bb 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -319,7 +319,6 @@ public class WidgetsContainerView extends BaseContainerView
CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen);
ItemInfo itemInfo = (ItemInfo) d.dragInfo;
if (layout != null) {
- layout.calculateSpans(itemInfo);
showOutOfSpaceMessage =
!layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY);
}
diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java
index d2ea25230..f1cde299d 100644
--- a/src/com/android/launcher3/widget/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java
@@ -87,6 +87,9 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
@Override
public int getItemCount() {
+ if (mWidgetsModel == null) {
+ return 0;
+ }
return mWidgetsModel.getPackageSize();
}
@@ -166,7 +169,7 @@ public class WidgetsListAdapter extends Adapter<WidgetsRowViewHolder> {
// if the end padding is 0, then container view (horizontal scroll view) doesn't respect
// the end of the linear layout width + the start padding and doesn't allow scrolling.
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ if (Utilities.ATLEAST_JB_MR1) {
cellList.setPaddingRelative(mIndent, 0, 1, 0);
} else {
cellList.setPadding(mIndent, 0, 1, 0);
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 61e63cdb7..884bdc418 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -64,10 +64,6 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
return Color.WHITE;
}
- public int getFastScrollerThumbInactiveColor(int defaultInactiveThumbColor) {
- return getResources().getColor(R.color.widgets_view_fastscroll_thumb_inactive_color);
- }
-
/**
* Sets the widget model in this view, used to determine the fast scroll position.
*/
@@ -92,6 +88,12 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
*/
@Override
public String scrollToPositionAtProgress(float touchFraction) {
+ // Skip early if widgets are not bound.
+ if (mWidgets == null) {
+ return "";
+ }
+
+ // Skip early if there are no widgets.
int rowCount = mWidgets.getPackageSize();
if (rowCount == 0) {
return "";
@@ -102,7 +104,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
getCurScrollState(mScrollPosState);
float pos = rowCount * touchFraction;
- int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 0);
+ int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
@@ -115,36 +117,44 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
* Updates the bounds for the scrollbar.
*/
@Override
- public void onUpdateScrollbar() {
- int rowCount = mWidgets.getPackageSize();
+ public void onUpdateScrollbar(int dy) {
+ // Skip early if widgets are not bound.
+ if (mWidgets == null) {
+ return;
+ }
- // Skip early if, there are no items.
+ // Skip early if there are no widgets.
+ int rowCount = mWidgets.getPackageSize();
if (rowCount == 0) {
- mScrollbar.setScrollbarThumbOffset(-1, -1);
+ mScrollbar.setThumbOffset(-1, -1);
return;
}
// Skip early if, there no child laid out in the container.
getCurScrollState(mScrollPosState);
if (mScrollPosState.rowIndex < 0) {
- mScrollbar.setScrollbarThumbOffset(-1, -1);
+ mScrollbar.setThumbOffset(-1, -1);
return;
}
- synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, 0);
+ synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount);
}
/**
* Returns the current scroll state.
*/
- private void getCurScrollState(ScrollPositionState stateOut) {
+ protected void getCurScrollState(ScrollPositionState stateOut) {
stateOut.rowIndex = -1;
stateOut.rowTopOffset = -1;
stateOut.rowHeight = -1;
- int rowCount = mWidgets.getPackageSize();
+ // Skip early if widgets are not bound.
+ if (mWidgets == null) {
+ return;
+ }
// Return early if there are no items
+ int rowCount = mWidgets.getPackageSize();
if (rowCount == 0) {
return;
}