summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk27
-rw-r--r--AndroidManifest.xml36
-rw-r--r--RemoteFolder/Android-prebuilt-libs.mk1
-rw-r--r--RemoteFolder/Android.mk1
-rw-r--r--RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java151
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java2
-rw-r--r--proguard.flags39
-rw-r--r--res/anim/drop_down.xml13
-rw-r--r--res/anim/enter_from_left.xml12
-rw-r--r--res/anim/enter_from_right.xml12
-rw-r--r--res/anim/exit_out_left.xml13
-rw-r--r--res/anim/exit_out_right.xml12
-rw-r--r--res/anim/fade_in_fast.xml (renamed from res/xml/launcher_preferences.xml)15
-rw-r--r--res/anim/fade_out_fast.xml23
-rw-r--r--res/color/listitem_text.xml21
-rw-r--r--res/drawable-hdpi/folder_bg.9.pngbin0 -> 823 bytes
-rwxr-xr-xres/drawable-hdpi/folder_bg_opaque.9.pngbin0 -> 199 bytes
-rw-r--r--res/drawable-hdpi/folder_fill_highlight.9.pngbin0 -> 14914 bytes
-rw-r--r--res/drawable-hdpi/ic_default_screen.pngbin0 -> 2046 bytes
-rw-r--r--res/drawable-hdpi/ic_default_screen_pressed.pngbin0 -> 1993 bytes
-rw-r--r--res/drawable-hdpi/letter_indicator.pngbin0 -> 21583 bytes
-rw-r--r--res/drawable-mdpi/folder_bg.9.pngbin0 -> 510 bytes
-rwxr-xr-xres/drawable-mdpi/folder_bg_opaque.9.pngbin0 -> 160 bytes
-rw-r--r--res/drawable-mdpi/folder_fill_highlight.9.pngbin0 -> 14764 bytes
-rw-r--r--res/drawable-mdpi/ic_default_screen.pngbin0 -> 1597 bytes
-rw-r--r--res/drawable-mdpi/ic_default_screen_pressed.pngbin0 -> 1626 bytes
-rw-r--r--res/drawable-mdpi/letter_indicator.pngbin0 -> 4095 bytes
-rw-r--r--res/drawable-xhdpi/folder_bg.9.pngbin0 -> 1023 bytes
-rwxr-xr-xres/drawable-xhdpi/folder_bg_opaque.9.pngbin0 -> 249 bytes
-rw-r--r--res/drawable-xhdpi/folder_fill_highlight.9.pngbin0 -> 15075 bytes
-rw-r--r--res/drawable-xhdpi/ic_default_screen.pngbin0 -> 2323 bytes
-rw-r--r--res/drawable-xhdpi/ic_default_screen_pressed.pngbin0 -> 2385 bytes
-rw-r--r--res/drawable-xhdpi/letter_indicator.pngbin0 -> 25268 bytes
-rw-r--r--res/drawable-xxhdpi/folder_bg.9.pngbin0 -> 1678 bytes
-rwxr-xr-xres/drawable-xxhdpi/folder_bg_opaque.9.pngbin0 -> 312 bytes
-rw-r--r--res/drawable-xxhdpi/folder_fill_highlight.9.pngbin0 -> 15836 bytes
-rw-r--r--res/drawable-xxhdpi/ic_default_screen.pngbin0 -> 3187 bytes
-rw-r--r--res/drawable-xxhdpi/ic_default_screen_pressed.pngbin0 -> 3237 bytes
-rw-r--r--res/drawable-xxhdpi/letter_indicator.pngbin0 -> 34597 bytes
-rw-r--r--res/drawable/above_shadow.xml8
-rw-r--r--res/drawable/below_shadow.xml8
-rw-r--r--res/drawable/default_screen_button.xml19
-rw-r--r--res/drawable/download_badge.pngbin0 -> 2711 bytes
-rw-r--r--res/drawable/folder_container.xml25
-rw-r--r--res/drawable/folder_locked.xml14
-rw-r--r--res/drawable/folder_unlocked.xml13
-rw-r--r--res/drawable/ic_navigation_next.xml28
-rw-r--r--res/drawable/ic_navigation_prev.xml28
-rw-r--r--res/drawable/launcheranimatedarrow_00000.pngbin0 -> 661 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00001.pngbin0 -> 749 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00002.pngbin0 -> 705 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00003.pngbin0 -> 746 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00004.pngbin0 -> 747 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00005.pngbin0 -> 664 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00006.pngbin0 -> 1007 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00007.pngbin0 -> 999 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00008.pngbin0 -> 710 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00009.pngbin0 -> 968 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00010.pngbin0 -> 990 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00011.pngbin0 -> 671 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00012.pngbin0 -> 744 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00013.pngbin0 -> 750 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00014.pngbin0 -> 747 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00015.pngbin0 -> 736 bytes
-rw-r--r--res/drawable/launcheranimatedarrow_00016.pngbin0 -> 668 bytes
-rw-r--r--res/drawable/listitem_bg.xml21
-rw-r--r--res/drawable/listitem_selector.xml21
-rw-r--r--res/drawable/listitem_text.xml6
-rw-r--r--res/drawable/scrubber_back.xml19
-rw-r--r--res/drawable/seek_back.xml30
-rw-r--r--res/drawable/transition_arrow.xml21
-rw-r--r--res/drawable/transition_arrow_reverse.xml21
-rw-r--r--res/drawable/triangle_icon.pngbin0 -> 12557 bytes
-rw-r--r--res/layout-land/launcher.xml28
-rw-r--r--res/layout-land/migration_cling.xml12
-rw-r--r--res/layout-port/launcher.xml21
-rw-r--r--res/layout-port/migration_cling.xml12
-rw-r--r--res/layout-sw720dp/launcher.xml19
-rw-r--r--res/layout/all_apps_container.xml9
-rw-r--r--res/layout/all_apps_empty_search.xml2
-rw-r--r--res/layout/all_apps_prediction_bar_icon.xml4
-rw-r--r--res/layout/all_apps_search_bar.xml4
-rw-r--r--res/layout/custom_grid_size_dialog.xml48
-rw-r--r--res/layout/custom_predicted_apps_footer.xml3
-rw-r--r--res/layout/custom_predicted_apps_header.xml3
-rw-r--r--res/layout/dynamic_grid_size_screen.xml69
-rw-r--r--res/layout/folder_icon.xml50
-rw-r--r--res/layout/hidden_apps_list.xml22
-rw-r--r--res/layout/hidden_apps_list_item.xml48
-rw-r--r--res/layout/longpress_cling_content.xml6
-rw-r--r--res/layout/longpress_cling_welcome_content.xml8
-rw-r--r--res/layout/overview_panel.xml175
-rw-r--r--res/layout/remote_folder.xml4
-rw-r--r--res/layout/scrub_layout.xml44
-rw-r--r--res/layout/scrubber_container.xml53
-rw-r--r--res/layout/settings_pane_list_header.xml34
-rw-r--r--res/layout/settings_pane_list_item.xml46
-rw-r--r--res/layout/user_folder.xml34
-rw-r--r--res/layout/widgets_view.xml33
-rw-r--r--res/mipmap-hdpi/ic_launcher_home.pngbin464 -> 5650 bytes
-rw-r--r--res/mipmap-mdpi/ic_launcher_home.pngbin416 -> 3403 bytes
-rw-r--r--res/mipmap-xhdpi/ic_launcher_home.pngbin443 -> 8133 bytes
-rw-r--r--res/mipmap-xxhdpi/ic_launcher_home.pngbin473 -> 13218 bytes
-rw-r--r--res/mipmap-xxxhdpi/ic_launcher_home.pngbin0 -> 19286 bytes
-rw-r--r--res/values-af/cm_strings.xml63
-rw-r--r--res/values-am/cm_strings.xml31
-rw-r--r--res/values-ar/cm_strings.xml54
-rw-r--r--res/values-as-rIN/cm_strings.xml31
-rw-r--r--res/values-ast-rES/cm_strings.xml54
-rw-r--r--res/values-ast-rES/strings.xml172
-rw-r--r--res/values-az-rAZ/cm_strings.xml54
-rw-r--r--res/values-be/cm_strings.xml63
-rw-r--r--res/values-be/strings.xml295
-rw-r--r--res/values-bg/cm_strings.xml63
-rw-r--r--res/values-bn-rBD/cm_strings.xml32
-rw-r--r--res/values-br-rFR/cm_strings.xml31
-rw-r--r--res/values-br-rFR/strings.xml121
-rw-r--r--res/values-ca/cm_strings.xml63
-rw-r--r--res/values-cs/cm_strings.xml63
-rw-r--r--res/values-cy/cm_strings.xml31
-rw-r--r--res/values-cy/strings.xml121
-rw-r--r--res/values-da/cm_strings.xml54
-rw-r--r--res/values-de/cm_strings.xml63
-rw-r--r--res/values-el/cm_strings.xml63
-rw-r--r--res/values-en-rAU/cm_strings.xml53
-rw-r--r--res/values-en-rGB/cm_strings.xml31
-rw-r--r--res/values-en-rIN/cm_strings.xml63
-rw-r--r--res/values-en-rPT/cm_strings.xml31
-rw-r--r--res/values-en-rPT/strings.xml121
-rw-r--r--res/values-eo/cm_strings.xml54
-rw-r--r--res/values-eo/strings.xml121
-rw-r--r--res/values-es-rMX/cm_strings.xml31
-rw-r--r--res/values-es-rMX/strings.xml121
-rw-r--r--res/values-es-rUS/cm_strings.xml31
-rw-r--r--res/values-es/cm_strings.xml63
-rw-r--r--res/values-et-rEE/cm_strings.xml63
-rw-r--r--res/values-eu-rES/cm_strings.xml63
-rw-r--r--res/values-fa/cm_strings.xml54
-rw-r--r--res/values-fi/cm_strings.xml63
-rw-r--r--res/values-fil-rPH/cm_strings.xml31
-rw-r--r--res/values-fr-rCA/cm_strings.xml31
-rw-r--r--res/values-fr/cm_strings.xml63
-rw-r--r--res/values-fy-rNL/cm_strings.xml32
-rw-r--r--res/values-fy-rNL/strings.xml121
-rw-r--r--res/values-gd-rGB/cm_strings.xml31
-rw-r--r--res/values-gd-rGB/strings.xml121
-rw-r--r--res/values-gl-rES/cm_strings.xml34
-rw-r--r--res/values-gu-rIN/cm_strings.xml31
-rw-r--r--res/values-hi/cm_strings.xml54
-rw-r--r--res/values-hr/cm_strings.xml63
-rw-r--r--res/values-hu/cm_strings.xml63
-rw-r--r--res/values-hy-rAM/cm_strings.xml31
-rw-r--r--res/values-in/cm_strings.xml54
-rw-r--r--res/values-is-rIS/cm_strings.xml31
-rw-r--r--res/values-it/cm_strings.xml63
-rw-r--r--res/values-iw/cm_strings.xml63
-rw-r--r--res/values-ja/cm_strings.xml63
-rw-r--r--res/values-ka-rGE/cm_strings.xml31
-rw-r--r--res/values-kk-rKZ/cm_strings.xml31
-rw-r--r--res/values-km-rKH/cm_strings.xml31
-rw-r--r--res/values-kn-rIN/cm_strings.xml54
-rw-r--r--res/values-ko/cm_strings.xml63
-rw-r--r--res/values-ku/cm_strings.xml49
-rw-r--r--res/values-ku/strings.xml162
-rw-r--r--res/values-ky-rKG/cm_strings.xml31
-rw-r--r--res/values-lb/cm_strings.xml54
-rw-r--r--res/values-lb/strings.xml171
-rw-r--r--res/values-lo-rLA/cm_strings.xml31
-rw-r--r--res/values-lt/cm_strings.xml54
-rw-r--r--res/values-lv/cm_strings.xml54
-rw-r--r--res/values-mk-rMK/cm_strings.xml31
-rw-r--r--res/values-ml-rIN/cm_strings.xml31
-rw-r--r--res/values-mn-rMN/cm_strings.xml31
-rw-r--r--res/values-mr-rIN/cm_strings.xml31
-rw-r--r--res/values-ms-rMY/cm_strings.xml31
-rw-r--r--res/values-my-rMM/cm_strings.xml31
-rw-r--r--res/values-nb/cm_strings.xml61
-rw-r--r--res/values-ne-rNP/cm_strings.xml31
-rw-r--r--res/values-nl/cm_strings.xml63
-rw-r--r--res/values-oc-rFR/cm_strings.xml31
-rw-r--r--res/values-oc-rFR/strings.xml121
-rw-r--r--res/values-or-rIN/cm_strings.xml31
-rw-r--r--res/values-pa-rIN/cm_strings.xml31
-rw-r--r--res/values-pl/cm_strings.xml63
-rw-r--r--res/values-pt-rBR/cm_strings.xml63
-rw-r--r--res/values-pt-rPT/cm_strings.xml63
-rw-r--r--res/values-rm/cm_strings.xml31
-rw-r--r--res/values-ro/cm_strings.xml59
-rw-r--r--res/values-ru/cm_strings.xml63
-rw-r--r--res/values-si-rLK/cm_strings.xml49
-rw-r--r--res/values-sk/cm_strings.xml54
-rw-r--r--res/values-sl/cm_strings.xml63
-rw-r--r--res/values-sq-rAL/cm_strings.xml31
-rw-r--r--res/values-sr/cm_strings.xml63
-rw-r--r--res/values-sv/cm_strings.xml63
-rw-r--r--res/values-sw/cm_strings.xml31
-rw-r--r--res/values-sw600dp/config.xml1
-rw-r--r--res/values-sw600dp/preferences_defaults.xml4
-rw-r--r--res/values-ta-rIN/cm_strings.xml31
-rw-r--r--res/values-te-rIN/cm_strings.xml31
-rw-r--r--res/values-th/cm_strings.xml63
-rw-r--r--res/values-tr/cm_strings.xml63
-rw-r--r--res/values-ug/cm_strings.xml31
-rw-r--r--res/values-ug/strings.xml121
-rw-r--r--res/values-uk/cm_strings.xml54
-rw-r--r--res/values-ur-rPK/cm_strings.xml31
-rw-r--r--res/values-uz-rUZ/cm_strings.xml31
-rw-r--r--res/values-vi/cm_strings.xml54
-rw-r--r--res/values-zh-rCN/cm_strings.xml63
-rw-r--r--res/values-zh-rHK/cm_strings.xml52
-rw-r--r--res/values-zh-rTW/cm_strings.xml63
-rw-r--r--res/values-zu/cm_strings.xml31
-rw-r--r--res/values/attrs.xml30
-rw-r--r--res/values/cm_colors.xml22
-rw-r--r--res/values/cm_strings.xml77
-rw-r--r--res/values/colors.xml24
-rw-r--r--res/values/config.xml1
-rw-r--r--res/values/dimens.xml47
-rw-r--r--res/values/preferences_defaults.xml17
-rw-r--r--res/values/strings.xml5
-rw-r--r--res/values/styles.xml2
-rw-r--r--res/values/tags.xml4
-rw-r--r--res/xml/ct_default_workspace_4x4.xml174
-rw-r--r--res/xml/default_workspace_4x4.xml201
-rw-r--r--res/xml/default_workspace_5x5.xml200
-rw-r--r--res/xml/default_workspace_5x6.xml203
-rw-r--r--res/xml/update_workspace.xml49
-rw-r--r--src/com/android/launcher3/AppInfo.java48
-rw-r--r--src/com/android/launcher3/AutoExpandTextView.java246
-rw-r--r--src/com/android/launcher3/BaseContainerView.java81
-rw-r--r--src/com/android/launcher3/BaseRecyclerView.java82
-rw-r--r--src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java128
-rw-r--r--src/com/android/launcher3/BaseRecyclerViewScrubber.java431
-rw-r--r--src/com/android/launcher3/BaseRecyclerViewScrubberSection.java267
-rw-r--r--src/com/android/launcher3/BubbleTextView.java83
-rw-r--r--src/com/android/launcher3/CellLayout.java23
-rw-r--r--src/com/android/launcher3/CheckLongPressHelper.java2
-rw-r--r--src/com/android/launcher3/DeleteDropTarget.java10
-rw-r--r--src/com/android/launcher3/DeviceProfile.java105
-rw-r--r--src/com/android/launcher3/DeviceUnlockedReceiver.java55
-rw-r--r--src/com/android/launcher3/DragLayer.java1
-rw-r--r--src/com/android/launcher3/DynamicGridSizeFragment.java381
-rw-r--r--src/com/android/launcher3/FocusIndicatorView.java3
-rw-r--r--src/com/android/launcher3/Folder.java567
-rw-r--r--src/com/android/launcher3/FolderIcon.java216
-rw-r--r--src/com/android/launcher3/FolderInfo.java71
-rw-r--r--src/com/android/launcher3/FolderPagedView.java53
-rw-r--r--src/com/android/launcher3/IconCache.java38
-rw-r--r--src/com/android/launcher3/InsettableFrameLayout.java15
-rw-r--r--src/com/android/launcher3/InsettableLinearLayout.java122
-rw-r--r--src/com/android/launcher3/InvariantDeviceProfile.java102
-rw-r--r--src/com/android/launcher3/ItemInfo.java13
-rw-r--r--src/com/android/launcher3/Launcher.java655
-rw-r--r--src/com/android/launcher3/LauncherAppState.java8
-rw-r--r--src/com/android/launcher3/LauncherApplication.java44
-rw-r--r--src/com/android/launcher3/LauncherModel.java365
-rw-r--r--src/com/android/launcher3/LauncherProvider.java201
-rw-r--r--src/com/android/launcher3/LauncherSettings.java15
-rw-r--r--src/com/android/launcher3/LauncherStateTransitionAnimation.java28
-rw-r--r--src/com/android/launcher3/NetworkConnectionReceiver.java62
-rw-r--r--src/com/android/launcher3/OverviewPanel.java32
-rw-r--r--src/com/android/launcher3/OverviewSettingsPanel.java107
-rw-r--r--src/com/android/launcher3/PagedView.java17
-rw-r--r--src/com/android/launcher3/ProtectedComponentsHelper.java83
-rw-r--r--src/com/android/launcher3/SearchDropTargetBar.java40
-rw-r--r--src/com/android/launcher3/SettingsActivity.java76
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java10
-rw-r--r--src/com/android/launcher3/ThemeChangedReceiver.java78
-rw-r--r--src/com/android/launcher3/Utilities.java48
-rw-r--r--src/com/android/launcher3/VerticalSlidingPanel.java1330
-rw-r--r--src/com/android/launcher3/WallpaperChangedReceiver.java15
-rw-r--r--src/com/android/launcher3/WidgetPreviewLoader.java9
-rw-r--r--src/com/android/launcher3/Workspace.java102
-rw-r--r--src/com/android/launcher3/allapps/AllAppsContainerView.java248
-rw-r--r--src/com/android/launcher3/allapps/AllAppsGridAdapter.java255
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerView.java222
-rw-r--r--src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java2
-rw-r--r--src/com/android/launcher3/allapps/AlphabeticalAppsList.java191
-rw-r--r--src/com/android/launcher3/list/AutoScrollListView.java117
-rw-r--r--src/com/android/launcher3/list/CompositeCursorAdapter.java532
-rw-r--r--src/com/android/launcher3/list/PinnedHeaderListAdapter.java137
-rw-r--r--src/com/android/launcher3/list/PinnedHeaderListView.java565
-rw-r--r--src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java434
-rw-r--r--src/com/android/launcher3/model/WidgetsModel.java15
-rw-r--r--src/com/android/launcher3/settings/SettingsProvider.java102
-rw-r--r--src/com/android/launcher3/stats/LauncherStats.java246
-rw-r--r--src/com/android/launcher3/stats/external/StatsUtil.java117
-rw-r--r--src/com/android/launcher3/stats/external/TrackingBundle.java67
-rw-r--r--src/com/android/launcher3/stats/internal/db/DatabaseHelper.java159
-rw-r--r--src/com/android/launcher3/stats/internal/db/TrackingEventContract.java31
-rw-r--r--src/com/android/launcher3/stats/internal/model/CountAction.java73
-rw-r--r--src/com/android/launcher3/stats/internal/model/CountOriginByPackageAction.java91
-rw-r--r--src/com/android/launcher3/stats/internal/model/ITrackingAction.java45
-rw-r--r--src/com/android/launcher3/stats/internal/model/TrackingEvent.java216
-rw-r--r--src/com/android/launcher3/stats/internal/service/AggregationIntentService.java232
-rw-r--r--src/com/android/launcher3/stats/util/Logger.java55
-rw-r--r--src/com/android/launcher3/widget/WidgetsContainerView.java55
-rw-r--r--src/com/android/launcher3/widget/WidgetsRecyclerView.java60
-rw-r--r--tests/Android.mk25
-rw-r--r--tests/src/com/android/launcher3/InvariantDeviceProfileTest.java2
300 files changed, 18379 insertions, 1146 deletions
diff --git a/Android.mk b/Android.mk
index 3a6442aee..ce21c3595 100644
--- a/Android.mk
+++ b/Android.mk
@@ -25,7 +25,9 @@ LOCAL_MODULE_TAGS := optional
LOCAL_STATIC_JAVA_LIBRARIES := \
android-support-v4 \
- android-support-v7-recyclerview
+ android-support-v7-recyclerview \
+ org.cyanogenmod.platform.internal
+
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
$(call all-java-files-under, WallpaperPicker/src) \
@@ -43,15 +45,31 @@ LOCAL_AAPT_FLAGS := \
--auto-add-overlay \
--extra-packages android.support.v7.recyclerview
-LOCAL_SDK_VERSION := current
-LOCAL_PACKAGE_NAME := Launcher3
+#LOCAL_SDK_VERSION := current
+LOCAL_PACKAGE_NAME := Trebuchet
LOCAL_PRIVILEGED_MODULE := true
#LOCAL_CERTIFICATE := shared
-LOCAL_OVERRIDES_PACKAGES := Home Launcher2
+LOCAL_AAPT_FLAGS += --rename-manifest-package com.cyanogenmod.trebuchet
+
+LOCAL_OVERRIDES_PACKAGES := Launcher3
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+LOCAL_PROGUARD_ENABLED := full
+
+REMOTE_FOLDER_UPDATER ?= $(LOCAL_PATH)/RemoteFolder
+include $(REMOTE_FOLDER_UPDATER)/Android.mk
include $(BUILD_PACKAGE)
+include $(CLEAR_VARS)
+
+REMOTE_FOLDER_UPDATER ?= $(LOCAL_PATH)/RemoteFolder
+include $(REMOTE_FOLDER_UPDATER)/Android-prebuilt-libs.mk
+
+include $(BUILD_MULTI_PREBUILT)
+
+include $(CLEAR_VARS)
#
# Protocol Buffer Debug Utility in Java
@@ -71,6 +89,7 @@ LOCAL_JAR_MANIFEST := util/etc/manifest.txt
include $(BUILD_HOST_JAVA_LIBRARY)
+
#
# Protocol Buffer Debug Utility Wrapper Script
#
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 99a6ad9aa..dbdabfa4d 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,7 +20,10 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="16"/>
+
+ <original-package android:name="com.android.launcher3" />
+
+ <uses-sdk android:targetSdkVersion="23" android:minSdkVersion="22"/>
<permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
@@ -53,22 +56,28 @@
<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" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />
<uses-permission android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST" />
+ <uses-permission android:name="cyanogenmod.permission.PROTECTED_APP" />
+ <uses-permission android:name="com.cyngn.stats.SEND_ANALYTICS" />
<application
+ android:name="com.android.launcher3.LauncherApplication"
android:allowBackup="@bool/enable_backup"
android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
android:hardwareAccelerated="true"
android:icon="@mipmap/ic_launcher_home"
- android:label="@string/application_name"
+ android:label="@string/cm_application_name"
android:largeHeap="@bool/config_largeHeap"
android:restoreAnyVersion="true"
android:supportsRtl="true" >
@@ -83,6 +92,8 @@
android:screenOrientation="nosensor"
android:resumeWhilePausing="true"
android:taskAffinity=""
+ android:excludeFromRecents="true"
+ android:configChanges="mcc|mnc"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -152,13 +163,6 @@
</intent-filter>
</activity>
- <activity
- android:name="com.android.launcher3.SettingsActivity"
- android:label="@string/settings_button_text"
- android:autoRemoveFromRecents="true"
- android:process=":settings_process">
- </activity>
-
<!-- Debugging tools -->
<activity
android:name="com.android.launcher3.MemoryDumpActivity"
@@ -180,6 +184,14 @@
>
</service>
+ <service
+ android:name="com.android.launcher3.stats.internal.service.AggregationIntentService"
+ android:exported="false">
+ <intent-filter>
+ <action android:name="com.cyanogenmod.trebuchet.AGGREGATE_AND_TRACK" />
+ </intent-filter>
+ </service>
+
<receiver
android:name="com.android.launcher3.WallpaperChangedReceiver">
<intent-filter>
@@ -209,6 +221,12 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.launcher3.ThemeChangedReceiver" >
+ <intent-filter>
+ <action android:name="org.cyanogenmod.intent.action.THEME_CHANGED"/>
+ </intent-filter>
+ </receiver>
+
<!-- The settings provider contains Home's data, like the workspace favorites -->
<provider
android:name="com.android.launcher3.LauncherProvider"
diff --git a/RemoteFolder/Android-prebuilt-libs.mk b/RemoteFolder/Android-prebuilt-libs.mk
new file mode 100644
index 000000000..65357a8b8
--- /dev/null
+++ b/RemoteFolder/Android-prebuilt-libs.mk
@@ -0,0 +1 @@
+# Empty file needed to mirror lib makefile in overlay \ No newline at end of file
diff --git a/RemoteFolder/Android.mk b/RemoteFolder/Android.mk
new file mode 100644
index 000000000..2a7441345
--- /dev/null
+++ b/RemoteFolder/Android.mk
@@ -0,0 +1 @@
+LOCAL_SRC_FILES += $(call all-java-files-under, RemoteFolder/src)
diff --git a/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java b/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java
new file mode 100644
index 000000000..142a4d124
--- /dev/null
+++ b/RemoteFolder/src/com/android/launcher3/RemoteFolderManager.java
@@ -0,0 +1,151 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.launcher3.allapps.AllAppsGridAdapter;
+import com.android.launcher3.allapps.AlphabeticalAppsList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages adding and removing the remote folder from the workspace.
+ */
+public class RemoteFolderManager {
+
+ public RemoteFolderManager(final Launcher launcher) { }
+
+ /**
+ * Called when launcher receives a non-initial {@link Launcher#onCreate(Bundle)} call.
+ * @param launcher new launcher activity.
+ */
+ public void onRecreateLauncher(final Launcher launcher) { }
+
+ /**
+ * Called when Launcher's views are loaded and ready.
+ */
+ public void onSetupViews() { }
+
+ /**
+ * Create a remote folder view.
+ * @param icon folder icon view on the workspace.
+ * @return a view for the remote folder.
+ */
+ public Folder createRemoteFolder(final FolderIcon icon, ViewGroup root) { return null; }
+
+ /**
+ * Get a drawable for the supplied item in the folder icon preview.
+ * @param items list of views in the folder.
+ * @param position index of icon to retreive.
+ * @return an icon to draw in the folder preview.
+ */
+ public Drawable getFolderIconDrawable(final ArrayList<View> items,
+ final int position) { return null; }
+
+ /**
+ * Called when Launcher finishes binding items from the model.
+ */
+ public void bindFinished() { }
+
+ /**
+ * Called when a setting for remote folder is updated.
+ */
+ public void onSettingChanged() { }
+
+ /**
+ * Called when the remote folder is dropped into the delete area on the workspace.
+ */
+ public void onFolderDeleted() { }
+
+ /**
+ * Called when the app drawer is opened.
+ */
+ public void onAppDrawerOpened() { }
+
+ /**
+ * Called when the app drawer is reloaded.
+ */
+ public void onReloadAppDrawer() { }
+
+ /**
+ * Called when the app drawer is measured.
+ * @param numAppsPerRow the number of apps the drawer will show in a row.
+ */
+ public void onMeasureDrawer(int numAppsPerRow) { }
+
+ /**
+ * Called when new apps are added to launcher.
+ * @param apps list of added apps.
+ */
+ public void onBindAddApps(ArrayList<AppInfo> apps) { }
+
+ /**
+ * Called when the info icon is clicked
+ */
+ public void onInfoIconClicked() { }
+
+ /**
+ * Called when the grid size for launcher is updated.
+ */
+ public void onGridSizeChanged() { }
+
+ /**
+ * Change the appearance of FolderIcon for our RemoteFolder by adding a badge
+ * @param icon the FolderIcon to update
+ * @return a FolderIcon with an added ImageView
+ */
+ public static FolderIcon addBadgeToFolderIcon(FolderIcon icon) {
+ return icon;
+ }
+
+ /**
+ * Called when adapter items for predicted apps are updated.
+ * @param items current list of built adapter items.
+ * @param fastScrollInfo fast scroller info for this section.
+ * @param sectionInfo info about apps in this section.
+ * @param position current position of item to be built into the adapter.
+ * @return the new position to start from for next adapter items.
+ */
+ public int onUpdateAdapterItems(final List<AlphabeticalAppsList.AdapterItem> items,
+ final AlphabeticalAppsList.FastScrollSectionInfo fastScrollInfo,
+ final AlphabeticalAppsList.SectionInfo sectionInfo,
+ int position) { return position; }
+
+ /**
+ * Called when a view holder is created for a remote app.
+ * @param holder remote view holder.
+ * @param viewType specific type of view holder.
+ */
+ public void onCreateViewHolder(final AllAppsGridAdapter.ViewHolder holder, final int viewType) { }
+
+ /**
+ * Called when a view holder is bound for a remote app.
+ * @param holder remote view holder.
+ * @param item info for this app.
+ */
+ public void onBindViewHolder(final AllAppsGridAdapter.ViewHolder holder, final AppInfo item) { }
+
+ /**
+ * Populate home settings list with additional values as needed.
+ * @param values list of settings strings.
+ * @param context application context.
+ */
+ public static void onInitializeHomeSettings(final ArrayList<String> values,
+ final Context context) { }
+
+ /**
+ * Populate drawer settings list with additional values as needed.
+ * @param values list of settings strings.
+ * @param context application context.
+ */
+ public static void onInitializeDrawerSettings(final ArrayList<String> values,
+ final Context context) { }
+
+ /**
+ * Apply icon pack when the theme changes.
+ */
+ public synchronized void onThemeChanged() { }
+}
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index f2459dd3e..4aa51dbd9 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -292,7 +292,7 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb
public boolean enableRotation() {
- return getResources().getBoolean(R.bool.allow_rotation);
+ return getResources().getBoolean(R.bool.preferences_interface_allow_rotation);
}
protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) {
diff --git a/proguard.flags b/proguard.flags
index d6b9ba3af..7dace3b36 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,3 +1,8 @@
+-keepattributes SourceFile,LineNumberTable,InnerClasses
+-keep class com.inmobi.** { *; }
+-dontwarn com.inmobi.**
+-dontwarn com.google.android.gms**
+
-keep class com.android.launcher3.allapps.AllAppsBackgroundDrawable {
public void setAlpha(int);
public int getAlpha();
@@ -69,3 +74,37 @@
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
}
+
+-keep class * extends java.util.ListResourceBundle {
+ protected Object[][] getContents();
+}
+
+-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
+ public static final *** NULL;
+}
+
+-keepnames @com.google.android.gms.common.annotation.KeepName class *
+-keepclassmembernames class * {
+ @com.google.android.gms.common.annotation.KeepName *;
+}
+
+-keepnames class * implements android.os.Parcelable {
+ public static final ** CREATOR;
+}
+
+-keep class android.content.res.** { *; }
+-dontwarn android.content.res.*
+
+-keep class android.view.inputmethod.** { *; }
+-dontwarn android.view.inputmethod.*
+
+-keep class com.google.android.gms.common.api.GoogleApiClient { public *; }
+-keep class com.google.android.gms.common.api.GoogleApiClient$* {public *;}
+-keep class com.google.android.gms.location.LocationServices {public *;}
+-keep class com.google.android.gms.location.FusedLocationProviderApi {public *;}
+-keep class com.google.android.gms.location.ActivityRecognition {public *;}
+-keep class com.google.android.gms.location.ActivityRecognitionApi {public *;}
+-keep class com.google.android.gms.location.ActivityRecognitionResult {public *;}
+-keep class com.google.android.gms.location.DetectedActivity {public *;}
+-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient{public *;}
+-keep class com.google.android.gms.ads.identifier.AdvertisingIdClient$Info{public *;} \ No newline at end of file
diff --git a/res/anim/drop_down.xml b/res/anim/drop_down.xml
new file mode 100644
index 000000000..49059a048
--- /dev/null
+++ b/res/anim/drop_down.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:interpolator/accelerate_decelerate">
+ <scale
+ android:fromXScale="1.0"
+ android:toXScale="1.0"
+ android:fromYScale="2.5"
+ android:toYScale="1.0"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:duration="300"
+ android:fillBefore="false" />
+</set>
diff --git a/res/anim/enter_from_left.xml b/res/anim/enter_from_left.xml
new file mode 100644
index 000000000..e2bdbdda3
--- /dev/null
+++ b/res/anim/enter_from_left.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="300"
+ android:propertyName="x"
+ android:valueFrom="-1000"
+ android:valueTo="0"
+ android:valueType="floatType" />
+</set>
diff --git a/res/anim/enter_from_right.xml b/res/anim/enter_from_right.xml
new file mode 100644
index 000000000..02a56c7ae
--- /dev/null
+++ b/res/anim/enter_from_right.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="300"
+ android:propertyName="x"
+ android:valueFrom="1000"
+ android:valueTo="0"
+ android:valueType="floatType" />
+</set>
diff --git a/res/anim/exit_out_left.xml b/res/anim/exit_out_left.xml
new file mode 100644
index 000000000..eae925a2a
--- /dev/null
+++ b/res/anim/exit_out_left.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="300"
+ android:propertyName="x"
+ android:valueFrom="0"
+ android:valueTo="-1000"
+ android:valueType="floatType" />
+</set>
+
diff --git a/res/anim/exit_out_right.xml b/res/anim/exit_out_right.xml
new file mode 100644
index 000000000..7345c942d
--- /dev/null
+++ b/res/anim/exit_out_right.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set
+ xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <objectAnimator
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="300"
+ android:propertyName="x"
+ android:valueFrom="0"
+ android:valueTo="1000"
+ android:valueType="floatType" />
+</set>
diff --git a/res/xml/launcher_preferences.xml b/res/anim/fade_in_fast.xml
index 624d9eb2c..4fa9847aa 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/anim/fade_in_fast.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2015 Google Inc.
+<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,13 +14,10 @@
limitations under the License.
-->
-<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator"
- <SwitchPreference
- android:key="pref_allowRotation"
- android:title="@string/allow_rotation_title"
- android:defaultValue="@bool/allow_rotation"
- android:persistent="true"
- />
+ android:fromAlpha="0.0"
+ android:toAlpha="1.0"
-</PreferenceScreen>
+ android:duration="@android:integer/config_mediumAnimTime" />
diff --git a/res/anim/fade_out_fast.xml b/res/anim/fade_out_fast.xml
new file mode 100644
index 000000000..a061a6ca9
--- /dev/null
+++ b/res/anim/fade_out_fast.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:interpolator="@android:anim/accelerate_interpolator"
+
+ android:fromAlpha="1.0"
+ android:toAlpha="0.0"
+
+ android:duration="@android:integer/config_mediumAnimTime" />
diff --git a/res/color/listitem_text.xml b/res/color/listitem_text.xml
new file mode 100644
index 000000000..ce981439a
--- /dev/null
+++ b/res/color/listitem_text.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_enabled="true"
+ android:state_pressed="true" android:color="@color/settings_bg_color" />
+ <item android:color="@android:color/white" />
+</selector>
diff --git a/res/drawable-hdpi/folder_bg.9.png b/res/drawable-hdpi/folder_bg.9.png
new file mode 100644
index 000000000..ee0090c09
--- /dev/null
+++ b/res/drawable-hdpi/folder_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/folder_bg_opaque.9.png b/res/drawable-hdpi/folder_bg_opaque.9.png
new file mode 100755
index 000000000..08e152e49
--- /dev/null
+++ b/res/drawable-hdpi/folder_bg_opaque.9.png
Binary files differ
diff --git a/res/drawable-hdpi/folder_fill_highlight.9.png b/res/drawable-hdpi/folder_fill_highlight.9.png
new file mode 100644
index 000000000..b82302ba6
--- /dev/null
+++ b/res/drawable-hdpi/folder_fill_highlight.9.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_default_screen.png b/res/drawable-hdpi/ic_default_screen.png
new file mode 100644
index 000000000..41dcf8f6e
--- /dev/null
+++ b/res/drawable-hdpi/ic_default_screen.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_default_screen_pressed.png b/res/drawable-hdpi/ic_default_screen_pressed.png
new file mode 100644
index 000000000..7779058c5
--- /dev/null
+++ b/res/drawable-hdpi/ic_default_screen_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/letter_indicator.png b/res/drawable-hdpi/letter_indicator.png
new file mode 100644
index 000000000..4770d819d
--- /dev/null
+++ b/res/drawable-hdpi/letter_indicator.png
Binary files differ
diff --git a/res/drawable-mdpi/folder_bg.9.png b/res/drawable-mdpi/folder_bg.9.png
new file mode 100644
index 000000000..4039da560
--- /dev/null
+++ b/res/drawable-mdpi/folder_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/folder_bg_opaque.9.png b/res/drawable-mdpi/folder_bg_opaque.9.png
new file mode 100755
index 000000000..673d740ae
--- /dev/null
+++ b/res/drawable-mdpi/folder_bg_opaque.9.png
Binary files differ
diff --git a/res/drawable-mdpi/folder_fill_highlight.9.png b/res/drawable-mdpi/folder_fill_highlight.9.png
new file mode 100644
index 000000000..7c6a0d456
--- /dev/null
+++ b/res/drawable-mdpi/folder_fill_highlight.9.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_default_screen.png b/res/drawable-mdpi/ic_default_screen.png
new file mode 100644
index 000000000..8a2c1e1c0
--- /dev/null
+++ b/res/drawable-mdpi/ic_default_screen.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_default_screen_pressed.png b/res/drawable-mdpi/ic_default_screen_pressed.png
new file mode 100644
index 000000000..20606ce62
--- /dev/null
+++ b/res/drawable-mdpi/ic_default_screen_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/letter_indicator.png b/res/drawable-mdpi/letter_indicator.png
new file mode 100644
index 000000000..2ecfe7c34
--- /dev/null
+++ b/res/drawable-mdpi/letter_indicator.png
Binary files differ
diff --git a/res/drawable-xhdpi/folder_bg.9.png b/res/drawable-xhdpi/folder_bg.9.png
new file mode 100644
index 000000000..1fbe1d80f
--- /dev/null
+++ b/res/drawable-xhdpi/folder_bg.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/folder_bg_opaque.9.png b/res/drawable-xhdpi/folder_bg_opaque.9.png
new file mode 100755
index 000000000..42a1e1d5d
--- /dev/null
+++ b/res/drawable-xhdpi/folder_bg_opaque.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/folder_fill_highlight.9.png b/res/drawable-xhdpi/folder_fill_highlight.9.png
new file mode 100644
index 000000000..f5f0bd08d
--- /dev/null
+++ b/res/drawable-xhdpi/folder_fill_highlight.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_default_screen.png b/res/drawable-xhdpi/ic_default_screen.png
new file mode 100644
index 000000000..735332ae5
--- /dev/null
+++ b/res/drawable-xhdpi/ic_default_screen.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_default_screen_pressed.png b/res/drawable-xhdpi/ic_default_screen_pressed.png
new file mode 100644
index 000000000..11dede0a0
--- /dev/null
+++ b/res/drawable-xhdpi/ic_default_screen_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/letter_indicator.png b/res/drawable-xhdpi/letter_indicator.png
new file mode 100644
index 000000000..6f2186017
--- /dev/null
+++ b/res/drawable-xhdpi/letter_indicator.png
Binary files differ
diff --git a/res/drawable-xxhdpi/folder_bg.9.png b/res/drawable-xxhdpi/folder_bg.9.png
new file mode 100644
index 000000000..3b2bc4253
--- /dev/null
+++ b/res/drawable-xxhdpi/folder_bg.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/folder_bg_opaque.9.png b/res/drawable-xxhdpi/folder_bg_opaque.9.png
new file mode 100755
index 000000000..25a4ffffb
--- /dev/null
+++ b/res/drawable-xxhdpi/folder_bg_opaque.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/folder_fill_highlight.9.png b/res/drawable-xxhdpi/folder_fill_highlight.9.png
new file mode 100644
index 000000000..4dc29f46c
--- /dev/null
+++ b/res/drawable-xxhdpi/folder_fill_highlight.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_default_screen.png b/res/drawable-xxhdpi/ic_default_screen.png
new file mode 100644
index 000000000..253bd2d4d
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_default_screen.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_default_screen_pressed.png b/res/drawable-xxhdpi/ic_default_screen_pressed.png
new file mode 100644
index 000000000..5367b7abd
--- /dev/null
+++ b/res/drawable-xxhdpi/ic_default_screen_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/letter_indicator.png b/res/drawable-xxhdpi/letter_indicator.png
new file mode 100644
index 000000000..acbacb067
--- /dev/null
+++ b/res/drawable-xxhdpi/letter_indicator.png
Binary files differ
diff --git a/res/drawable/above_shadow.xml b/res/drawable/above_shadow.xml
new file mode 100644
index 000000000..99db324ab
--- /dev/null
+++ b/res/drawable/above_shadow.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#20000000"
+ android:endColor="@android:color/transparent"
+ android:angle="90" >
+ </gradient>
+</shape> \ No newline at end of file
diff --git a/res/drawable/below_shadow.xml b/res/drawable/below_shadow.xml
new file mode 100644
index 000000000..fc70073a9
--- /dev/null
+++ b/res/drawable/below_shadow.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <gradient
+ android:startColor="#20000000"
+ android:endColor="@android:color/transparent"
+ android:angle="270" >
+ </gradient>
+</shape> \ No newline at end of file
diff --git a/res/drawable/default_screen_button.xml b/res/drawable/default_screen_button.xml
new file mode 100644
index 000000000..ad66137b0
--- /dev/null
+++ b/res/drawable/default_screen_button.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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="@drawable/ic_default_screen_pressed" />
+ <item android:state_pressed="true" android:drawable="@drawable/ic_default_screen_pressed" />
+ <item android:state_activated="true" android:drawable="@drawable/ic_default_screen_pressed" />
+ <item android:drawable="@drawable/ic_default_screen" />
+</selector> \ No newline at end of file
diff --git a/res/drawable/download_badge.png b/res/drawable/download_badge.png
new file mode 100644
index 000000000..fad49b65f
--- /dev/null
+++ b/res/drawable/download_badge.png
Binary files differ
diff --git a/res/drawable/folder_container.xml b/res/drawable/folder_container.xml
new file mode 100644
index 000000000..b0a1c8492
--- /dev/null
+++ b/res/drawable/folder_container.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="72dp"
+ android:width="72dp"
+ android:viewportHeight="72"
+ android:viewportWidth="72">
+
+ <group
+ android:name="folder_container">
+ <path
+ android:name="folder_path"
+ android:pathData=" M6,4
+ h60
+ c1.2,0 2,0.8 2,2
+ v60
+ c0,1.2 -0.8,2 -2,2
+ h-60
+ c-1.2,0 -2,-0.8 -2,-2
+ v-60
+ c0,-1.2 0.8,-2 2,-2
+ z"
+ android:fillColor="#ff524e5e"/>
+ </group>
+
+</vector> \ No newline at end of file
diff --git a/res/drawable/folder_locked.xml b/res/drawable/folder_locked.xml
new file mode 100644
index 000000000..8b887896d
--- /dev/null
+++ b/res/drawable/folder_locked.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#fafafa"
+ android:pathData="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1 .9 2 2
+2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6-5.1c1.71 0 3.1 1.39 3.1
+3.1v2H9V6h-.1c0-1.71 1.39-3.1 3.1-3.1zM18 20H6V10h12v10zm-6-3c1.1 0 2-.9
+2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z" />
+</vector>
diff --git a/res/drawable/folder_unlocked.xml b/res/drawable/folder_unlocked.xml
new file mode 100644
index 000000000..d34d9b764
--- /dev/null
+++ b/res/drawable/folder_unlocked.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#fafafa"
+ android:pathData="M12 17c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2zm6-9h-1V6c0-2.76-2.24-5-5-5S7
+3.24 7 6h1.9c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2H6c-1.1 0-2 .9-2
+2v10c0 1.1 .9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm0 12H6V10h12v10z" />
+</vector>
diff --git a/res/drawable/ic_navigation_next.xml b/res/drawable/ic_navigation_next.xml
new file mode 100644
index 000000000..571280bb9
--- /dev/null
+++ b/res/drawable/ic_navigation_next.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector> \ No newline at end of file
diff --git a/res/drawable/ic_navigation_prev.xml b/res/drawable/ic_navigation_prev.xml
new file mode 100644
index 000000000..f0a6a3b32
--- /dev/null
+++ b/res/drawable/ic_navigation_prev.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+
+ <path
+ android:fillColor="#FFFFFF"
+ android:pathData="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z" />
+ <path
+ android:pathData="M0 0h24v24H0z" />
+</vector> \ No newline at end of file
diff --git a/res/drawable/launcheranimatedarrow_00000.png b/res/drawable/launcheranimatedarrow_00000.png
new file mode 100644
index 000000000..2ed7fe9a7
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00000.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00001.png b/res/drawable/launcheranimatedarrow_00001.png
new file mode 100644
index 000000000..f3707e07c
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00001.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00002.png b/res/drawable/launcheranimatedarrow_00002.png
new file mode 100644
index 000000000..3549389d0
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00002.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00003.png b/res/drawable/launcheranimatedarrow_00003.png
new file mode 100644
index 000000000..891e86c42
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00003.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00004.png b/res/drawable/launcheranimatedarrow_00004.png
new file mode 100644
index 000000000..7cfb1ef8c
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00004.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00005.png b/res/drawable/launcheranimatedarrow_00005.png
new file mode 100644
index 000000000..121f4d516
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00005.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00006.png b/res/drawable/launcheranimatedarrow_00006.png
new file mode 100644
index 000000000..3a38e71f7
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00006.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00007.png b/res/drawable/launcheranimatedarrow_00007.png
new file mode 100644
index 000000000..e81a719fd
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00007.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00008.png b/res/drawable/launcheranimatedarrow_00008.png
new file mode 100644
index 000000000..bd6f40981
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00008.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00009.png b/res/drawable/launcheranimatedarrow_00009.png
new file mode 100644
index 000000000..c7cb60daf
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00009.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00010.png b/res/drawable/launcheranimatedarrow_00010.png
new file mode 100644
index 000000000..1bf30dcc3
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00010.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00011.png b/res/drawable/launcheranimatedarrow_00011.png
new file mode 100644
index 000000000..3cb598806
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00011.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00012.png b/res/drawable/launcheranimatedarrow_00012.png
new file mode 100644
index 000000000..58070de06
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00012.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00013.png b/res/drawable/launcheranimatedarrow_00013.png
new file mode 100644
index 000000000..810d0a229
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00013.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00014.png b/res/drawable/launcheranimatedarrow_00014.png
new file mode 100644
index 000000000..3f9e51861
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00014.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00015.png b/res/drawable/launcheranimatedarrow_00015.png
new file mode 100644
index 000000000..348bfbbc6
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00015.png
Binary files differ
diff --git a/res/drawable/launcheranimatedarrow_00016.png b/res/drawable/launcheranimatedarrow_00016.png
new file mode 100644
index 000000000..5b0b28649
--- /dev/null
+++ b/res/drawable/launcheranimatedarrow_00016.png
Binary files differ
diff --git a/res/drawable/listitem_bg.xml b/res/drawable/listitem_bg.xml
new file mode 100644
index 000000000..55dbfcf27
--- /dev/null
+++ b/res/drawable/listitem_bg.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_enabled="true"
+ android:state_pressed="true" android:drawable="@android:color/white" />
+ <item android:drawable="@color/settings_bg_color" />
+</selector>
diff --git a/res/drawable/listitem_selector.xml b/res/drawable/listitem_selector.xml
new file mode 100644
index 000000000..1a1e93d53
--- /dev/null
+++ b/res/drawable/listitem_selector.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_enabled="true"
+ android:state_pressed="true" android:drawable="@android:color/white" />
+ <item android:drawable="@color/settings_bg_color" />
+</selector>
diff --git a/res/drawable/listitem_text.xml b/res/drawable/listitem_text.xml
new file mode 100644
index 000000000..d6c2503a6
--- /dev/null
+++ b/res/drawable/listitem_text.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android" >
+ <item android:state_enabled="true"
+ android:state_pressed="true" android:color="@color/settings_bg_color" />
+ <item android:color="@android:color/white" />
+</selector>
diff --git a/res/drawable/scrubber_back.xml b/res/drawable/scrubber_back.xml
new file mode 100644
index 000000000..c5022dec5
--- /dev/null
+++ b/res/drawable/scrubber_back.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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">
+ <solid android:color="@color/scrubber_background"/>
+</shape>
diff --git a/res/drawable/seek_back.xml b/res/drawable/seek_back.xml
new file mode 100644
index 000000000..d97a870ea
--- /dev/null
+++ b/res/drawable/seek_back.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="360dp"
+ android:height="48dp"
+ android:viewportWidth="360"
+ android:viewportHeight="48">
+
+ <path
+ android:strokeColor="#FFFFFF"
+ android:strokeWidth="2"
+ android:strokeMiterLimit="10"
+ android:strokeLineCap="round"
+ android:pathData="M16,24h328" />
+</vector> \ No newline at end of file
diff --git a/res/drawable/transition_arrow.xml b/res/drawable/transition_arrow.xml
new file mode 100644
index 000000000..540db93d8
--- /dev/null
+++ b/res/drawable/transition_arrow.xml
@@ -0,0 +1,21 @@
+<animation-list
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/transition_arrow" android:oneshot="true">
+ <item android:drawable="@drawable/launcheranimatedarrow_00000" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00001" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00002" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00003" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00004" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00005" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00006" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00007" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00008" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00009" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00010" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00011" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00012" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00013" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00014" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00015" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00016" android:duration="30" />
+</animation-list>
diff --git a/res/drawable/transition_arrow_reverse.xml b/res/drawable/transition_arrow_reverse.xml
new file mode 100644
index 000000000..855f08fb4
--- /dev/null
+++ b/res/drawable/transition_arrow_reverse.xml
@@ -0,0 +1,21 @@
+<animation-list
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/transition_arrow_reverse" android:oneshot="true">
+ <item android:drawable="@drawable/launcheranimatedarrow_00016" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00015" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00014" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00013" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00012" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00011" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00010" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00009" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00008" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00007" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00006" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00005" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00004" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00003" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00002" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00001" android:duration="30" />
+ <item android:drawable="@drawable/launcheranimatedarrow_00000" android:duration="30" />
+</animation-list>
diff --git a/res/drawable/triangle_icon.png b/res/drawable/triangle_icon.png
new file mode 100644
index 000000000..209cf851e
--- /dev/null
+++ b/res/drawable/triangle_icon.png
Binary files differ
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 6500ebcd2..5508f1374 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -18,10 +18,29 @@
<com.android.launcher3.LauncherRootView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ xmlns:insettable="http://schemas.android.com/apk/res-auto"
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:background="@drawable/workspace_bg">
+
+ <FrameLayout
+ android:id="@+id/reveal_fake_page_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:alpha="1.0"
+ insettable:layout_ignoreInsets="true"
+ android:clipToPadding="false">
+
+ <ImageView
+ android:id="@+id/reveal_fake_folder_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:background="@drawable/folder_bg_opaque"/>
+ </FrameLayout>
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
@@ -57,6 +76,13 @@
android:id="@+id/overview_panel"
android:visibility="gone" />
+ <include
+ android:id="@+id/page_indicator"
+ layout="@layout/page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"/>
+
<include layout="@layout/widgets_view"
android:id="@+id/widgets_view"
android:layout_width="match_parent"
diff --git a/res/layout-land/migration_cling.xml b/res/layout-land/migration_cling.xml
index 269c1aee6..82bba0d72 100644
--- a/res/layout-land/migration_cling.xml
+++ b/res/layout-land/migration_cling.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:layout_ignoreInsets="true"
- android:background="#FF009688"
+ android:background="@color/migration_cling_background_color"
android:baselineAligned="false"
android:gravity="center_vertical" >
@@ -56,7 +56,7 @@
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:text="@string/first_run_cling_title"
- android:textColor="#E1000000"
+ android:textColor="@color/first_run_cling_title_color"
android:textSize="34sp" />
<TextView
@@ -64,7 +64,7 @@
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_title"
- android:textColor="#E1000000"
+ android:textColor="@color/first_run_cling_title_color"
android:textSize="20sp" />
<TextView
@@ -72,7 +72,7 @@
android:layout_height="wrap_content"
android:paddingBottom="24dp"
android:text="@string/migration_cling_description"
- android:textColor="#99000000"
+ android:textColor="@color/migration_cling_description_text_color"
android:textSize="16sp" />
<LinearLayout
@@ -87,7 +87,7 @@
android:layout_weight="1"
android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_copy_apps"
- android:textColor="#FFFFFFFF"
+ android:textColor="@color/migration_cling_copy_apps_text_color"
android:textSize="14sp" />
<Button
@@ -98,7 +98,7 @@
android:layout_weight="1"
android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_use_default"
- android:textColor="#deFFFFFF"
+ android:textColor="@color/migration_cling_use_default_text_color"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index d0772ee70..cbdb00103 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -18,11 +18,30 @@
<com.android.launcher3.LauncherRootView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto"
+ xmlns:insettable="http://schemas.android.com/apk/res-auto"
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:background="@drawable/workspace_bg">
+
+ <FrameLayout
+ android:id="@+id/reveal_fake_page_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:alpha="1.0"
+ insettable:layout_ignoreInsets="true"
+ android:clipToPadding="false">
+
+ <ImageView
+ android:id="@+id/reveal_fake_folder_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:background="@drawable/folder_bg_opaque"/>
+ </FrameLayout>
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
diff --git a/res/layout-port/migration_cling.xml b/res/layout-port/migration_cling.xml
index 3f696a216..dc54bc8e5 100644
--- a/res/layout-port/migration_cling.xml
+++ b/res/layout-port/migration_cling.xml
@@ -20,7 +20,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:layout_ignoreInsets="true"
- android:background="#FF009688" >
+ android:background="@color/migration_cling_background_color" >
<RelativeLayout
android:layout_width="match_parent"
@@ -58,7 +58,7 @@
android:layout_height="wrap_content"
android:paddingBottom="8dp"
android:text="@string/first_run_cling_title"
- android:textColor="#E1000000"
+ android:textColor="@color/first_run_cling_title_color"
android:textSize="34sp" />
<TextView
@@ -66,7 +66,7 @@
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_title"
- android:textColor="#E1000000"
+ android:textColor="@color/first_run_cling_title_color"
android:textSize="20sp" />
<TextView
@@ -74,7 +74,7 @@
android:layout_height="wrap_content"
android:paddingBottom="24dp"
android:text="@string/migration_cling_description"
- android:textColor="#99000000"
+ android:textColor="@color/migration_cling_description_text_color"
android:textSize="16sp" />
<LinearLayout
@@ -89,7 +89,7 @@
android:layout_weight="1"
android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_copy_apps"
- android:textColor="#FFFFFFFF"
+ android:textColor="@color/migration_cling_copy_apps_text_color"
android:textSize="14sp" />
<Button
@@ -100,7 +100,7 @@
android:layout_weight="1"
android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_use_default"
- android:textColor="#deFFFFFF"
+ android:textColor="@color/migration_cling_use_default_text_color"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 802922ec1..c448e760c 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -21,7 +21,24 @@
android:id="@+id/launcher"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:fitsSystemWindows="true">
+ android:fitsSystemWindows="true"
+ android:background="@drawable/workspace_bg">
+
+ <FrameLayout
+ android:id="@+id/reveal_fake_page_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:alpha="1.0"
+ android:clipToPadding="false">
+
+ <ImageView
+ android:id="@+id/reveal_fake_folder_icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:visibility="invisible"
+ android:background="@drawable/folder_bg_opaque"/>
+ </FrameLayout>
<com.android.launcher3.DragLayer
android:id="@+id/drag_layer"
diff --git a/res/layout/all_apps_container.xml b/res/layout/all_apps_container.xml
index 626edafab..3a2c96cd0 100644
--- a/res/layout/all_apps_container.xml
+++ b/res/layout/all_apps_container.xml
@@ -35,4 +35,13 @@
android:focusable="true"
android:descendantFocusability="afterDescendants" />
+ <ViewStub
+ android:id="@+id/scrubber_container_stub"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inflatedId="@+id/scrubber_container"
+ android:layout="@layout/scrubber_container"
+ android:layout_gravity="bottom"
+ android:clipToPadding="false"/>
+
</com.android.launcher3.allapps.AllAppsRecyclerViewContainerView> \ No newline at end of file
diff --git a/res/layout/all_apps_empty_search.xml b/res/layout/all_apps_empty_search.xml
index 5439111a2..c4e91844c 100644
--- a/res/layout/all_apps_empty_search.xml
+++ b/res/layout/all_apps_empty_search.xml
@@ -25,7 +25,7 @@
android:paddingRight="16dp"
android:fontFamily="sans-serif-medium"
android:textSize="14sp"
- android:textColor="#212121"
+ android:textColor="@color/all_apps_empty_search_text_color"
android:alpha="0.56"
android:focusable="false" />
diff --git a/res/layout/all_apps_prediction_bar_icon.xml b/res/layout/all_apps_prediction_bar_icon.xml
index 341d8ef4f..0985e95c0 100644
--- a/res/layout/all_apps_prediction_bar_icon.xml
+++ b/res/layout/all_apps_prediction_bar_icon.xml
@@ -21,8 +21,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
- android:paddingTop="@dimen/all_apps_prediction_icon_top_padding"
- android:paddingBottom="@dimen/all_apps_prediction_icon_bottom_padding"
+ android:paddingTop="@dimen/all_apps_icon_top_bottom_padding"
+ android:paddingBottom="@dimen/all_apps_icon_top_bottom_padding"
android:focusable="true"
android:background="@drawable/focusable_view_bg"
launcher:iconDisplay="all_apps" />
diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml
index 69a66c817..7ed1b1f21 100644
--- a/res/layout/all_apps_search_bar.xml
+++ b/res/layout/all_apps_search_bar.xml
@@ -52,8 +52,8 @@
android:paddingLeft="8dp"
android:scrollHorizontally="true"
android:singleLine="true"
- android:textColor="#4c4c4c"
- android:textColorHint="#9c9c9c"
+ android:textColor="@color/search_box_input_text_color"
+ android:textColorHint="@color/search_box_input_hint_text_color"
android:textSize="16sp" />
</LinearLayout>
diff --git a/res/layout/custom_grid_size_dialog.xml b/res/layout/custom_grid_size_dialog.xml
new file mode 100644
index 000000000..59a5db484
--- /dev/null
+++ b/res/layout/custom_grid_size_dialog.xml
@@ -0,0 +1,48 @@
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:id="@+id/grid_number_pickers"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:orientation="horizontal">
+
+ <NumberPicker
+ android:id="@+id/custom_rows"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="@dimen/dialog_padding"/>
+
+ <NumberPicker
+ android:id="@+id/custom_columns"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="@dimen/dialog_padding"/>
+
+ </LinearLayout>
+
+ <Button
+ android:id="@+id/dialog_confirm_button"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/dialog_confirm"
+ android:layout_below="@id/grid_number_pickers"
+ android:layout_marginTop="@dimen/dialog_padding"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/custom_predicted_apps_footer.xml b/res/layout/custom_predicted_apps_footer.xml
new file mode 100644
index 000000000..14c171ee6
--- /dev/null
+++ b/res/layout/custom_predicted_apps_footer.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+</merge> \ No newline at end of file
diff --git a/res/layout/custom_predicted_apps_header.xml b/res/layout/custom_predicted_apps_header.xml
new file mode 100644
index 000000000..14c171ee6
--- /dev/null
+++ b/res/layout/custom_predicted_apps_header.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+</merge> \ No newline at end of file
diff --git a/res/layout/dynamic_grid_size_screen.xml b/res/layout/dynamic_grid_size_screen.xml
new file mode 100644
index 000000000..85dc992fb
--- /dev/null
+++ b/res/layout/dynamic_grid_size_screen.xml
@@ -0,0 +1,69 @@
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.InsettableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:insettable="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:background="@color/settings_bg_color"
+ android:orientation="vertical"
+ android:clickable="true" >
+
+ <LinearLayout
+ android:id="@+id/dynamic_grid_title"
+ insettable:layout_ignoreBottomInsets="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@drawable/listitem_bg"
+ android:clickable="true" >
+
+ <ImageView
+ android:id="@+id/nav_prev"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:adjustViewBounds="true"
+ android:src="@drawable/ic_navigation_prev" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/grid_size_text"
+ android:textAllCaps="true"
+ android:fontFamily="sans-serif-condensed"
+ android:textColor="@drawable/listitem_text"
+ android:layout_gravity="center_vertical"
+ android:textSize="16sp" />
+ </LinearLayout>
+
+ <view
+ insettable:layout_ignoreInsets="true"
+ class="com.android.launcher3.DynamicGridSizeFragment$GridSizeView"
+ android:id="@+id/dynamic_grid_size_image"
+ android:layout_width="150dp"
+ android:layout_height="150dp"
+ android:layout_gravity="center_horizontal"
+ android:layout_marginBottom="@dimen/grid_padding"/>
+
+ <ListView
+ insettable:layout_ignoreTopInsets="true"
+ android:id="@+id/dynamic_grid_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:listSelector="@android:color/transparent"
+ android:splitMotionEvents="false"/>
+</com.android.launcher3.InsettableLinearLayout>
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 237af6890..5e29a1a0d 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -20,17 +20,53 @@
android:layout_height="match_parent"
android:orientation="vertical"
android:focusable="true" >
- <ImageView
+ <RelativeLayout
android:id="@+id/preview_background"
- android:layout_gravity="center_horizontal"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:antialias="true"
- android:src="@drawable/portal_ring_inner"/>
+ android:layout_width="@dimen/folder_icon"
+ android:layout_height="@dimen/folder_icon"
+ android:layout_gravity="center_horizontal|top"
+ android:background="@drawable/folder_bg" >
+ <ImageView
+ android:id="@+id/folder_lock_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/folder_locked"
+ android:scaleType="center"
+ android:visibility="invisible" />
+ <ImageView
+ android:id="@+id/app_0"
+ android:layout_width="@dimen/folder_icon_app_preview"
+ android:layout_height="@dimen/folder_icon_app_preview"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentTop="true"
+ android:antialias="true"/>
+ <ImageView
+ android:id="@+id/app_1"
+ android:layout_width="@dimen/folder_icon_app_preview"
+ android:layout_height="@dimen/folder_icon_app_preview"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:antialias="true"/>
+ <ImageView
+ android:id="@+id/app_2"
+ android:layout_width="@dimen/folder_icon_app_preview"
+ android:layout_height="@dimen/folder_icon_app_preview"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentBottom="true"
+ android:antialias="true"/>
+ <ImageView
+ android:id="@+id/app_3"
+ android:layout_width="@dimen/folder_icon_app_preview"
+ android:layout_height="@dimen/folder_icon_app_preview"
+ android:layout_alignParentBottom="true"
+ android:layout_alignParentRight="true"
+ android:antialias="true"/>
+ </RelativeLayout>
+
<com.android.launcher3.BubbleTextView
style="@style/Icon"
android:id="@+id/folder_icon_name"
- android:layout_gravity="top"
+ android:layout_gravity="bottom"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.android.launcher3.FolderIcon>
diff --git a/res/layout/hidden_apps_list.xml b/res/layout/hidden_apps_list.xml
new file mode 100644
index 000000000..a639ae094
--- /dev/null
+++ b/res/layout/hidden_apps_list.xml
@@ -0,0 +1,22 @@
+<?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.
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@android:id/list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="5dp"
+ android:choiceMode="multipleChoice" />
diff --git a/res/layout/hidden_apps_list_item.xml b/res/layout/hidden_apps_list_item.xml
new file mode 100644
index 000000000..7061b35c3
--- /dev/null
+++ b/res/layout/hidden_apps_list_item.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<com.android.launcher3.widget.CheckableLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="48dp"
+ android:gravity="center_vertical"
+ android:paddingRight="?android:attr/scrollbarSize"
+ android:background="?android:attr/selectableItemBackground"
+ android:descendantFocusability="blocksDescendants">
+
+ <ImageView android:id="@+id/icon"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_gravity="center" />
+
+ <TextView android:id="@+id/title"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="15dip"
+ android:layout_marginRight="6dip"
+ android:layout_marginTop="6dip"
+ android:layout_marginBottom="6dip"
+ android:layout_weight="1"
+ android:singleLine="true"
+ android:textAppearance="?android:attr/textAppearanceMedium"
+ android:ellipsize="marquee"
+ android:fadingEdge="horizontal" />
+
+ <com.android.launcher3.widget.InertCheckBox android:id="@+id/checkbox"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+</com.android.launcher3.widget.CheckableLinearLayout>
diff --git a/res/layout/longpress_cling_content.xml b/res/layout/longpress_cling_content.xml
index 47a8e9797..1cda55b2f 100644
--- a/res/layout/longpress_cling_content.xml
+++ b/res/layout/longpress_cling_content.xml
@@ -12,7 +12,7 @@
android:paddingLeft="36dp"
android:paddingRight="36dp"
android:text="@string/workspace_cling_longpress_title"
- android:textColor="#E1000000"
+ android:textColor="@color/workspace_cling_longpress_title_color"
android:textSize="24sp" />
<TextView
@@ -22,7 +22,7 @@
android:paddingLeft="36dp"
android:paddingRight="36dp"
android:text="@string/workspace_cling_longpress_description"
- android:textColor="#99000000"
+ android:textColor="@color/workspace_cling_longpress_description_color"
android:textSize="16sp" />
<Button
@@ -37,7 +37,7 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:text="@string/workspace_cling_longpress_dismiss"
- android:textColor="#FFFFFFFF"
+ android:textColor="@color/workspace_cling_longpress_dismiss_text_color"
android:textSize="14sp" />
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/longpress_cling_welcome_content.xml b/res/layout/longpress_cling_welcome_content.xml
index dd4f8d767..ede489f53 100644
--- a/res/layout/longpress_cling_welcome_content.xml
+++ b/res/layout/longpress_cling_welcome_content.xml
@@ -13,7 +13,7 @@
android:paddingLeft="36dp"
android:paddingRight="36dp"
android:text="@string/first_run_cling_title"
- android:textColor="#E1000000"
+ android:textColor="@color/first_run_cling_title_color"
android:textSize="34sp" />
<TextView
@@ -24,7 +24,7 @@
android:paddingLeft="36dp"
android:paddingRight="36dp"
android:text="@string/workspace_cling_longpress_title"
- android:textColor="#E1000000"
+ android:textColor="@color/workspace_cling_longpress_title_color"
android:textSize="20sp" />
<TextView
@@ -33,7 +33,7 @@
android:paddingLeft="36dp"
android:paddingRight="36dp"
android:text="@string/workspace_cling_longpress_description"
- android:textColor="#99000000"
+ android:textColor="@color/workspace_cling_longpress_description_color"
android:textSize="16sp" />
<Button
@@ -48,7 +48,7 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:text="@string/workspace_cling_longpress_dismiss"
- android:textColor="#FFFFFFFF"
+ android:textColor="@color/workspace_cling_longpress_dismiss_text_color"
android:textSize="14sp" />
</LinearLayout> \ No newline at end of file
diff --git a/res/layout/overview_panel.xml b/res/layout/overview_panel.xml
index 1f02dce3c..fcf50c99f 100644
--- a/res/layout/overview_panel.xml
+++ b/res/layout/overview_panel.xml
@@ -14,53 +14,130 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:gravity="top"
- android:orientation="horizontal" >
-
- <TextView
- android:id="@+id/wallpaper_button"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawablePadding="4dp"
- android:drawableTop="@drawable/wallpaper_button"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:text="@string/wallpaper_button_text"
- android:textAllCaps="true"
- android:textColor="@android:color/white"
- android:textSize="12sp" />
-
- <TextView
- android:id="@+id/widget_button"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawablePadding="4dp"
- android:drawableTop="@drawable/widget_button"
- android:fontFamily="sans-serif-condensed"
- android:gravity="center_horizontal"
- android:text="@string/widget_button_text"
- android:textAllCaps="true"
- android:textColor="@android:color/white"
- android:textSize="12sp" />
-
- <TextView
- android:id="@+id/settings_button"
- android:layout_width="0dp"
+<com.android.launcher3.OverviewPanel xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:slidingpanel="http://schemas.android.com/apk/res/com.android.launcher3"
+ android:id="@+id/sliding_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="bottom"
+ slidingpanel:overlay="false"
+ slidingpanel:panelHeight="@dimen/sliding_panel_padding" >
+
+ <LinearLayout
+ android:id="@+id/default_home_screen_panel"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:drawablePadding="4dp"
- android:drawableTop="@drawable/setting_button"
- android:fontFamily="sans-serif-condensed"
- 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
+ android:layout_gravity="center_horizontal"
+ android:background="@color/settings_bg_color"
+ android:paddingTop="@dimen/overview_panel_top_padding" >
+
+ <ImageView
+ android:id="@+id/default_screen_button"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/app_icon_size"
+ android:layout_weight="1"
+ android:src="@drawable/default_screen_button"
+ android:scaleType="fitCenter"
+ android:gravity="top"
+ android:paddingLeft="@dimen/overview_panel_button_spacing"
+ android:paddingRight="@dimen/overview_panel_button_spacing"/>
+ </LinearLayout>
+
+ <FrameLayout android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:background="@color/settings_bg_color"
+ android:layout_height="match_parent" >
+
+ <LinearLayout
+ android:id="@+id/settings_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center_horizontal|bottom"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:id="@+id/settings_pane_header"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:paddingTop="@dimen/overview_panel_top_padding" >
+
+ <ImageView
+ android:id="@+id/settings_drag_arrow"
+ android:layout_width="@dimen/overview_panel_button_spacing"
+ android:layout_height="@dimen/overview_panel_button_spacing"
+ android:layout_gravity="center_horizontal"
+ android:adjustViewBounds="true"
+ android:background="@drawable/launcheranimatedarrow_00000"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:paddingBottom="@dimen/overview_panel_bottom_padding"
+ android:paddingTop="@dimen/overview_panel_list_padding" >
+
+ <TextView
+ android:id="@+id/wallpaper_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawablePadding="4dp"
+ android:drawableTop="@drawable/wallpaper_button"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:text="@string/wallpaper_button_text"
+ android:textAllCaps="true"
+ android:textColor="@color/wallpaper_button_text_color"
+ android:textSize="12sp" />
+
+ <TextView
+ android:id="@+id/widget_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawablePadding="4dp"
+ android:drawableTop="@drawable/widget_button"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:text="@string/widget_button_text"
+ android:textAllCaps="true"
+ android:textColor="@color/widget_button_text_color"
+ android:textSize="12sp" />
+
+ <TextView
+ android:id="@+id/settings_button"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:drawablePadding="4dp"
+ android:drawableTop="@drawable/setting_button"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="center_horizontal"
+ android:text="@string/settings_button_text"
+ android:textAllCaps="true"
+ android:textColor="@color/settings_button_text_color"
+ android:textSize="12sp" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <view
+ class="com.android.launcher3.list.PinnedHeaderListView"
+ android:id="@+id/settings_home_screen_listview"
+ android:layout_width="match_parent"
+ android:layout_height="0dip"
+ android:fastScrollEnabled="true"
+ android:layout_weight="1" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/dark_panel"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/overview_panel_background_color"
+ android:visibility="gone" />
+ </FrameLayout>
+</com.android.launcher3.OverviewPanel> \ No newline at end of file
diff --git a/res/layout/remote_folder.xml b/res/layout/remote_folder.xml
new file mode 100644
index 000000000..49d47292d
--- /dev/null
+++ b/res/layout/remote_folder.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+</merge> \ No newline at end of file
diff --git a/res/layout/scrub_layout.xml b/res/layout/scrub_layout.xml
new file mode 100644
index 000000000..987a2f2b6
--- /dev/null
+++ b/res/layout/scrub_layout.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/app_drawer_scrubber_container"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/scrubber_height"
+ android:paddingLeft="@dimen/app_drawer_scrubber_padding"
+ android:paddingRight="@dimen/app_drawer_scrubber_padding"
+ android:layout_alignParentBottom="true"
+ android:background="@drawable/seek_back">
+
+ <com.android.launcher3.AutoExpandTextView
+ android:id="@+id/scrubberText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textColor="@color/scrubber_text_color"
+ android:maxLines="1"
+ android:lines="1"
+ android:textSize="8sp" />
+
+ <SeekBar
+ android:id="@+id/scrubber"
+ android:thumb="@android:color/transparent"
+ android:progressDrawable="@android:color/transparent"
+ android:layout_width="match_parent"
+ android:layout_gravity="center"
+ android:layout_height="match_parent" />
+
+</FrameLayout>
diff --git a/res/layout/scrubber_container.xml b/res/layout/scrubber_container.xml
new file mode 100644
index 000000000..28224114a
--- /dev/null
+++ b/res/layout/scrubber_container.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/scrubber_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:clipToPadding="false">
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:layout_below="@+id/scrubberIndicator"
+ android:background="@drawable/scrubber_back"
+ android:clipToPadding="false">
+
+ <com.android.launcher3.BaseRecyclerViewScrubber
+ android:id="@+id/base_scrubber"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clickable="true"
+ android:layout_gravity="bottom"/>
+ </FrameLayout>
+
+ <TextView
+ android:id="@+id/scrubberIndicator"
+ android:background="@drawable/letter_indicator"
+ android:layout_width="100dp"
+ android:layout_marginLeft="@dimen/app_drawer_scrubber_padding"
+ android:layout_marginRight="@dimen/app_drawer_scrubber_padding"
+ android:paddingTop="18dp"
+ android:textSize="24sp"
+ android:gravity="center_horizontal|top"
+ android:textColor="@color/scrubber_container_text_color"
+ android:clickable="false"
+ android:layout_marginBottom="-40dp"
+ android:visibility="invisible"
+ android:layout_height="100dp" />
+</RelativeLayout>
diff --git a/res/layout/settings_pane_list_header.xml b/res/layout/settings_pane_list_header.xml
new file mode 100644
index 000000000..8c35fb865
--- /dev/null
+++ b/res/layout/settings_pane_list_header.xml
@@ -0,0 +1,34 @@
+<!--
+ Copyright (C) 2015 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/overview_panel_list_header_height"
+ android:layout_gravity="center_horizontal|bottom"
+ android:background="@color/settings_bg_color"
+ android:paddingLeft="@dimen/overview_panel_list_padding"
+ android:paddingRight="@dimen/overview_panel_list_padding"
+ android:orientation="horizontal" >
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawablePadding="4dp"
+ android:fontFamily="sans-serif-medium"
+ android:gravity="left"
+ android:textSize="14sp"
+ android:textColor="@color/settings_header_text"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/settings_pane_list_item.xml b/res/layout/settings_pane_list_item.xml
new file mode 100644
index 000000000..6c8c3c719
--- /dev/null
+++ b/res/layout/settings_pane_list_item.xml
@@ -0,0 +1,46 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/overview_panel_list_item_height"
+ android:layout_gravity="center_horizontal|bottom"
+ android:background="@drawable/listitem_bg"
+ android:paddingStart="@dimen/overview_panel_list_padding"
+ android:paddingEnd="@dimen/overview_panel_list_padding"
+ android:paddingBottom="@dimen/overview_panel_list_padding"
+ android:paddingTop="@dimen/overview_panel_list_padding"
+ android:orientation="horizontal" >
+
+ <Switch
+ android:id="@+id/setting_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerVertical="true"
+ android:layout_alignParentEnd="true"
+ android:visibility="invisible"/>
+
+ <TextView
+ android:id="@+id/item_state"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:drawablePadding="4dp"
+ android:fontFamily="sans-serif"
+ android:textSize="16sp"
+ android:textAllCaps="true"
+ android:textColor="@drawable/listitem_text"
+ android:layout_alignParentEnd="true"
+ android:ellipsize="end"
+ android:maxLines="1"/>
+
+ <TextView
+ android:id="@+id/item_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentRight="true"
+ android:drawablePadding="4dp"
+ android:fontFamily="sans-serif"
+ android:textSize="16sp"
+ android:textColor="@drawable/listitem_text"
+ android:layout_toStartOf="@id/item_state"
+ android:layout_alignParentStart="true"
+ android:ellipsize="end"
+ android:maxLines="1"/>
+</RelativeLayout> \ No newline at end of file
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 252ebf01e..516da303c 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -18,14 +18,20 @@
xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:background="@drawable/quantum_panel"
+ android:background="@drawable/folder_bg"
android:elevation="5dp"
- android:orientation="vertical" >
+ android:orientation="vertical"
+ android:layout_margin="@dimen/folder_margin"
+ android:layout_gravity="bottom|center_horizontal">
+
+ <include layout="@layout/remote_folder"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/folder_content_wrapper"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
<!-- Actual size of the indicator doesn't matter as it is scaled to match the view size -->
@@ -36,8 +42,8 @@
<com.android.launcher3.FolderPagedView
android:id="@+id/folder_content"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:paddingTop="8dp"
@@ -67,9 +73,9 @@
android:paddingBottom="8dp"
android:paddingTop="4dp"
android:singleLine="true"
- android:textColor="#ff777777"
- android:textColorHighlight="#ffCCCCCC"
- android:textColorHint="#ff808080"
+ android:textColor="@color/workspace_icon_text_color"
+ android:textColorHighlight="@color/folder_name_highlight_text_color"
+ android:textColorHint="@color/folder_name_hint_text_color"
android:textSize="14sp" />
<include
@@ -79,6 +85,14 @@
android:layout_gravity="center_vertical"
layout="@layout/page_indicator" />
+ <ImageView
+ android:id="@+id/folder_lock"
+ android:layout_width="@dimen/folder_lock_icon"
+ android:layout_height="@dimen/folder_lock_icon"
+ android:padding="@dimen/folder_name_padding"
+ android:layout_gravity="center_vertical"
+ android:src="@drawable/folder_unlocked" />
+
</LinearLayout>
-</com.android.launcher3.Folder> \ No newline at end of file
+</com.android.launcher3.Folder>
diff --git a/res/layout/widgets_view.xml b/res/layout/widgets_view.xml
index 755634f82..5d9702158 100644
--- a/res/layout/widgets_view.xml
+++ b/res/layout/widgets_view.xml
@@ -21,8 +21,7 @@
android:id="@+id/widgets_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:descendantFocusability="afterDescendants">
+ android:orientation="vertical">
<FrameLayout
android:id="@+id/content"
@@ -37,15 +36,33 @@
android:elevation="2dp"
android:visibility="invisible" />
- <!-- DO NOT CHANGE THE ID -->
- <com.android.launcher3.widget.WidgetsRecyclerView
- android:id="@+id/widgets_list_view"
- android:theme="@style/Theme.Dark.CustomOverscroll"
+ <FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
android:elevation="15dp"
- android:visibility="gone" />
+ android:focusable="true"
+ android:focusableInTouchMode="true">
+
+ <!-- DO NOT CHANGE THE ID -->
+ <com.android.launcher3.widget.WidgetsRecyclerView
+ android:id="@+id/widgets_list_view"
+ android:theme="@style/Theme.Dark.CustomOverscroll"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center"
+ android:visibility="gone"
+ android:descendantFocusability="afterDescendants"/>
+
+ <ViewStub
+ android:id="@+id/scrubber_container_stub"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inflatedId="@+id/scrubber_container"
+ android:layout="@layout/scrubber_container"
+ android:layout_gravity="bottom"
+ android:clipToPadding="false"/>
+
+ </FrameLayout>
</FrameLayout>
</com.android.launcher3.widget.WidgetsContainerView> \ No newline at end of file
diff --git a/res/mipmap-hdpi/ic_launcher_home.png b/res/mipmap-hdpi/ic_launcher_home.png
index b556d7aba..dc40251c2 100644
--- a/res/mipmap-hdpi/ic_launcher_home.png
+++ b/res/mipmap-hdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_home.png b/res/mipmap-mdpi/ic_launcher_home.png
index 961bb7dbd..e2625e431 100644
--- a/res/mipmap-mdpi/ic_launcher_home.png
+++ b/res/mipmap-mdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_home.png b/res/mipmap-xhdpi/ic_launcher_home.png
index 46ec2b775..70e72523c 100644
--- a/res/mipmap-xhdpi/ic_launcher_home.png
+++ b/res/mipmap-xhdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_home.png b/res/mipmap-xxhdpi/ic_launcher_home.png
index d2975a39f..9652a687f 100644
--- a/res/mipmap-xxhdpi/ic_launcher_home.png
+++ b/res/mipmap-xxhdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/ic_launcher_home.png b/res/mipmap-xxxhdpi/ic_launcher_home.png
new file mode 100644
index 000000000..652593d3f
--- /dev/null
+++ b/res/mipmap-xxxhdpi/ic_launcher_home.png
Binary files differ
diff --git a/res/values-af/cm_strings.xml b/res/values-af/cm_strings.xml
new file mode 100644
index 000000000..42ef7a917
--- /dev/null
+++ b/res/values-af/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">TUIS SKERM INSTELLINGS</string>
+ <string name="drawer_settings">LAAI INSTELLINGS</string>
+ <string name="app_settings">PROGRAM INSTELLINGS</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">AAN</string>
+ <string name="setting_state_off">AF</string>
+ <string name="setting_state_disabled">Gedeaktiveer</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Rol agtergrond</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rooster grootte</string>
+ <string name="grid_size_comfortable">Gemaklik</string>
+ <string name="grid_size_cozy">Snoesig</string>
+ <string name="grid_size_condensed">Verkorte</string>
+ <string name="grid_size_custom">Persoonlik (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Kies persoonlike grootte</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bevestig</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Soekbalk</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Groter ikone</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikoon etikette</string>
+ <string name="icon_labels_show">Vertoon</string>
+ <string name="icon_labels_hide">Wegsteek</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Beskermde programme</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Laai styl</string>
+ <string name="app_drawer_style_compact">Kompak</string>
+ <string name="app_drawer_style_sections">Seksies</string>
+ <string name="app_drawer_color">Laai kleur</string>
+ <string name="app_drawer_color_dark">Donker</string>
+ <string name="app_drawer_color_light">Lig</string>
+ <string name="fast_scroller">Vinnige blaai</string>
+ <string name="fast_scroller_type">Vinnige blaai tipe</string>
+ <string name="fast_scroller_type_horizontal">Horisontaal</string>
+ <string name="fast_scroller_type_vertical">Vertikaal</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">\'n Soektog aktiwiteit kon nie gevind word nie!</string>
+</resources>
diff --git a/res/values-am/cm_strings.xml b/res/values-am/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-am/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ar/cm_strings.xml b/res/values-ar/cm_strings.xml
new file mode 100644
index 000000000..246e3634a
--- /dev/null
+++ b/res/values-ar/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">إعدادات الشاشة الرئيسية</string>
+ <string name="drawer_settings">إعدادات أداة السحب</string>
+ <string name="app_settings">إعدادات التطبيق</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">تشغيل</string>
+ <string name="setting_state_off">إيقاف</string>
+ <string name="setting_state_disabled">معطل</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">تشغيل</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">تمرير خلفية الشاشة</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">حجم الشبكة</string>
+ <string name="grid_size_comfortable">مريح</string>
+ <string name="grid_size_cozy">مريح</string>
+ <string name="grid_size_condensed">مكثف</string>
+ <string name="grid_size_custom">مخصص (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">حدد حجم مخصص</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">تأكيد</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">شريط البحث</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">أيقونات أكبر</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">تسميات الأيقونة</string>
+ <string name="icon_labels_show">إظهار</string>
+ <string name="icon_labels_hide">إخفاء</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">التطبيقات المحمية</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">رأسي</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">لا يمكن العثور على نشاط بحثي!</string>
+</resources>
diff --git a/res/values-as-rIN/cm_strings.xml b/res/values-as-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-as-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ast-rES/cm_strings.xml b/res/values-ast-rES/cm_strings.xml
new file mode 100644
index 000000000..8ce7bb330
--- /dev/null
+++ b/res/values-ast-rES/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">AXUSTES DE PANTALLA D\'ANICIU</string>
+ <string name="drawer_settings">AXUSTES DE LES APLICACIONES</string>
+ <string name="app_settings">AXUSTES D\'APLICACIÓN</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">SÍ</string>
+ <string name="setting_state_off">NON</string>
+ <string name="setting_state_disabled">DESHABILITÁU</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Reproducir</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Desplazar fondu</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Tamañu del rexáu</string>
+ <string name="grid_size_comfortable">Confortable</string>
+ <string name="grid_size_cozy">Acoyedor</string>
+ <string name="grid_size_condensed">Condensáu</string>
+ <string name="grid_size_custom">Personalizar (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Esbilla\'l tamañu personalizáu</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirmar</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barra de gueta</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Iconos grandes</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etiquetes de los iconos</string>
+ <string name="icon_labels_show">Amosar</string>
+ <string name="icon_labels_hide">Anubrir</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Apps protexíes</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">¡Nun pudo atopase un encontu de gueta!</string>
+</resources>
diff --git a/res/values-ast-rES/strings.xml b/res/values-ast-rES/strings.xml
new file mode 100644
index 000000000..657fa7c19
--- /dev/null
+++ b/res/values-ast-rES/strings.xml
@@ -0,0 +1,172 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <string name="app_name">Launcher3</string>
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <string name="activity_not_found">L\'aplicación nun ta instalada.</string>
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <string name="safemode_shortcut_error">Descargar app deshabilitada en Mou Seguru</string>
+ <!-- SafeMode widget error string -->
+ <string name="toggle_weight_watcher">Amosar memoria</string>
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <string name="long_press_widget_to_add">Caltén primíu\'l widget que quieras esbillar.</string>
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <string name="widget_dims_format">%1$d \u00d7 %2$d</string>
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <string name="out_of_space">Nun queda espaciu na pantalla d\'aniciu.</string>
+ <!-- Error message when user has filled the hotseat -->
+ <string name="hotseat_out_of_space">Nun queda espaciu na barra d\'accesos direutos</string>
+ <!-- All applications label -->
+ <string name="all_apps_button_label">Aplicaciones</string>
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <string name="all_apps_home_button_label">Aniciu</string>
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <string name="delete_target_label">Desaniciar</string>
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <string name="delete_target_uninstall_label">Desinstalar</string>
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <string name="info_target_label">Información de l\'aplicación</string>
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <string name="permlab_install_shortcut">instalar accesos direutos</string>
+ <!-- Permission description -->
+ <string name="permdesc_install_shortcut">Permite qu\'una aplicación amieste accesos direutos ensin intervención del usuariu.</string>
+ <!-- Permission short label -->
+ <string name="permlab_read_settings">lleer información d\'accesos direutos y d\'axustes</string>
+ <!-- Permission description -->
+ <string name="permdesc_read_settings">Permite que l\'aplicación consulte los axustes y
+ los accesos direutos de la pantalla d\'aniciu.</string>
+ <!-- Permission short label -->
+ <string name="permlab_write_settings">escribir información d\'atayos y d\'axustes de la pantalla d\'aniciu</string>
+ <!-- Permission description -->
+ <string name="permdesc_write_settings">Permite que les aplicaciones camuden los axustes y los accesos direutos de la pantalla d\'aniciu.</string>
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <string name="gadget_error_text">Fallu al cargar el widget</string>
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <string name="gadget_setup_text">Configuración</string>
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <string name="uninstall_system_app_text">Esta aplicación ye del sistema y nun pue desinstalase.</string>
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <string name="default_scroll_format">Páxina %1$d de %2$d</string>
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <string name="workspace_scroll_format">Pantalla d\'aniciu %1$d de %2$d</string>
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <string name="first_run_cling_title">Dámoste l\'acoyida</string>
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <string name="migration_cling_title">Copia los tos iconos d\'aplicación</string>
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <string name="migration_cling_description">¿Importar los iconos y carpetes de les tos pantalles d\'aniciu vieyes?</string>
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <string name="migration_cling_copy_apps">COPIAR ICONOS</string>
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <string name="migration_cling_use_default">ANICIAR REFRESCU</string>
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <string name="workspace_cling_longpress_title">Fondos pantalla, widgets, &amp; axustes</string>
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <string name="workspace_cling_longpress_description">Toca y caltén fondu pa personalizar</string>
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <string name="workspace_cling_longpress_dismiss">FECHO</string>
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <string name="folder_opened">Carpeta abierta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g></string>
+ <!-- Instruction that clicking outside will close folder -->
+ <string name="folder_tap_to_close">Primi pa zarrar la carpeta</string>
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <string name="folder_tap_to_rename">Primi pa camudar el nome nuevu</string>
+ <!-- Indication that folder closed -->
+ <string name="folder_closed">Carpeta zarrada</string>
+ <!-- Folder renamed format -->
+ <string name="folder_renamed">Camudóse\'l nome la carpeta a <xliff:g id="NAME">%1$s</xliff:g></string>
+ <!-- Folder name format -->
+ <string name="folder_name_format">Carpeta: <xliff:g id="NAME">%1$s</xliff:g></string>
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <string name="debug_memory_activity">* HPROF</string>
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <string name="widget_button_text">Widgets</string>
+ <!-- Text for wallpaper change button -->
+ <string name="wallpaper_button_text">Fondos de pantalla</string>
+ <!-- Text for settings button -->
+ <string name="settings_button_text">Axustes</string>
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <string name="package_state_unknown">Desconocíu</string>
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <string name="abandoned_clean_this">Desaniciar</string>
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <string name="abandoned_search">Guetar</string>
+ <!-- Title for abandoned promise dialog. -->
+ <string name="abandoned_promises_title">Esta app nun ta instalada</string>
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <string name="abandoned_promise_explanation">La app pa esti iconu nun ta instalada.
+ Pues desanicialu, o guetar la app ya instalala manualmente.
+ </string>
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <string name="action_add_to_workspace">Amestar a la pantalla d\'aniciu</string>
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-az-rAZ/cm_strings.xml b/res/values-az-rAZ/cm_strings.xml
new file mode 100644
index 000000000..838f13c33
--- /dev/null
+++ b/res/values-az-rAZ/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ƏSAS EKRAN TƏNZİMLƏMƏLƏRİ</string>
+ <string name="drawer_settings">TƏTBİQETMƏ MENYUSU</string>
+ <string name="app_settings">TƏTBİQETMƏ TƏNZİMLƏMƏLƏRİ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">AÇIQ</string>
+ <string name="setting_state_off">BAĞLI</string>
+ <string name="setting_state_disabled">LƏĞV EDİLDİ</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Divar kağızını sürüşdür</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Tor ölçüsü</string>
+ <string name="grid_size_comfortable">Geniş</string>
+ <string name="grid_size_cozy">Rahat</string>
+ <string name="grid_size_condensed">Sıx</string>
+ <string name="grid_size_custom">Özəl (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Xüsusi ölçü seçin</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Təsdiqlə</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Axtarış sətri</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Böyük nişanlar</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Nişan etiketləri</string>
+ <string name="icon_labels_show">Göstər</string>
+ <string name="icon_labels_hide">Gizlət</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Qorunan tətbiqetmələr</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Şaquli</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Axtarış fəaliyyəti tapılmadı!</string>
+</resources>
diff --git a/res/values-be/cm_strings.xml b/res/values-be/cm_strings.xml
new file mode 100644
index 000000000..7bd7cb5ff
--- /dev/null
+++ b/res/values-be/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ХАТНІ ЭКРАН</string>
+ <string name="drawer_settings">МЕНЮ ДАДАТКАЎ</string>
+ <string name="app_settings">АГУЛЬНЫЯ НАЛАДЫ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">УКЛ.</string>
+ <string name="setting_state_off">АДКЛ.</string>
+ <string name="setting_state_disabled">НЕДАСТУПНА</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Сэрвісы Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Прагортка шпалер</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Памер сеткі</string>
+ <string name="grid_size_comfortable">Прасторная</string>
+ <string name="grid_size_cozy">Зручная</string>
+ <string name="grid_size_condensed">Сціснутая</string>
+ <string name="grid_size_custom">Свая (<xliff:g id="rows">%1$d</xliff:g>\u00d7<xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Абярыце памер сеткі</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Пацвердзіць</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Радок пошуку</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Буйныя значкі</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Подпісы значкоў</string>
+ <string name="icon_labels_show">Паказваць</string>
+ <string name="icon_labels_hide">Хаваць</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Заблакаваныя дадаткі</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Стыль мэню</string>
+ <string name="app_drawer_style_compact">Кампактны</string>
+ <string name="app_drawer_style_sections">Падзелы</string>
+ <string name="app_drawer_color">Колер фону</string>
+ <string name="app_drawer_color_dark">Цёмны</string>
+ <string name="app_drawer_color_light">Светлы</string>
+ <string name="fast_scroller">Хуткая прагортка</string>
+ <string name="fast_scroller_type">Тып хуткай прагорткі</string>
+ <string name="fast_scroller_type_horizontal">Гарызантальны</string>
+ <string name="fast_scroller_type_vertical">Вертыкальнае</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Немагчыма запусціць дадатак пошуку!</string>
+</resources>
diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml
index fc5390b80..d8fc2a04b 100644
--- a/res/values-be/strings.xml
+++ b/res/values-be/strings.xml
@@ -1,5 +1,6 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
/*
* Copyright (C) 2008 The Android Open Source Project
*
@@ -15,192 +16,106 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- no translation found for application_name (5181331383435256801) -->
- <skip />
- <!-- no translation found for home (7658288663002113681) -->
- <skip />
- <!-- no translation found for uid_name (7820867637514617527) -->
- <skip />
- <string name="folder_name" msgid="7371454440695724752"></string>
- <!-- no translation found for wallpaper_instructions (563973358787555519) -->
- <skip />
- <!-- no translation found for image_load_fail (2821429163328561136) -->
- <skip />
- <!-- no translation found for wallpaper_load_fail (1261270681127096352) -->
- <skip />
- <!-- no translation found for number_of_items_selected:zero (7464587177007785408) -->
- <!-- no translation found for number_of_items_selected:one (142482526010824029) -->
- <!-- no translation found for number_of_items_selected:other (1418352074806573570) -->
- <!-- no translation found for wallpaper_accessibility_name (1655953108132967972) -->
- <skip />
- <!-- no translation found for announce_selection (8338254712932127413) -->
- <skip />
- <!-- no translation found for wallpaper_delete (8095005658756613921) -->
- <skip />
- <!-- no translation found for pick_image (1272073934062909527) -->
- <skip />
- <!-- no translation found for pick_wallpaper (8179698221502010609) -->
- <skip />
- <!-- no translation found for crop_wallpaper (8334345984491368009) -->
- <skip />
- <!-- no translation found for activity_not_found (8071924732094499514) -->
- <skip />
- <!-- no translation found for widgets_tab_label (2921133187116603919) -->
- <skip />
- <!-- no translation found for widget_adder (3201040140710381657) -->
- <skip />
- <!-- no translation found for toggle_weight_watcher (5645299835184636119) -->
- <skip />
- <!-- no translation found for long_press_widget_to_add (7699152356777458215) -->
- <skip />
- <!-- no translation found for market (2619650989819296998) -->
- <skip />
- <!-- no translation found for widget_dims_format (2370757736025621599) -->
- <skip />
- <!-- no translation found for external_drop_widget_error (3165821058322217155) -->
- <skip />
- <!-- no translation found for external_drop_widget_pick_title (3486317258037690630) -->
- <skip />
- <!-- no translation found for rename_folder_label (3727762225964550653) -->
- <skip />
- <!-- no translation found for rename_folder_title (3771389277707820891) -->
- <skip />
- <!-- no translation found for rename_action (5559600076028658757) -->
- <skip />
- <!-- no translation found for cancel_action (7009134900002915310) -->
- <skip />
- <!-- no translation found for menu_item_add_item (1264911265836810421) -->
- <skip />
- <!-- no translation found for group_applications (3797214114206693605) -->
- <skip />
- <!-- no translation found for group_shortcuts (6012256992764410535) -->
- <skip />
- <!-- no translation found for group_widgets (1569030723286851002) -->
- <skip />
- <!-- no translation found for completely_out_of_space (6106288382070760318) -->
- <skip />
- <!-- no translation found for out_of_space (4691004494942118364) -->
- <skip />
- <!-- no translation found for hotseat_out_of_space (9139760413395605841) -->
- <skip />
- <!-- no translation found for invalid_hotseat_item (1211534262129849507) -->
- <skip />
- <!-- no translation found for shortcut_installed (1701742129426969556) -->
- <skip />
- <!-- no translation found for shortcut_uninstalled (8176767991305701821) -->
- <skip />
- <!-- no translation found for shortcut_duplicate (9167217446062498127) -->
- <skip />
- <!-- no translation found for title_select_shortcut (6680642571148153868) -->
- <skip />
- <!-- no translation found for title_select_application (3280812711670683644) -->
- <skip />
- <!-- no translation found for all_apps_button_label (9110807029020582876) -->
- <skip />
- <!-- no translation found for all_apps_home_button_label (252062713717058851) -->
- <skip />
- <!-- no translation found for delete_zone_label_workspace (4009607676751398685) -->
- <skip />
- <!-- no translation found for delete_zone_label_all_apps (8083826390278958980) -->
- <skip />
- <!-- no translation found for delete_target_label (1822697352535677073) -->
- <skip />
- <!-- no translation found for delete_target_uninstall_label (5100785476250872595) -->
- <skip />
- <!-- no translation found for info_target_label (8053346143994679532) -->
- <skip />
- <!-- no translation found for accessibility_search_button (1628520399424565142) -->
- <skip />
- <!-- no translation found for accessibility_voice_search_button (4637324840434406584) -->
- <skip />
- <!-- no translation found for accessibility_all_apps_button (2603132375383800483) -->
- <skip />
- <!-- no translation found for accessibility_delete_button (6466114477993744621) -->
- <skip />
- <!-- no translation found for delete_zone_label_all_apps_system_app (449755632749610895) -->
- <skip />
- <!-- no translation found for cab_menu_delete_app (7435191475867183689) -->
- <skip />
- <!-- no translation found for cab_menu_app_info (8593722221450362342) -->
- <skip />
- <!-- no translation found for cab_app_selection_text (374688303047985416) -->
- <skip />
- <!-- no translation found for cab_widget_selection_text (1833458597831541241) -->
- <skip />
- <!-- no translation found for cab_folder_selection_text (7999992513806132118) -->
- <skip />
- <!-- no translation found for cab_shortcut_selection_text (2103811025667946450) -->
- <skip />
- <!-- no translation found for permlab_install_shortcut (5632423390354674437) -->
- <skip />
- <!-- no translation found for permdesc_install_shortcut (923466509822011139) -->
- <skip />
- <!-- no translation found for permlab_uninstall_shortcut (864595034498083837) -->
- <skip />
- <!-- no translation found for permdesc_uninstall_shortcut (5134129545001836849) -->
- <skip />
- <!-- no translation found for permlab_read_settings (1941457408239617576) -->
- <skip />
- <!-- no translation found for permdesc_read_settings (5833423719057558387) -->
- <skip />
- <!-- no translation found for permlab_write_settings (3574213698004620587) -->
- <skip />
- <!-- no translation found for permdesc_write_settings (5440712911516509985) -->
- <skip />
- <!-- no translation found for gadget_error_text (6081085226050792095) -->
- <skip />
- <!-- no translation found for uninstall_system_app_text (4172046090762920660) -->
- <skip />
- <!-- no translation found for dream_name (1530253749244328964) -->
- <skip />
- <!-- no translation found for folder_hint_text (6617836969016293992) -->
- <skip />
- <!-- no translation found for workspace_description_format (2950174241104043327) -->
- <skip />
- <!-- no translation found for default_scroll_format (7475544710230993317) -->
- <skip />
- <!-- no translation found for workspace_scroll_format (8458889198184077399) -->
- <skip />
- <!-- no translation found for apps_customize_apps_scroll_format (370005296147130238) -->
- <skip />
- <!-- no translation found for apps_customize_widgets_scroll_format (3106209519974971521) -->
- <skip />
- <!-- no translation found for first_run_cling_title (7257389003637362144) -->
- <skip />
- <!-- no translation found for first_run_cling_description (6447072552696253358) -->
- <skip />
- <!-- no translation found for first_run_cling_create_screens_hint (6950729526680114157) -->
- <skip />
- <!-- no translation found for workspace_cling_title (5626202359865825661) -->
- <skip />
- <!-- no translation found for workspace_cling_move_item (528201129978005352) -->
- <skip />
- <!-- no translation found for folder_cling_title (3894908818693254164) -->
- <skip />
- <!-- no translation found for folder_cling_create_folder (6158215559475836131) -->
- <skip />
- <!-- no translation found for cling_dismiss (8962359497601507581) -->
- <skip />
- <!-- no translation found for folder_opened (94695026776264709) -->
- <skip />
- <!-- no translation found for folder_tap_to_close (1884479294466410023) -->
- <skip />
- <!-- no translation found for folder_tap_to_rename (9191075570492871147) -->
- <skip />
- <!-- no translation found for folder_closed (4100806530910930934) -->
- <skip />
- <!-- no translation found for folder_renamed (1794088362165669656) -->
- <skip />
- <!-- no translation found for folder_name_format (6629239338071103179) -->
- <skip />
- <!-- no translation found for widget_button_text (2880537293434387943) -->
- <skip />
- <!-- no translation found for wallpaper_button_text (8404103075899945851) -->
- <skip />
- <!-- no translation found for settings_button_text (8119458837558863227) -->
- <skip />
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
</resources>
diff --git a/res/values-bg/cm_strings.xml b/res/values-bg/cm_strings.xml
new file mode 100644
index 000000000..5963610b0
--- /dev/null
+++ b/res/values-bg/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">НАСТРОЙКИ НА НАЧАЛЕН ЕКРАН</string>
+ <string name="drawer_settings">НАСТРОЙКИ НА МЕНЮ</string>
+ <string name="app_settings">НАСТРОЙКИ НА ПРИЛОЖЕНИЯ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ВКЛЮЧЕНО</string>
+ <string name="setting_state_off">ИЗКЛЮЧЕНО</string>
+ <string name="setting_state_disabled">ДЕАКТИВИРАНО</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Изпълни</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Превъртане на тапета</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Размер на мрежата</string>
+ <string name="grid_size_comfortable">Нормален</string>
+ <string name="grid_size_cozy">Удобен</string>
+ <string name="grid_size_condensed">Сгъстен</string>
+ <string name="grid_size_custom">По свой избор (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Изберете размер по свой избор</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Потвърдете</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Лента за търсене</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Големи икони</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Етикети на иконите</string>
+ <string name="icon_labels_show">Покажи</string>
+ <string name="icon_labels_hide">Скрий</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Защитени приложения</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Стил на менюто</string>
+ <string name="app_drawer_style_compact">Компактно</string>
+ <string name="app_drawer_style_sections">Разделено</string>
+ <string name="app_drawer_color">Цвят на менюто за известия</string>
+ <string name="app_drawer_color_dark">Тъмно</string>
+ <string name="app_drawer_color_light">Светло</string>
+ <string name="fast_scroller">Бързо прелистване</string>
+ <string name="fast_scroller_type">Бързо прелистване</string>
+ <string name="fast_scroller_type_horizontal">Хоризонтално</string>
+ <string name="fast_scroller_type_vertical">Вертикално</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Не може да бъде намерено приложението за търсене!</string>
+</resources>
diff --git a/res/values-bn-rBD/cm_strings.xml b/res/values-bn-rBD/cm_strings.xml
new file mode 100644
index 000000000..cab645ccd
--- /dev/null
+++ b/res/values-bn-rBD/cm_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="app_settings">অ্যাপ এর রুপান্তর নির্ধারণ</string>
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-br-rFR/cm_strings.xml b/res/values-br-rFR/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-br-rFR/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-br-rFR/strings.xml b/res/values-br-rFR/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-br-rFR/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-ca/cm_strings.xml b/res/values-ca/cm_strings.xml
new file mode 100644
index 000000000..7e3aea97e
--- /dev/null
+++ b/res/values-ca/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">CONFIGURACIÓ DE LA PANTALLA D\'INICI</string>
+ <string name="drawer_settings">CONFIGURACIÓ DEL CALAIX</string>
+ <string name="app_settings">CONFIGURACIÓ DE L\'APLICACIÓ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">SÍ</string>
+ <string name="setting_state_off">NO</string>
+ <string name="setting_state_disabled">DESHABILITAT</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Desplaça el fons de pantalla</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Mida de la graella</string>
+ <string name="grid_size_comfortable">Còmoda</string>
+ <string name="grid_size_cozy">Acollidora</string>
+ <string name="grid_size_condensed">Condensada</string>
+ <string name="grid_size_custom">Personalitzat (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Selecciona la mida personalitzada</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirma</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barra de cerca</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Icones grans</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etiquetes de les icones</string>
+ <string name="icon_labels_show">Mostra</string>
+ <string name="icon_labels_hide">Amaga</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aplicacions protegides</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Estil del calaix</string>
+ <string name="app_drawer_style_compact">Compacte</string>
+ <string name="app_drawer_style_sections">Seccions</string>
+ <string name="app_drawer_color">Color del calaix</string>
+ <string name="app_drawer_color_dark">Fosc</string>
+ <string name="app_drawer_color_light">Clar</string>
+ <string name="fast_scroller">Desplaçament ràpid</string>
+ <string name="fast_scroller_type">Tipus de desplaçament ràpid</string>
+ <string name="fast_scroller_type_horizontal">Horitzontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">No s\'ha pogut trobar una activitat de cerca!</string>
+</resources>
diff --git a/res/values-cs/cm_strings.xml b/res/values-cs/cm_strings.xml
new file mode 100644
index 000000000..5370f6926
--- /dev/null
+++ b/res/values-cs/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">NASTAVENÍ DOMOVSKÉ OBRAZOVKY</string>
+ <string name="drawer_settings">NASTAVENÍ SLOŽKY</string>
+ <string name="app_settings">NASTAVENÍ APL</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">Zap.</string>
+ <string name="setting_state_off">Vyp.</string>
+ <string name="setting_state_disabled">ZAKÁZÁNO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Hry Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Posouvání tapety</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Velikost mřížky</string>
+ <string name="grid_size_comfortable">Komfortní</string>
+ <string name="grid_size_cozy">Pohodlná</string>
+ <string name="grid_size_condensed">Zůžená</string>
+ <string name="grid_size_custom">Vlastní (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Zvolte vlastní velikost</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Potvrdit</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Vyhledávací lišta</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Větší ikony</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Popisky ikon</string>
+ <string name="icon_labels_show">Zobrazit</string>
+ <string name="icon_labels_hide">Skrýt</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Chráněné aplikace</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Styl složky</string>
+ <string name="app_drawer_style_compact">Kompaktní</string>
+ <string name="app_drawer_style_sections">Sekce</string>
+ <string name="app_drawer_color">Barva složky</string>
+ <string name="app_drawer_color_dark">Tmavý</string>
+ <string name="app_drawer_color_light">Světlý</string>
+ <string name="fast_scroller">Rychlý posuvník</string>
+ <string name="fast_scroller_type">Typ rychlého posuvníku</string>
+ <string name="fast_scroller_type_horizontal">Horizontální</string>
+ <string name="fast_scroller_type_vertical">Vertikální</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Vyhledávací aktivita nenalezena!</string>
+</resources>
diff --git a/res/values-cy/cm_strings.xml b/res/values-cy/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-cy/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-cy/strings.xml b/res/values-cy/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-cy/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-da/cm_strings.xml b/res/values-da/cm_strings.xml
new file mode 100644
index 000000000..732521bfe
--- /dev/null
+++ b/res/values-da/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">INDSTILLINGER FOR STARTSKÆRM</string>
+ <string name="drawer_settings">INDSTILLINGER FOR OVERSIGT</string>
+ <string name="app_settings">APP-INDSTILLINGER</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">TIL</string>
+ <string name="setting_state_off">FRA</string>
+ <string name="setting_state_disabled">HANDICAPPET</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play-apps</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Rul baggrund</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Gitterstørrelse</string>
+ <string name="grid_size_comfortable">Behageligt</string>
+ <string name="grid_size_cozy">Hyggeligt</string>
+ <string name="grid_size_condensed">Sammentrængt</string>
+ <string name="grid_size_custom">Brugerdefineret (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Vælg brugerdefineret størrelse</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bekræft</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Søgelinje</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Større ikoner</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikontekster</string>
+ <string name="icon_labels_show">Vis</string>
+ <string name="icon_labels_hide">Skjul</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Beskyttede apps</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Lodret</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">En funktion til søgning findes ikke!</string>
+</resources>
diff --git a/res/values-de/cm_strings.xml b/res/values-de/cm_strings.xml
new file mode 100644
index 000000000..a721e94b6
--- /dev/null
+++ b/res/values-de/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">STARTBILDSCHIRM-EINSTELLUNGEN</string>
+ <string name="drawer_settings">EINSTELLUNGEN FÜR APP-ÜBERSICHT</string>
+ <string name="app_settings">APP-EINSTELLUNGEN</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">AN</string>
+ <string name="setting_state_off">AUS</string>
+ <string name="setting_state_disabled">DEAKTIVIERT</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Hintergrundbild scrollen</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rastergröße</string>
+ <string name="grid_size_comfortable">Normal</string>
+ <string name="grid_size_cozy">Mittel</string>
+ <string name="grid_size_condensed">Klein</string>
+ <string name="grid_size_custom">Benutzerdefiniert (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Benutzerdefinierte Größe auswählen</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bestätigen</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Suchleiste</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Große Symbole</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Symbol-Beschriftungen</string>
+ <string name="icon_labels_show">Anzeigen</string>
+ <string name="icon_labels_hide">Ausblenden</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Geschützte Apps</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Stil der App-Übersicht</string>
+ <string name="app_drawer_style_compact">Kompakt</string>
+ <string name="app_drawer_style_sections">Abschnitte</string>
+ <string name="app_drawer_color">Farbe der App-Übersicht</string>
+ <string name="app_drawer_color_dark">Dunkel</string>
+ <string name="app_drawer_color_light">Hell</string>
+ <string name="fast_scroller">Schnelles Scrollen</string>
+ <string name="fast_scroller_type">Typ für schnelles Scrollen</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertikal</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Es konnte keine Suche-Aktivität gefunden werden!</string>
+</resources>
diff --git a/res/values-el/cm_strings.xml b/res/values-el/cm_strings.xml
new file mode 100644
index 000000000..e2a74a849
--- /dev/null
+++ b/res/values-el/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ΡΥΘΜΙΣΕΙΣ ΑΡΧΙΚΗΣ ΟΘΟΝΗΣ</string>
+ <string name="drawer_settings">ΡΥΘΜΙΣΕΙΣ ΣΥΡΤΑΡΙΟΥ</string>
+ <string name="app_settings">ΡΥΘΜΙΣΕΙΣ ΕΦΑΡΜΟΓΩΝ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ΕΝΕΡΓΟ</string>
+ <string name="setting_state_off">ΑΝΕΝΕΡΓΟ</string>
+ <string name="setting_state_disabled">ΑΠΕΝΕΡΓΟΠΟΙΗΜΕΝΟ</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Κύλιση ταπετσαρίας</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Μέγεθος πλέγματος</string>
+ <string name="grid_size_comfortable">Άνετο</string>
+ <string name="grid_size_cozy">Κανονικό</string>
+ <string name="grid_size_condensed">Συμπυκνωμένο</string>
+ <string name="grid_size_custom">Προσαρμοσμένο (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Επιλέξτε προσαρμοσμένο μέγεθος</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Επιβεβαίωση</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Μπάρα αναζήτησης</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Μεγαλύτερα εικονίδια</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ετικέτες εικονιδίων</string>
+ <string name="icon_labels_show">Εμφάνιση</string>
+ <string name="icon_labels_hide">Απόκρυψη</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Προστατευμένες εφαρμογές</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Στυλ συρταριού</string>
+ <string name="app_drawer_style_compact">Συμπαγές</string>
+ <string name="app_drawer_style_sections">Τμήματα</string>
+ <string name="app_drawer_color">Χρώμα συρταριού</string>
+ <string name="app_drawer_color_dark">Σκοτεινό</string>
+ <string name="app_drawer_color_light">Ανοιχτόχρωμο</string>
+ <string name="fast_scroller">Γρήγορη κύλιση</string>
+ <string name="fast_scroller_type">Τύπος γρήγορης κύλισης</string>
+ <string name="fast_scroller_type_horizontal">Οριζόντια</string>
+ <string name="fast_scroller_type_vertical">Κάθετη</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Δεν βρέθηκε καμία δραστηριότητα αναζήτησης!</string>
+</resources>
diff --git a/res/values-en-rAU/cm_strings.xml b/res/values-en-rAU/cm_strings.xml
new file mode 100644
index 000000000..67af95597
--- /dev/null
+++ b/res/values-en-rAU/cm_strings.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">HOME SCREEN SETTINGS</string>
+ <string name="drawer_settings">DRAWER SETTINGS</string>
+ <string name="app_settings">APP SETTINGS</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ON</string>
+ <string name="setting_state_off">OFF</string>
+ <string name="setting_state_disabled">DISABLED</string>
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Scroll wallpaper</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Grid size</string>
+ <string name="grid_size_comfortable">Comfortable</string>
+ <string name="grid_size_cozy">Cozy</string>
+ <string name="grid_size_condensed">Condensed</string>
+ <string name="grid_size_custom">Custom (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Select custom size</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirm</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Search bar</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Larger icons</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Icon labels</string>
+ <string name="icon_labels_show">Show</string>
+ <string name="icon_labels_hide">Hide</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Protected apps</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">A search activity could not be found!</string>
+</resources>
diff --git a/res/values-en-rGB/cm_strings.xml b/res/values-en-rGB/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-en-rGB/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-en-rIN/cm_strings.xml b/res/values-en-rIN/cm_strings.xml
new file mode 100644
index 000000000..a9cbc07cc
--- /dev/null
+++ b/res/values-en-rIN/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">HOME SCREEN SETTINGS</string>
+ <string name="drawer_settings">DRAWER SETTINGS</string>
+ <string name="app_settings">APP SETTINGS</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ON</string>
+ <string name="setting_state_off">OFF</string>
+ <string name="setting_state_disabled">DISABLED</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Scroll wallpaper</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Grid size</string>
+ <string name="grid_size_comfortable">Comfortable</string>
+ <string name="grid_size_cozy">Cozy</string>
+ <string name="grid_size_condensed">Condensed</string>
+ <string name="grid_size_custom">Custom (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Select custom size</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirm</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Search bar</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Larger icons</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Icon labels</string>
+ <string name="icon_labels_show">Show</string>
+ <string name="icon_labels_hide">Hide</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Protected apps</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Drawer style</string>
+ <string name="app_drawer_style_compact">Compact</string>
+ <string name="app_drawer_style_sections">Sections</string>
+ <string name="app_drawer_color">Drawer color</string>
+ <string name="app_drawer_color_dark">Dark</string>
+ <string name="app_drawer_color_light">Light</string>
+ <string name="fast_scroller">Fast scroller</string>
+ <string name="fast_scroller_type">Fast scroller type</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">A search activity could not be found!</string>
+</resources>
diff --git a/res/values-en-rPT/cm_strings.xml b/res/values-en-rPT/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-en-rPT/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-en-rPT/strings.xml b/res/values-en-rPT/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-en-rPT/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-eo/cm_strings.xml b/res/values-eo/cm_strings.xml
new file mode 100644
index 000000000..6152bd549
--- /dev/null
+++ b/res/values-eo/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">AGORDOJ DE LA HEJMA PAĜO</string>
+ <string name="drawer_settings">AGORDOJ DE LA APLIKAĴA EKRANO</string>
+ <string name="app_settings">AGORDOJ DE LA APLIKAĴO</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">EK</string>
+ <string name="setting_state_off">FOR</string>
+ <string name="setting_state_disabled">MALŜALTITA</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Ludi</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Rulumo de la ekranfono</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Grandeco de la krado</string>
+ <string name="grid_size_comfortable">Komforta</string>
+ <string name="grid_size_cozy">Koketa</string>
+ <string name="grid_size_condensed">Densiga</string>
+ <string name="grid_size_custom">Propra (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Elekti propran tipar-grandecon</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Konfirmi</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Serĉ-breto</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Grandaj piktogramoj</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etikedo de la piktogramoj</string>
+ <string name="icon_labels_show">Montri</string>
+ <string name="icon_labels_hide">Kaŝi</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Protektitaj aplikaĵoj</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertikala</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Neniu serĉilo troveblas!</string>
+</resources>
diff --git a/res/values-eo/strings.xml b/res/values-eo/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-eo/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-es-rMX/cm_strings.xml b/res/values-es-rMX/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-es-rMX/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-es-rMX/strings.xml b/res/values-es-rMX/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-es-rMX/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-es-rUS/cm_strings.xml b/res/values-es-rUS/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-es-rUS/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-es/cm_strings.xml b/res/values-es/cm_strings.xml
new file mode 100644
index 000000000..d54f9ce38
--- /dev/null
+++ b/res/values-es/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">AJUSTES DE INICIO</string>
+ <string name="drawer_settings">AJUSTES DEL PANEL</string>
+ <string name="app_settings">AJUSTES DE LAS APLICACIONES</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">SÍ</string>
+ <string name="setting_state_off">NO</string>
+ <string name="setting_state_disabled">DESACTIVADO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Reproducir</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Desplazar fondo de pantalla</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Tamaño de la cuadrícula</string>
+ <string name="grid_size_comfortable">Cómodo</string>
+ <string name="grid_size_cozy">Cómoda</string>
+ <string name="grid_size_condensed">Comprimida</string>
+ <string name="grid_size_custom">Personalizado (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Selecciona el tamaño personalizado</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirmar</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barra de búsqueda</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Iconos grandes</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etiquetas de iconos</string>
+ <string name="icon_labels_show">Mostrar</string>
+ <string name="icon_labels_hide">Ocultar</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aplicaciones protegidas</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Estilo del panel</string>
+ <string name="app_drawer_style_compact">Compacto</string>
+ <string name="app_drawer_style_sections">Secciones</string>
+ <string name="app_drawer_color">Color del panel</string>
+ <string name="app_drawer_color_dark">Oscuro</string>
+ <string name="app_drawer_color_light">Claro</string>
+ <string name="fast_scroller">Desplazamiento rápido</string>
+ <string name="fast_scroller_type">Tipo de desplazamiento rápido</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">¡No se pudo encontrar un asistente de búsqueda!</string>
+</resources>
diff --git a/res/values-et-rEE/cm_strings.xml b/res/values-et-rEE/cm_strings.xml
new file mode 100644
index 000000000..abbc119c5
--- /dev/null
+++ b/res/values-et-rEE/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">KODUEKRAANI SEADED</string>
+ <string name="drawer_settings">SAHTLI SEADED</string>
+ <string name="app_settings">RAKENDUSE SEADED</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">SEES</string>
+ <string name="setting_state_off">VÄLJAS</string>
+ <string name="setting_state_disabled">KEELATUD</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Keri taustapilti</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Ruudustiku suurus</string>
+ <string name="grid_size_comfortable">Mugav</string>
+ <string name="grid_size_cozy">Kodune</string>
+ <string name="grid_size_condensed">Kärbitud</string>
+ <string name="grid_size_custom">Kohandatud (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Vali sobiv suurus</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Kinnita</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Otsinguriba</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Suuremad ikoonid</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikooni sildid</string>
+ <string name="icon_labels_show">Näita</string>
+ <string name="icon_labels_hide">Peida</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Kaitstud rakendused</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Sahtli stiil</string>
+ <string name="app_drawer_style_compact">Kompaktne</string>
+ <string name="app_drawer_style_sections">Jaotised</string>
+ <string name="app_drawer_color">Sahtli värv</string>
+ <string name="app_drawer_color_dark">Tume</string>
+ <string name="app_drawer_color_light">Hele</string>
+ <string name="fast_scroller">Kiire kerimine</string>
+ <string name="fast_scroller_type">Kiire kerimise tüüp</string>
+ <string name="fast_scroller_type_horizontal">Horisontaalne</string>
+ <string name="fast_scroller_type_vertical">Vertikaalne</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Otsimise tegevusala ei leitud!</string>
+</resources>
diff --git a/res/values-eu-rES/cm_strings.xml b/res/values-eu-rES/cm_strings.xml
new file mode 100644
index 000000000..4b02077c1
--- /dev/null
+++ b/res/values-eu-rES/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">HASIERA-PANTAILAREN EZARPENAK</string>
+ <string name="drawer_settings">TIRADERAREN EZARPENAK</string>
+ <string name="app_settings">APLIKAZIOAREN EZARPENAK</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">BAI</string>
+ <string name="setting_state_off">EZ</string>
+ <string name="setting_state_disabled">EZGAITUTA</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Korritu horma-papera</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Sarearen tamaina</string>
+ <string name="grid_size_comfortable">Arrunta</string>
+ <string name="grid_size_cozy">Erosoa</string>
+ <string name="grid_size_condensed">Trinkotua</string>
+ <string name="grid_size_custom">Pertsonalizatua (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Hautatu tamaina pertsonalizatua</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Berretsi</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Bilaketa-barra</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Ikono handiak</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikonoen etiketak</string>
+ <string name="icon_labels_show">Erakutsi</string>
+ <string name="icon_labels_hide">Ezkutatu</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Babesturiko aplikazioak</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Tiraderaren estiloa</string>
+ <string name="app_drawer_style_compact">Konpaktua</string>
+ <string name="app_drawer_style_sections">Sekzioak</string>
+ <string name="app_drawer_color">Tiraderaren kolorea</string>
+ <string name="app_drawer_color_dark">Iluna</string>
+ <string name="app_drawer_color_light">Argia</string>
+ <string name="fast_scroller">Korritze azkarra</string>
+ <string name="fast_scroller_type">Korritze azkar mota</string>
+ <string name="fast_scroller_type_horizontal">Horizontala</string>
+ <string name="fast_scroller_type_vertical">Bertikala</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Ezin izan da bilaketa aktibitaterik aurkitu!</string>
+</resources>
diff --git a/res/values-fa/cm_strings.xml b/res/values-fa/cm_strings.xml
new file mode 100644
index 000000000..77bfd3b84
--- /dev/null
+++ b/res/values-fa/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">تنظیمات صفحه اصلی</string>
+ <string name="drawer_settings">تنظیمات منوی برنامه‌ها</string>
+ <string name="app_settings">تنظیمات برنامه</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">روشن</string>
+ <string name="setting_state_off">خاموش</string>
+ <string name="setting_state_disabled">غیرفعال</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">پلی</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">حرکت کاغذدیواری</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">میزان شبکه‌بندی</string>
+ <string name="grid_size_comfortable">راحت</string>
+ <string name="grid_size_cozy">ساده</string>
+ <string name="grid_size_condensed">متراکم</string>
+ <string name="grid_size_custom">سفارشی (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">انتخاب میزان مورد نظر</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">تایید</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">نوار جستجو</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">آیکون‌های بزرگتر</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">نوشته آیکون‌ها</string>
+ <string name="icon_labels_show">قابل مشاهده</string>
+ <string name="icon_labels_hide">پنهان</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">برنامه‌های محافظت شده</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">عمودی</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">عملگری برای جستجو یافت نشد!</string>
+</resources>
diff --git a/res/values-fi/cm_strings.xml b/res/values-fi/cm_strings.xml
new file mode 100644
index 000000000..f884bcde0
--- /dev/null
+++ b/res/values-fi/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">KOTINÄYTÖN ASETUKSET</string>
+ <string name="drawer_settings">SOVELLUSVALIKON ASETUKSET</string>
+ <string name="app_settings">SOVELLUKSEN ASETUKSET</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">PÄÄLLÄ</string>
+ <string name="setting_state_off">POIS</string>
+ <string name="setting_state_disabled">POIS KÄYTÖSTÄ</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Vieritä taustakuvaa</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Ruudukon koko</string>
+ <string name="grid_size_comfortable">Mukava</string>
+ <string name="grid_size_cozy">Kodikas</string>
+ <string name="grid_size_condensed">Tiivistetty</string>
+ <string name="grid_size_custom">Mukautettu (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Valitse mukautettu koko</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Vahvista</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Hakupalkki</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Suuremmat kuvakkeet</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Kuvakkeiden otsikot</string>
+ <string name="icon_labels_show">Näytä</string>
+ <string name="icon_labels_hide">Piilota</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Suojatut sovellukset</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Sovellusvalikon tyyli</string>
+ <string name="app_drawer_style_compact">Kompakti</string>
+ <string name="app_drawer_style_sections">Osat</string>
+ <string name="app_drawer_color">Sovellusvalikon väri</string>
+ <string name="app_drawer_color_dark">Tumma</string>
+ <string name="app_drawer_color_light">Vaalea</string>
+ <string name="fast_scroller">Nopea selaus</string>
+ <string name="fast_scroller_type">Nopean selauksen tyyppi</string>
+ <string name="fast_scroller_type_horizontal">Vaaka</string>
+ <string name="fast_scroller_type_vertical">Pysty</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Hakuhistoriaa ei löytynyt!</string>
+</resources>
diff --git a/res/values-fil-rPH/cm_strings.xml b/res/values-fil-rPH/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-fil-rPH/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-fr-rCA/cm_strings.xml b/res/values-fr-rCA/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-fr-rCA/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-fr/cm_strings.xml b/res/values-fr/cm_strings.xml
new file mode 100644
index 000000000..c460d2cdf
--- /dev/null
+++ b/res/values-fr/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">PARAMÈTRES DE L\'ÉCRAN D\'ACCUEIL</string>
+ <string name="drawer_settings">PARAMÈTRES DE L\'ÉCRAN DES APPLICATIONS</string>
+ <string name="app_settings">PARAMÈTRES DE L\'APPLICATION</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">OUI</string>
+ <string name="setting_state_off">NON</string>
+ <string name="setting_state_disabled">DÉSACTIVÉ</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Défilement du fond d\'écran</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Taille de la grille</string>
+ <string name="grid_size_comfortable">Confortable</string>
+ <string name="grid_size_cozy">Confortable</string>
+ <string name="grid_size_condensed">Condensé</string>
+ <string name="grid_size_custom">Personnalisé (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Sélectionner la taille personnalisée</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirmer</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barre de recherche</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Grandes icônes</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Nom des icônes</string>
+ <string name="icon_labels_show">Afficher</string>
+ <string name="icon_labels_hide">Masquer</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Applications protégées</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Style de tiroir</string>
+ <string name="app_drawer_style_compact">Compact</string>
+ <string name="app_drawer_style_sections">Sections</string>
+ <string name="app_drawer_color">Couleur du tiroir</string>
+ <string name="app_drawer_color_dark">Sombre</string>
+ <string name="app_drawer_color_light">Clair</string>
+ <string name="fast_scroller">Défilement rapide</string>
+ <string name="fast_scroller_type">Type de défilement</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Aucune recherche n\'a pu être trouvée !</string>
+</resources>
diff --git a/res/values-fy-rNL/cm_strings.xml b/res/values-fy-rNL/cm_strings.xml
new file mode 100644
index 000000000..d6dcd9d6d
--- /dev/null
+++ b/res/values-fy-rNL/cm_strings.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <string name="grid_size_comfortable">Komfortabel</string>
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-fy-rNL/strings.xml b/res/values-fy-rNL/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-fy-rNL/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-gd-rGB/cm_strings.xml b/res/values-gd-rGB/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-gd-rGB/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-gd-rGB/strings.xml b/res/values-gd-rGB/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-gd-rGB/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-gl-rES/cm_strings.xml b/res/values-gl-rES/cm_strings.xml
new file mode 100644
index 000000000..332de2d80
--- /dev/null
+++ b/res/values-gl-rES/cm_strings.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">AXUSTES DA PANTALLA DE INICIO</string>
+ <string name="app_settings">AXUSTES DA APLICACIÓN</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ACESO</string>
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-gu-rIN/cm_strings.xml b/res/values-gu-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-gu-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-hi/cm_strings.xml b/res/values-hi/cm_strings.xml
new file mode 100644
index 000000000..9a04ca642
--- /dev/null
+++ b/res/values-hi/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">होम स्क्रीन सेटिंग</string>
+ <string name="drawer_settings">दराज़ सेटिंग</string>
+ <string name="app_settings">एपीपी सेटिंग</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">बंद</string>
+ <string name="setting_state_off">चालू</string>
+ <string name="setting_state_disabled">अक्षम</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">प्ले</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">वॉलपेपर को स्क्रॉल करें</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">ग्रिड आमाप</string>
+ <string name="grid_size_comfortable">सहज</string>
+ <string name="grid_size_cozy">आरामदायक</string>
+ <string name="grid_size_condensed">सघन</string>
+ <string name="grid_size_custom">मनपसंद (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">मनपसंद आमाप चुनें</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">पुष्टि करें</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">खोज पट्टी</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">बड़े आइकन</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">आइकन लेबल</string>
+ <string name="icon_labels_show">दिखाएँ</string>
+ <string name="icon_labels_hide">छिपाएँ</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">रक्षित ऐप</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">ऊर्ध्व</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">एक खोज गतिविधि नहीं मिली।</string>
+</resources>
diff --git a/res/values-hr/cm_strings.xml b/res/values-hr/cm_strings.xml
new file mode 100644
index 000000000..c99f7ab4b
--- /dev/null
+++ b/res/values-hr/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">POSTAVKE POČETNOG ZASLONA</string>
+ <string name="drawer_settings">POSTAVKE LADICE</string>
+ <string name="app_settings">POSTAVKE APLIKACIJA</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">UKLJUČENO</string>
+ <string name="setting_state_off">ISKLJUČENO</string>
+ <string name="setting_state_disabled">ONEMOGUĆENO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Sviraj</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Klizanje pozadine</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Veličina rešetke</string>
+ <string name="grid_size_comfortable">Udoban</string>
+ <string name="grid_size_cozy">Udobno</string>
+ <string name="grid_size_condensed">Kondenzirano</string>
+ <string name="grid_size_custom">Prilagođeno (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Odabir prilagođene veličine</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Potvrdi</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Traka za pretraživanje</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Veće ikone</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Oznake ikona</string>
+ <string name="icon_labels_show">Pokaži</string>
+ <string name="icon_labels_hide">Sakrij</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Zaštićene aplikacije</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Stil ladice</string>
+ <string name="app_drawer_style_compact">Kompaktno</string>
+ <string name="app_drawer_style_sections">Sekcije</string>
+ <string name="app_drawer_color">Boja ladice</string>
+ <string name="app_drawer_color_dark">Tamno</string>
+ <string name="app_drawer_color_light">Svijetlo</string>
+ <string name="fast_scroller">Brzi skroler</string>
+ <string name="fast_scroller_type">Vrsta brzog skrolera</string>
+ <string name="fast_scroller_type_horizontal">Vodoravno</string>
+ <string name="fast_scroller_type_vertical">Okomito</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Aktivnosti pretraživanja nije moguće pronaći.</string>
+</resources>
diff --git a/res/values-hu/cm_strings.xml b/res/values-hu/cm_strings.xml
new file mode 100644
index 000000000..664ba7a68
--- /dev/null
+++ b/res/values-hu/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">KEZDŐKÉPERNYŐ BEÁLLÍTÁSOK</string>
+ <string name="drawer_settings">ALKALMAZÁSKÉPERNYŐ BEÁLLÍTÁSOK</string>
+ <string name="app_settings">ALKALMAZÁS BEÁLLÍTÁSAI</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">BE</string>
+ <string name="setting_state_off">KI</string>
+ <string name="setting_state_disabled">LETILTVA</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Háttérkép görgetése</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rácsméret</string>
+ <string name="grid_size_comfortable">Kényelmes</string>
+ <string name="grid_size_cozy">Hangulatos</string>
+ <string name="grid_size_condensed">Tömörített</string>
+ <string name="grid_size_custom">Egyéni (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Válassza ki az egyéni méretet</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Megerősítés</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Keresési sáv</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Nagyobb ikonok</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikon feliratok</string>
+ <string name="icon_labels_show">Megjelenítés</string>
+ <string name="icon_labels_hide">Elrejtés</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Védett alkalmazások</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Alkalmazásindító stílusa</string>
+ <string name="app_drawer_style_compact">Kompakt</string>
+ <string name="app_drawer_style_sections">Szakaszok</string>
+ <string name="app_drawer_color">Alkalmazásindító színe</string>
+ <string name="app_drawer_color_dark">Sötét</string>
+ <string name="app_drawer_color_light">Világos</string>
+ <string name="fast_scroller">Gyors görgető</string>
+ <string name="fast_scroller_type">Gyors görgető típusa</string>
+ <string name="fast_scroller_type_horizontal">Vízszintes</string>
+ <string name="fast_scroller_type_vertical">Függőleges</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Keresési tevékenység nem található!</string>
+</resources>
diff --git a/res/values-hy-rAM/cm_strings.xml b/res/values-hy-rAM/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-hy-rAM/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-in/cm_strings.xml b/res/values-in/cm_strings.xml
new file mode 100644
index 000000000..48425eb72
--- /dev/null
+++ b/res/values-in/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">PENGATURAN LAYAR HOME</string>
+ <string name="drawer_settings">PENGATURAN DRAWER</string>
+ <string name="app_settings">PENGATURAN APL</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ON</string>
+ <string name="setting_state_off">OFF</string>
+ <string name="setting_state_disabled">DINONAKTIFKAN</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Putar</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Gulir wallpaper</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Ukuran grid</string>
+ <string name="grid_size_comfortable">Comfortable</string>
+ <string name="grid_size_cozy">Cozy</string>
+ <string name="grid_size_condensed">Condensed</string>
+ <string name="grid_size_custom">Kustom (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Pilih ukuran kustom</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Konfirmasi</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Bar pencarian</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Ikon lebih besar</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Label ikon</string>
+ <string name="icon_labels_show">Tampilkan</string>
+ <string name="icon_labels_hide">Sembunyikan</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aplikasi yang di lindungi</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertikal</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Aktivitas pencarian tidak dapat ditemukan!</string>
+</resources>
diff --git a/res/values-is-rIS/cm_strings.xml b/res/values-is-rIS/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-is-rIS/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-it/cm_strings.xml b/res/values-it/cm_strings.xml
new file mode 100644
index 000000000..9f4d917c2
--- /dev/null
+++ b/res/values-it/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">IMPOSTAZIONI SCHERMATA HOME</string>
+ <string name="drawer_settings">IMPOSTAZIONI DRAWER</string>
+ <string name="app_settings">IMPOSTAZIONI APP</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ON</string>
+ <string name="setting_state_off">OFF</string>
+ <string name="setting_state_disabled">DISATTIVATO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Sfondo scorrevole</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Dimensione griglia</string>
+ <string name="grid_size_comfortable">Confortevole</string>
+ <string name="grid_size_cozy">Accogliente</string>
+ <string name="grid_size_condensed">Condensato</string>
+ <string name="grid_size_custom">Personalizzato (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Personalizza dimensione</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Conferma</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barra di ricerca</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Icone grandi</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etichette icone</string>
+ <string name="icon_labels_show">Mostra</string>
+ <string name="icon_labels_hide">Nascondi</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">App protette</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Stile drawer</string>
+ <string name="app_drawer_style_compact">Compatto</string>
+ <string name="app_drawer_style_sections">Sezioni</string>
+ <string name="app_drawer_color">Colore drawer</string>
+ <string name="app_drawer_color_dark">Scuro</string>
+ <string name="app_drawer_color_light">Chiaro</string>
+ <string name="fast_scroller">Scorrimento veloce</string>
+ <string name="fast_scroller_type">Tipo scorrimento veloce</string>
+ <string name="fast_scroller_type_horizontal">Orizzontale</string>
+ <string name="fast_scroller_type_vertical">Verticale</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Impossibile trovare un\'activity di ricerca!</string>
+</resources>
diff --git a/res/values-iw/cm_strings.xml b/res/values-iw/cm_strings.xml
new file mode 100644
index 000000000..84fa5070d
--- /dev/null
+++ b/res/values-iw/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">הגדרות מסך הבית</string>
+ <string name="drawer_settings">הגדרות מגירת יישומים</string>
+ <string name="app_settings">הגדרות יישום</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">פעיל</string>
+ <string name="setting_state_off">כבוי</string>
+ <string name="setting_state_disabled">כבוי</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">נגן</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">גלילת טפט</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">גודל רשת</string>
+ <string name="grid_size_comfortable">נוח</string>
+ <string name="grid_size_cozy">נעים</string>
+ <string name="grid_size_condensed">מרוכז</string>
+ <string name="grid_size_custom">מותאם אישית (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">בחר גודל מותאם אישית</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">אשר</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">סרגל החיפוש</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">סמלים גדולים יותר</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">תוויות סמלים</string>
+ <string name="icon_labels_show">הצג</string>
+ <string name="icon_labels_hide">הסתר</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">יישומים מוגנים</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">סגנון מגירת יישומים</string>
+ <string name="app_drawer_style_compact">קומפקטי</string>
+ <string name="app_drawer_style_sections">חלקים</string>
+ <string name="app_drawer_color">צבע מגירת היישומים</string>
+ <string name="app_drawer_color_dark">כהה</string>
+ <string name="app_drawer_color_light">בהיר</string>
+ <string name="fast_scroller">גלילה מהירה</string>
+ <string name="fast_scroller_type">סוג גלילה מהירה</string>
+ <string name="fast_scroller_type_horizontal">אופקי</string>
+ <string name="fast_scroller_type_vertical">לאורך</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">לא ניתן היה למצוא פעולת חיפוש!</string>
+</resources>
diff --git a/res/values-ja/cm_strings.xml b/res/values-ja/cm_strings.xml
new file mode 100644
index 000000000..4d35322f9
--- /dev/null
+++ b/res/values-ja/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ホーム画面の設定</string>
+ <string name="drawer_settings">ドロワーの設定</string>
+ <string name="app_settings">アプリの設定</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ON</string>
+ <string name="setting_state_off">OFF</string>
+ <string name="setting_state_disabled">無効</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">壁紙をスクロール</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">グリッドサイズ</string>
+ <string name="grid_size_comfortable">Comfortable</string>
+ <string name="grid_size_cozy">Cozy</string>
+ <string name="grid_size_condensed">Condensed</string>
+ <string name="grid_size_custom">カスタム(<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">カスタムのサイズを選択</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">確認</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">検索バー</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">大きいアイコン</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">アイコンラベル</string>
+ <string name="icon_labels_show">表示する</string>
+ <string name="icon_labels_hide">表示しない</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">アプリの保護</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">ドロワーのスタイル</string>
+ <string name="app_drawer_style_compact">コンパクト</string>
+ <string name="app_drawer_style_sections">セクション</string>
+ <string name="app_drawer_color">ドロワーの色</string>
+ <string name="app_drawer_color_dark">ダーク</string>
+ <string name="app_drawer_color_light">ライト</string>
+ <string name="fast_scroller">高速スクロール</string>
+ <string name="fast_scroller_type">高速スクロールの種類</string>
+ <string name="fast_scroller_type_horizontal">横方向</string>
+ <string name="fast_scroller_type_vertical">縦</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">検索アクティビティが見つかりませんでした。</string>
+</resources>
diff --git a/res/values-ka-rGE/cm_strings.xml b/res/values-ka-rGE/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ka-rGE/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-kk-rKZ/cm_strings.xml b/res/values-kk-rKZ/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-kk-rKZ/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-km-rKH/cm_strings.xml b/res/values-km-rKH/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-km-rKH/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-kn-rIN/cm_strings.xml b/res/values-kn-rIN/cm_strings.xml
new file mode 100644
index 000000000..208aeaf62
--- /dev/null
+++ b/res/values-kn-rIN/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ಹೋಮ್ ಪರದೆ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
+ <string name="drawer_settings">ಡ್ರಾ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
+ <string name="app_settings">ಆಪ್ ಸೆಟ್ಟಿಂಗ್ಸ್</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ಆನ್</string>
+ <string name="setting_state_off">ಆಫ್</string>
+ <string name="setting_state_disabled">ನಿಷ್ಕ್ರಿಯಗೊಂಡಿದೆ</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">ಪ್ಲೇ</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">ವಾಲ್‍ಪೇಪರ್ ಸ್ಕ್ರೋಲ್ ಮಾಡು</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">ಗ್ರಿಡ್ ಗಾತ್ರ</string>
+ <string name="grid_size_comfortable">ಆರಾಮದಾಯಕ</string>
+ <string name="grid_size_cozy">ಅನುಕೂಲಕರ</string>
+ <string name="grid_size_condensed">ಸಾಂದ್ರೀಕೃತ</string>
+ <string name="grid_size_custom">ಕಸ್ಟಮ್ (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">ಕಸ್ಟಮ್ ಗಾತ್ರ ಆಯ್ಕೆಮಾಡಿ</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">ಖಚಿತಪಡಿಸಿ</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">ಹುಡುಕುವ ಬಾರ್</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">ದೊಡ್ಡ ಐಕಾನ್ಸ್</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">ಐಕಾನ್ ಲೇಬಲ್ಸ್</string>
+ <string name="icon_labels_show">ಪ್ರದರ್ಶಿಸು</string>
+ <string name="icon_labels_hide">ಮರೆಮಾಡು</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">ಸಂರಕ್ಷಿತ ಆಪ್ಸ್</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">ಲಂಬ</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">ಶೋಧನೆ ಚಟುವಟಿಕೆ ಪತ್ತೆಯಾಗಲಿಲ್ಲ!</string>
+</resources>
diff --git a/res/values-ko/cm_strings.xml b/res/values-ko/cm_strings.xml
new file mode 100644
index 000000000..0b5d71aaf
--- /dev/null
+++ b/res/values-ko/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">홈 화면 설정</string>
+ <string name="drawer_settings">서랍 설정</string>
+ <string name="app_settings">애플리케이션 설정</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">켜짐</string>
+ <string name="setting_state_off">꺼짐</string>
+ <string name="setting_state_disabled">사용 안함</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">배경 스크롤</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">격자 크기</string>
+ <string name="grid_size_comfortable">느슨하게</string>
+ <string name="grid_size_cozy">적당히</string>
+ <string name="grid_size_condensed">빽빽하게</string>
+ <string name="grid_size_custom">사용자 지정 (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">사용자 정의 크기 선택</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">확인</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">검색 바</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">큰 아이콘</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">아이콘 레이블</string>
+ <string name="icon_labels_show">보이기</string>
+ <string name="icon_labels_hide">숨기기</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">보호된 앱</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">서랍 스타일</string>
+ <string name="app_drawer_style_compact">촘촘함</string>
+ <string name="app_drawer_style_sections">부분</string>
+ <string name="app_drawer_color">서랍 색상</string>
+ <string name="app_drawer_color_dark">어두움</string>
+ <string name="app_drawer_color_light">밝음</string>
+ <string name="fast_scroller">빠른 스크롤러</string>
+ <string name="fast_scroller_type">바른 스크롤러 유형</string>
+ <string name="fast_scroller_type_horizontal">수평</string>
+ <string name="fast_scroller_type_vertical">수직</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">검색 작업을 찾아 낼 수 없습니다.</string>
+</resources>
diff --git a/res/values-ku/cm_strings.xml b/res/values-ku/cm_strings.xml
new file mode 100644
index 000000000..4231e4b02
--- /dev/null
+++ b/res/values-ku/cm_strings.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">رێکخستنی ڕوونمای سەرەکی</string>
+ <string name="drawer_settings">ڕێکخستنی کێشان</string>
+ <string name="app_settings">ڕێکخستنەکانی بەرنامە</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">هەڵگیرساو</string>
+ <string name="setting_state_off">کووژاوە</string>
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">بابۆڵه‌کردنی سه‌رڕوونما</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">قه‌باره‌ی تۆڕ</string>
+ <string name="grid_size_comfortable">ئاسووده‌</string>
+ <string name="grid_size_cozy">پێدانی ئاسوده‌یی</string>
+ <string name="grid_size_condensed">خه‌ستکراو</string>
+ <string name="preferences_interface_homescreen_custom">دیاریکردنی قه‌باره‌ی خوازراو</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">دلنیاکردنەوە</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">شریتی گەڕان</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">وێنۆچکەی گەورەتر</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">ته‌خته‌ی وێنۆچکه‌کان</string>
+ <string name="icon_labels_show">پیشاندان</string>
+ <string name="icon_labels_hide">شاردنەوە</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">بەرنامە پارێزراوەکان</string>
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ku/strings.xml b/res/values-ku/strings.xml
new file mode 100644
index 000000000..872f1a132
--- /dev/null
+++ b/res/values-ku/strings.xml
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <string name="app_name">کارخەری٣</string>
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <string name="activity_not_found">بەرنامە دانه‌به‌زیوه‌.</string>
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <string name="toggle_weight_watcher">پیشاندانی بیرگە</string>
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <string name="long_press_widget_to_add">بیسوو &amp; دەست ڕاگرە بۆ هەڵگرتنی ویجێتێک.</string>
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <string name="widget_dims_format">%1$d \u00d7 %2$d</string>
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <string name="out_of_space">لەم ماڵی ڕوونمایەدا دیوی تر نییە.</string>
+ <!-- Error message when user has filled the hotseat -->
+ <string name="hotseat_out_of_space">لە کەشەفەی دڵخوازەکانت دیوی تری لێ نییە</string>
+ <!-- All applications label -->
+ <string name="all_apps_button_label">بەرنامەکان</string>
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <string name="all_apps_home_button_label">ماڵەوە</string>
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <string name="delete_target_label">لابردن</string>
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <string name="delete_target_uninstall_label">دامه‌زرانسڕینه‌وه‌</string>
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <string name="info_target_label">زانیاریی بەرنامە</string>
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <string name="permlab_install_shortcut">دامەزراندنی کورتەڕێگەکان</string>
+ <!-- Permission description -->
+ <string name="permdesc_install_shortcut">ڕێگە بە بەرنامەیەک بدە بۆ
+ زیاکردنی کورتەڕێگەکان بەبێ دەستێوەردانی بەکارهێنەر.</string>
+ <!-- Permission short label -->
+ <string name="permlab_read_settings">خوێندنەوەی ڕێکخستنەکانی ماڵەوە و کورتەڕێگەکان</string>
+ <!-- Permission description -->
+ <string name="permdesc_read_settings">ڕێگە بە بەرنامەکە بدە بۆ خوێندنەوەی ڕێکخستنەکان و
+ کورتەڕێگەکانی سەر ماڵ.</string>
+ <!-- Permission short label -->
+ <string name="permlab_write_settings">نووسینی ڕێکخستنەکانی ماڵەوە و کورتەڕێگەکان</string>
+ <!-- Permission description -->
+ <string name="permdesc_write_settings">ڕێگە بە بەرنامەکە بدە بۆ نووسینی ڕێکخستنەکان و
+ کورتەڕێگەکانی سەر ماڵ.</string>
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <string name="gadget_error_text">کێشەی بارکردنی ویجێت</string>
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <string name="uninstall_system_app_text">ئەمە بەرنامەیەکی سیستەمییە و ناتوانرێت دامەزرانسڕینەوەی بۆ بکرێت.</string>
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <string name="default_scroll_format">پەڕەی %1$d لە %2$d</string>
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <string name="workspace_scroll_format">ڕوونمای ماڵەوە %1$d of %2$d</string>
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <string name="first_run_cling_title">بە خێربێيت</string>
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <string name="migration_cling_title">وێنۆچکه‌ی به‌رنامه‌که‌ت لەبەر بگرەوە</string>
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <string name="migration_cling_description">هاوردنی وێنۆچکە و بوخچەکان لە ماڵی ڕوونما کۆنەکانتەوە؟</string>
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <string name="migration_cling_copy_apps">له‌به‌رگرتنه‌وه‌ی وێنۆچکه‌کان</string>
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <string name="migration_cling_use_default">ده‌ستپێکردنی بووژاندنەوە</string>
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <string name="folder_opened">بوخچە کراوە, <xliff:g id="width" example="5">%1$d</xliff:g> لەلایەن <xliff:g id="height" example="3">%2$d</xliff:g></string>
+ <!-- Instruction that clicking outside will close folder -->
+ <string name="folder_tap_to_close">بسوو بۆ داخستنی بوخچە</string>
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <string name="folder_tap_to_rename">بسوو بۆ پاشەکەوتکردنی ناولێنانەوە</string>
+ <!-- Indication that folder closed -->
+ <string name="folder_closed">بوخچە داخرا</string>
+ <!-- Folder renamed format -->
+ <string name="folder_renamed">بوخچە نالێنرایەوە بۆ <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+ <!-- Folder name format -->
+ <string name="folder_name_format">بوخچە: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <string name="debug_memory_activity">* HPROF</string>
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <string name="widget_button_text">ویجێتەکان</string>
+ <!-- Text for wallpaper change button -->
+ <string name="wallpaper_button_text">دیوارپۆشەکان</string>
+ <!-- Text for settings button -->
+ <string name="settings_button_text">ڕێکخستنەکان</string>
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <string name="action_add_to_workspace">زیاکردن بۆ پاشبنەمای ڕوونما</string>
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-ky-rKG/cm_strings.xml b/res/values-ky-rKG/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ky-rKG/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-lb/cm_strings.xml b/res/values-lb/cm_strings.xml
new file mode 100644
index 000000000..90c0db731
--- /dev/null
+++ b/res/values-lb/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">STARTSCHIERM-ASTELLUNGEN</string>
+ <string name="drawer_settings">ASTELLUNGE VUN DER APP-IWWERSIICHT</string>
+ <string name="app_settings">APP-ASTELLUNGEN</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">UN</string>
+ <string name="setting_state_off">AUS</string>
+ <string name="setting_state_disabled">DESAKTIVÉIEREN</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Hannergrondbild scrollen</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Gittergréisst</string>
+ <string name="grid_size_comfortable">Gemittlech</string>
+ <string name="grid_size_cozy">Heemlech</string>
+ <string name="grid_size_condensed">Schmuel</string>
+ <string name="grid_size_custom">Personaliséiert (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Personaliséiert Gréisst auswielen</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bestätegen</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Sichkëscht</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Grouss Symboler</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Symbol-Beschrëftungen</string>
+ <string name="icon_labels_show">Uweisen</string>
+ <string name="icon_labels_hide">Verstoppen</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Geschützt Appen</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertikal</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Eng Sichaktivitéit konnt net fonnt ginn!</string>
+</resources>
diff --git a/res/values-lb/strings.xml b/res/values-lb/strings.xml
new file mode 100644
index 000000000..1df52a717
--- /dev/null
+++ b/res/values-lb/strings.xml
@@ -0,0 +1,171 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <string name="app_name">Launcher3</string>
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <string name="activity_not_found">D\'App ass net installéiert.</string>
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <string name="activity_not_available">App ass net disponibel</string>
+ <!-- SafeMode shortcut error string -->
+ <string name="safemode_shortcut_error">Erofgelueden App am séchere Modus desaktivéiert</string>
+ <!-- SafeMode widget error string -->
+ <string name="safemode_widget_error">Widgeten desaktivéiert am Sécherheetsmodus</string>
+ <string name="toggle_weight_watcher">Späicher uweisen</string>
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <string name="long_press_widget_to_add">Drécken an halen, fir e Widget opzerafen.</string>
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <string name="widget_dims_format">%1$d \u00d7 %2$d</string>
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <string name="out_of_space">Keng Plaz méi op dësem Startschierm.</string>
+ <!-- Error message when user has filled the hotseat -->
+ <string name="hotseat_out_of_space">Keng Plaz méi an der Favoritteläischt</string>
+ <!-- All applications label -->
+ <string name="all_apps_button_label">Appen</string>
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <string name="all_apps_home_button_label">Startschierm</string>
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <string name="delete_target_label">Läschen</string>
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <string name="delete_target_uninstall_label">Desinstalléieren</string>
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <string name="info_target_label">App-Info</string>
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <string name="permlab_install_shortcut">Ofkierzungen installéieren</string>
+ <!-- Permission description -->
+ <string name="permdesc_install_shortcut">Erlaabt der App, Ofkierzungen ouni Benotzerinterventioun dobäizesetzen.</string>
+ <!-- Permission short label -->
+ <string name="permlab_read_settings">Startschierm-Astellungen an Ofkierzunge liesen</string>
+ <!-- Permission description -->
+ <string name="permdesc_read_settings">Erlaabt der App, d\'Startschierm-Astellungen an -Ofkierzungen ze liesen.</string>
+ <!-- Permission short label -->
+ <string name="permlab_write_settings">Startschierm-Astellungen an Ofkierzunge schreiwen</string>
+ <!-- Permission description -->
+ <string name="permdesc_write_settings">Erlaabt der App, d\'Startschierm-Astellungen an -Ofkierzungen z\'änneren.</string>
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <string name="gadget_error_text">Problem beim Luede vum Widget</string>
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <string name="gadget_setup_text">Astellen</string>
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <string name="uninstall_system_app_text">Dëst ass eng System-App a kann net desinstalléiert ginn.</string>
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <string name="default_scroll_format">Säit %1$d / %2$d</string>
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <string name="workspace_scroll_format">Startschierm %1$d / %2$d</string>
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <string name="first_run_cling_title">Wëllkomm</string>
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <string name="migration_cling_title">App-Symboler kopéieren</string>
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <string name="migration_cling_description">Symboler an Dossiere vun dengem ale Startschierm importéieren?</string>
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <string name="migration_cling_copy_apps">SYMBOLER KOPÉIEREN</string>
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <string name="migration_cling_use_default">FRËSCH UFÄNKEN</string>
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <string name="workspace_cling_longpress_title">Hannergrondbiller, Widgeten an Astellungen</string>
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <string name="workspace_cling_longpress_description">Hannergrond drécken an hale fir unzepassen</string>
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <string name="workspace_cling_longpress_dismiss">OK</string>
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <string name="folder_opened">Dossier opgemaach, <xliff:g id="width" example="5">%1$d</xliff:g>-mol <xliff:g id="height" example="3">%2$d</xliff:g></string>
+ <!-- Instruction that clicking outside will close folder -->
+ <string name="folder_tap_to_close">Dréck fir den Dossier zouzemaachen</string>
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <string name="folder_tap_to_rename">Dréck fir den ëmbenannten Dossier ze späicheren</string>
+ <!-- Indication that folder closed -->
+ <string name="folder_closed">Dossier zougemaach</string>
+ <!-- Folder renamed format -->
+ <string name="folder_renamed">Dossier ëmbenannt a(n) <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+ <!-- Folder name format -->
+ <string name="folder_name_format">Dossier: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <string name="debug_memory_activity">* HPROF</string>
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <string name="widget_button_text">Widgeten</string>
+ <!-- Text for wallpaper change button -->
+ <string name="wallpaper_button_text">Hannergrondbiller</string>
+ <!-- Text for settings button -->
+ <string name="settings_button_text">Astellungen</string>
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <string name="package_state_unknown">Onbekannt</string>
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <string name="abandoned_clean_this">Ewechhuelen</string>
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <string name="abandoned_search">Sichen</string>
+ <!-- Title for abandoned promise dialog. -->
+ <string name="abandoned_promises_title">Dës App ass net installéiert</string>
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <string name="abandoned_promise_explanation">D\'App fir dëst Symbol ass net installéiert. Du kanns et läschen, oder no der App sichen a se manuell installéieren.</string>
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <string name="action_add_to_workspace">Op de Startschierm setzen</string>
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-lo-rLA/cm_strings.xml b/res/values-lo-rLA/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-lo-rLA/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-lt/cm_strings.xml b/res/values-lt/cm_strings.xml
new file mode 100644
index 000000000..81a6be7d4
--- /dev/null
+++ b/res/values-lt/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">PAGRINDINIO EKRANO NUSTATYMAI</string>
+ <string name="drawer_settings">STALČIAUS NUSTATYMAI</string>
+ <string name="app_settings">PROGRAMOS NUSTATYMAI</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ĮJUNGTA</string>
+ <string name="setting_state_off">IŠJUNGTA</string>
+ <string name="setting_state_disabled">NELEIDŽIAMA</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Slinkti darbalaukio foną</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Tinklelio dydis</string>
+ <string name="grid_size_comfortable">Patogus</string>
+ <string name="grid_size_cozy">Jaukus</string>
+ <string name="grid_size_condensed">Suglaustas</string>
+ <string name="grid_size_custom">Priskirtas (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Pasirinkite tinkintą dydį</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Patvirtinti</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Paieškos juosta</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Didesnės piktogramos</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Piktogramų pavadinimai</string>
+ <string name="icon_labels_show">Rodyti</string>
+ <string name="icon_labels_hide">Paslėpti</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Apsaugotos programos</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertikalus</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Paieškos veikla negali būti rasta!</string>
+</resources>
diff --git a/res/values-lv/cm_strings.xml b/res/values-lv/cm_strings.xml
new file mode 100644
index 000000000..ba272ce8d
--- /dev/null
+++ b/res/values-lv/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">MĀJAS EKRĀNA IESTATĪJUMI</string>
+ <string name="drawer_settings">ATVILKTNES IESTATĪJUMI</string>
+ <string name="app_settings">LIETOTŅU IESTATĪJUMI</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">IESLĒGTS</string>
+ <string name="setting_state_off">IZSLĒGTS</string>
+ <string name="setting_state_disabled">IZSLĒGTS</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Atskaņot</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Pārtīt tapeti</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Režģa izmērs</string>
+ <string name="grid_size_comfortable">Ērts</string>
+ <string name="grid_size_cozy">Mājīgs</string>
+ <string name="grid_size_condensed">Saīspiests</string>
+ <string name="grid_size_custom">Pielāgots (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Izvēlieties pielāgotu izmēru</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Apstiprināt</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Meklēšanas josla</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Lielākas ikonas</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikonas etiķete</string>
+ <string name="icon_labels_show">Rādīt</string>
+ <string name="icon_labels_hide">Slēpt</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aizsargātās lietotnes</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Vertikāli</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Meklētājs nav atrasts!</string>
+</resources>
diff --git a/res/values-mk-rMK/cm_strings.xml b/res/values-mk-rMK/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-mk-rMK/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ml-rIN/cm_strings.xml b/res/values-ml-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ml-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-mn-rMN/cm_strings.xml b/res/values-mn-rMN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-mn-rMN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-mr-rIN/cm_strings.xml b/res/values-mr-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-mr-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ms-rMY/cm_strings.xml b/res/values-ms-rMY/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ms-rMY/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-my-rMM/cm_strings.xml b/res/values-my-rMM/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-my-rMM/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-nb/cm_strings.xml b/res/values-nb/cm_strings.xml
new file mode 100644
index 000000000..7672e7dab
--- /dev/null
+++ b/res/values-nb/cm_strings.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">HJEMMESKJERM INNSTILLINGER</string>
+ <string name="drawer_settings">SKUFF INNSTILLINGER</string>
+ <string name="app_settings">APP-INNSTILLINGER</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">PÅ</string>
+ <string name="setting_state_off">AV</string>
+ <string name="setting_state_disabled">DEAKTIVERT</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Scroll bakgrunn</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rutenettstørrelse</string>
+ <string name="grid_size_comfortable">Komfortabel</string>
+ <string name="grid_size_cozy">Koselig</string>
+ <string name="grid_size_condensed">Kondensert</string>
+ <string name="grid_size_custom">Egendefinert (<xliff:g id="rows">%1$d </xliff:g> \u00d7 <xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Velg egendefinert størrelse</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bekreft</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Søkefelt</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Større ikoner</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikonetiketter</string>
+ <string name="icon_labels_show">Vis</string>
+ <string name="icon_labels_hide">Skjul</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Beskyttede apps</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Skuff stil</string>
+ <string name="app_drawer_style_compact">Kompakt</string>
+ <string name="app_drawer_style_sections">Inndelinger</string>
+ <string name="app_drawer_color">Skuff farge</string>
+ <string name="app_drawer_color_dark">Mørk</string>
+ <string name="app_drawer_color_light">Lys</string>
+ <string name="fast_scroller_type_horizontal">Vannrett</string>
+ <string name="fast_scroller_type_vertical">Loddrett</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Finner ikke en søkeaktivitet!</string>
+</resources>
diff --git a/res/values-ne-rNP/cm_strings.xml b/res/values-ne-rNP/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ne-rNP/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-nl/cm_strings.xml b/res/values-nl/cm_strings.xml
new file mode 100644
index 000000000..74a762706
--- /dev/null
+++ b/res/values-nl/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">INSTELLINGEN THUISSCHERM</string>
+ <string name="drawer_settings">INSTELLINGEN APP-OVERZICHT</string>
+ <string name="app_settings">INSTELLINGEN APPS</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">AAN</string>
+ <string name="setting_state_off">UIT</string>
+ <string name="setting_state_disabled">UITGESCHAKELD</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Achtergrond schuiven</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rastergrootte</string>
+ <string name="grid_size_comfortable">Comfortabel</string>
+ <string name="grid_size_cozy">Knus</string>
+ <string name="grid_size_condensed">Smal</string>
+ <string name="grid_size_custom">Aangepast (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Aangepaste grootte</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bevestig</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Zoekbalk</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Grote pictogrammen</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Pictogramlabels</string>
+ <string name="icon_labels_show">Aan</string>
+ <string name="icon_labels_hide">Uit</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Beschermde apps</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Stijl app-overzicht</string>
+ <string name="app_drawer_style_compact">Compact</string>
+ <string name="app_drawer_style_sections">Secties</string>
+ <string name="app_drawer_color">Kleur app-overzicht</string>
+ <string name="app_drawer_color_dark">Donker</string>
+ <string name="app_drawer_color_light">Licht</string>
+ <string name="fast_scroller">Snel bladeren</string>
+ <string name="fast_scroller_type">Richting snel bladeren</string>
+ <string name="fast_scroller_type_horizontal">Horizontaal</string>
+ <string name="fast_scroller_type_vertical">Verticaal</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Geen zoekactiviteit gevonden</string>
+</resources>
diff --git a/res/values-oc-rFR/cm_strings.xml b/res/values-oc-rFR/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-oc-rFR/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-oc-rFR/strings.xml b/res/values-oc-rFR/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-oc-rFR/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-or-rIN/cm_strings.xml b/res/values-or-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-or-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-pa-rIN/cm_strings.xml b/res/values-pa-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-pa-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-pl/cm_strings.xml b/res/values-pl/cm_strings.xml
new file mode 100644
index 000000000..bc4825b52
--- /dev/null
+++ b/res/values-pl/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">USTAWIENIA EKRANU GŁÓWNEGO</string>
+ <string name="drawer_settings">USTAWIENIA SZUFLADY APLIKACJI</string>
+ <string name="app_settings">USTAWIENIA APLIKACJI</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">WŁĄCZONE</string>
+ <string name="setting_state_off">WYŁĄCZONE</string>
+ <string name="setting_state_disabled">WYŁĄCZONE</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Przewijanie tapety</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rozmiar siatki</string>
+ <string name="grid_size_comfortable">Wygodny</string>
+ <string name="grid_size_cozy">Luźny</string>
+ <string name="grid_size_condensed">Zagęszczony</string>
+ <string name="grid_size_custom">Niestandardowy (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Wybierz własny rozmiar</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Potwierdź</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Pasek wyszukiwania</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Większe ikony</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etykiety ikon</string>
+ <string name="icon_labels_show">Pokaż</string>
+ <string name="icon_labels_hide">Ukryj</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Chronione aplikacje</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Styl szuflady</string>
+ <string name="app_drawer_style_compact">Kompaktowy</string>
+ <string name="app_drawer_style_sections">Sekcje</string>
+ <string name="app_drawer_color">Kolor szuflady</string>
+ <string name="app_drawer_color_dark">Ciemny</string>
+ <string name="app_drawer_color_light">Jasny</string>
+ <string name="fast_scroller">Szybkie przesuwanie</string>
+ <string name="fast_scroller_type">Typ szybkiego przesuwania</string>
+ <string name="fast_scroller_type_horizontal">Poziomy</string>
+ <string name="fast_scroller_type_vertical">Pionowo</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Nie można odnaleźć aktywności związanej z wyszukiwaniem!</string>
+</resources>
diff --git a/res/values-pt-rBR/cm_strings.xml b/res/values-pt-rBR/cm_strings.xml
new file mode 100644
index 000000000..a3a0eb0d1
--- /dev/null
+++ b/res/values-pt-rBR/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">CONFIGURAÇÕES DA TELA DE INÍCIO</string>
+ <string name="drawer_settings">CONFIGURAÇÕES DA GAVETA APLICAÇÕES</string>
+ <string name="app_settings">CONFIGURAÇÕES DE APLICATIVO</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">LIG</string>
+ <string name="setting_state_off">DESL</string>
+ <string name="setting_state_disabled">DESATIVADO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Reproduzir</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Rolar papel de parede</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Tamanho da grade</string>
+ <string name="grid_size_comfortable">Confortável</string>
+ <string name="grid_size_cozy">Aconchegante</string>
+ <string name="grid_size_condensed">Condensado</string>
+ <string name="grid_size_custom">Personalizado (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Selecione tamanho personalizado</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirmar</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barra de pesquisa</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Ícones grandes</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etiquetas de ícones</string>
+ <string name="icon_labels_show">Mostrar</string>
+ <string name="icon_labels_hide">Ocultar</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aplicativos protegidos</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Estilo da gaveta</string>
+ <string name="app_drawer_style_compact">Compacto</string>
+ <string name="app_drawer_style_sections">Seções</string>
+ <string name="app_drawer_color">Cor da gaveta</string>
+ <string name="app_drawer_color_dark">Escuro</string>
+ <string name="app_drawer_color_light">Clara</string>
+ <string name="fast_scroller">Rolagem rápida</string>
+ <string name="fast_scroller_type">Tipo de rolagem rápida</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Uma atividade de pesquisa não pôde ser encontrada!</string>
+</resources>
diff --git a/res/values-pt-rPT/cm_strings.xml b/res/values-pt-rPT/cm_strings.xml
new file mode 100644
index 000000000..76a1fb07f
--- /dev/null
+++ b/res/values-pt-rPT/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">DEFINIÇÕES DO ECRÃ INICIAL</string>
+ <string name="drawer_settings">DEFINIÇÕES DA GAVETA DE APLICAÇÕES</string>
+ <string name="app_settings">DEFINIÇÕES DA APLICAÇÃO</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">LIGADO</string>
+ <string name="setting_state_off">DESLIGADO</string>
+ <string name="setting_state_disabled">DESATIVADO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Deslizar imagem de fundo</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Tamanho da grelha</string>
+ <string name="grid_size_comfortable">Confortável</string>
+ <string name="grid_size_cozy">Cómodo</string>
+ <string name="grid_size_condensed">Condensado</string>
+ <string name="grid_size_custom">Personalizado (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Selecionar o tamanho personalizado</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirmar</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Barra de pesquisa</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Ícones grandes</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Legendas dos ícones</string>
+ <string name="icon_labels_show">Mostrar</string>
+ <string name="icon_labels_hide">Ocultar</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aplicações protegidas</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Estilo da gaveta de aplicações</string>
+ <string name="app_drawer_style_compact">Compacto</string>
+ <string name="app_drawer_style_sections">Secções</string>
+ <string name="app_drawer_color">Cor da gaveta de aplicações</string>
+ <string name="app_drawer_color_dark">Escura</string>
+ <string name="app_drawer_color_light">Clara</string>
+ <string name="fast_scroller">Deslocamento rápido</string>
+ <string name="fast_scroller_type">Tipo de deslocamento rápido</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Não foi encontrado nenhum gestor de pesquisas!</string>
+</resources>
diff --git a/res/values-rm/cm_strings.xml b/res/values-rm/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-rm/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ro/cm_strings.xml b/res/values-ro/cm_strings.xml
new file mode 100644
index 000000000..df6161c5a
--- /dev/null
+++ b/res/values-ro/cm_strings.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">SETĂRI ECRAN PRINCIPAL</string>
+ <string name="drawer_settings">SETĂRI ECRAN APLICAȚII</string>
+ <string name="app_settings">SETĂRI APLICAȚIE</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ACTIV</string>
+ <string name="setting_state_off">INACTIV</string>
+ <string name="setting_state_disabled">DEZACTIVAT</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Scroll-ează fundal</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Dimensiune grilă</string>
+ <string name="grid_size_comfortable">Confortabil</string>
+ <string name="grid_size_cozy">Comod</string>
+ <string name="grid_size_condensed">Condensat</string>
+ <string name="grid_size_custom">Particularizat (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Selectare dimensiune particularizată</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirmă</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Bară de căutare</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Icon-uri mai mari</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Etichete icon-uri</string>
+ <string name="icon_labels_show">Arată</string>
+ <string name="icon_labels_hide">Ascunde</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Aplicații protejate</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style_compact">Compact</string>
+ <string name="app_drawer_style_sections">Secțiuni</string>
+ <string name="app_drawer_color_dark">Întunecat</string>
+ <string name="fast_scroller">Derulare rapidă</string>
+ <string name="fast_scroller_type_horizontal">Orizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Imposibil de găsit o activitate de căutare!</string>
+</resources>
diff --git a/res/values-ru/cm_strings.xml b/res/values-ru/cm_strings.xml
new file mode 100644
index 000000000..444647220
--- /dev/null
+++ b/res/values-ru/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ДОМАШНИЙ ЭКРАН</string>
+ <string name="drawer_settings">МЕНЮ ПРИЛОЖЕНИЙ</string>
+ <string name="app_settings">ОБЩИЕ НАСТРОЙКИ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ВКЛ.</string>
+ <string name="setting_state_off">ВЫКЛ.</string>
+ <string name="setting_state_disabled">НЕДОСТУПНО</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Сервисы Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Прокрутка обоев</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Размер сетки</string>
+ <string name="grid_size_comfortable">Просторная</string>
+ <string name="grid_size_cozy">Удобная</string>
+ <string name="grid_size_condensed">Сжатая</string>
+ <string name="grid_size_custom">Своя (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Выберите размер сетки</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Подтвердить</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Строка поиска</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Крупные значки</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Подписи значков</string>
+ <string name="icon_labels_show">Показывать</string>
+ <string name="icon_labels_hide">Скрывать</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Заблокированные приложения</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Стиль меню</string>
+ <string name="app_drawer_style_compact">Компактный</string>
+ <string name="app_drawer_style_sections">Разделы</string>
+ <string name="app_drawer_color">Цвет меню</string>
+ <string name="app_drawer_color_dark">Темный</string>
+ <string name="app_drawer_color_light">Светлый</string>
+ <string name="fast_scroller">Быстрая прокрутка</string>
+ <string name="fast_scroller_type">Тип быстрой прокрутки</string>
+ <string name="fast_scroller_type_horizontal">Горизонтально</string>
+ <string name="fast_scroller_type_vertical">Вертикальное</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Невозможно запустить приложение поиска!</string>
+</resources>
diff --git a/res/values-si-rLK/cm_strings.xml b/res/values-si-rLK/cm_strings.xml
new file mode 100644
index 000000000..57df510b1
--- /dev/null
+++ b/res/values-si-rLK/cm_strings.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">මුල් තිරයේ සැකසීම්</string>
+ <string name="drawer_settings">ලාච්චුවේ සැකසීම්</string>
+ <string name="app_settings">යෙදුම් සැකසීම්</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">සක්‍රීයයි</string>
+ <string name="setting_state_off">අක්‍රීයයි</string>
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">වෝල්පේපරය අනුචලනය කරන්න</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">කොටු සැලැස්මේ ප්‍රමාණය</string>
+ <string name="grid_size_comfortable">සැපපහසු</string>
+ <string name="grid_size_cozy">ප්‍රියජනක</string>
+ <string name="grid_size_condensed">ඝනීභූත</string>
+ <string name="preferences_interface_homescreen_custom">රිසිකළ ප්‍රමාණය තෝරන්න</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">තහවුරු කරන්න</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">සෙවුම් තීරුව</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">විශාල අයිකන</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">අයිකන ලේබල</string>
+ <string name="icon_labels_show">පෙන්වන්න</string>
+ <string name="icon_labels_hide">සඟවන්න</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">ආරක්ෂිත යෙදුම්</string>
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-sk/cm_strings.xml b/res/values-sk/cm_strings.xml
new file mode 100644
index 000000000..8a9ac7821
--- /dev/null
+++ b/res/values-sk/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">NASTAVENIA DOMOVSKEJ OBRAZOVKY</string>
+ <string name="drawer_settings">NASTAVENIA PONUKY</string>
+ <string name="app_settings">NASTAVENIA APLIKÁCIÍ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">Zapnuté</string>
+ <string name="setting_state_off">VYPNUTÉ</string>
+ <string name="setting_state_disabled">ZAKÁZANÉ</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Posun tapety</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Veľkosť mriežky</string>
+ <string name="grid_size_comfortable">Pohodlná</string>
+ <string name="grid_size_cozy">Útulná</string>
+ <string name="grid_size_condensed">Zhustená</string>
+ <string name="grid_size_custom">Vlastná (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Vyberte vlastnú veľkosť</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Potvrdiť</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Vyhľadávací panel</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Väčšie ikony</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Menovky ikon</string>
+ <string name="icon_labels_show">Zobraziť</string>
+ <string name="icon_labels_hide">Skryť</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Chránené aplikácie</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Zvislé</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Aktivita vyhľadávania sa nenašla!</string>
+</resources>
diff --git a/res/values-sl/cm_strings.xml b/res/values-sl/cm_strings.xml
new file mode 100644
index 000000000..7ccd7c6f1
--- /dev/null
+++ b/res/values-sl/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">NASTAVITVE DOMAČEGA ZASLONA</string>
+ <string name="drawer_settings">NASTAVITVE PREDALA</string>
+ <string name="app_settings">NASTAVITVE APLIKACIJ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">VKL.</string>
+ <string name="setting_state_off">IZK.</string>
+ <string name="setting_state_disabled">ONEMOGOČENO</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Predvajaj</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Pomakni sliko ozadja</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Velikost mreže</string>
+ <string name="grid_size_comfortable">Udobna</string>
+ <string name="grid_size_cozy">Prijetna</string>
+ <string name="grid_size_condensed">Zgoščena</string>
+ <string name="grid_size_custom">Po meri (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Izberite velikost po meri</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Potrdi</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Iskalna vrstica</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Večje ikone</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Oznake ikon</string>
+ <string name="icon_labels_show">Prikaži</string>
+ <string name="icon_labels_hide">Skrij</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Zaščitene aplikacije</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Slog predala</string>
+ <string name="app_drawer_style_compact">Strnjen</string>
+ <string name="app_drawer_style_sections">Odseki</string>
+ <string name="app_drawer_color">Barva predala</string>
+ <string name="app_drawer_color_dark">Temna</string>
+ <string name="app_drawer_color_light">Svetla</string>
+ <string name="fast_scroller">Hiter drsnik</string>
+ <string name="fast_scroller_type">Vrsta hitrega drsnika</string>
+ <string name="fast_scroller_type_horizontal">Vodoravno</string>
+ <string name="fast_scroller_type_vertical">Navpična</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Dejavnosti iskanja ni bilo mogoče najti!</string>
+</resources>
diff --git a/res/values-sq-rAL/cm_strings.xml b/res/values-sq-rAL/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-sq-rAL/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-sr/cm_strings.xml b/res/values-sr/cm_strings.xml
new file mode 100644
index 000000000..57409f81c
--- /dev/null
+++ b/res/values-sr/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ПОДЕШАВАЊА ПОЧЕТНОГ ЕКРАНА</string>
+ <string name="drawer_settings">ПОДЕШАВАЊА ФИОКЕ</string>
+ <string name="app_settings">ПОДЕШАВАЊА АПЛИКАЦИЈА</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">ДА</string>
+ <string name="setting_state_off">НЕ</string>
+ <string name="setting_state_disabled">ОНЕМОГУЋЕНО</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Пусти</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Скроловање позадине</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Величина мреже</string>
+ <string name="grid_size_comfortable">Комфорно</string>
+ <string name="grid_size_cozy">Удобно</string>
+ <string name="grid_size_condensed">Кондензовано</string>
+ <string name="grid_size_custom">Прилагођено (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Изабери прилагођену величину</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Потврди</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Трака за претрагу</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Веће иконе</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ознаке икона</string>
+ <string name="icon_labels_show">Прикажи</string>
+ <string name="icon_labels_hide">Сакриј</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Заштићене апликације</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Стил фиоке</string>
+ <string name="app_drawer_style_compact">Компактан</string>
+ <string name="app_drawer_style_sections">Одељци</string>
+ <string name="app_drawer_color">Боја фиоке</string>
+ <string name="app_drawer_color_dark">Мрак</string>
+ <string name="app_drawer_color_light">Светло</string>
+ <string name="fast_scroller">Брзи скролер</string>
+ <string name="fast_scroller_type">Тип брзог скролера</string>
+ <string name="fast_scroller_type_horizontal">Хоризонтални</string>
+ <string name="fast_scroller_type_vertical">Вертикално</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Претрага активности не може бити пронађена!</string>
+</resources>
diff --git a/res/values-sv/cm_strings.xml b/res/values-sv/cm_strings.xml
new file mode 100644
index 000000000..b451d1c7e
--- /dev/null
+++ b/res/values-sv/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">HEMSKÄRMSINSTÄLLNINGAR</string>
+ <string name="drawer_settings">APPMENYINSTÄLLNINGAR</string>
+ <string name="app_settings">APPINSTÄLLNINGAR</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">PÅ</string>
+ <string name="setting_state_off">AV</string>
+ <string name="setting_state_disabled">INAKTIVERAD</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Spela</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Rulla bakgrundsbild</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Rutnätsstorlek</string>
+ <string name="grid_size_comfortable">Komfortabel</string>
+ <string name="grid_size_cozy">Mysig</string>
+ <string name="grid_size_condensed">Kondenserad</string>
+ <string name="grid_size_custom">Anpassad (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Välj anpassad storlek</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Bekräfta</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Sökfält</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Större ikoner</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Ikonetiketter</string>
+ <string name="icon_labels_show">Visa</string>
+ <string name="icon_labels_hide">Dölj</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Skyddade appar</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Stil på appmenyn</string>
+ <string name="app_drawer_style_compact">Kompakt</string>
+ <string name="app_drawer_style_sections">Sektioner</string>
+ <string name="app_drawer_color">Färg på appmenyn</string>
+ <string name="app_drawer_color_dark">Mörk</string>
+ <string name="app_drawer_color_light">Ljus</string>
+ <string name="fast_scroller">Snabbrullare</string>
+ <string name="fast_scroller_type">Typ av snabbrullare</string>
+ <string name="fast_scroller_type_horizontal">Horisontell</string>
+ <string name="fast_scroller_type_vertical">Vertikal</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Kunde inte hitta en sökaktivitet!</string>
+</resources>
diff --git a/res/values-sw/cm_strings.xml b/res/values-sw/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-sw/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index a7345a705..6e14e88a1 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -1,6 +1,5 @@
<resources>
<bool name="is_tablet">true</bool>
- <bool name="allow_rotation">true</bool>
<!-- DragController -->
<integer name="config_flingToDeleteMinVelocity">-1000</integer>
diff --git a/res/values-sw600dp/preferences_defaults.xml b/res/values-sw600dp/preferences_defaults.xml
new file mode 100644
index 000000000..b289d625e
--- /dev/null
+++ b/res/values-sw600dp/preferences_defaults.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <bool name="preferences_interface_allow_rotation">true</bool>
+</resources>
diff --git a/res/values-ta-rIN/cm_strings.xml b/res/values-ta-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ta-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-te-rIN/cm_strings.xml b/res/values-te-rIN/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-te-rIN/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-th/cm_strings.xml b/res/values-th/cm_strings.xml
new file mode 100644
index 000000000..5a036c7d8
--- /dev/null
+++ b/res/values-th/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">การตั้งค่าหน้าจอหลัก</string>
+ <string name="drawer_settings">การตั้งค่าแผงแสดงแอป</string>
+ <string name="app_settings">การตั้งค่าแอป</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">เปิด</string>
+ <string name="setting_state_off">ปิด</string>
+ <string name="setting_state_disabled">ปิดการใช้งาน</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">เล่น</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">เลื่อนภาพพื้นหลัง</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">ขนาดกริด</string>
+ <string name="grid_size_comfortable">สะดวก</string>
+ <string name="grid_size_cozy">โคซี่</string>
+ <string name="grid_size_condensed">บีบ</string>
+ <string name="grid_size_custom">กำหนดเอง (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">เลือกขนาดที่กำหนดเอง</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">ยืนยัน</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">แถบค้นหา</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">ไอคอนใหญ่ขึ้น</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">ป้ายชื่อไอคอน</string>
+ <string name="icon_labels_show">แสดง</string>
+ <string name="icon_labels_hide">ซ่อน</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">แอปที่ได้รับการปกป้อง</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">รูปแบบหน้าแอป</string>
+ <string name="app_drawer_style_compact">กระชับ</string>
+ <string name="app_drawer_style_sections">หมวดหมู่</string>
+ <string name="app_drawer_color">สีหน้าแอป</string>
+ <string name="app_drawer_color_dark">มืด</string>
+ <string name="app_drawer_color_light">สว่าง</string>
+ <string name="fast_scroller">เลื่อนอย่างเร็ว</string>
+ <string name="fast_scroller_type">ชนิดการเลื่อนอย่างเร็ว</string>
+ <string name="fast_scroller_type_horizontal">แนวนอน</string>
+ <string name="fast_scroller_type_vertical">แนวตั้ง</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">ไม่พบกิจกรรมการค้นหา!</string>
+</resources>
diff --git a/res/values-tr/cm_strings.xml b/res/values-tr/cm_strings.xml
new file mode 100644
index 000000000..00483adbf
--- /dev/null
+++ b/res/values-tr/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">ANA EKRAN AYARLARI</string>
+ <string name="drawer_settings">ÇEKMECE AYARLARI</string>
+ <string name="app_settings">UYGULAMA AYARLARI</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">AÇIK</string>
+ <string name="setting_state_off">KAPALI</string>
+ <string name="setting_state_disabled">DEVRE DIŞI</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Duvarkağıdını kaydır</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Izgara boyutu</string>
+ <string name="grid_size_comfortable">Seyrek</string>
+ <string name="grid_size_cozy">Rahat</string>
+ <string name="grid_size_condensed">Yoğun</string>
+ <string name="grid_size_custom">Özel (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Özel boyut seçin</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Onayla</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Arama çubuğu</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Daha büyük simgeler</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Simge etiketleri</string>
+ <string name="icon_labels_show">Göster</string>
+ <string name="icon_labels_hide">Gizle</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Korunan uygulamalar</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Çekmece stili</string>
+ <string name="app_drawer_style_compact">Kompakt</string>
+ <string name="app_drawer_style_sections">Bölümler</string>
+ <string name="app_drawer_color">Çekmece rengi</string>
+ <string name="app_drawer_color_dark">Karanlık</string>
+ <string name="app_drawer_color_light">Aydınlık</string>
+ <string name="fast_scroller">Hızlı kaydırıcı</string>
+ <string name="fast_scroller_type">Hızlı kaydırıcı türü</string>
+ <string name="fast_scroller_type_horizontal">Yatay</string>
+ <string name="fast_scroller_type_vertical">Dikey</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Arama etkinliği bulunamadı!</string>
+</resources>
diff --git a/res/values-ug/cm_strings.xml b/res/values-ug/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ug/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-ug/strings.xml b/res/values-ug/strings.xml
new file mode 100644
index 000000000..d8fc2a04b
--- /dev/null
+++ b/res/values-ug/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- General -->
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent. [DO NOT TRANSLATE] -->
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent. [DO NOT TRANSLATE] -->
+ <!-- Application name -->
+ <!-- Default folder name -->
+ <!-- Work folder name -->
+ <!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
+ <!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
+ <!-- SafeMode shortcut error string -->
+ <!-- SafeMode widget error string -->
+ <!-- Widgets -->
+ <!-- Message to tell the user to press and hold on a widget to add it [CHAR_LIMIT=50] -->
+ <!-- The format string for the dimensions of a widget in the drawer -->
+ <!-- There is a special version of this format string for Farsi -->
+ <!-- All Apps -->
+ <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
+ <!-- Loading apps text. [CHAR_LIMIT=50] -->
+ <!-- No-search-results text. [CHAR_LIMIT=50] -->
+ <!-- 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] -->
+ <!-- Drag and drop -->
+ <!-- Error message when user has filled a home screen -->
+ <!-- Error message when user has filled the hotseat -->
+ <!-- All applications label -->
+ <!-- Label for button in all applications label to go back home (to the workspace / desktop)
+ for accessibilty (spoken when the button gets focus). -->
+ <!-- Label for delete drop target. [CHAR_LIMIT=20] -->
+ <!-- Label for uninstall drop target. [CHAR_LIMIT=20]-->
+ <!-- Label for the info icon. [CHAR_LIMIT=20] -->
+ <!-- Permissions: -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Permission short label -->
+ <!-- Permission description -->
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <!-- Widgets: -->
+ <!-- Text to show user in place of a gadget when we can't display it properly -->
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <!-- Text to inform the user that they can't uninstall a system application -->
+ <!-- Default folder title -->
+ <!-- Accessibility -->
+ <!-- The format string for default page scroll text [CHAR_LIMIT=none] -->
+ <!-- The format string for Workspace page scroll text [CHAR_LIMIT=none] -->
+ <!-- Clings -->
+ <!-- The title text for the workspace cling [CHAR_LIMIT=30] -->
+ <!-- The title text for the migration cling [CHAR_LIMIT=30] -->
+ <!-- The description of what migration does [CHAR_LIMIT=70] -->
+ <!-- The description of the button to migrate apps from another launcher [CHAR_LIMIT=30] -->
+ <!-- The description of the button to use the default launcher layout [CHAR_LIMIT=30] -->
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <!-- Folder accessibility -->
+ <!-- The format string for when a folder is opened, speaks the dimensions -->
+ <!-- Instruction that clicking outside will close folder -->
+ <!-- Instruction that clicking outside will commit folder rename -->
+ <!-- Indication that folder closed -->
+ <!-- Folder renamed format -->
+ <!-- Folder name format -->
+ <!-- Debug-only activity name. [DO NOT TRANSLATE] -->
+ <!-- Strings for the customization mode -->
+ <!-- Text for widget add button -->
+ <!-- Text for wallpaper change button -->
+ <!-- Text for settings button -->
+ <!-- Strings for settings -->
+ <!-- Title for Allow Rotation setting. [CHAR LIMIT=50] -->
+ <!-- Label on an icon that references an uninstalled package, for which we have no information about when it might be installed. [CHAR_LIMIT=15] -->
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <!-- Title for abandoned promise dialog. -->
+ <!-- Explanation for abandoned promise dialog. "The first 'it' refers to the shortcut icon.
+ The second "it" refers to the app. -->
+ <!-- Strings for accessibility actions -->
+ <!-- Accessibility action to add an app to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move item to the current location. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for item added to workspace. -->
+ <!-- Accessibility confirmation for item removed. -->
+ <!-- Accessibility action to move an item on the workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility description to move item to empty cell. -->
+ <!-- Accessibility description to move item inside a folder. -->
+ <!-- Accessibility description to move item to the hotseat. -->
+ <!-- Accessibility confirmation for item move. -->
+ <!-- Accessibility description to move item into an existing folder. -->
+ <!-- Accessibility description to move item into an existing folder containing an app. -->
+ <!-- Accessibility confirmation for item added to folder. -->
+ <!-- Accessibility description to create folder with another item. -->
+ <!-- Accessibility confirmation for folder created. -->
+ <!-- Accessibility action to move an item from folder to workspace. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the left. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to move an homescreen to the right. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation when a screen was moved. -->
+ <!-- Accessibility action to resize a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to increase height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease width of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility action to decrease height of a widget. [CHAR_LIMIT=30] -->
+ <!-- Accessibility confirmation for widget resize. -->
+</resources>
diff --git a/res/values-uk/cm_strings.xml b/res/values-uk/cm_strings.xml
new file mode 100644
index 000000000..ff9e2e446
--- /dev/null
+++ b/res/values-uk/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">НАЛАШТУВАННЯ ДОМІВКИ</string>
+ <string name="drawer_settings">МЕНЮ ПРОГРАМ</string>
+ <string name="app_settings">НАЛАШТУВАННЯ ПРОГРАМ</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">УВІМК</string>
+ <string name="setting_state_off">ВИМК</string>
+ <string name="setting_state_disabled">ВИМКНЕНО</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Прокручувати шпалери</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Розмір сітки</string>
+ <string name="grid_size_comfortable">Комфортна</string>
+ <string name="grid_size_cozy">Зручна</string>
+ <string name="grid_size_condensed">Стиснута</string>
+ <string name="grid_size_custom">Користувацький (<xliff:g id="rows">%1$d</xliff:g>\u00d7<xliff:g id="columns">%2$d </xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Виберіть розмір сітки</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Підтвердити</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Рядок пошуку</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Великі піктограми</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Мітки піктограм</string>
+ <string name="icon_labels_show">Показати</string>
+ <string name="icon_labels_hide">Приховати</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Заблоковані програми</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Вертикальне</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Діяльність з пошуку не виявлена!</string>
+</resources>
diff --git a/res/values-ur-rPK/cm_strings.xml b/res/values-ur-rPK/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-ur-rPK/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-uz-rUZ/cm_strings.xml b/res/values-uz-rUZ/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-uz-rUZ/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values-vi/cm_strings.xml b/res/values-vi/cm_strings.xml
new file mode 100644
index 000000000..cfb26b38f
--- /dev/null
+++ b/res/values-vi/cm_strings.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">CÀI ĐẶT MÀN HÌNH CHÍNH</string>
+ <string name="drawer_settings">CÀI ĐẶT BẢNG</string>
+ <string name="app_settings">CÀI ĐẶT ỨNG DỤNG</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">BẬT</string>
+ <string name="setting_state_off">TẮT</string>
+ <string name="setting_state_disabled">ĐÃ TẮT</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Phát</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Cuộn hình nền</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Kích thước lưới</string>
+ <string name="grid_size_comfortable">Thoải mái</string>
+ <string name="grid_size_cozy">Ấm cúng</string>
+ <string name="grid_size_condensed">Đã rút gọn</string>
+ <string name="grid_size_custom">Tùy chỉnh (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Chọn kích thước tùy chỉnh</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">Xác nhận</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Thanh tìm kiếm</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Biểu tượng lớn hơn</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">Nhãn biểu tượng</string>
+ <string name="icon_labels_show">Hiện</string>
+ <string name="icon_labels_hide">Ẩn</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Các ứng dụng được bảo vệ</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">Theo chiều dọc</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">Không tìm thấy hoạt động tìm kiếm!</string>
+</resources>
diff --git a/res/values-zh-rCN/cm_strings.xml b/res/values-zh-rCN/cm_strings.xml
new file mode 100644
index 000000000..96dbc4ef8
--- /dev/null
+++ b/res/values-zh-rCN/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">主屏幕设置</string>
+ <string name="drawer_settings">抽屉设置</string>
+ <string name="app_settings">应用设置</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">打开</string>
+ <string name="setting_state_off">关闭</string>
+ <string name="setting_state_disabled">已禁用</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">Play</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">滚动壁纸</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">网格大小</string>
+ <string name="grid_size_comfortable">宽松</string>
+ <string name="grid_size_cozy">合适</string>
+ <string name="grid_size_condensed">紧凑</string>
+ <string name="grid_size_custom">自定义(<xliff:g id="rows">%1$d</xliff:g>\u00d7<xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">选择自定义尺寸</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">确认</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">搜索栏</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">大图标</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">图标标签</string>
+ <string name="icon_labels_show">显示</string>
+ <string name="icon_labels_hide">隐藏</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">受保护的应用</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">抽屉样式</string>
+ <string name="app_drawer_style_compact">紧凑</string>
+ <string name="app_drawer_style_sections">分组</string>
+ <string name="app_drawer_color">抽屉颜色</string>
+ <string name="app_drawer_color_dark">暗色</string>
+ <string name="app_drawer_color_light">亮色</string>
+ <string name="fast_scroller">快速滚动</string>
+ <string name="fast_scroller_type">快速滚动类型</string>
+ <string name="fast_scroller_type_horizontal">横向</string>
+ <string name="fast_scroller_type_vertical">垂直</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">无法找到搜索活动!</string>
+</resources>
diff --git a/res/values-zh-rHK/cm_strings.xml b/res/values-zh-rHK/cm_strings.xml
new file mode 100644
index 000000000..18f10525a
--- /dev/null
+++ b/res/values-zh-rHK/cm_strings.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">主螢幕設定</string>
+ <string name="drawer_settings">抽屜設定</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">開啟</string>
+ <string name="setting_state_off">關閉</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">播放</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">捲動牆紙</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">格子大小</string>
+ <string name="grid_size_comfortable">舒適</string>
+ <string name="grid_size_cozy">愜意</string>
+ <string name="grid_size_condensed">濃縮</string>
+ <string name="grid_size_custom">自訂 (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">選擇自訂大小</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">確認</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">搜尋列</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">大圖示</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">圖示標籤</string>
+ <string name="icon_labels_show">顯示</string>
+ <string name="icon_labels_hide">隱藏</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">受保護的應用程式</string>
+ <!-- Drawer settings -->
+ <string name="fast_scroller_type_vertical">垂直</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">找不到搜尋活動!</string>
+</resources>
diff --git a/res/values-zh-rTW/cm_strings.xml b/res/values-zh-rTW/cm_strings.xml
new file mode 100644
index 000000000..fda048147
--- /dev/null
+++ b/res/values-zh-rTW/cm_strings.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="home_screen_settings">主螢幕設定</string>
+ <string name="drawer_settings">抽屜設定</string>
+ <string name="app_settings">應用程式設定</string>
+ <!-- Settings states -->
+ <string name="setting_state_on">開啟</string>
+ <string name="setting_state_off">關閉</string>
+ <string name="setting_state_disabled">已停用</string>
+ <!-- Folder titles -->
+ <string name="play_folder_title">播放</string>
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">滾動桌面</string>
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">網格大小</string>
+ <string name="grid_size_comfortable">舒適</string>
+ <string name="grid_size_cozy">自在</string>
+ <string name="grid_size_condensed">密集</string>
+ <string name="grid_size_custom">自訂 (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">選擇自訂大小</string>
+ <!-- Dialog -->
+ <string name="dialog_confirm">確認</string>
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">搜尋列</string>
+ <!-- Larger icons -->
+ <string name="larger_icons_text">大圖示</string>
+ <!-- Icon labels -->
+ <string name="icon_labels">程式名稱</string>
+ <string name="icon_labels_show">顯示</string>
+ <string name="icon_labels_hide">隱藏</string>
+ <!-- Protected apps -->
+ <string name="protected_app_settings">受保護的應用程式</string>
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">程式集風格</string>
+ <string name="app_drawer_style_compact">連續</string>
+ <string name="app_drawer_style_sections">分頁</string>
+ <string name="app_drawer_color">程式集色彩</string>
+ <string name="app_drawer_color_dark">深色</string>
+ <string name="app_drawer_color_light">淺色</string>
+ <string name="fast_scroller">快速捲動</string>
+ <string name="fast_scroller_type">快速捲動類型</string>
+ <string name="fast_scroller_type_horizontal">橫向</string>
+ <string name="fast_scroller_type_vertical">垂直</string>
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">找不到搜索活動 !</string>
+</resources>
diff --git a/res/values-zu/cm_strings.xml b/res/values-zu/cm_strings.xml
new file mode 100644
index 000000000..bdeba326d
--- /dev/null
+++ b/res/values-zu/cm_strings.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--Generated by crowdin.com-->
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <!-- Settings states -->
+ <!-- Folder titles -->
+ <!-- Wallpaper scroll effect -->
+ <!-- Dynamic Grid -->
+ <!-- Dialog -->
+ <!-- Home screen search bar -->
+ <!-- Larger icons -->
+ <!-- Icon labels -->
+ <!-- Protected apps -->
+ <!-- Drawer settings -->
+ <!-- Search Manager doesn't exist -->
+</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 827332ad7..787e5ca23 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -113,7 +113,35 @@
<attr name="indicatorSize" format="dimension" />
</declare-styleable>
+ <attr name="layout_ignoreInsets" format="boolean" />
+ <attr name="layout_ignoreBottomInsets" format="boolean" />
+ <attr name="layout_ignoreTopInsets" format="boolean" />
+
<declare-styleable name="InsettableFrameLayout_Layout">
- <attr name="layout_ignoreInsets" format="boolean" />
+ <attr name="layout_ignoreInsets" />
+ <attr name="layout_ignoreTopInsets" />
+ <attr name="layout_ignoreBottomInsets" />
+ </declare-styleable>
+
+ <declare-styleable name="InsettableLinearLayout_Layout">
+ <attr name="layout_ignoreInsets" />
+ <attr name="layout_ignoreTopInsets" />
+ <attr name="layout_ignoreBottomInsets" />
+ </declare-styleable>
+
+ <declare-styleable name="AutofitTextView">
+ <attr name="minTextSize" format="dimension" />
+ <attr name="precision" format="float" />
+ <attr name="sizeToFit" format="boolean" />
+ </declare-styleable>
+
+ <declare-styleable name="VerticalSlidingPanel">
+ <attr name="panelHeight" format="dimension" />
+ <attr name="shadowHeight" format="dimension" />
+ <attr name="parallaxOffset" format="dimension" />
+ <attr name="fadeColor" format="color" />
+ <attr name="flingVelocity" format="integer" />
+ <attr name="dragView" format="reference" />
+ <attr name="overlay" format="boolean"/>
</declare-styleable>
</resources>
diff --git a/res/values/cm_colors.xml b/res/values/cm_colors.xml
new file mode 100644
index 000000000..85d4cc8cf
--- /dev/null
+++ b/res/values/cm_colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="workspace_cling_longpress_title_color">#E1000000</color>
+ <color name="workspace_cling_longpress_description_color">#99000000</color>
+ <color name="workspace_cling_longpress_dismiss_text_color">#FFFFFFFF</color>
+ <color name="first_run_cling_title_color">#E1000000</color>
+ <color name="folder_name_highlight_text_color">#ffCCCCCC</color>
+ <color name="folder_name_hint_text_color">#ff808080</color>
+ <color name="all_apps_empty_search_text_color">#212121</color>
+ <color name="search_box_input_text_color">#4c4c4c</color>
+ <color name="search_box_input_hint_text_color">#9c9c9c</color>
+ <color name="migration_cling_background_color">#FF009688</color>
+ <color name="migration_cling_description_text_color">#99000000</color>
+ <color name="migration_cling_copy_apps_text_color">#FFFFFFFF</color>
+ <color name="migration_cling_use_default_text_color">#deFFFFFF</color>
+ <color name="wallpaper_button_text_color">@android:color/white</color>
+ <color name="widget_button_text_color">@android:color/white</color>
+ <color name="settings_button_text_color">@android:color/white</color>
+ <color name="overview_panel_background_color">@android:color/background_dark</color>
+ <color name="scrubber_container_text_color">@android:color/black</color>
+ <color name="scrubber_text_color">@android:color/white</color>
+</resources>
diff --git a/res/values/cm_strings.xml b/res/values/cm_strings.xml
new file mode 100644
index 000000000..8d3527e9f
--- /dev/null
+++ b/res/values/cm_strings.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The CyanogenMod Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Application name -->
+ <string name="cm_application_name" translatable="false">Trebuchet</string>
+
+ <string name="home_screen_settings">HOME SCREEN SETTINGS</string>
+ <string name="drawer_settings">DRAWER SETTINGS</string>
+ <string name="app_settings">APP SETTINGS</string>
+
+ <!-- Settings states -->
+ <string name="setting_state_on">ON</string>
+ <string name="setting_state_off">OFF</string>
+ <string name="setting_state_disabled">DISABLED</string>
+
+ <!-- Folder titles -->
+ <string name="google_title" translatable="false">Google</string>
+ <string name="play_folder_title">Play</string>
+ <string name="partner_title" translatable="false"></string>
+
+ <!-- Wallpaper scroll effect -->
+ <string name="scrolling_wallpaper">Scroll wallpaper</string>
+
+ <!-- Dynamic Grid -->
+ <string name="grid_size_text">Grid size</string>
+ <string name="grid_size_comfortable">Comfortable</string>
+ <string name="grid_size_cozy">Cozy</string>
+ <string name="grid_size_condensed">Condensed</string>
+ <string name="grid_size_custom">Custom (<xliff:g id="rows">%1$d</xliff:g> \u00d7 <xliff:g id="columns">%2$d</xliff:g>)</string>
+ <string name="preferences_interface_homescreen_custom">Select custom size</string>
+
+ <!-- Dialog -->
+ <string name="dialog_confirm">Confirm</string>
+
+ <!-- Home screen search bar -->
+ <string name="home_screen_search_text">Search bar</string>
+
+ <!-- Larger icons -->
+ <string name="larger_icons_text">Larger icons</string>
+
+ <!-- Icon labels -->
+ <string name="icon_labels">Icon labels</string>
+ <string name="icon_labels_show">Show</string>
+ <string name="icon_labels_hide">Hide</string>
+
+ <!-- Protected apps -->
+ <string name="protected_app_settings">Protected apps</string>
+
+ <!-- Drawer settings -->
+ <string name="app_drawer_style">Drawer style</string>
+ <string name="app_drawer_style_compact">Compact</string>
+ <string name="app_drawer_style_sections">Sections</string>
+ <string name="app_drawer_color">Drawer color</string>
+ <string name="app_drawer_color_dark">Dark</string>
+ <string name="app_drawer_color_light">Light</string>
+ <string name="fast_scroller">Fast scroller</string>
+ <string name="fast_scroller_type">Fast scroller type</string>
+ <string name="fast_scroller_type_horizontal">Horizontal</string>
+ <string name="fast_scroller_type_vertical">Vertical</string>
+
+ <!-- Search Manager doesn't exist -->
+ <string name="search_activity_not_found">A search activity could not be found!</string>
+</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 8a7f62743..3ab7f8800 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -33,8 +33,9 @@
<color name="folder_edge_effect_color">#FF757575</color>
<color name="quantum_panel_text_color">#FF666666</color>
+ <color name="quantum_panel_text_color_dark">#FFF</color>
<color name="quantum_panel_bg_color">#FFF5F5F5</color>
- <color name="quantum_panel_bg_color_dark">#FF374248</color>
+ <color name="quantum_panel_bg_color_dark">#76000000</color>
<color name="outline_color">#FFFFFFFF</color>
@@ -42,12 +43,33 @@
<color name="container_fastscroll_thumb_inactive_color">#009688</color>
<color name="container_fastscroll_thumb_active_color">#009688</color>
+ <color name="settings_header_text">#FF6cd2ea</color>
+ <color name="settings_bg_color">#FF485459</color>
+ <color name="settings_bg_header_color">#FFb2b0ab</color>
+ <color name="settings_bg_selected_color">#26000000</color>
+ <color name="settings_transition_selected_color">#50000000</color>
+
<!-- All Apps -->
<color name="all_apps_grid_section_text_color">#009688</color>
+ <color name="all_apps_grid_section_text_color_dark">#FFF</color>
<color name="all_apps_search_market_button_focused_bg_color">#DDDDDD</color>
+ <color name="drawer_divider_light">#14000000</color>
+ <color name="drawer_divider_dark">#14ffffff</color>
+
<!-- Widgets view -->
<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>
+
+ <!-- CM Settings -->
+ <color name="settings_bg_color">#424242</color>
+ <color name="settings_header_text">#00B1E5</color>
+
+ <color name="dynamic_grid_preview_background">#FFFFFFFF</color>
+ <color name="dynamic_grid_preview_foreground">#FF000000</color>
+
+ <color name="app_scrubber_highlight_color">@android:color/white</color>
+ <color name="app_scrubber_gray_color">@android:color/darker_gray</color>
+ <color name="scrubber_background">#CC14191E</color>
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index 93c6d14f4..4d3f01111 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -7,7 +7,6 @@
<bool name="config_largeHeap">false</bool>
<bool name="is_tablet">false</bool>
<bool name="is_large_tablet">false</bool>
- <bool name="allow_rotation">false</bool>
<!-- Max number of page indicators to show -->
<integer name="config_maxNumberOfPageIndicatorsToShow">21</integer>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 36721797e..dfd9428bd 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -28,6 +28,9 @@
<dimen name="dynamic_grid_overview_max_icon_zone_height">120dp</dimen>
<dimen name="dynamic_grid_overview_bar_item_width">80dp</dimen>
<dimen name="dynamic_grid_overview_bar_spacer_width">20dp</dimen>
+ <dimen name="dialog_padding">10dp</dimen>
+ <dimen name="grid_padding">15dp</dimen>
+ <dimen name="grid_custom_text">50dp</dimen>
<!-- App Widget resize frame -->
<dimen name="default_widget_padding">8dp</dimen>
@@ -44,6 +47,15 @@
<!-- Workspace -->
<dimen name="workspace_max_gap">16dp</dimen>
+ <dimen name="workspace_overscroll_drawable_padding">0dp</dimen>
+ <dimen name="workspace_spring_loaded_page_spacing">15dp</dimen>
+ <dimen name="overview_panel_top_padding">20dp</dimen>
+ <dimen name="overview_panel_bottom_padding">50dp</dimen>
+ <dimen name="overview_panel_button_spacing">30dp</dimen>
+ <dimen name="overview_panel_list_padding">16dp</dimen>
+ <dimen name="overview_mode_page_offset">60dp</dimen>
+ <dimen name="sliding_panel_padding">175dp</dimen>
+ <dimen name="overview_scaling_padding">50dp</dimen>
<!-- QSB -->
<dimen name="toolbar_button_vertical_padding">4dip</dimen>
@@ -64,20 +76,21 @@
<!-- All Apps -->
<dimen name="all_apps_grid_view_start_margin">0dp</dimen>
+ <dimen name="all_apps_grid_view_start_margin_with_sections">36dp</dimen>
<dimen name="all_apps_grid_section_y_offset">8dp</dimen>
<dimen name="all_apps_grid_section_text_size">24sp</dimen>
<dimen name="all_apps_search_bar_height">48dp</dimen>
<dimen name="all_apps_search_bar_prediction_bar_padding">8dp</dimen>
<dimen name="all_apps_icon_top_bottom_padding">8dp</dimen>
<dimen name="all_apps_icon_width_gap">24dp</dimen>
- <!-- The top padding should account for the existing all_apps_list_top_bottom_padding -->
- <dimen name="all_apps_prediction_icon_top_padding">8dp</dimen>
- <dimen name="all_apps_prediction_icon_bottom_padding">18dp</dimen>
+ <dimen name="all_apps_icon_width_gap_with_sections">12dp</dimen>
+ <dimen name="all_apps_icon_size_with_sections">48dp</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>
+ <dimen name="all_apps_prediction_bar_divider_offset">5dp</dimen>
<!-- Widget tray -->
<dimen name="widget_container_inset">8dp</dimen>
@@ -121,6 +134,14 @@
<!-- Folders -->
<!-- The amount that the preview contents are inset from the preview background -->
<dimen name="folder_preview_padding">4dp</dimen>
+ <dimen name="folder_lock_margin">6dp</dimen>
+ <dimen name="folder_name_padding">10dp</dimen>
+ <dimen name="folder_lock_icon">48dp</dimen>
+ <dimen name="folder_margin">24dp</dimen>
+
+ <!-- Folder icon dimens -->
+ <dimen name="folder_icon">64dp</dimen>
+ <dimen name="folder_icon_app_preview">22dp</dimen>
<!-- Sizes for managed profile badges -->
<dimen name="profile_badge_size">24dp</dimen>
@@ -137,4 +158,24 @@
<dimen name="pending_widget_min_padding">8dp</dimen>
<dimen name="pending_widget_elevation">2dp</dimen>
+ <!-- Scrubber -->
+ <dimen name="scrubber_bottom_padding">30dp</dimen>
+ <dimen name="scrubber_height">48dp</dimen>
+ <dimen name="app_drawer_scrubber_padding">20dp</dimen>
+
+<!-- Folder open animation -->
+ <integer name="folder_translate_y_dist">300</integer>
+ <integer name="folder_icon_translate_y_dist">100</integer>
+
+<!-- Overview panel -->
+ <dimen name="sliding_panel_padding">175dp</dimen>
+ <dimen name="overview_panel_list_header_height">48dp</dimen>
+ <dimen name="overview_panel_list_item_height">56dp</dimen>
+ <dimen name="overview_panel_top_padding">20dp</dimen>
+ <dimen name="overview_panel_bottom_padding">50dp</dimen>
+ <dimen name="overview_panel_button_spacing">30dp</dimen>
+ <dimen name="overview_panel_list_padding">16dp</dimen>
+ <dimen name="overview_mode_page_offset">60dp</dimen>
+ <dimen name="overview_scaling_padding">50dp</dimen>
+
</resources>
diff --git a/res/values/preferences_defaults.xml b/res/values/preferences_defaults.xml
new file mode 100644
index 000000000..63c5e9c19
--- /dev/null
+++ b/res/values/preferences_defaults.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <bool name="preferences_interface_homescreen_search_default">true</bool>
+ <bool name="preferences_interface_homescreen_scrolling_wallpaper_scroll_default">true</bool>
+ <bool name="preferences_interface_homescreen_hide_icon_labels_default">false</bool>
+ <bool name="preferences_interface_drawer_hide_icon_labels_default">false</bool>
+ <bool name="preferences_interface_drawer_compact_default">false</bool>
+ <bool name="preferences_interface_drawer_dark_default">true</bool>
+ <bool name="preferences_interface_general_icons_large_default">false</bool>
+ <bool name="preferences_interface_use_scroller_default">true</bool>
+ <bool name="preferences_interface_use_horizontal_scrubber_default">true</bool>
+ <bool name="preferences_interface_drawer_search_default">true</bool>
+ <bool name="preferences_interface_allow_rotation">false</bool>
+ <bool name="preferences_interface_homescreen_remote_folder_default">false</bool>
+ <bool name="preferences_interface_drawer_remote_apps_default">false</bool>
+ <integer name="preferences_interface_homescreen_id_default">1</integer>
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index fefadef28..b54478860 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -31,7 +31,7 @@
<string name="receive_first_load_broadcast_permission" translatable="false">com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST</string>
<!-- Application name -->
- <string name="application_name">Launcher3</string>
+ <string name="app_name">Launcher3</string>
<!-- Default folder name -->
<string name="folder_name"></string>
<!-- Work folder name -->
@@ -103,6 +103,9 @@
<string name="permdesc_write_settings">Allows the app to change the settings and
shortcuts in Home.</string>
+ <!-- Toast shown on clicking a direct call shortcut. [CHAR_LIMIT=80] -->
+ <string name="msg_no_phone_permission"><xliff:g id="app_name" example="Launcher3">%1$s</xliff:g> is not allowed to make phone calls</string>
+
<!-- Widgets: -->
<skip />
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 7d60cbe0a..9104bc92d 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -49,7 +49,7 @@
<style name="Icon.Folder">
<item name="android:background">@null</item>
- <item name="android:textColor">@color/quantum_panel_text_color</item>
+ <item name="android:textColor">@color/workspace_icon_text_color</item>
<item name="android:shadowRadius">0</item>
<item name="customShadows">false</item>
</style>
diff --git a/res/values/tags.xml b/res/values/tags.xml
new file mode 100644
index 000000000..6fb722840
--- /dev/null
+++ b/res/values/tags.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <item type="id" name="fast_scroll_focus_applicator_tag"/>
+</resources> \ No newline at end of file
diff --git a/res/xml/ct_default_workspace_4x4.xml b/res/xml/ct_default_workspace_4x4.xml
new file mode 100644
index 000000000..8efb1493d
--- /dev/null
+++ b/res/xml/ct_default_workspace_4x4.xml
@@ -0,0 +1,174 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
+ Not a Contribution.
+
+ Copyright (C) 2009 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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">
+ <!-- Far-left screen [0] -->
+ <!-- Left screen [1] -->
+ <!-- Middle screen [2] -->
+ <favorite
+ launcher:packageName="com.android.deskclock"
+ launcher:className="com.android.deskclock.DeskClock"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="3"
+ />
+ <favorite
+ launcher:packageName="com.android.calendar"
+ launcher:className="com.android.calendar.AllInOneActivity"
+ launcher:screen="2"
+ launcher:x="3"
+ launcher:y="3" />
+
+ <!-- Right screen [3] -->
+ <favorite
+ launcher:packageName="com.chinatelecom.bestpayclient"
+ launcher:className="com.chinatelecom.bestpayclient.BufferActivity"
+ launcher:screen="3"
+ launcher:x="0"
+ launcher:y="0"
+ />
+ <favorite
+ launcher:packageName="com.gwsoft.imusic.controller"
+ launcher:className="com.gwsoft.imusic.controller.IMusicActivity"
+ launcher:screen="3"
+ launcher:x="1"
+ launcher:y="0"
+ />
+ <favorite
+ launcher:packageName="com.telecom.video"
+ launcher:className="com.telecom.video.ui.activity.LoadingActivity"
+ launcher:screen="3"
+ launcher:x="2"
+ launcher:y="0"
+ />
+ <favorite
+ launcher:packageName="com.eshore.ezone"
+ launcher:className="com.eshore.ezone.StartActivity"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0"
+ />
+ <favorite
+ launcher:packageName="com.pdager"
+ launcher:className="com.pdager.enavi.Act.APIMain"
+ launcher:screen="3"
+ launcher:x="0"
+ launcher:y="1"
+ />
+ <favorite
+ launcher:packageName="com.erdo.android.FJDXCartoon"
+ launcher:className="com.erdo.android.FJDXCartoon.Activity_Start"
+ launcher:screen="3"
+ launcher:x="1"
+ launcher:y="1"
+ />
+ <favorite
+ launcher:packageName="com.corp21cn.mail189"
+ launcher:className="com.corp21cn.mailapp.activity.ClientIntroducePage"
+ launcher:screen="3"
+ launcher:x="2"
+ launcher:y="1"
+ />
+ <favorite
+ launcher:packageName="com.lectek.android.sfreader"
+ launcher:className="com.lectek.android.sfreader.ui.SplashActivity"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="1"
+ />
+ <favorite
+ launcher:packageName="com.pica.elive"
+ launcher:className="com.pica.elive.SplashActivity"
+ launcher:screen="3"
+ launcher:x="0"
+ launcher:y="2"
+ />
+ <favorite
+ launcher:packageName="com.lectek.android.ecp"
+ launcher:className="com.lectek.android.ecp.ui.LoginActivity"
+ launcher:screen="3"
+ launcher:x="1"
+ launcher:y="2"
+ />
+ <favorite
+ launcher:packageName="com.chinatelecom.pim"
+ launcher:className="com.chinatelecom.pim.activity.PimLauncherActivity"
+ launcher:screen="3"
+ launcher:x="2"
+ launcher:y="2"
+ />
+ <favorite
+ launcher:packageName="com.besttone.hall"
+ launcher:className="com.besttone.hall.StartAppActivity"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="2" />
+ <favorite
+ launcher:packageName="com.ideal.mobile.jt"
+ launcher:className="com.gift.android.Mains"
+ launcher:screen="3"
+ launcher:x="0"
+ launcher:y="3"
+ />
+ <favorite
+ launcher:packageName="com.egame"
+ launcher:className="com.egame.app.FlashScreenActivity"
+ launcher:screen="3"
+ launcher:x="1"
+ launcher:y="3"
+ />
+ <favorite
+ launcher:packageName="telecom.mdesk"
+ launcher:className="telecom.mdesk.Launcher"
+ 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) -->
+ <favorite
+ launcher:packageName="com.android.messaging"
+ launcher:className="com.android.messaging.ui.conversationlist.ConversationListActivity"
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.android.dialer"
+ launcher:className="com.android.dialer.DialtactsActivity"
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.android.contacts"
+ launcher:className="com.android.contacts.activities.PeopleActivity"
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity"
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" />
+</favorites>
diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml
index 060a1f880..d2ca38a45 100644
--- a/res/xml/default_workspace_4x4.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2015 The CyanogenMod Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,33 +15,204 @@
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+ <!-- Far-left screen [0] -->
- <!-- Hotseat -->
- <include launcher:workspace="@xml/dw_phone_hotseat" />
+ <!-- Left screen [1] -->
- <!-- Bottom row -->
- <resolve
- launcher:screen="0"
+ <!-- Clock Widget -->
+ <appwidget
+ launcher:packageName="com.cyanogenmod.lockclock"
+ launcher:className="com.cyanogenmod.lockclock.ClockWidgetProvider"
+ launcher:screen="1"
launcher:x="0"
+ launcher:y="0"
+ launcher:spanX="4"
+ launcher:spanY="2" />
+
+ <!-- Google Folder -->
+ <folder launcher:screen="1" launcher:x="0" launcher:y="3" launcher:title="@string/google_title">
+ <favorite
+ launcher:className="com.google.android.googlequicksearchbox.SearchActivity"
+ launcher:packageName="com.google.android.googlequicksearchbox" />
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main" />
+ <favorite
+ launcher:className="com.google.android.gm.ConversationListActivityGmail"
+ launcher:packageName="com.google.android.gm" />
+ <favorite
+ launcher:className="com.google.android.maps.MapsActivity"
+ launcher:packageName="com.google.android.apps.maps" />
+ <favorite
+ launcher:className="com.google.android.youtube.app.honeycomb.Shell$HomeActivity"
+ launcher:packageName="com.google.android.youtube" />
+ <favorite
+ launcher:className="com.google.android.apps.docs.app.NewMainProxyActivity"
+ launcher:packageName="com.google.android.apps.docs" />
+ <favorite
+ launcher:className="com.google.android.talk.SigningInActivity"
+ launcher:packageName="com.google.android.talk" />
+ <favorite
+ launcher:className="com.google.android.apps.photos.home.HomeActivity"
+ launcher:packageName="com.google.android.apps.photos" />
+ <favorite
+ launcher:className="com.google.android.apps.wallet.entrypoint.WalletRootActivity"
+ launcher:packageName="com.google.android.apps.walletnfcrel" />
+ <favorite
+ launcher:className="com.android.calendar.AllInOneActivity"
+ launcher:packageName="com.google.android.calendar" />
+ <favorite
+ launcher:className="com.google.android.apps.plus.phone.HomeActivity"
+ launcher:packageName="com.google.android.apps.plus" />
+ </folder>
+
+ <!-- Play Folder -->
+ <folder launcher:screen="1" launcher:x="1" launcher:y="3" launcher:title="@string/play_folder_title">
+ <favorite
+ launcher:className="com.android.music.activitymanagement.TopLevelActivity"
+ launcher:packageName="com.google.android.music" />
+ <favorite
+ launcher:className="com.google.android.youtube.videos.EntryPoint"
+ launcher:packageName="com.google.android.videos" />
+ <favorite
+ launcher:className="com.google.android.gms.games.ui.destination.main.MainActivity"
+ launcher:packageName="com.google.android.play.games" />
+ <favorite
+ launcher:className="com.google.android.apps.books.app.BooksActivity"
+ launcher:packageName="com.google.android.apps.books" />
+ <favorite
+ launcher:className="com.google.apps.dots.android.app.activity.CurrentsStartActivity"
+ launcher:packageName="com.google.android.apps.magazines" />
+ </folder>
+
+ <favorite
+ launcher:packageName="com.cyngn.themestore"
+ launcher:className="com.cyngn.themestore.ui.StoreActivity"
+ launcher:screen="1"
+ launcher:x="2"
+ launcher:y="3" />
+
+ <resolve
+ launcher:screen="1"
+ launcher:x="3"
launcher:y="3" >
- <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.vending"
+ launcher:className="com.android.vending.AssetBrowserActivity" />
+ <favorite
+ launcher:packageName="org.cyanogenmod.theme.chooser"
+ launcher:className="org.cyanogenmod.theme.chooser.ChooserActivity" />
</resolve>
+ <!-- Screen [2] -->
+
+ <appwidget
+ launcher:packageName="com.cyanogenmod.eleven"
+ launcher:className="com.cyanogenmod.eleven.appwidgets.AppWidgetLarge"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="0"
+ launcher:spanX="4"
+ launcher:spanY="2" />
+
+ <favorite
+ launcher:packageName="com.android.email"
+ launcher:className="com.android.email.activity.Welcome"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="3" />
+
<resolve
- launcher:screen="0"
+ launcher:screen="2"
launcher:x="1"
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" />
+ <favorite
+ launcher:packageName="com.cyngn.gallerynext"
+ launcher:className="com.cyngn.gallerynext.app.GalleryActivity" />
+ <favorite
+ launcher:packageName="com.android.gallery3d"
+ launcher:className="com.android.gallery3d.app.GalleryActivity" />
</resolve>
+ <favorite
+ launcher:packageName="com.cyanogenmod.eleven"
+ launcher:className="com.cyanogenmod.eleven.ui.activities.HomeActivity"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="3" />
+
+ <favorite
+ launcher:packageName="com.android.settings"
+ launcher:className="com.android.settings.Settings"
+ launcher:screen="2"
+ launcher:x="3"
+ launcher:y="3" />
+
+ <!-- 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="3"
- 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" />
+ 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" />
+ <favorite
+ launcher:packageName="com.cyngn.dialer"
+ launcher:className="com.android.dialer.DialtactsActivity" />
+ <favorite
+ launcher:packageName="com.android.contacts"
+ launcher:className="com.android.contacts.activities.PeopleActivity" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.android.messaging"
+ launcher:className="com.android.messaging.ui.conversationlist.ConversationListActivity" />
+ <favorite
+ launcher:packageName="com.android.email"
+ launcher:className="com.android.email.activity.Welcome" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="2"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity" />
+ <favorite
+ launcher:packageName="com.cyngn.browser"
+ launcher:className="com.android.browser.BrowserLauncher" />
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.cyngn.cameranext"
+ launcher:className="com.android.camera.CameraLauncher" />
+ <favorite
+ launcher:packageName="org.cyanogenmod.snap"
+ launcher:className="com.android.camera.CameraLauncher" />
+ <favorite
+ launcher:packageName="com.android.camera2"
+ launcher:className="com.android.camera.CameraLauncher" />
</resolve>
</favorites>
diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml
index 322661720..09aa09804 100644
--- a/res/xml/default_workspace_5x5.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- Copyright (C) 2015 The CyanogenMod Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,34 +15,204 @@
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+ <!-- Far-left screen [0] -->
- <!-- Hotseat -->
- <include launcher:workspace="@xml/dw_phone_hotseat" />
+ <!-- Left screen [1] -->
- <!-- Bottom row -->
- <resolve
- launcher:screen="0"
+ <!-- Clock Widget -->
+ <appwidget
+ launcher:packageName="com.cyanogenmod.lockclock"
+ launcher:className="com.cyanogenmod.lockclock.ClockWidgetProvider"
+ launcher:screen="1"
launcher:x="0"
+ launcher:y="0"
+ launcher:spanX="5"
+ launcher:spanY="2" />
+
+ <!-- Google Folder -->
+ <folder launcher:screen="1" launcher:x="0" launcher:y="4" launcher:title="@string/google_title">
+ <favorite
+ launcher:className="com.google.android.googlequicksearchbox.SearchActivity"
+ launcher:packageName="com.google.android.googlequicksearchbox" />
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main" />
+ <favorite
+ launcher:className="com.google.android.gm.ConversationListActivityGmail"
+ launcher:packageName="com.google.android.gm" />
+ <favorite
+ launcher:className="com.google.android.maps.MapsActivity"
+ launcher:packageName="com.google.android.apps.maps" />
+ <favorite
+ launcher:className="com.google.android.youtube.app.honeycomb.Shell$HomeActivity"
+ launcher:packageName="com.google.android.youtube" />
+ <favorite
+ launcher:className="com.google.android.apps.docs.app.NewMainProxyActivity"
+ launcher:packageName="com.google.android.apps.docs" />
+ <favorite
+ launcher:className="com.google.android.talk.SigningInActivity"
+ launcher:packageName="com.google.android.talk" />
+ <favorite
+ launcher:className="com.google.android.apps.photos.home.HomeActivity"
+ launcher:packageName="com.google.android.apps.photos" />
+ <favorite
+ launcher:className="com.google.android.apps.wallet.entrypoint.WalletRootActivity"
+ launcher:packageName="com.google.android.apps.walletnfcrel" />
+ <favorite
+ launcher:className="com.android.calendar.AllInOneActivity"
+ launcher:packageName="com.google.android.calendar" />
+ <favorite
+ launcher:className="com.google.android.apps.plus.phone.HomeActivity"
+ launcher:packageName="com.google.android.apps.plus" />
+ </folder>
+
+ <!-- Play Folder -->
+ <folder launcher:screen="1" launcher:x="1" launcher:y="4" launcher:title="@string/play_folder_title">
+ <favorite
+ launcher:className="com.android.music.activitymanagement.TopLevelActivity"
+ launcher:packageName="com.google.android.music" />
+ <favorite
+ launcher:className="com.google.android.youtube.videos.EntryPoint"
+ launcher:packageName="com.google.android.videos" />
+ <favorite
+ launcher:className="com.google.android.gms.games.ui.destination.main.MainActivity"
+ launcher:packageName="com.google.android.play.games" />
+ <favorite
+ launcher:className="com.google.android.apps.books.app.BooksActivity"
+ launcher:packageName="com.google.android.apps.books" />
+ <favorite
+ launcher:className="com.google.apps.dots.android.app.activity.CurrentsStartActivity"
+ launcher:packageName="com.google.android.apps.magazines" />
+ </folder>
+
+ <favorite
+ launcher:packageName="com.cyngn.themestore"
+ launcher:className="com.cyngn.themestore.ui.StoreActivity"
+ launcher:screen="1"
+ launcher:x="3"
+ launcher:y="4" />
+
+ <resolve
+ launcher:screen="1"
+ launcher:x="4"
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.vending"
+ launcher:className="com.android.vending.AssetBrowserActivity" />
+ <favorite
+ launcher:packageName="org.cyanogenmod.theme.chooser"
+ launcher:className="org.cyanogenmod.theme.chooser.ChooserActivity" />
+ </resolve>
+
+ <!-- Screen [2] -->
+
+ <appwidget
+ launcher:packageName="com.cyanogenmod.eleven"
+ launcher:className="com.cyanogenmod.eleven.appwidgets.AppWidgetLarge"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="0"
+ launcher:spanX="5"
+ launcher:spanY="2" />
+ <favorite
+ launcher:packageName="com.android.email"
+ launcher:className="com.android.email.activity.Welcome"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="4" />
+
+ <resolve
+ launcher:screen="2"
+ launcher:x="1"
+ launcher:y="4" >
+ <favorite
+ launcher:packageName="com.cyngn.gallerynext"
+ launcher:className="com.cyngn.gallerynext.app.GalleryActivity" />
+ <favorite
+ launcher:packageName="com.android.gallery3d"
+ launcher:className="com.android.gallery3d.app.GalleryActivity" />
</resolve>
+ <favorite
+ launcher:packageName="com.cyanogenmod.eleven"
+ launcher:className="com.cyanogenmod.eleven.ui.activities.HomeActivity"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="4" />
+
+ <favorite
+ launcher:packageName="com.android.settings"
+ launcher:className="com.android.settings.Settings"
+ launcher:screen="2"
+ launcher:x="4"
+ launcher:y="4" />
+
+ <!-- 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" />
+
+ <favorite
+ launcher:packageName="com.android.dialer"
+ launcher:className="com.android.dialer.DialtactsActivity" />
+ <favorite
+ launcher:packageName="com.cyngn.dialer"
+ launcher:className="com.android.dialer.DialtactsActivity" />
+ <favorite
+ launcher:packageName="com.android.contacts"
+ launcher:className="com.android.contacts.activities.PeopleActivity" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="1"
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" />
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.android.messaging"
+ launcher:className="com.android.messaging.ui.conversationlist.ConversationListActivity" />
+ <favorite
+ launcher:packageName="com.android.email"
+ launcher:className="com.android.email.activity.Welcome" />
+ </resolve>
+ <resolve
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="2"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity" />
+ <favorite
+ launcher:packageName="com.cyngn.browser"
+ launcher:className="com.android.browser.BrowserLauncher" />
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main" />
</resolve>
<resolve
- launcher:screen="0"
+ launcher:container="-101"
+ launcher:screen="4"
launcher:x="4"
- 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" />
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.cyngn.cameranext"
+ launcher:className="com.android.camera.CameraLauncher" />
+ <favorite
+ launcher:packageName="org.cyanogenmod.snap"
+ launcher:className="com.android.camera.CameraLauncher" />
+ <favorite
+ launcher:packageName="com.android.camera2"
+ launcher:className="com.android.camera.CameraLauncher" />
</resolve>
+
</favorites>
diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml
index bc236fb14..558f525c0 100644
--- a/res/xml/default_workspace_5x6.xml
+++ b/res/xml/default_workspace_5x6.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- Copyright (C) 2015 The CyanogenMod Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,23 +15,204 @@
-->
<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+ <!-- Far-left screen [0] -->
- <!-- Hotseat -->
- <include launcher:workspace="@xml/dw_tablet_hotseat" />
+ <!-- Left screen [1] -->
- <!-- Bottom row -->
- <favorite
- launcher:screen="0"
+ <!-- Clock Widget -->
+ <appwidget
+ launcher:packageName="com.cyanogenmod.lockclock"
+ launcher:className="com.cyanogenmod.lockclock.ClockWidgetProvider"
+ launcher:screen="1"
launcher:x="0"
- launcher:y="4"
- launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CONTACTS;end" />
+ launcher:y="0"
+ launcher:spanX="6"
+ launcher:spanY="2" />
+
+ <!-- Google Folder -->
+ <folder launcher:screen="1" launcher:x="0" launcher:y="4" launcher:title="@string/google_title">
+ <favorite
+ launcher:className="com.google.android.googlequicksearchbox.SearchActivity"
+ launcher:packageName="com.google.android.googlequicksearchbox" />
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main" />
+ <favorite
+ launcher:className="com.google.android.gm.ConversationListActivityGmail"
+ launcher:packageName="com.google.android.gm" />
+ <favorite
+ launcher:className="com.google.android.maps.MapsActivity"
+ launcher:packageName="com.google.android.apps.maps" />
+ <favorite
+ launcher:className="com.google.android.youtube.app.honeycomb.Shell$HomeActivity"
+ launcher:packageName="com.google.android.youtube" />
+ <favorite
+ launcher:className="com.google.android.apps.docs.app.NewMainProxyActivity"
+ launcher:packageName="com.google.android.apps.docs" />
+ <favorite
+ launcher:className="com.google.android.talk.SigningInActivity"
+ launcher:packageName="com.google.android.talk" />
+ <favorite
+ launcher:className="com.google.android.apps.photos.home.HomeActivity"
+ launcher:packageName="com.google.android.apps.photos" />
+ <favorite
+ launcher:className="com.google.android.apps.wallet.entrypoint.WalletRootActivity"
+ launcher:packageName="com.google.android.apps.walletnfcrel" />
+ <favorite
+ launcher:className="com.android.calendar.AllInOneActivity"
+ launcher:packageName="com.google.android.calendar" />
+ <favorite
+ launcher:className="com.google.android.apps.plus.phone.HomeActivity"
+ launcher:packageName="com.google.android.apps.plus" />
+ </folder>
+
+ <!-- Play Folder -->
+ <folder launcher:screen="1" launcher:x="1" launcher:y="4" launcher:title="@string/play_folder_title">
+ <favorite
+ launcher:className="com.android.music.activitymanagement.TopLevelActivity"
+ launcher:packageName="com.google.android.music" />
+ <favorite
+ launcher:className="com.google.android.youtube.videos.EntryPoint"
+ launcher:packageName="com.google.android.videos" />
+ <favorite
+ launcher:className="com.google.android.gms.games.ui.destination.main.MainActivity"
+ launcher:packageName="com.google.android.play.games" />
+ <favorite
+ launcher:className="com.google.android.apps.books.app.BooksActivity"
+ launcher:packageName="com.google.android.apps.books" />
+ <favorite
+ launcher:className="com.google.apps.dots.android.app.activity.CurrentsStartActivity"
+ launcher:packageName="com.google.android.apps.magazines" />
+ </folder>
+
+ <favorite
+ launcher:packageName="com.cyngn.themestore"
+ launcher:className="com.cyngn.themestore.ui.StoreActivity"
+ launcher:screen="1"
+ launcher:x="2"
+ launcher:y="3" />
<resolve
- launcher:screen="0"
+ launcher:screen="1"
launcher:x="5"
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" />
+ <favorite
+ launcher:packageName="com.android.vending"
+ launcher:className="com.android.vending.AssetBrowserActivity" />
+ <favorite
+ launcher:packageName="org.cyanogenmod.theme.chooser"
+ launcher:className="org.cyanogenmod.theme.chooser.ChooserActivity" />
+ </resolve>
+
+ <!-- Screen [2] -->
+
+ <appwidget
+ launcher:packageName="com.cyanogenmod.eleven"
+ launcher:className="com.cyanogenmod.eleven.appwidgets.AppWidgetLarge"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="0"
+ launcher:spanX="6"
+ launcher:spanY="2" />
+
+ <favorite
+ launcher:packageName="com.android.email"
+ launcher:className="com.android.email.activity.Welcome"
+ launcher:screen="2"
+ launcher:x="0"
+ launcher:y="4" />
+
+ <resolve
+ launcher:screen="2"
+ launcher:x="1"
+ launcher:y="4" >
+ <favorite
+ launcher:packageName="com.cyngn.gallerynext"
+ launcher:className="com.cyngn.gallerynext.app.GalleryActivity" />
+ <favorite
+ launcher:packageName="com.android.gallery3d"
+ launcher:className="com.android.gallery3d.app.GalleryActivity" />
+ </resolve>
+
+ <favorite
+ launcher:packageName="com.cyanogenmod.eleven"
+ launcher:className="com.cyanogenmod.eleven.ui.activities.HomeActivity"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="4" />
+
+ <favorite
+ launcher:packageName="com.android.settings"
+ launcher:className="com.android.settings.Settings"
+ launcher:screen="2"
+ launcher:x="5"
+ launcher:y="4" />
+
+ <!-- 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="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" />
+ <favorite
+ launcher:packageName="com.cyngn.dialer"
+ launcher:className="com.android.dialer.DialtactsActivity" />
+ <favorite
+ launcher:packageName="com.android.contacts"
+ launcher:className="com.android.contacts.activities.PeopleActivity" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.android.messaging"
+ launcher:className="com.android.messaging.ui.conversationlist.ConversationListActivity" />
+ <favorite
+ launcher:packageName="com.android.email"
+ launcher:className="com.android.email.activity.Welcome" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity" />
+ <favorite
+ launcher:packageName="com.cyngn.browser"
+ launcher:className="com.android.browser.BrowserLauncher" />
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main" />
+ </resolve>
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="5"
+ launcher:x="5"
+ launcher:y="0" >
+ <favorite
+ launcher:packageName="com.cyngn.cameranext"
+ launcher:className="com.android.camera.CameraLauncher" />
+ <favorite
+ launcher:packageName="org.cyanogenmod.snap"
+ launcher:className="com.android.camera.CameraLauncher" />
+ <favorite
+ launcher:packageName="com.android.camera2"
+ launcher:className="com.android.camera.CameraLauncher" />
</resolve>
</favorites>
diff --git a/res/xml/update_workspace.xml b/res/xml/update_workspace.xml
new file mode 100644
index 000000000..c3e67147d
--- /dev/null
+++ b/res/xml/update_workspace.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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">
+ <!-- Update the db with new hotseat items. Note that we reference the browser's original
+ package name. -->
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <favorite
+ launcher:packageName="com.android.dialer"
+ launcher:className="com.android.dialer.DialtactsActivity"
+ launcher:container="-101"
+ launcher:screen="0"
+ launcher:x="0"
+ launcher:y="0" />
+ <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" />
+ <favorite
+ launcher:packageName="com.android.messaging"
+ launcher:className="com.android.messaging.ui.conversationlist.ConversationListActivity"
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity"
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" />
+</favorites>
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index c95d5585a..ca8ae255c 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -58,10 +58,11 @@ public class AppInfo extends ItemInfo {
public ComponentName componentName;
- static final int DOWNLOADED_FLAG = 1;
+ public static final int DOWNLOADED_FLAG = 1;
static final int UPDATED_SYSTEM_APP_FLAG = 2;
+ static final int REMOTE_APP_FLAG = 4;
- int flags = 0;
+ public int flags = 0;
AppInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
@@ -85,11 +86,21 @@ public class AppInfo extends ItemInfo {
flags = initFlags(info);
firstInstallTime = info.getFirstInstallTime();
- iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
+ // Using the full res icon on init might need to be made configurable for low spec devices.
+ iconCache.getTitleAndIcon(this, info, false /* useLowResIcon */);
intent = makeLaunchIntent(context, info, user);
this.user = user;
}
+ public AppInfo(Intent intent, String title, UserHandleCompat user) {
+ this.componentName = intent.getComponent();
+ this.container = ItemInfo.NO_ID;
+
+ this.intent = intent;
+ this.title = title;
+ this.user = user;
+ }
+
public static int initFlags(LauncherActivityInfoCompat info) {
int appFlags = info.getApplicationInfo().flags;
int flags = 0;
@@ -113,6 +124,23 @@ public class AppInfo extends ItemInfo {
iconBitmap = info.iconBitmap;
}
+ /**
+ * Check if this app has a specific flag.
+ * @param flag flag to check.
+ * @return true if the flag is present, false otherwise.
+ */
+ public boolean hasFlag(int flag) {
+ return (flags & flag) != 0;
+ }
+
+ /**
+ * Set a flag for this app
+ * @param flag flag to apply.
+ */
+ public void setFlag(int flag) {
+ flags |= flag;
+ }
+
@Override
public String toString() {
return "ApplicationInfo(title=" + title + " id=" + this.id
@@ -151,4 +179,18 @@ public class AppInfo extends ItemInfo {
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
.putExtra(EXTRA_PROFILE, serialNumber);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof AppInfo) {
+ return componentName.equals(((AppInfo) o).componentName);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return componentName.hashCode();
+ }
}
diff --git a/src/com/android/launcher3/AutoExpandTextView.java b/src/com/android/launcher3/AutoExpandTextView.java
new file mode 100644
index 000000000..ea7ac896e
--- /dev/null
+++ b/src/com/android/launcher3/AutoExpandTextView.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2014 Grantland Chew
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.method.TransformationMethod;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+
+/**
+ * A single-line TextView that resizes it's letter spacing to fit the width of the view
+ *
+ * @author Grantland Chew <grantlandchew@gmail.com>
+ * @author Linus Lee <llee@cyngn.com>
+ */
+public class AutoExpandTextView extends TextView {
+ // How precise we want to be when reaching the target textWidth size
+ private static final float PRECISION = 0.01f;
+
+ // Attributes
+ private float mPrecision;
+ private TextPaint mPaint;
+ private float[] mPositions;
+
+ public static class HighlightedText {
+ public String mText;
+ public boolean mHighlight;
+
+ public HighlightedText(String text, boolean highlight) {
+ mText = text;
+ mHighlight = highlight;
+ }
+ }
+
+ public AutoExpandTextView(Context context) {
+ super(context);
+ init(context, null, 0);
+ }
+
+ public AutoExpandTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs, 0);
+ }
+
+ public AutoExpandTextView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init(context, attrs, defStyle);
+ }
+
+ private void init(Context context, AttributeSet attrs, int defStyle) {
+ float precision = PRECISION;
+
+ if (attrs != null) {
+ TypedArray ta = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.AutofitTextView,
+ defStyle,
+ 0);
+ precision = ta.getFloat(R.styleable.AutofitTextView_precision, precision);
+ }
+
+ mPaint = new TextPaint();
+ setPrecision(precision);
+ }
+
+ /**
+ * @return the amount of precision used to calculate the correct text size to fit within it's
+ * bounds.
+ */
+ public float getPrecision() {
+ return mPrecision;
+ }
+
+ /**
+ * Set the amount of precision used to calculate the correct text size to fit within it's
+ * bounds. Lower precision is more precise and takes more time.
+ *
+ * @param precision The amount of precision.
+ */
+ public void setPrecision(float precision) {
+ if (precision != mPrecision) {
+ mPrecision = precision;
+ refitText();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setLines(int lines) {
+ super.setLines(1);
+ refitText();
+ }
+
+ /**
+ * Only allow max lines of 1
+ */
+ @Override
+ public void setMaxLines(int maxLines) {
+ super.setMaxLines(1);
+ refitText();
+ }
+
+ /**
+ * Re size the font so the specified text fits in the text box assuming the text box is the
+ * specified width.
+ */
+ private void refitText() {
+ CharSequence text = getText();
+
+ if (TextUtils.isEmpty(text)) {
+ return;
+ }
+
+ TransformationMethod method = getTransformationMethod();
+ if (method != null) {
+ text = method.getTransformation(text, this);
+ }
+ int targetWidth = getWidth() - getPaddingLeft() - getPaddingRight();
+ if (targetWidth > 0) {
+ float high = 100;
+ float low = 0;
+
+ mPaint.set(getPaint());
+ mPaint.setTextSize(getTextSize());
+ float letterSpacing = getLetterSpacing(text, mPaint, targetWidth, low, high,
+ mPrecision);
+ mPaint.setLetterSpacing(letterSpacing);
+ calculateSections(text);
+
+ super.setLetterSpacing(letterSpacing);
+ }
+ }
+
+ public float getPositionOfSection(int position) {
+ if (mPositions == null || position >= mPositions.length) {
+ return 0;
+ }
+ return mPositions[position];
+ }
+
+ /**
+ * This calculates the different horizontal positions of each character
+ */
+ private void calculateSections(CharSequence text) {
+ mPositions = new float[text.length()];
+ for (int i = 0; i < text.length(); i++) {
+ if (i == 0) {
+ mPositions[0] = mPaint.measureText(text, 0, 1) / 2;
+ } else {
+ // try to be lazy and just add the width of the newly added char
+ mPositions[i] = mPaint.measureText(text, i, i + 1) + mPositions[i - 1];
+ }
+ }
+ }
+
+ /**
+ * Sets the list of sections in the text view. This will take the first character of each
+ * and space it out in the text view using letter spacing
+ */
+ public void setSections(ArrayList<HighlightedText> sections) {
+ mPositions = null;
+ if (sections == null || sections.size() == 0) {
+ setText("");
+ return;
+ }
+
+ Resources r = getContext().getResources();
+ int highlightColor = r.getColor(R.color.app_scrubber_highlight_color);
+ int grayColor = r.getColor(R.color.app_scrubber_gray_color);
+
+ SpannableStringBuilder builder = new SpannableStringBuilder();
+ for (HighlightedText highlightText : sections) {
+ SpannableString spannable = new SpannableString(highlightText.mText.substring(0, 1));
+ spannable.setSpan(
+ new ForegroundColorSpan(highlightText.mHighlight ? highlightColor : grayColor),
+ 0, spannable.length(), 0);
+ builder.append(spannable);
+ }
+
+ setText(builder);
+ }
+
+ private static float getLetterSpacing(CharSequence text, TextPaint paint, float targetWidth,
+ float low, float high, float precision) {
+ float mid = (low + high) / 2.0f;
+ paint.setLetterSpacing(mid);
+
+ float measuredWidth = paint.measureText(text, 0, text.length());
+
+ if (high - low < precision) {
+ if (measuredWidth < targetWidth) {
+ return mid;
+ } else {
+ return low;
+ }
+ } else if (measuredWidth > targetWidth) {
+ return getLetterSpacing(text, paint, targetWidth, low, mid, precision);
+ } else if (measuredWidth < targetWidth) {
+ return getLetterSpacing(text, paint, targetWidth, mid, high, precision);
+ } else {
+ return mid;
+ }
+ }
+
+ @Override
+ protected void onTextChanged(final CharSequence text, final int start,
+ final int lengthBefore, final int lengthAfter) {
+ super.onTextChanged(text, start, lengthBefore, lengthAfter);
+ refitText();
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ if (w != oldw) {
+ refitText();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java
index c11824054..dfc2d7b9c 100644
--- a/src/com/android/launcher3/BaseContainerView.java
+++ b/src/com/android/launcher3/BaseContainerView.java
@@ -17,10 +17,16 @@
package com.android.launcher3;
import android.content.Context;
+import android.content.res.Resources;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
+import android.view.View;
+import android.view.ViewStub;
import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.launcher3.settings.SettingsProvider;
/**
* A base container view, which supports resizing.
@@ -43,6 +49,12 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
// The inset to apply to the edges and between the search bar and the container
private int mContainerBoundsInset;
private boolean mHasSearchBar;
+ private boolean mUseScroller;
+ private boolean mUseScrubber;
+
+ protected View mScrubberContainerView;
+ protected BaseRecyclerViewScrubber mScrubber;
+ protected final int mScrubberHeight;
public BaseContainerView(Context context) {
this(context, null);
@@ -54,7 +66,9 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
public BaseContainerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
- mContainerBoundsInset = getResources().getDimensionPixelSize(R.dimen.container_bounds_inset);
+ mContainerBoundsInset = getResources().getDimensionPixelSize(
+ R.dimen.container_bounds_inset);
+ mScrubberHeight = getResources().getDimensionPixelSize(R.dimen.scrubber_height);
}
@Override
@@ -63,8 +77,12 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
updateBackgroundAndPaddings();
}
- protected void setHasSearchBar() {
- mHasSearchBar = true;
+ public void setHasSearchBar(boolean hasSearchBar) {
+ mHasSearchBar = hasSearchBar;
+ }
+
+ public boolean hasSearchBar() {
+ return mHasSearchBar;
}
/**
@@ -87,10 +105,57 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
});
}
+ public final void setScroller() {
+ Context context = getContext();
+ mUseScroller = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_USE_SCROLLER,
+ R.bool.preferences_interface_use_scroller_default);
+ mUseScrubber = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_USE_HORIZONTAL_SCRUBBER,
+ R.bool.preferences_interface_use_horizontal_scrubber_default);
+
+ if (mUseScroller && mUseScrubber) {
+ ViewStub stub = (ViewStub) findViewById(R.id.scrubber_container_stub);
+ mScrubberContainerView = stub.inflate();
+ if (mScrubberContainerView == null) {
+ throw new IllegalStateException(
+ "Layout must contain an id: R.id.scrubber_container");
+ }
+ mScrubber = (BaseRecyclerViewScrubber)
+ mScrubberContainerView.findViewById(R.id.base_scrubber);
+ BaseRecyclerView recyclerView = getRecyclerView();
+ if (recyclerView != null) {
+ mScrubber.setRecycler(recyclerView);
+ mScrubber
+ .setScrubberIndicator((TextView) mScrubberContainerView
+ .findViewById(R.id.scrubberIndicator));
+ mScrubber.updateSections();
+ }
+ } else {
+ removeView(mScrubberContainerView);
+ BaseRecyclerView recyclerView = getRecyclerView();
+ if (recyclerView != null) {
+ recyclerView.setUseScrollbar(mUseScroller);
+ }
+ }
+ }
+
+ public final boolean useScroller() {
+ return mUseScroller;
+ }
+
+ public final boolean useScrubber() {
+ return mUseScrubber;
+ }
+
+ protected void updateBackgroundAndPaddings() {
+ updateBackgroundAndPaddings(false);
+ }
+
/**
* Update the backgrounds and padding in response to a change in the bounds or insets.
*/
- protected void updateBackgroundAndPaddings() {
+ protected void updateBackgroundAndPaddings(boolean force) {
Rect padding;
Rect searchBarBounds = new Rect();
if (!isValidSearchBarBounds(mFixedSearchBarBounds)) {
@@ -119,7 +184,8 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
// 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)) {
+ if (force || !padding.equals(mContentPadding) ||
+ !searchBarBounds.equals(mSearchBarBounds)) {
mContentPadding.set(padding);
mContentBounds.set(padding.left, padding.top,
getMeasuredWidth() - padding.right,
@@ -135,6 +201,11 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab
protected abstract void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding);
/**
+ * This might be null if the container doesn't have a recycler.
+ */
+ protected abstract BaseRecyclerView getRecyclerView();
+
+ /**
* Returns whether the search bar bounds we got are considered valid.
*/
private boolean isValidSearchBarBounds(Rect searchBarBounds) {
diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java
index f0d8b3b3d..d89c92270 100644
--- a/src/com/android/launcher3/BaseRecyclerView.java
+++ b/src/com/android/launcher3/BaseRecyclerView.java
@@ -57,6 +57,7 @@ public abstract class BaseRecyclerView extends RecyclerView
}
protected BaseRecyclerViewFastScrollBar mScrollbar;
+ protected boolean mUseScrollbar = false;
private int mDownX;
private int mDownY;
@@ -74,10 +75,9 @@ public abstract class BaseRecyclerView extends RecyclerView
public BaseRecyclerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mDeltaThreshold = getResources().getDisplayMetrics().density * SCROLL_DELTA_THRESHOLD_DP;
- mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
ScrollListener listener = new ScrollListener();
- setOnScrollListener(listener);
+ addOnScrollListener(listener);
}
private class ScrollListener extends OnScrollListener {
@@ -93,12 +93,16 @@ public abstract class BaseRecyclerView extends RecyclerView
// initiate that here if the recycler view scroll state is not
// RecyclerView.SCROLL_STATE_IDLE.
- onUpdateScrollbar(dy);
+ if (mUseScrollbar) {
+ onUpdateScrollbar(dy);
+ }
}
}
public void reset() {
- mScrollbar.reattachThumbToScroll();
+ if (mUseScrollbar) {
+ mScrollbar.reattachThumbToScroll();
+ }
}
@Override
@@ -137,19 +141,28 @@ public abstract class BaseRecyclerView extends RecyclerView
if (shouldStopScroll(ev)) {
stopScroll();
}
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
+ if (mScrollbar != null) {
+ mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
+ }
break;
case MotionEvent.ACTION_MOVE:
mLastY = y;
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
+ if (mScrollbar != null) {
+ mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
+ }
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
onFastScrollCompleted();
- mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
+ if (mScrollbar != null) {
+ mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY);
+ }
break;
}
- return mScrollbar.isDraggingThumb();
+ if (mUseScrollbar) {
+ return mScrollbar.isDraggingThumb();
+ }
+ return false;
}
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
@@ -183,7 +196,10 @@ public abstract class BaseRecyclerView extends RecyclerView
* Returns the scroll bar width when the user is scrolling.
*/
public int getMaxScrollbarWidth() {
- return mScrollbar.getThumbMaxWidth();
+ if (mUseScrollbar) {
+ return mScrollbar.getThumbMaxWidth();
+ }
+ return 0;
}
/**
@@ -204,9 +220,12 @@ public abstract class BaseRecyclerView extends RecyclerView
* AvailableScrollBarHeight = Total height of the visible view - thumb height
*/
protected int getAvailableScrollBarHeight() {
- int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
- int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight();
- return availableScrollBarHeight;
+ if (mUseScrollbar) {
+ int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom;
+ int availableScrollBarHeight = visibleHeight - mScrollbar.getThumbHeight();
+ return availableScrollBarHeight;
+ }
+ return 0;
}
/**
@@ -223,11 +242,23 @@ public abstract class BaseRecyclerView extends RecyclerView
return defaultInactiveThumbColor;
}
+ public void setUseScrollbar(boolean useScrollbar) {
+ mUseScrollbar = useScrollbar;
+ if (useScrollbar) {
+ mScrollbar = new BaseRecyclerViewFastScrollBar(this, getResources());
+ } else {
+ mScrollbar = null;
+ }
+ invalidate();
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
- onUpdateScrollbar(0);
- mScrollbar.draw(canvas);
+ if (mUseScrollbar) {
+ onUpdateScrollbar(0);
+ mScrollbar.draw(canvas);
+ }
}
/**
@@ -241,6 +272,9 @@ public abstract class BaseRecyclerView extends RecyclerView
*/
protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState,
int rowCount) {
+ if (!mUseScrollbar) {
+ return;
+ }
// Only show the scrollbar if there is height to be scrolled
int availableScrollBarHeight = getAvailableScrollBarHeight();
int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight);
@@ -252,8 +286,7 @@ public abstract class BaseRecyclerView extends RecyclerView
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
- int scrollY = getPaddingTop() +
- (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
+ int scrollY = getCurrentScroll(scrollPosState);
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -268,11 +301,28 @@ public abstract class BaseRecyclerView extends RecyclerView
}
/**
+ * @param scrollPosState current state of view scrolling.
+ * @return the vertical scroll position
+ */
+ protected int getCurrentScroll(ScrollPositionState scrollPosState) {
+ return getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) -
+ scrollPosState.rowTopOffset;
+ }
+
+ /**
* Maps the touch (from 0..1) to the adapter position that should be visible.
* <p>Override in each subclass of this base class.
*/
public abstract String scrollToPositionAtProgress(float touchFraction);
+ public abstract String scrollToSection(String sectionName);
+
+ public abstract String[] getSectionNames();
+
+ public void setFastScrollDragging(boolean dragging) {}
+
+ public void setPreviousSectionFastScrollFocused() {}
+
/**
* Updates the bounds for the scrollbar.
* <p>Override in each subclass of this base class.
diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
index fcee7e8dd..006cfccef 100644
--- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
+++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java
@@ -18,6 +18,7 @@ package com.android.launcher3;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -27,8 +28,11 @@ import android.graphics.Path;
import android.graphics.Point;
import android.graphics.Rect;
import android.view.MotionEvent;
+import android.view.View;
import android.view.ViewConfiguration;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
import com.android.launcher3.util.Thunk;
/**
@@ -36,8 +40,128 @@ import com.android.launcher3.util.Thunk;
*/
public class BaseRecyclerViewFastScrollBar {
- public interface FastScrollFocusableView {
+ public interface FastScrollFocusable {
+ int FAST_SCROLL_FOCUS_DIMMABLE = 1;
+ int FAST_SCROLL_FOCUS_SCALABLE = 2;
+
void setFastScrollFocused(boolean focused, boolean animated);
+ void setFastScrollDimmed(boolean dimmed, boolean animated);
+ }
+
+ /**
+ * Helper class to apply fast scroll focus functionality to any view.
+ */
+ public static class FastScrollFocusApplicator implements FastScrollFocusable {
+ private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
+ private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;
+ private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
+
+ private final View mView;
+ private final int mFastScrollMode;
+
+ private ObjectAnimator mFastScrollFocusAnimator;
+ private ObjectAnimator mFastScrollDimAnimator;
+ private boolean mFastScrollFocused;
+ private boolean mFastScrollDimmed;
+
+ public static void createApplicator(final View v, int mode) {
+ FastScrollFocusApplicator applicator = new FastScrollFocusApplicator(v, mode);
+ v.setTag(R.id.fast_scroll_focus_applicator_tag, applicator);
+ }
+
+ public static void setFastScrollFocused(final View v, boolean focused, boolean animated) {
+ FastScrollFocusable focusable = getFromView(v);
+ if (focusable == null) return;
+
+ focusable.setFastScrollFocused(focused, animated);
+ }
+
+ public static void setFastScrollDimmed(final View v, boolean dimmed, boolean animated) {
+ FastScrollFocusable focusable = getFromView(v);
+ if (focusable == null) return;
+
+ focusable.setFastScrollDimmed(dimmed, animated);
+ }
+
+ private static FastScrollFocusable getFromView(final View v) {
+ Object tag = v.getTag(R.id.fast_scroll_focus_applicator_tag);
+ if (tag != null) {
+ return (FastScrollFocusApplicator) tag;
+ }
+ return null;
+ }
+
+ private FastScrollFocusApplicator(final View v, final int mode) {
+ mView = v;
+ mFastScrollMode = mode & ~FAST_SCROLL_FOCUS_SCALABLE; // Globally disable scaling.
+ }
+
+ public void setFastScrollFocused(boolean focused, boolean animated) {
+ if ((mFastScrollMode & FAST_SCROLL_FOCUS_SCALABLE) == 0) {
+ return;
+ }
+
+ if (mFastScrollFocused != focused) {
+ mFastScrollFocused = focused;
+
+ if (animated) {
+ // Clean up the previous focus animator
+ if (mFastScrollFocusAnimator != null) {
+ mFastScrollFocusAnimator.cancel();
+ }
+
+ // Setup animator for bi-directional scaling.
+ float value = focused ? FAST_SCROLL_FOCUS_MAX_SCALE : 1f;
+ PropertyValuesHolder pvhScaleX =
+ PropertyValuesHolder.ofFloat(View.SCALE_X, value);
+ PropertyValuesHolder pvhScaleY =
+ PropertyValuesHolder.ofFloat(View.SCALE_Y, value);
+ mFastScrollFocusAnimator = ObjectAnimator.ofPropertyValuesHolder(mView,
+ pvhScaleX, pvhScaleY);
+
+ if (focused) {
+ mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
+ } else {
+ mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
+ }
+ mFastScrollFocusAnimator.setDuration(focused ?
+ FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
+ mFastScrollFocusAnimator.start();
+ }
+ }
+
+ // Let the view do any additional operations if it wants.
+ if (mView instanceof FastScrollFocusable) {
+ ((FastScrollFocusable) mView).setFastScrollFocused(focused, animated);
+ }
+ }
+
+ public void setFastScrollDimmed(boolean dimmed, boolean animated) {
+ if ((mFastScrollMode & FAST_SCROLL_FOCUS_DIMMABLE) == 0) {
+ return;
+ }
+
+ if (!animated) {
+ mFastScrollDimmed = dimmed;
+ mView.setAlpha(dimmed ? 0.4f : 1f);
+ } else if (mFastScrollDimmed != dimmed) {
+ mFastScrollDimmed = dimmed;
+
+ // Clean up the previous dim animator
+ if (mFastScrollDimAnimator != null) {
+ mFastScrollDimAnimator.cancel();
+ }
+ mFastScrollDimAnimator = ObjectAnimator.ofFloat(mView, View.ALPHA, dimmed ? 0.4f : 1f);
+ mFastScrollDimAnimator.setDuration(dimmed ?
+ FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
+ mFastScrollDimAnimator.start();
+ }
+
+ // Let the view do any additional operations if it wants.
+ if (mView instanceof FastScrollFocusable) {
+ ((FastScrollFocusable) mView).setFastScrollDimmed(dimmed, animated);
+ }
+ }
}
private final static int MAX_TRACK_ALPHA = 30;
@@ -193,6 +317,7 @@ public class BaseRecyclerViewFastScrollBar {
Math.abs(y - downY) > config.getScaledTouchSlop()) {
mRv.getParent().requestDisallowInterceptTouchEvent(true);
mIsDragging = true;
+ mRv.setFastScrollDragging(mIsDragging);
if (mCanThumbDetach) {
mIsThumbDetached = true;
}
@@ -220,6 +345,7 @@ public class BaseRecyclerViewFastScrollBar {
mIgnoreDragGesture = false;
if (mIsDragging) {
mIsDragging = false;
+ mRv.setFastScrollDragging(mIsDragging);
mPopup.animateVisibility(false);
animateScrollbar(false);
}
diff --git a/src/com/android/launcher3/BaseRecyclerViewScrubber.java b/src/com/android/launcher3/BaseRecyclerViewScrubber.java
new file mode 100644
index 000000000..7795de207
--- /dev/null
+++ b/src/com/android/launcher3/BaseRecyclerViewScrubber.java
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.LinearSmoothScroller;
+import android.support.v7.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.lang.IllegalArgumentException;
+import java.util.ArrayList;
+import java.util.Collections;
+
+/**
+ * BaseRecyclerViewScrubber
+ * <pre>
+ * This is the scrubber at the bottom of a BaseRecyclerView
+ * </pre>
+ *
+ * @see {@link LinearLayout}
+ */
+public class BaseRecyclerViewScrubber extends LinearLayout {
+ private BaseRecyclerView mBaseRecyclerView;
+ private TextView mScrubberIndicator;
+ private SeekBar mSeekBar;
+ private AutoExpandTextView mScrubberText;
+ private SectionContainer mSectionContainer;
+ private ScrubberAnimationState mScrubberAnimationState;
+ private Drawable mTransparentDrawable;
+ private boolean mIsRtl;
+
+ private static final int MSG_SET_TARGET = 1000;
+ private static final int MSG_ANIMATE_PICK = MSG_SET_TARGET + 1;
+
+ /**
+ * UiHandler
+ * <pre>
+ * Using a handler for sending signals to perform certain actions. The reason for
+ * using this is to be able to remove and replace a signal if signals are being
+ * sent too fast (e.g. user scrubbing like crazy). This allows the touch loop to
+ * complete then later run the animations in their own loops.
+ * </pre>
+ */
+ private class UiHandler extends Handler {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_TARGET:
+ int adapterIndex = msg.arg1;
+ performSetTarget(adapterIndex);
+
+ break;
+ case MSG_ANIMATE_PICK:
+ int index = msg.arg1;
+ int width = msg.arg2;
+ int lastIndex = (Integer)msg.obj;
+ performAnimatePickMessage(index, width, lastIndex);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+
+ /**
+ * Overidden to remove identical calls if they are called subsequently fast enough.
+ *
+ * This is the final point that is public in the call chain. Other calls to sendMessageXXX
+ * will eventually call this function which calls "enqueueMessage" which is private.
+ *
+ * @param msg {@link Message}
+ * @param uptimeMillis {@link Long}
+ *
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ @Override
+ public boolean sendMessageAtTime(Message msg, long uptimeMillis) throws
+ IllegalArgumentException {
+ if (msg == null) {
+ throw new IllegalArgumentException("'msg' cannot be null!");
+ }
+ if (hasMessages(msg.what)) {
+ removeMessages(msg.what);
+ }
+ return super.sendMessageAtTime(msg, uptimeMillis);
+ }
+
+ }
+ private Handler mUiHandler = new UiHandler();
+ private void sendSetTargetMessage(int adapterIndex) {
+ Message msg = mUiHandler.obtainMessage(MSG_SET_TARGET);
+ msg.what = MSG_SET_TARGET;
+ msg.arg1 = adapterIndex;
+ mUiHandler.sendMessage(msg);
+ }
+ private void performSetTarget(int adapterIndex) {
+ mBaseRecyclerView.scrollToSection(mSectionContainer.getSectionName(adapterIndex, mIsRtl));
+ }
+ private void sendAnimatePickMessage(int index, int width, int lastIndex) {
+ Message msg = mUiHandler.obtainMessage(MSG_ANIMATE_PICK);
+ msg.what = MSG_ANIMATE_PICK;
+ msg.arg1 = index;
+ msg.arg2 = width;
+ msg.obj = lastIndex;
+ mUiHandler.sendMessage(msg);
+ }
+ private void performAnimatePickMessage(int index, int width, int lastIndex) {
+ if (mScrubberIndicator != null) {
+ // get the index based on the direction the user is scrolling
+ int directionalIndex = mSectionContainer.getDirectionalIndex(lastIndex, index);
+ String sectionText = mSectionContainer.getSectionName(directionalIndex, mIsRtl);
+ float translateX = (index * width) / (float) mSectionContainer.size();
+ // if we are showing letters, grab the position based on the text view
+ if (mSectionContainer.showLetters()) {
+ translateX = mScrubberText.getPositionOfSection(index);
+ }
+ // center the x position
+ translateX -= mScrubberIndicator.getMeasuredWidth() / 2;
+ if (mIsRtl) {
+ translateX = -translateX;
+ }
+ mScrubberIndicator.setTranslationX(translateX);
+ mScrubberIndicator.setText(sectionText);
+ }
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context {@link Context}
+ * @param attrs {@link AttributeSet}
+ */
+ public BaseRecyclerViewScrubber(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context {@link Context}
+ */
+ public BaseRecyclerViewScrubber(Context context) {
+ super(context);
+ init(context);
+ }
+
+ @Override
+ public void onRtlPropertiesChanged(int layoutDirection) {
+ super.onRtlPropertiesChanged(layoutDirection);
+ mIsRtl = Utilities.isRtl(getResources());
+ updateSections();
+ }
+
+ /**
+ * Simple container class that tries to abstract out the knowledge of complex sections vs
+ * simple string sections
+ */
+ private static class SectionContainer {
+ private BaseRecyclerViewScrubberSection.
+ RtlIndexArrayList<BaseRecyclerViewScrubberSection> mSections;
+ private String[] mSectionNames;
+ private final boolean mIsRtl;
+
+ public SectionContainer(String[] sections, boolean isRtl) {
+ mIsRtl = isRtl;
+ mSections = BaseRecyclerViewScrubberSection.createSections(sections, isRtl);
+ mSectionNames = sections;
+ if (isRtl && mSections != null) {
+ final int N = mSectionNames.length;
+ for(int i = 0; i < N / 2; i++) {
+ String temp = mSectionNames[i];
+ mSectionNames[i] = mSectionNames[N - i - 1];
+ mSectionNames[N - i - 1] = temp;
+ }
+ Collections.reverse(mSections);
+ }
+ }
+
+ public int size() {
+ return showLetters() ? mSections.size() : mSectionNames.length;
+ }
+
+ public String getSectionName(int idx, boolean isRtl) {
+ if (size() == 0) {
+ return null;
+ }
+ return showLetters() ? mSections.get(idx, isRtl).getText() : mSectionNames[idx];
+ }
+
+ /**
+ * Because the list section headers is not necessarily the same size as the scrubber
+ * letters, we need to map from the larger list to the smaller list.
+ * In the case that curIdx is not highlighted, it will use the directional index to
+ * determine the adapter index
+ * @return the mSectionNames index (aka the underlying adapter index).
+ */
+ public int getAdapterIndex(int prevIdx, int curIdx) {
+ if (!showLetters() || size() == 0) {
+ return curIdx;
+ }
+
+ // because we have some unhighlighted letters, we need to first get the directional
+ // index before getting the adapter index
+ return mSections.get(getDirectionalIndex(prevIdx, curIdx), mIsRtl).getAdapterIndex();
+ }
+
+ /**
+ * Given the direction the user is scrolling in, return the closest index which is a
+ * highlighted index
+ */
+ public int getDirectionalIndex(int prevIdx, int curIdx) {
+ if (!showLetters() || size() == 0 || mSections.get(curIdx, mIsRtl).getHighlight()) {
+ return curIdx;
+ }
+
+ if (prevIdx < curIdx) {
+ if (mIsRtl) {
+ return mSections.get(curIdx).getPreviousIndex();
+ } else {
+ return mSections.get(curIdx).getNextIndex();
+ }
+ } else {
+ if (mIsRtl) {
+ return mSections.get(curIdx).getNextIndex();
+ } else {
+ return mSections.get(curIdx).getPreviousIndex();
+ }
+
+ }
+ }
+
+ /**
+ * @return true if the scrubber is showing characters as opposed to a line
+ */
+ public boolean showLetters() {
+ return mSections != null;
+ }
+
+ /**
+ * Initializes the scrubber text with the proper characters
+ */
+ public void initializeScrubberText(AutoExpandTextView scrubberText) {
+ scrubberText.setSections(BaseRecyclerViewScrubberSection.getHighlightText(mSections));
+ }
+ }
+
+ public void updateSections() {
+ if (mBaseRecyclerView != null) {
+ mSectionContainer = new SectionContainer(mBaseRecyclerView.getSectionNames(), mIsRtl);
+ mSectionContainer.initializeScrubberText(mScrubberText);
+ mSeekBar.setMax(mSectionContainer.size() - 1);
+
+ // show a white line if there are no letters, otherwise show transparent
+ Drawable d = mSectionContainer.showLetters() ? mTransparentDrawable
+ : getContext().getResources().getDrawable(R.drawable.seek_back);
+ ((ViewGroup) mSeekBar.getParent()).setBackground(d);
+ }
+ }
+
+ public void setRecycler(BaseRecyclerView baseRecyclerView) {
+ mBaseRecyclerView = baseRecyclerView;
+ }
+
+ public void setScrubberIndicator(TextView scrubberIndicator) {
+ mScrubberIndicator = scrubberIndicator;
+ }
+
+ private boolean isReady() {
+ return mBaseRecyclerView != null &&
+ mSectionContainer != null;
+ }
+
+ private void init(Context context) {
+ mIsRtl = Utilities.isRtl(context.getResources());
+ LayoutInflater.from(context).inflate(R.layout.scrub_layout, this);
+ mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT);
+ mScrubberAnimationState = new ScrubberAnimationState();
+ mSeekBar = (SeekBar) findViewById(R.id.scrubber);
+ mScrubberText = (AutoExpandTextView) findViewById(R.id.scrubberText);
+ mSeekBar.setOnSeekBarChangeListener(mScrubberAnimationState);
+ }
+
+ /**
+ * Handles the animations of the scrubber indicator
+ */
+ private class ScrubberAnimationState implements SeekBar.OnSeekBarChangeListener {
+ private static final long SCRUBBER_DISPLAY_DURATION_IN = 60;
+ private static final long SCRUBBER_DISPLAY_DURATION_OUT = 150;
+ private static final long SCRUBBER_DISPLAY_DELAY_IN = 0;
+ private static final long SCRUBBER_DISPLAY_DELAY_OUT = 200;
+ private static final float SCRUBBER_SCALE_START = 0f;
+ private static final float SCRUBBER_SCALE_END = 1f;
+ private static final float SCRUBBER_ALPHA_START = 0f;
+ private static final float SCRUBBER_ALPHA_END = 1f;
+
+ private boolean mTouchingTrack = false;
+ private boolean mAnimatingIn = false;
+ private int mLastIndex = -1;
+
+ private void touchTrack(boolean touching) {
+ mTouchingTrack = touching;
+
+ if (mScrubberIndicator != null) {
+ if (mTouchingTrack) {
+ animateIn();
+ } else if (!mAnimatingIn) { // finish animating in before animating out
+ animateOut();
+ }
+
+ mBaseRecyclerView.setFastScrollDragging(mTouchingTrack);
+ if (mTouchingTrack) {
+ mBaseRecyclerView.setPreviousSectionFastScrollFocused();
+ }
+
+ }
+ }
+
+ private void animateIn() {
+ if (mScrubberIndicator == null) {
+ return;
+ }
+ // start from a scratch position when animating in
+ mScrubberIndicator.animate().cancel();
+ mScrubberIndicator.setPivotX(mScrubberIndicator.getMeasuredWidth() / 2);
+ mScrubberIndicator.setPivotY(mScrubberIndicator.getMeasuredHeight() * 0.9f);
+ mScrubberIndicator.setAlpha(SCRUBBER_ALPHA_START);
+ mScrubberIndicator.setScaleX(SCRUBBER_SCALE_START);
+ mScrubberIndicator.setScaleY(SCRUBBER_SCALE_START);
+ mScrubberIndicator.setVisibility(View.VISIBLE);
+ mAnimatingIn = true;
+
+ mScrubberIndicator.animate()
+ .alpha(SCRUBBER_ALPHA_END)
+ .scaleX(SCRUBBER_SCALE_END)
+ .scaleY(SCRUBBER_SCALE_END)
+ .setStartDelay(SCRUBBER_DISPLAY_DELAY_IN)
+ .setDuration(SCRUBBER_DISPLAY_DURATION_IN)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAnimatingIn = false;
+ // if the user has stopped touching the seekbar, animate back out
+ if (!mTouchingTrack) {
+ animateOut();
+ }
+ }
+ }).start();
+ }
+
+ private void animateOut() {
+ if (mScrubberIndicator == null) {
+ return;
+ }
+ mScrubberIndicator.animate()
+ .alpha(SCRUBBER_ALPHA_START)
+ .scaleX(SCRUBBER_SCALE_START)
+ .scaleY(SCRUBBER_SCALE_START)
+ .setStartDelay(SCRUBBER_DISPLAY_DELAY_OUT)
+ .setDuration(SCRUBBER_DISPLAY_DURATION_OUT)
+ .setListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mScrubberIndicator.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int index, boolean fromUser) {
+ if (!isReady()) {
+ return;
+ }
+ progressChanged(seekBar, index, fromUser);
+ }
+
+ private void progressChanged(SeekBar seekBar, int index, boolean fromUser) {
+ if (!fromUser) {
+ return;
+ }
+
+ sendAnimatePickMessage(index, seekBar.getWidth(), mLastIndex);
+
+ // get the index of the underlying list
+ int adapterIndex = mSectionContainer.getDirectionalIndex(mLastIndex, index);
+ // Post set target index on queue to get processed by Looper later
+ sendSetTargetMessage(adapterIndex);
+
+ mLastIndex = index;
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ touchTrack(true);
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ touchTrack(false);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/BaseRecyclerViewScrubberSection.java b/src/com/android/launcher3/BaseRecyclerViewScrubberSection.java
new file mode 100644
index 000000000..1d17ea887
--- /dev/null
+++ b/src/com/android/launcher3/BaseRecyclerViewScrubberSection.java
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+
+public class BaseRecyclerViewScrubberSection {
+ private static final String TAG = "BRVScrubberSections";
+ private static final String ALPHA_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ private static final int MAX_NUMBER_CUSTOM_SECTIONS = 8;
+ private static final int MAX_SECTIONS = ALPHA_LETTERS.length() + MAX_NUMBER_CUSTOM_SECTIONS;
+ public static final int INVALID_INDEX = -1;
+
+ private AutoExpandTextView.HighlightedText mHighlightedText;
+ private int mPreviousValidIndex;
+ private int mNextValidIndex;
+ private int mAdapterIndex;
+
+ public BaseRecyclerViewScrubberSection(String text, boolean highlight, int idx) {
+ mHighlightedText = new AutoExpandTextView.HighlightedText(text, highlight);
+ mAdapterIndex = idx;
+ mPreviousValidIndex = mNextValidIndex = idx;
+ }
+
+ public boolean getHighlight() {
+ return mHighlightedText.mHighlight;
+ }
+
+ public String getText() {
+ return mHighlightedText.mText;
+ }
+
+ public int getPreviousIndex() {
+ return mPreviousValidIndex;
+ }
+
+ public int getNextIndex() {
+ return mNextValidIndex;
+ }
+
+ public int getAdapterIndex() {
+ return mAdapterIndex;
+ }
+
+ private static int
+ getFirstValidIndex(RtlIndexArrayList<BaseRecyclerViewScrubberSection> sections,
+ boolean isRtl) {
+ for (int i = 0; i < sections.size(); i++) {
+ if (sections.get(i, isRtl).getHighlight()) {
+ return i;
+ }
+ }
+
+ return INVALID_INDEX;
+ }
+
+ private static void createIndices(RtlIndexArrayList<BaseRecyclerViewScrubberSection> sections,
+ boolean isRtl) {
+ if (sections == null || sections.size() == 0) {
+ return;
+ }
+
+ // walk forwards and fill out the previous valid index based on the previous highlight
+ int currentIdx = getFirstValidIndex(sections, isRtl);
+ for (int i = 0; i < sections.size(); i++) {
+ if (sections.get(i, isRtl).getHighlight()) {
+ currentIdx = i;
+ }
+
+ sections.get(i, isRtl).mPreviousValidIndex = currentIdx;
+ }
+
+ // currentIdx should be now on the last valid index so walk back and fill the other way
+ for (int i = sections.size() - 1; i >= 0; i--) {
+ if (sections.get(i, isRtl).getHighlight()) {
+ currentIdx = i;
+ }
+
+ sections.get(i, isRtl).mNextValidIndex = currentIdx;
+ }
+ }
+
+ public static ArrayList<AutoExpandTextView.HighlightedText> getHighlightText(
+ RtlIndexArrayList<BaseRecyclerViewScrubberSection> sections) {
+ if (sections == null) {
+ return null;
+ }
+
+ ArrayList<AutoExpandTextView.HighlightedText> highlights = new ArrayList<>(sections.size());
+ for (BaseRecyclerViewScrubberSection section : sections) {
+ highlights.add(section.mHighlightedText);
+ }
+
+ return highlights;
+ }
+
+ private static void addAlphaLetters(RtlIndexArrayList<BaseRecyclerViewScrubberSection> sections,
+ HashMap<Integer, Integer> foundAlphaLetters) {
+ for (int i = 0; i < ALPHA_LETTERS.length(); i++) {
+ boolean highlighted = foundAlphaLetters.containsKey(i);
+ int index = highlighted
+ ? foundAlphaLetters.get(i) : BaseRecyclerViewScrubberSection.INVALID_INDEX;
+
+ sections.add(new BaseRecyclerViewScrubberSection(ALPHA_LETTERS.substring(i, i + 1),
+ highlighted, index));
+ }
+ }
+
+ /**
+ * Takes the sections and runs some checks to see if we can create a valid
+ * appDrawerScrubberSection out of it. This list will contain the original header list plus
+ * fill out the remaining sections based on the ALPHA_LETTERS. It will then determine which
+ * ones to highlight as well as what letters to highlight when scrolling over the
+ * grayed out sections
+ * @param sectionNames list of sectionName Strings
+ * @return the list of scrubber sections
+ */
+ public static RtlIndexArrayList<BaseRecyclerViewScrubberSection>
+ createSections(String[] sectionNames, boolean isRtl) {
+ // check if we have a valid header section
+ if (!validSectionNameList(sectionNames)) {
+ return null;
+ }
+
+ // this will track the mapping of ALPHA_LETTERS index to the headers index
+ HashMap<Integer, Integer> foundAlphaLetters = new HashMap<>();
+ RtlIndexArrayList<BaseRecyclerViewScrubberSection> sections =
+ new RtlIndexArrayList<>(sectionNames.length);
+ boolean inAlphaLetterSection = false;
+
+ for (int i = 0; i < sectionNames.length; i++) {
+ int alphaLetterIndex = TextUtils.isEmpty(sectionNames[i])
+ ? -1 : ALPHA_LETTERS.indexOf(sectionNames[i]);
+
+ // if we found an ALPHA_LETTERS store that in foundAlphaLetters and continue
+ if (alphaLetterIndex >= 0) {
+ foundAlphaLetters.put(alphaLetterIndex, i);
+ inAlphaLetterSection = true;
+ } else {
+ // if we are exiting the ALPHA_LETTERS section, add it here
+ if (inAlphaLetterSection) {
+ addAlphaLetters(sections, foundAlphaLetters);
+ inAlphaLetterSection = false;
+ }
+
+ // add the custom header
+ sections.add(new BaseRecyclerViewScrubberSection(sectionNames[i], true, i));
+ }
+ }
+
+ // if the last section are the alpha letters, then add it
+ if (inAlphaLetterSection) {
+ addAlphaLetters(sections, foundAlphaLetters);
+ }
+
+ // create the forward and backwards indices for scrolling over the grayed out sections
+ BaseRecyclerViewScrubberSection.createIndices(sections, isRtl);
+
+ return sections;
+ }
+
+ /**
+ * Walk through the sectionNames and check for a few things:
+ * 1) No more than MAX_NUMBER_CUSTOM_SECTIONS sectionNames exist in the sectionNames list or no more
+ * than MAX_SECTIONS sectionNames exist in the list
+ * 2) the headers that fall in the ALPHA_LETTERS category are in the same order as ALPHA_LETTERS
+ * 3) There are no sectionNames that exceed length of 1
+ * 4) The alpha letter sectionName is together and not separated by other things
+ */
+ private static boolean validSectionNameList(String[] sectionNames) {
+ int numCustomSections = 0;
+ int previousAlphaIndex = -1;
+ boolean foundAlphaSections = false;
+
+ for (String s : sectionNames) {
+ if (TextUtils.isEmpty(s)) {
+ numCustomSections++;
+ continue;
+ }
+
+ if (s.length() > 1) {
+ Log.w(TAG, "Found section " + s + " with length: " + s.length());
+ return false;
+ }
+
+ int alphaIndex = ALPHA_LETTERS.indexOf(s);
+ if (alphaIndex >= 0) {
+ if (previousAlphaIndex != -1) {
+ // if the previous alpha index is >= alphaIndex then it is in the wrong order
+ if (previousAlphaIndex >= alphaIndex) {
+ Log.w(TAG, "Found letter index " + previousAlphaIndex
+ + " which is greater than " + alphaIndex);
+ return false;
+ }
+ }
+
+ // if we've found headers previously and the index is -1 that means the alpha
+ // letters are separated out into two sections so return false
+ if (foundAlphaSections && previousAlphaIndex == -1) {
+ Log.w(TAG, "Found alpha letters twice");
+ return false;
+ }
+
+ previousAlphaIndex = alphaIndex;
+ foundAlphaSections = true;
+ } else {
+ numCustomSections++;
+ previousAlphaIndex = -1;
+ }
+ }
+
+ final int listSize = foundAlphaSections
+ ? numCustomSections + ALPHA_LETTERS.length()
+ : numCustomSections;
+
+ // if one of these conditions are satisfied, then return true
+ if (numCustomSections <= MAX_NUMBER_CUSTOM_SECTIONS || listSize <= MAX_SECTIONS) {
+ return true;
+ }
+
+ if (numCustomSections > MAX_NUMBER_CUSTOM_SECTIONS) {
+ Log.w(TAG, "Found " + numCustomSections + "# custom sections when " +
+ MAX_NUMBER_CUSTOM_SECTIONS + " is allowed!");
+ } else if (listSize > MAX_SECTIONS) {
+ Log.w(TAG, "Found " + listSize + " sections when " +
+ MAX_SECTIONS + " is allowed!");
+ }
+
+ return false;
+ }
+
+ public static class RtlIndexArrayList<T> extends ArrayList<T> {
+
+ public RtlIndexArrayList(int capacity) {
+ super(capacity);
+ }
+
+ public T get(int index, boolean isRtl) {
+ if (isRtl) {
+ index = size() - 1 - index;
+ }
+ return super.get(index);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 507087824..facafd354 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -16,7 +16,6 @@
package com.android.launcher3;
-import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
@@ -37,8 +36,6 @@ 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;
@@ -50,7 +47,7 @@ import com.android.launcher3.model.PackageItemInfo;
* too aggressive.
*/
public class BubbleTextView extends TextView
- implements BaseRecyclerViewFastScrollBar.FastScrollFocusableView {
+ implements BaseRecyclerViewFastScrollBar.FastScrollFocusable {
private static SparseArray<Theme> sPreloaderThemes = new SparseArray<Theme>(2);
@@ -63,12 +60,9 @@ public class BubbleTextView extends TextView
private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1;
- private static final float FAST_SCROLL_FOCUS_MAX_SCALE = 1.15f;
private static final int FAST_SCROLL_FOCUS_MODE_NONE = 0;
private static final int FAST_SCROLL_FOCUS_MODE_SCALE_ICON = 1;
private static final int FAST_SCROLL_FOCUS_MODE_DRAW_CIRCLE_BG = 2;
- private static final int FAST_SCROLL_FOCUS_FADE_IN_DURATION = 175;
- private static final int FAST_SCROLL_FOCUS_FADE_OUT_DURATION = 125;
private final Launcher mLauncher;
private Drawable mIcon;
@@ -93,10 +87,8 @@ public class BubbleTextView extends TextView
private boolean mIgnorePressedStateChange;
private boolean mDisableRelayout = false;
- private ObjectAnimator mFastScrollFocusAnimator;
private Paint mFastScrollFocusBgPaint;
private float mFastScrollFocusFraction;
- private boolean mFastScrollFocused;
private final int mFastScrollMode = FAST_SCROLL_FOCUS_MODE_SCALE_ICON;
private IconLoadRequest mIconLoadRequest;
@@ -167,10 +159,21 @@ public class BubbleTextView extends TextView
public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
boolean promiseStateChanged) {
- Bitmap b = info.getIcon(iconCache);
+ Drawable iconDrawable;
+ if (info.customDrawable != null) {
+ iconDrawable = info.customDrawable;
+ } else {
+ Bitmap b = info.getIcon(iconCache);
+
+ if (b.getWidth() > mIconSize || b.getHeight() > mIconSize) {
+ b = Bitmap.createScaledBitmap(b, mIconSize, mIconSize, false);
+ info.setIcon(b);
+ info.updateIcon(iconCache);
+ }
- FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
- iconDrawable.setGhostModeEnabled(info.isDisabled != 0);
+ iconDrawable = mLauncher.createIconDrawable(b);
+ ((FastBitmapDrawable) iconDrawable).setGhostModeEnabled(info.isDisabled != 0);
+ }
setIcon(iconDrawable, mIconSize);
if (info.contentDescription != null) {
@@ -185,7 +188,13 @@ public class BubbleTextView extends TextView
}
public void applyFromApplicationInfo(AppInfo info) {
- setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
+ Drawable iconDrawable;
+ if (info.customDrawable != null) {
+ iconDrawable = info.customDrawable;
+ } else {
+ iconDrawable = mLauncher.createIconDrawable(info.iconBitmap);
+ }
+ setIcon(iconDrawable, mIconSize);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
@@ -558,6 +567,11 @@ public class BubbleTextView extends TextView
* Verifies that the current icon is high-res otherwise posts a request to load the icon.
*/
public void verifyHighRes() {
+ // Custom drawables cannot be verified.
+ if (getTag() instanceof ItemInfo && ((ItemInfo) getTag()).customDrawable != null) {
+ return;
+ }
+
if (mIconLoadRequest != null) {
mIconLoadRequest.cancel();
mIconLoadRequest = null;
@@ -583,51 +597,22 @@ public class BubbleTextView extends TextView
}
}
- // Setters & getters for the animation
- public void setFastScrollFocus(float fraction) {
- mFastScrollFocusFraction = fraction;
- if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_SCALE_ICON) {
- setScaleX(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
- setScaleY(1f + fraction * (FAST_SCROLL_FOCUS_MAX_SCALE - 1f));
- } else {
- invalidate();
- }
- }
-
- public float getFastScrollFocus() {
- return mFastScrollFocusFraction;
- }
-
@Override
public void setFastScrollFocused(final boolean focused, boolean animated) {
if (mFastScrollMode == FAST_SCROLL_FOCUS_MODE_NONE) {
return;
}
- if (mFastScrollFocused != focused) {
- mFastScrollFocused = focused;
-
- if (animated) {
- // Clean up the previous focus animator
- if (mFastScrollFocusAnimator != null) {
- mFastScrollFocusAnimator.cancel();
- }
- mFastScrollFocusAnimator = ObjectAnimator.ofFloat(this, "fastScrollFocus",
- focused ? 1f : 0f);
- if (focused) {
- mFastScrollFocusAnimator.setInterpolator(new DecelerateInterpolator());
- } else {
- mFastScrollFocusAnimator.setInterpolator(new AccelerateInterpolator());
- }
- mFastScrollFocusAnimator.setDuration(focused ?
- FAST_SCROLL_FOCUS_FADE_IN_DURATION : FAST_SCROLL_FOCUS_FADE_OUT_DURATION);
- mFastScrollFocusAnimator.start();
- } else {
- mFastScrollFocusFraction = focused ? 1f : 0f;
- }
+ if (!animated) {
+ mFastScrollFocusFraction = focused ? 1f : 0f;
}
}
+ @Override
+ public void setFastScrollDimmed(boolean dimmed, boolean animated) {
+ // No special functionality here.
+ }
+
/**
* Interface to be implemented by the grand parent to allow click shadow effect.
*/
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 84e2d49c2..10599fa5e 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -504,13 +504,15 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
// Draw inner ring
d = FolderRingAnimator.sSharedInnerRingDrawable;
- width = (int) (fra.getInnerRingSize() * getChildrenScale());
- height = width;
- canvas.save();
- canvas.translate(centerX - width / 2, centerY - width / 2);
- d.setBounds(0, 0, width, height);
- d.draw(canvas);
- canvas.restore();
+ if (d != null) {
+ width = (int) (fra.getInnerRingSize() * getChildrenScale());
+ height = width;
+ canvas.save();
+ canvas.translate(centerX - width / 2, centerY - width / 2);
+ d.setBounds(0, 0, width, height);
+ d.draw(canvas);
+ canvas.restore();
+ }
}
}
@@ -613,9 +615,8 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
final LayoutParams lp = params;
// Hotseat icons - remove text
- if (child instanceof BubbleTextView) {
- BubbleTextView bubbleChild = (BubbleTextView) child;
- bubbleChild.setTextVisibility(!mIsHotseat);
+ if (mIsHotseat && child instanceof BubbleTextView) {
+ ((BubbleTextView) child).setTextVisibility(false);
}
child.setScaleX(getChildrenScale());
@@ -2223,7 +2224,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler {
mLauncher.getWorkspace().updateItemLocationsInDatabase(this);
}
- private void setUseTempCoords(boolean useTempCoords) {
+ public void setUseTempCoords(boolean useTempCoords) {
int childCount = mShortcutsAndWidgets.getChildCount();
for (int i = 0; i < childCount; i++) {
LayoutParams lp = (LayoutParams) mShortcutsAndWidgets.getChildAt(i).getLayoutParams();
diff --git a/src/com/android/launcher3/CheckLongPressHelper.java b/src/com/android/launcher3/CheckLongPressHelper.java
index 483c62249..bd1d84128 100644
--- a/src/com/android/launcher3/CheckLongPressHelper.java
+++ b/src/com/android/launcher3/CheckLongPressHelper.java
@@ -30,6 +30,8 @@ public class CheckLongPressHelper {
class CheckForLongPress implements Runnable {
public void run() {
+ if (!mView.isLongClickable()) return;
+
if ((mView.getParent() != null) && mView.hasWindowFocus()
&& !mHasPerformedLongPress) {
boolean handled;
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 9c8659c29..f51a43a91 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -74,8 +74,14 @@ public class DeleteDropTarget extends ButtonDropTarget {
LauncherModel.deleteItemFromDatabase(launcher, item);
} else if (item instanceof FolderInfo) {
FolderInfo folder = (FolderInfo) item;
- launcher.removeFolder(folder);
- LauncherModel.deleteFolderContentsFromDatabase(launcher, folder);
+
+ // Remote folder should not really be deleted. Let the manager handle it.
+ if (folder.isRemote()) {
+ launcher.getRemoteFolderManager().onFolderDeleted();
+ } else {
+ launcher.removeFolder(folder);
+ LauncherModel.deleteFolderContentsFromDatabase(launcher, folder);
+ }
} else if (item instanceof LauncherAppWidgetInfo) {
final LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) item;
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 774594fe2..51c634d4f 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -29,12 +29,13 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
+import com.android.launcher3.settings.SettingsProvider;
-public class DeviceProfile {
+import com.android.launcher3.allapps.AllAppsContainerView;
+public class DeviceProfile {
public final InvariantDeviceProfile inv;
// Device properties
@@ -94,8 +95,10 @@ public class DeviceProfile {
public final int allAppsIconTextSizePx;
// QSB
+ public boolean searchBarVisible;
private int searchBarSpaceWidthPx;
private int searchBarSpaceHeightPx;
+ private int defaultSearchBarSpaceHeightPx;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
Point minSize, Point maxSize,
@@ -156,8 +159,16 @@ public class DeviceProfile {
}
// Calculate the remaining vars
- updateAvailableDimensions(dm, res);
+ updateAvailableDimensions(dm, res, isLandscape);
computeAllAppsButtonSize(context);
+
+ // Search Bar
+ searchBarVisible = isSearchBarEnabled(context);
+ searchBarSpaceWidthPx = Math.min(searchBarSpaceWidthPx, widthPx);
+ defaultSearchBarSpaceHeightPx = getSearchBarTopOffset()
+ + res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
+ searchBarSpaceHeightPx = 2 * edgeMarginPx + (searchBarVisible ?
+ defaultSearchBarSpaceHeightPx - getSearchBarTopOffset() : 3 * edgeMarginPx);
}
/**
@@ -170,7 +181,7 @@ public class DeviceProfile {
allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding));
}
- private void updateAvailableDimensions(DisplayMetrics dm, Resources res) {
+ private void updateAvailableDimensions(DisplayMetrics dm, Resources res, boolean isLandscape) {
// Check to see if the icons fit in the new available height. If not, then we need to
// shrink the icon size.
float scale = 1f;
@@ -181,6 +192,9 @@ public class DeviceProfile {
// We only care about the top and bottom workspace padding, which is not affected by RTL.
Rect workspacePadding = getWorkspacePadding(false /* isLayoutRtl */);
int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
+ if (!isLandscape) { //Include the hotseat and search bar if portrait
+ maxHeight -= (hotseatBarHeightPx + searchBarSpaceHeightPx);
+ }
if (usedHeight > maxHeight) {
scale = maxHeight / usedHeight;
drawablePadding = 0;
@@ -198,8 +212,9 @@ public class DeviceProfile {
// Search Bar
searchBarSpaceWidthPx = Math.min(widthPx,
res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width));
- searchBarSpaceHeightPx = getSearchBarTopOffset()
+ defaultSearchBarSpaceHeightPx = getSearchBarTopOffset()
+ res.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_height);
+ searchBarSpaceHeightPx = defaultSearchBarSpaceHeightPx;
// Calculate the actual text height
Paint textPaint = new Paint();
@@ -225,11 +240,13 @@ public class DeviceProfile {
/**
* @param recyclerViewWidth the available width of the AllAppsRecyclerView
*/
- public void updateAppsViewNumCols(Resources res, int recyclerViewWidth) {
- int appsViewLeftMarginPx =
- res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
- int allAppsCellWidthGap =
- res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap);
+ public void updateAppsViewNumCols(Resources res, int recyclerViewWidth, int gridStrategy) {
+ int appsViewLeftMarginPx = gridStrategy == AllAppsContainerView.SECTION_STRATEGY_GRID ?
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin) :
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin_with_sections);
+ int allAppsCellWidthGap = gridStrategy == AllAppsContainerView.SECTION_STRATEGY_GRID ?
+ res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap) :
+ res.getDimensionPixelSize(R.dimen.all_apps_icon_width_gap_with_sections);
int availableAppsWidthPx = (recyclerViewWidth > 0) ? recyclerViewWidth : availableWidthPx;
int numAppsCols = (availableAppsWidthPx - appsViewLeftMarginPx) /
(allAppsIconSizePx + allAppsCellWidthGap);
@@ -241,9 +258,9 @@ public class DeviceProfile {
/** Returns the search bar top offset */
private int getSearchBarTopOffset() {
if (isTablet && !isVerticalBarLayout()) {
- return 4 * edgeMarginPx;
+ return searchBarVisible ? 4 * edgeMarginPx : 0;
} else {
- return 2 * edgeMarginPx;
+ return searchBarVisible ? 2 * edgeMarginPx : 0;
}
}
@@ -269,12 +286,13 @@ public class DeviceProfile {
(inv.numColumns * cellWidthPx)) / (2 * (inv.numColumns + 1)));
bounds.set(edgeMarginPx + gap, getSearchBarTopOffset(),
availableWidthPx - (edgeMarginPx + gap),
- searchBarSpaceHeightPx);
+ searchBarVisible ? searchBarSpaceHeightPx : edgeMarginPx);
} else {
bounds.set(desiredWorkspaceLeftRightMarginPx - defaultWidgetPadding.left,
getSearchBarTopOffset(),
availableWidthPx - (desiredWorkspaceLeftRightMarginPx -
- defaultWidgetPadding.right), searchBarSpaceHeightPx);
+ defaultWidgetPadding.right), searchBarVisible ? searchBarSpaceHeightPx :
+ edgeMarginPx);
}
}
return bounds;
@@ -378,15 +396,18 @@ public class DeviceProfile {
return visibleChildren;
}
- public void layout(Launcher launcher) {
- FrameLayout.LayoutParams lp;
- boolean hasVerticalBarLayout = isVerticalBarLayout();
- final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
+ public void layoutSearchBar(Launcher launcher, boolean hasVerticalBarLayout) {
+ // Update search bar for live settings
+ searchBarVisible = isSearchBarEnabled(launcher);
// Layout the search bar space
View searchBar = launcher.getSearchDropTargetBar();
- lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams();
if (hasVerticalBarLayout) {
+ // If search bar is invisible add some extra padding for the drop targets
+ searchBarSpaceHeightPx = searchBarVisible ? searchBarSpaceHeightPx
+ : defaultSearchBarSpaceHeightPx + 5 * edgeMarginPx;
+
// Vertical search bar space -- The search bar is fixed in the layout to be on the left
// of the screen regardless of RTL
lp.gravity = Gravity.LEFT;
@@ -394,7 +415,8 @@ public class DeviceProfile {
LinearLayout targets = (LinearLayout) searchBar.findViewById(R.id.drag_target_bar);
targets.setOrientation(LinearLayout.VERTICAL);
- FrameLayout.LayoutParams targetsLp = (FrameLayout.LayoutParams) targets.getLayoutParams();
+ FrameLayout.LayoutParams targetsLp =
+ (FrameLayout.LayoutParams) targets.getLayoutParams();
targetsLp.gravity = Gravity.TOP;
targetsLp.height = LayoutParams.WRAP_CONTENT;
@@ -408,6 +430,23 @@ public class DeviceProfile {
}
searchBar.setLayoutParams(lp);
+ View qsbBar = launcher.getOrCreateQsbBar();
+ if (qsbBar != null) {
+ qsbBar.setVisibility(searchBarVisible ? View.VISIBLE : View.GONE);
+ LayoutParams vglp = qsbBar.getLayoutParams();
+ vglp.width = LayoutParams.MATCH_PARENT;
+ vglp.height = LayoutParams.MATCH_PARENT;
+ qsbBar.setLayoutParams(vglp);
+ }
+ }
+
+ public void layout(Launcher launcher) {
+ FrameLayout.LayoutParams lp;
+ boolean hasVerticalBarLayout = isVerticalBarLayout();
+ final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources());
+
+ layoutSearchBar(launcher, hasVerticalBarLayout);
+
// Layout the workspace
PagedView workspace = (PagedView) launcher.findViewById(R.id.workspace);
lp = (FrameLayout.LayoutParams) workspace.getLayoutParams();
@@ -458,13 +497,13 @@ public class DeviceProfile {
lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
lp.width = LayoutParams.WRAP_CONTENT;
lp.height = LayoutParams.WRAP_CONTENT;
- lp.bottomMargin = hotseatBarHeightPx;
+ lp.bottomMargin = Math.max(hotseatBarHeightPx , lp.bottomMargin);
pageIndicator.setLayoutParams(lp);
}
}
// Layout the Overview Mode
- ViewGroup overviewMode = launcher.getOverviewPanel();
+ /*ViewGroup overviewMode = launcher.getOverviewPanel();
if (overviewMode != null) {
int overviewButtonBarHeight = getOverviewModeButtonBarHeight();
lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams();
@@ -501,7 +540,7 @@ public class DeviceProfile {
}
}
}
- }
+ }*/
}
private int getCurrentWidth() {
@@ -515,4 +554,24 @@ public class DeviceProfile {
? Math.min(widthPx, heightPx)
: Math.max(widthPx, heightPx);
}
+
+ private boolean isSearchBarEnabled(Context context) {
+ boolean searchActivityExists = Utilities.searchActivityExists(context);
+
+ boolean isSearchEnabled = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH,
+ R.bool.preferences_interface_homescreen_search_default);
+
+ if (searchActivityExists) {
+ return isSearchEnabled;
+ } else {
+ if (isSearchEnabled) {
+ // Disable search bar
+ SettingsProvider.putBoolean(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH, false);
+ }
+
+ return false;
+ }
+ }
}
diff --git a/src/com/android/launcher3/DeviceUnlockedReceiver.java b/src/com/android/launcher3/DeviceUnlockedReceiver.java
new file mode 100644
index 000000000..6e800e338
--- /dev/null
+++ b/src/com/android/launcher3/DeviceUnlockedReceiver.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class DeviceUnlockedReceiver extends BroadcastReceiver {
+ public static final String INTENT_ACTION = Intent.ACTION_USER_PRESENT;
+
+ private final Set<DeviceUnlockedListener> mListeners;
+
+ interface DeviceUnlockedListener {
+ void onDeviceUnlocked();
+ }
+
+ public DeviceUnlockedReceiver() {
+ mListeners = new HashSet<DeviceUnlockedListener>();
+ }
+
+ public void registerListener(final DeviceUnlockedListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void deregisterListener(final DeviceUnlockedListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(INTENT_ACTION)) return;
+
+ for (DeviceUnlockedListener listener: mListeners) {
+ listener.onDeviceUnlocked();
+ }
+ }
+}
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 1c18747c1..03c5a1491 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -823,6 +823,7 @@ public class DragLayer extends InsettableFrameLayout {
@Override
public void onChildViewRemoved(View parent, View child) {
+ super.onChildViewRemoved(parent, child);
updateChildIndices();
}
diff --git a/src/com/android/launcher3/DynamicGridSizeFragment.java b/src/com/android/launcher3/DynamicGridSizeFragment.java
new file mode 100644
index 000000000..103d660dd
--- /dev/null
+++ b/src/com/android/launcher3/DynamicGridSizeFragment.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.app.Dialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.NumberPicker;
+import android.widget.TextView;
+import com.android.launcher3.settings.SettingsProvider;
+
+public class DynamicGridSizeFragment extends Fragment
+ implements NumberPicker.OnValueChangeListener, Dialog.OnDismissListener {
+ public static final String DYNAMIC_GRID_SIZE_FRAGMENT = "DynamicGridSizeFragment";
+
+ public static final int MIN_DYNAMIC_GRID_ROWS = 2;
+ public static final int MIN_DYNAMIC_GRID_COLUMNS = 3;
+
+ GridSizeView mDynamicGrid;
+
+ ListView mListView;
+ View mCurrentSelection;
+ GridSizeAdapter mAdapter;
+ InvariantDeviceProfile.GridSize mCurrentSize;
+
+ Dialog mDialog;
+
+ int mCustomGridRows = 0;
+ int mCustomGridColumns = 0;
+
+ View.OnClickListener mSettingsItemListener = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ mCurrentSize = InvariantDeviceProfile.GridSize.getModeForValue((Integer) v.getTag());
+
+ setCleared(mCurrentSelection);
+ setSelected(v);
+ mCurrentSelection = v;
+
+ if (mCurrentSize == InvariantDeviceProfile.GridSize.Custom) {
+ showNumberPicker();
+ }
+
+ ((GridSizeAdapter) mListView.getAdapter()).notifyDataSetChanged();
+
+ mAdapter.notifyDataSetInvalidated();
+ updateGridMetrics();
+ }
+ };
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ View v = inflater.inflate(R.layout.dynamic_grid_size_screen, container, false);
+ mDynamicGrid = (GridSizeView) v.findViewById(R.id.dynamic_grid_size_image);
+ mListView = (ListView) v.findViewById(R.id.dynamic_grid_list);
+
+ Launcher launcher = (Launcher) getActivity();
+ LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams)
+ mListView.getLayoutParams();
+ lp.bottomMargin = ((FrameLayout.LayoutParams) launcher.getOverviewPanel()
+ .findViewById(R.id.settings_container).getLayoutParams()).bottomMargin;
+ mListView.setLayoutParams(lp);
+
+ LinearLayout titleLayout = (LinearLayout) v.findViewById(R.id.dynamic_grid_title);
+ titleLayout.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setSize();
+ }
+ });
+
+ mCurrentSize = InvariantDeviceProfile.GridSize.getModeForValue(
+ SettingsProvider.getIntCustomDefault(getActivity(),
+ SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0));
+
+ InvariantDeviceProfile grid = getInvariantDeviceProfile();
+ mCustomGridRows = grid.numRows;
+ mCustomGridColumns = grid.numColumns;
+
+ updateGridMetrics();
+
+ Resources res = getResources();
+ int[] valueResIds = {
+ R.string.grid_size_comfortable,
+ R.string.grid_size_cozy,
+ R.string.grid_size_condensed,
+ R.string.grid_size_custom
+ };
+ mAdapter = new GridSizeAdapter(getActivity(), valueResIds);
+ mListView.setAdapter(mAdapter);
+
+ // RTL
+ ImageView navPrev = (ImageView) v.findViewById(R.id.nav_prev);
+ Configuration config = getResources().getConfiguration();
+ if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ navPrev.setImageResource(R.drawable.ic_navigation_next);
+ }
+
+ return v;
+ }
+
+ private void updateGridMetrics() {
+ if (mCurrentSize == InvariantDeviceProfile.GridSize.Custom) {
+ mDynamicGrid.setMetrics(mCustomGridRows, mCustomGridColumns);
+ } else {
+ InvariantDeviceProfile grid = getInvariantDeviceProfile();
+ mDynamicGrid.setMetrics(grid.numRowsBase + mCurrentSize.getValue(),
+ grid.numColumnsBase + mCurrentSize.getValue());
+ }
+ }
+
+ @Override
+ public Animator onCreateAnimator(int transit, boolean enter, int nextAnim) {
+ if (enter) {
+ DisplayMetrics displaymetrics = new DisplayMetrics();
+ getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
+ int width = displaymetrics.widthPixels;
+ Configuration config = getResources().getConfiguration();
+ final ObjectAnimator anim;
+ if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ anim = ObjectAnimator.ofFloat(this, "translationX", -width, 0);
+ } else {
+ anim = ObjectAnimator.ofFloat(this, "translationX", width, 0);
+ }
+
+ final View darkPanel = ((Launcher) getActivity()).getDarkPanel();
+ darkPanel.setVisibility(View.VISIBLE);
+ ObjectAnimator anim2 = ObjectAnimator.ofFloat(darkPanel, "alpha", 0.0f, 0.3f);
+ anim2.start();
+
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd (Animator animation) {
+ darkPanel.setVisibility(View.GONE);
+ }
+ });
+
+ return anim;
+ } else {
+ return super.onCreateAnimator(transit, enter, nextAnim);
+ }
+ }
+
+ public void setSize() {
+ ((Launcher) getActivity()).setDynamicGridSize(mCurrentSize);
+ }
+
+ private void setSelected(View v) {
+ v.setBackgroundColor(Color.WHITE);
+ TextView t = (TextView) v.findViewById(R.id.item_name);
+ t.setTextColor(getResources().getColor(R.color.settings_bg_color));
+ }
+
+ private void setCleared(View v) {
+ v.setBackgroundColor(getResources().getColor(R.color.settings_bg_color));
+ TextView t = (TextView) v.findViewById(R.id.item_name);
+ t.setTextColor(Color.WHITE);
+ }
+
+ private void showNumberPicker() {
+ mDialog = new Dialog(getActivity());
+ mDialog.setTitle(getResources().getString(
+ R.string.preferences_interface_homescreen_custom));
+ mDialog.setContentView(R.layout.custom_grid_size_dialog);
+
+ NumberPicker nPRows = (NumberPicker) mDialog.findViewById(R.id.custom_rows);
+ NumberPicker nPColumns = (NumberPicker) mDialog.findViewById(R.id.custom_columns);
+
+ InvariantDeviceProfile grid = getInvariantDeviceProfile();
+ int rows = grid.numRowsBase;
+ int columns = grid.numColumnsBase;
+
+ nPRows.setMinValue(Math.max(MIN_DYNAMIC_GRID_ROWS, rows - InvariantDeviceProfile.GRID_SIZE_MIN));
+ nPRows.setMaxValue(rows + InvariantDeviceProfile.GRID_SIZE_MAX);
+ nPRows.setValue(mCustomGridRows);
+ nPRows.setWrapSelectorWheel(false);
+ nPRows.setOnValueChangedListener(this);
+ nPRows.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
+
+ nPColumns.setMinValue(Math.max(MIN_DYNAMIC_GRID_COLUMNS,
+ columns - InvariantDeviceProfile.GRID_SIZE_MIN));
+ nPColumns.setMaxValue(columns + InvariantDeviceProfile.GRID_SIZE_MAX);
+ nPColumns.setValue(mCustomGridColumns);
+ nPColumns.setWrapSelectorWheel(false);
+ nPColumns.setOnValueChangedListener(this);
+ nPColumns.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
+
+ Button button = (Button) mDialog.findViewById(R.id.dialog_confirm_button);
+ button.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ }
+ });
+
+ mDialog.setOnDismissListener(this);
+ mDialog.show();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ }
+
+ @Override
+ public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
+ if (picker.getId() == R.id.custom_rows) {
+ mCustomGridRows = newVal;
+ } else if (picker.getId() == R.id.custom_columns) {
+ mCustomGridColumns = newVal;
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ SettingsProvider.putInt(getActivity(),
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, mCustomGridRows);
+ SettingsProvider.putInt(getActivity(),
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, mCustomGridColumns);
+
+ mAdapter.notifyDataSetInvalidated();
+ mDynamicGrid.setMetrics(mCustomGridRows, mCustomGridColumns);
+ }
+
+ private class GridSizeAdapter extends BaseAdapter {
+ Context mContext;
+ int[] mTitleResIds;
+
+ public GridSizeAdapter(Context context, int[] resIds) {
+ mContext = context;
+ mTitleResIds = resIds;
+ }
+
+ @Override
+ public int getCount() {
+ return mTitleResIds.length;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mContext.getString(mTitleResIds[position]);
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ LayoutInflater inflater = (LayoutInflater)
+ mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ convertView = inflater.inflate(R.layout.settings_pane_list_item, parent, false);
+ }
+
+ TextView textView = (TextView) convertView.findViewById(R.id.item_name);
+
+ // RTL
+ Configuration config = getResources().getConfiguration();
+ if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ textView.setGravity(Gravity.RIGHT);
+ }
+
+ // Set selected state
+ if (position == mCurrentSize.getValue()) {
+ if (mCurrentSelection != null) {
+ setCleared(mCurrentSelection);
+ }
+ mCurrentSelection = convertView;
+ setSelected(mCurrentSelection);
+ }
+
+ if (position == InvariantDeviceProfile.GridSize.Custom.getValue()) {
+ InvariantDeviceProfile grid = getInvariantDeviceProfile();
+
+ int rows = SettingsProvider.getIntCustomDefault(getActivity(),
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, grid.numRowsBase);
+ int columns = SettingsProvider.getIntCustomDefault(getActivity(),
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, grid.numColumnsBase);
+ textView.setText(mContext.getString(mTitleResIds[position], rows, columns));
+ } else {
+ textView.setText(mTitleResIds[position]);
+ }
+
+ convertView.setOnClickListener(mSettingsItemListener);
+ convertView.setTag(position);
+ return convertView;
+ }
+ }
+
+ private InvariantDeviceProfile getInvariantDeviceProfile() {
+ LauncherAppState app = LauncherAppState.getInstance();
+ return app.getInvariantDeviceProfile();
+ }
+
+ private static class GridSizeView extends View {
+ private int mRows = 0, mColumns = 0;
+ private Paint mForegroundPaint;
+ private int mBackgroundColor;
+
+ public GridSizeView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ Resources res = context.getResources();
+
+ mForegroundPaint = new Paint();
+ mForegroundPaint.setColor(res.getColor(R.color.dynamic_grid_preview_foreground));
+ mBackgroundColor = res.getColor(R.color.dynamic_grid_preview_background);
+ }
+
+ public void setMetrics(int rows, int columns) {
+ mRows = rows;
+ mColumns = columns;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ float width = getWidth() - getPaddingLeft() - getPaddingRight();
+ float height = getHeight() - getPaddingTop() - getPaddingBottom();
+ float xOffset = getPaddingLeft();
+ float yOffset = getPaddingTop();
+
+ canvas.drawColor(mBackgroundColor);
+
+ // Draw rows
+ for (int i = 1; i < mRows; i++) {
+ float yPos = yOffset + height / mRows * i;
+ canvas.drawLine(xOffset, yPos, xOffset + width, yPos, mForegroundPaint);
+ }
+
+ // Draw columns
+ for (int j = 1; j < mColumns; j++) {
+ float xPos = xOffset + width / mColumns * j;
+ canvas.drawLine(xPos, yOffset, xPos, yOffset + height, mForegroundPaint);
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
index ecf93e4b3..c30b56177 100644
--- a/src/com/android/launcher3/FocusIndicatorView.java
+++ b/src/com/android/launcher3/FocusIndicatorView.java
@@ -165,6 +165,9 @@ public class FocusIndicatorView extends View implements View.OnFocusChangeListen
private static void computeLocationRelativeToParentHelper(View child,
View commonParent, int[] shift) {
+ if (child == null) {
+ return;
+ }
View parent = (View) child.getParent();
shift[0] += child.getLeft();
shift[1] += child.getTop();
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index c1aa35669..1ac5cf85e 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -23,18 +23,26 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Resources;
+import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
import android.os.Build;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.provider.Settings;
import android.text.InputType;
import android.text.Selection;
import android.text.Spannable;
import android.util.AttributeSet;
import android.util.Log;
+import android.util.Pair;
import android.view.ActionMode;
import android.view.KeyEvent;
import android.view.Menu;
@@ -46,8 +54,11 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.AnimationUtils;
+import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -57,12 +68,18 @@ import com.android.launcher3.FolderInfo.FolderListener;
import com.android.launcher3.UninstallDropTarget.UninstallSource;
import com.android.launcher3.Workspace.ItemOperator;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
+import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.UiThreadCircularReveal;
+import static cyanogenmod.content.Intent.ACTION_PROTECTED;
+import static cyanogenmod.content.Intent.EXTRA_PROTECTED_COMPONENTS;
+import static cyanogenmod.content.Intent.EXTRA_PROTECTED_STATE;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.List;
/**
* Represents a set of icons chosen by the user or generated by the system.
@@ -89,6 +106,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
*/
public static final int SCROLL_HINT_DURATION = DragController.SCROLL_DELAY;
+ private static final int CLOSE_FOLDER_DELAY_MS = 150;
+
+ private static final int ALPHA_DELAY_MULT = 15;
+
/**
* Fraction of icon width which behave as scroll region.
*/
@@ -96,6 +117,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private static final int FOLDER_NAME_ANIMATION_DURATION = 633;
+ private static final int REORDER_ANIMATION_DURATION = 230;
private static final int REORDER_DELAY = 250;
private static final int ON_EXIT_CLOSE_DELAY = 400;
private static final Rect sTempRect = new Rect();
@@ -116,6 +138,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private final InputMethodManager mInputMethodManager;
+ private final PowerManager mPowerManager;
+
protected final Launcher mLauncher;
protected DragController mDragController;
protected FolderInfo mInfo;
@@ -127,7 +151,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
ExtendedEditText mFolderName;
private View mFooter;
- private int mFooterHeight;
// Cell ranks used for drag and drop
@Thunk int mTargetRank, mPrevTargetRank, mEmptyCellRank;
@@ -147,6 +170,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Thunk float mFolderIconPivotY;
private boolean mIsEditingName = false;
+ ImageView mFolderLock;
+
private boolean mDestroyed;
@Thunk Runnable mDeferredAction;
@@ -156,6 +181,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// Folder scrolling
private int mScrollAreaOffset;
+ private Handler mHandler;
+
@Thunk int mScrollHintDir = DragController.SCROLL_NONE;
@Thunk int mCurrentScrollDir = DragController.SCROLL_NONE;
@@ -168,9 +195,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public Folder(Context context, AttributeSet attrs) {
super(context, attrs);
setAlwaysDrawnWithCacheEnabled(false);
+ mHandler = new Handler();
mInputMethodManager = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+ mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+
Resources res = getResources();
mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration);
mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
@@ -214,13 +244,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mFolderName.setInputType(mFolderName.getInputType() |
InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
- mFooter = findViewById(R.id.folder_footer);
+ boolean hideLabels = SettingsProvider.getBoolean(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default);
+ if (hideLabels) {
+ mFolderName.setVisibility(View.GONE);
+ }
+ mFolderLock = (ImageView) findViewById(R.id.folder_lock);
+ mFolderLock.setOnClickListener(this);
- // We find out how tall footer wants to be (it is set to wrap_content), so that
- // we can allocate the appropriate amount of space for it.
- int measureSpec = MeasureSpec.UNSPECIFIED;
- mFooter.measure(measureSpec, measureSpec);
- mFooterHeight = mFooter.getMeasuredHeight();
+ mFooter = findViewById(R.id.folder_footer);
}
private ActionMode.Callback mActionModeCallback = new ActionMode.Callback() {
@@ -245,6 +278,72 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (tag instanceof ShortcutInfo) {
mLauncher.onClick(v);
}
+
+ if (v.getId() == R.id.folder_lock) {
+ startHiddenFolderManager(mInfo.hidden ?
+ Launcher.REQUEST_UNPROTECT_FOLDER :
+ Launcher.REQUEST_PROTECT_FOLDER);
+ }
+ }
+
+ public void startHiddenFolderManager(int action) {
+ mLauncher.validateLockForHiddenFolders(mFolderIcon, action);
+ }
+
+ public List<Pair<ComponentName, CharSequence>> getComponents() {
+ int size = mItemsInReadingOrder.size();
+ List<Pair<ComponentName, CharSequence>> components =
+ new ArrayList<Pair<ComponentName, CharSequence>>();
+
+ for (int i = 0; i < size; i++) {
+ View v = mItemsInReadingOrder.get(i);
+ Object tag = v.getTag();
+ if (tag instanceof ShortcutInfo) {
+ ShortcutInfo shortcut = (ShortcutInfo) tag;
+ components.add(Pair.create(shortcut.getIntent().getComponent(), shortcut.title));
+ }
+ }
+
+ return components;
+ }
+
+ public void modifyProtectedApps(boolean protect) {
+ ArrayList<ComponentName> components = new ArrayList<>();
+ for (Pair<ComponentName, CharSequence> item : getComponents()) {
+ if (item.first != null) {
+ components.add(item.first);
+ }
+ }
+
+ Intent intent = new Intent();
+ intent.setAction(ACTION_PROTECTED);
+ // flip the boolean value to accomodate framework
+ // in framework "false" is "protected" and "true" is "visible"
+ intent.putExtra(EXTRA_PROTECTED_STATE, !protect);
+ intent.putExtra(EXTRA_PROTECTED_COMPONENTS, components);
+
+ mLauncher.sendBroadcast(intent);
+ }
+
+ private void removeProtectedApp(ComponentName componentName) {
+ ArrayList<ComponentName> components = new ArrayList<>();
+ components.add(componentName);
+ Intent intent = new Intent();
+ intent.setAction(ACTION_PROTECTED);
+ // flip the boolean value to accomodate framework
+ // in framework "false" is "protected" and "true" is "visible"
+ intent.putExtra(EXTRA_PROTECTED_STATE, true);
+ intent.putExtra(EXTRA_PROTECTED_COMPONENTS, components);
+
+ mLauncher.sendBroadcast(intent);
+ }
+
+ public void saveHiddenFolderState(boolean protect) {
+ mInfo.hidden = protect;
+ modifyProtectedApps(protect);
+ LauncherModel.updateItemInDatabase(mLauncher, mInfo);
+ mLauncher.mModel.flushWorkerThread();
+ rebind(mInfo);
}
public boolean onLongClick(View v) {
@@ -311,7 +410,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// Convert to a string here to ensure that no other state associated with the text field
// gets saved.
String newTitle = mFolderName.getText().toString();
- mInfo.setTitle(newTitle);
+ if (!SettingsProvider.getBoolean(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default)) {
+ mInfo.setTitle(newTitle);
+ }
LauncherModel.updateItemInDatabase(mLauncher, mInfo);
if (commit) {
@@ -324,6 +427,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
Selection.setSelection((Spannable) mFolderName.getText(), 0, 0);
mIsEditingName = false;
+ mLauncher.notifyFolderNameChanged();
}
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
@@ -368,7 +472,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mInfo;
}
- void bind(FolderInfo info) {
+ void bind(final FolderInfo info) {
mInfo = info;
ArrayList<ShortcutInfo> children = info.contents;
Collections.sort(children, ITEM_POS_COMPARATOR);
@@ -383,18 +487,19 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
LauncherModel.deleteItemFromDatabase(mLauncher, item);
}
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
- if (lp == null) {
- lp = new DragLayer.LayoutParams(0, 0);
- lp.customPosition = true;
- setLayoutParams(lp);
- }
- centerAboutIcon();
-
mItemsInvalidated = true;
updateTextViewFocus();
mInfo.addListener(this);
+ setFolderName();
+ updateLock();
+ }
+
+ public void rebind(final FolderInfo info) {
+ bind(info);
+ }
+
+ public void setFolderName() {
if (!sDefaultFolderName.contentEquals(mInfo.title)) {
mFolderName.setText(mInfo.title);
} else {
@@ -411,16 +516,25 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
});
}
+ private void updateLock() {
+ if (mInfo != null) {
+ mFolderLock.setImageResource(mInfo.hidden ?
+ R.drawable.folder_locked :
+ R.drawable.folder_unlocked);
+ }
+ }
+
/**
* Creates a new UserFolder, inflated from R.layout.user_folder.
*
* @param context The application's context.
+ * @param root The {@link View} parent of this folder.
*
* @return A new UserFolder.
*/
@SuppressLint("InflateParams")
- static Folder fromXml(Launcher launcher) {
- return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, null);
+ static Folder fromXml(Launcher launcher, ViewGroup root) {
+ return (Folder) launcher.getLayoutInflater().inflate(R.layout.user_folder, root, false);
}
/**
@@ -440,6 +554,39 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
setScaleY(1f);
setAlpha(1f);
mState = STATE_SMALL;
+
+ View reveal = mLauncher.findViewById(R.id.reveal_fake_page_container);
+ reveal.setVisibility(View.VISIBLE);
+ View revealFolderIcon = mLauncher.findViewById(R.id.reveal_fake_folder_icon);
+ revealFolderIcon.setVisibility(View.INVISIBLE);
+ }
+
+ private void prepareFakeFolderIcon() {
+ mFolderIcon.destroyDrawingCache();
+ mFolderIcon.buildDrawingCache(true);
+
+ Bitmap fakeFolderIcon = Bitmap.createBitmap(mFolderIcon.getDrawingCache());
+ View fakeFolderIconView = mLauncher.findViewById(R.id.reveal_fake_folder_icon);
+ FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams)
+ fakeFolderIconView.getLayoutParams();
+
+ // Get globalVisibleRect of the folderIcon. getWidth and getHeight are inaccurate for
+ // hotseat icons
+ Rect rect = new Rect();
+ mFolderIcon.getGlobalVisibleRect(rect);
+
+ flp.height = rect.height();
+ flp.width = rect.width();
+
+ fakeFolderIconView.setLayoutParams(flp);
+
+ int [] folderIconXY = new int[2];
+ mFolderIcon.getLocationOnScreen(folderIconXY);
+ fakeFolderIconView.setX(folderIconXY[0]);
+ fakeFolderIconView.setY(folderIconXY[1]);
+
+ fakeFolderIconView.setBackground(new BitmapDrawable(null, fakeFolderIcon));
+ fakeFolderIconView.setVisibility(View.INVISIBLE);
}
public void animateOpen() {
@@ -455,7 +602,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
final Runnable onCompleteRunnable;
if (!Utilities.ATLEAST_LOLLIPOP) {
positionAndSizeAsIcon();
- centerAboutIcon();
+ calculatePivot();
PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1);
PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
@@ -473,12 +620,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
};
} else {
- prepareReveal();
- centerAboutIcon();
+ calculatePivot();
AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
- int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
- int height = getFolderHeight();
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
float transX = - 0.075f * (width / 2 - getPivotX());
float transY = - 0.075f * (height / 2 - getPivotY());
@@ -490,7 +636,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
Animator drift = ObjectAnimator.ofPropertyValuesHolder(this, tx, ty);
drift.setDuration(mMaterialExpandDuration);
drift.setStartDelay(mMaterialExpandStagger);
- drift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ drift.setInterpolator(new LogDecelerateInterpolator(60, 0));
int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
@@ -513,6 +659,34 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
textAlpha.setStartDelay(mMaterialExpandStagger);
textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+ prepareFakeFolderIcon();
+ float iconTransY = getResources().getInteger(R.integer.folder_icon_translate_y_dist);
+
+ final View fakeFolderIconView = mLauncher.findViewById(R.id.reveal_fake_folder_icon);
+ float baseIconTranslationY = fakeFolderIconView.getTranslationY();
+ PropertyValuesHolder iconty = PropertyValuesHolder.ofFloat("translationY",
+ baseIconTranslationY, baseIconTranslationY + iconTransY);
+ PropertyValuesHolder iconAlpha = PropertyValuesHolder.ofFloat("alpha", 1f, 0f);
+
+ Animator fakeFolderIcon = LauncherAnimUtils.ofPropertyValuesHolder(fakeFolderIconView,
+ iconty, iconAlpha);
+ fakeFolderIcon.setDuration(mMaterialExpandDuration);
+ fakeFolderIcon.setInterpolator(new AccelerateInterpolator(1.5f));
+ fakeFolderIcon.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mFolderIcon.setAlpha(0);
+ fakeFolderIconView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ fakeFolderIconView.setVisibility(View.INVISIBLE);
+ }
+ });
+
+ prepareReveal();
+
anim.play(drift);
anim.play(iconsAlpha);
anim.play(textAlpha);
@@ -526,7 +700,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public void run() {
mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
- mContentWrapper.setLayerType(LAYER_TYPE_NONE, null);
}
};
}
@@ -535,6 +708,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void onAnimationStart(Animator animation) {
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
mContent.getAccessibilityDescription());
+ hideWorkspace();
mState = STATE_ANIMATING;
}
@Override
@@ -628,31 +802,128 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
}
- public void animateClosed() {
+ public int getState() {
+ return mState;
+ }
+
+ public void animateClosed(final boolean animate) {
if (!(getParent() instanceof DragLayer)) return;
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 0.9f);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 0.9f);
- final ObjectAnimator oa =
- LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
+ AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
- oa.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- onCloseComplete();
- setLayerType(LAYER_TYPE_NONE, null);
- mState = STATE_SMALL;
+ PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
+ float transY = getResources().getInteger(R.integer.folder_translate_y_dist);
+ PropertyValuesHolder translationY = PropertyValuesHolder.ofFloat("translationY", 0f,
+ transY);
+
+ setLayerType(LAYER_TYPE_HARDWARE, null);
+
+ float animatorDurationScale = Settings.Global.getFloat(getContext().getContentResolver(),
+ Settings.Global.ANIMATOR_DURATION_SCALE, 1);
+ ObjectAnimator oa;
+ if (mPowerManager.isPowerSaveMode() || animatorDurationScale < 0.01f) {
+ // power save mode is no fun - skip alpha animation and just set it to 0
+ // otherwise the icons will stay around until the duration of the animation
+ if (animate) {
+ oa = LauncherAnimUtils.ofPropertyValuesHolder(this, translationY);
+ } else {
+ oa = LauncherAnimUtils.ofPropertyValuesHolder(this);
+ }
+ setAlpha(0f);
+ } else {
+ if (animate) {
+ oa = LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, translationY);
+ } else {
+ oa = LauncherAnimUtils.ofPropertyValuesHolder(this, alpha);
}
+ }
+
+ oa.setDuration(mMaterialExpandDuration);
+ oa.setInterpolator(new LogDecelerateInterpolator(60, 0));
+ anim.play(oa);
+
+ Animator reverseRevealAnim = null;
+ Animator fakeFolderIconAnim = null;
+
+ if (animate) {
+
+ prepareFakeFolderIcon();
+ float iconTransY = getResources().getInteger(R.integer.folder_icon_translate_y_dist);
+
+ final View fakeFolderIconView = mLauncher.findViewById(R.id.reveal_fake_folder_icon);
+ float baseIconTranslationY = fakeFolderIconView.getTranslationY();
+ PropertyValuesHolder iconty = PropertyValuesHolder.ofFloat("translationY",
+ baseIconTranslationY + iconTransY, baseIconTranslationY);
+ PropertyValuesHolder iconAlpha = PropertyValuesHolder.ofFloat("alpha", 0f, 1f);
+
+ fakeFolderIconAnim = LauncherAnimUtils.ofPropertyValuesHolder(fakeFolderIconView,
+ iconty, iconAlpha);
+ fakeFolderIconAnim.setDuration(mMaterialExpandDuration);
+ fakeFolderIconAnim.setInterpolator(new DecelerateInterpolator(2f));
+ fakeFolderIconAnim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationStart(Animator animation) {
+ mFolderIcon.setAlpha(0);
+ fakeFolderIconView.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ fakeFolderIconView.setVisibility(View.INVISIBLE);
+ mFolderIcon.setAlpha(1);
+
+ View revealView = mLauncher.findViewById(R.id.reveal_fake_page_container);
+ revealView.setVisibility(View.INVISIBLE);
+ }
+ });
+ } else {
+ View revealView = mLauncher.findViewById(R.id.reveal_fake_page_container);
+ revealView.setVisibility(View.INVISIBLE);
+ mFolderIcon.setAlpha(1);
+ }
+
+ if (reverseRevealAnim != null) {
+ anim.play(reverseRevealAnim);
+ }
+ if (fakeFolderIconAnim != null) {
+ anim.play(fakeFolderIconAnim);
+ }
+
+ anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
getContext().getString(R.string.folder_closed));
+ unHideWorkspace();
mState = STATE_ANIMATING;
}
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ onCloseComplete();
+ setLayerType(LAYER_TYPE_NONE, null);
+ mState = STATE_SMALL;
+ }
});
- oa.setDuration(mExpandDuration);
- setLayerType(LAYER_TYPE_HARDWARE, null);
- oa.start();
+
+ anim.start();
+ }
+
+ private int mSavedWidgetsVisibilityState = INVISIBLE;
+ private void hideWorkspace() {
+ mSavedWidgetsVisibilityState = mLauncher.getWidgetsView().getVisibility();
+ mLauncher.getWidgetsView().setVisibility(INVISIBLE);
+ mLauncher.getWorkspace().setVisibility(INVISIBLE);
+ mLauncher.getHotseat().setVisibility(INVISIBLE);
+ mLauncher.getSearchDropTargetBar().setVisibility(INVISIBLE);
+ mLauncher.getPageIndicator().setVisibility(INVISIBLE);
+ }
+
+ private void unHideWorkspace() {
+ mLauncher.getWidgetsView().setVisibility(mSavedWidgetsVisibilityState);
+ mLauncher.getWorkspace().setVisibility(VISIBLE);
+ mLauncher.getHotseat().setVisibility(VISIBLE);
+ mLauncher.getSearchDropTargetBar().setVisibility(VISIBLE);
+ mLauncher.getPageIndicator().setVisibility(VISIBLE);
}
public boolean acceptDrop(DragObject d) {
@@ -847,6 +1118,13 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
mScrollPauseAlarm.cancelAlarm();
completeDragExit();
+ if (successfulDrop) {
+ ShortcutInfo info = (ShortcutInfo) d.dragInfo;
+ Intent intent = info.getIntent();
+ if (intent != null && mInfo.hidden) {
+ removeProtectedApp(intent.getComponent());
+ }
+ }
}
}
@@ -914,7 +1192,16 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// Do nothing
}
+ /**
+ * @return true if contents should persist their status to the database.
+ */
+ protected boolean shouldUpdateContentsInDatabase() {
+ return true;
+ }
+
private void updateItemLocationsInDatabaseBatch() {
+ if (!shouldUpdateContentsInDatabase()) return;
+
ArrayList<View> list = getItemsInReadingOrder();
ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
for (int i = 0; i < list.size(); i++) {
@@ -928,6 +1215,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
public void addItemLocationsInDatabase() {
+ if (!shouldUpdateContentsInDatabase()) return;
+
ArrayList<View> list = getItemsInReadingOrder();
for (int i = 0; i < list.size(); i++) {
View v = list.get(i);
@@ -951,12 +1240,22 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mContent.isFull();
}
- private void centerAboutIcon() {
- DragLayer.LayoutParams lp = (DragLayer.LayoutParams) getLayoutParams();
+ protected void calculatePivot() {
+ int width = getMeasuredWidth();
+ int height = getMeasuredHeight();
+
+ // If we haven't measured ourselves yet, force one now to determine our dimensions.
+ if (width <= 0 && height <= 0) {
+ measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
+ width = getMeasuredWidth();
+ height = getMeasuredHeight();
+ }
+
+ calculatePivot(width, height);
+ }
+ private void calculatePivot(int width, int height) {
DragLayer parent = (DragLayer) mLauncher.findViewById(R.id.drag_layer);
- int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
- int height = getFolderHeight();
float scale = parent.getDescendantRectRelativeToSelf(mFolderIcon, sTempRect);
@@ -966,22 +1265,22 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
int centerY = (int) (sTempRect.top + sTempRect.height() * scale / 2);
int centeredLeft = centerX - width / 2;
int centeredTop = centerY - height / 2;
+ int currentPage = mLauncher.getWorkspace().getNextPage();
+
+ // We first fetch the currently visible CellLayoutChildren
+ CellLayout currentLayout = (CellLayout) mLauncher.getWorkspace().getChildAt(currentPage);
+ ShortcutAndWidgetContainer boundingLayout = currentLayout.getShortcutsAndWidgets();
+ Rect bounds = new Rect();
+ parent.getDescendantRectRelativeToSelf(boundingLayout, bounds);
- // We need to bound the folder to the currently visible workspace area
- mLauncher.getWorkspace().getPageAreaRelativeToDragLayer(sTempRect);
- int left = Math.min(Math.max(sTempRect.left, centeredLeft),
- sTempRect.left + sTempRect.width() - width);
- int top = Math.min(Math.max(sTempRect.top, centeredTop),
- sTempRect.top + sTempRect.height() - height);
- if (grid.isPhone && (grid.availableWidthPx - width) < grid.iconSizePx) {
- // Center the folder if it is full (on phones only)
- left = (grid.availableWidthPx - width) / 2;
- } else if (width >= sTempRect.width()) {
+ // Center the folder
+ int left = (grid.availableWidthPx - width) / 2;
+ // Drop the top down a little so it isn't bounded by the page indicators
+ int top = (int) (bounds.top + (bounds.height() * 1.15) - height);
+
+ if (width >= bounds.width()) {
// If the folder doesn't fit within the bounds, center it about the desired bounds
- left = sTempRect.left + (sTempRect.width() - width) / 2;
- }
- if (height >= sTempRect.height()) {
- top = sTempRect.top + (sTempRect.height() - height) / 2;
+ left = bounds.left + (bounds.width() - width) / 2;
}
int folderPivotX = width / 2 + (centeredLeft - left);
@@ -992,11 +1291,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
(1.0f * folderPivotX / width));
mFolderIconPivotY = (int) (mFolderIcon.getMeasuredHeight() *
(1.0f * folderPivotY / height));
-
- lp.width = width;
- lp.height = height;
- lp.x = left;
- lp.y = top;
}
float getPivotXForIconAnimation() {
@@ -1006,53 +1300,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mFolderIconPivotY;
}
- private int getContentAreaHeight() {
- DeviceProfile grid = mLauncher.getDeviceProfile();
- Rect workspacePadding = grid.getWorkspacePadding(mContent.mIsRtl);
- int maxContentAreaHeight = grid.availableHeightPx -
- workspacePadding.top - workspacePadding.bottom -
- mFooterHeight;
- int height = Math.min(maxContentAreaHeight,
- mContent.getDesiredHeight());
- return Math.max(height, MIN_CONTENT_DIMEN);
+ protected int getContentAreaHeight() {
+ return Math.max(mContent.getDesiredHeight(), MIN_CONTENT_DIMEN);
}
- private int getContentAreaWidth() {
+ protected int getContentAreaWidth() {
return Math.max(mContent.getDesiredWidth(), MIN_CONTENT_DIMEN);
}
- private int getFolderHeight() {
- return getFolderHeight(getContentAreaHeight());
- }
-
- private int getFolderHeight(int contentAreaHeight) {
- return getPaddingTop() + getPaddingBottom() + contentAreaHeight + mFooterHeight;
- }
-
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int contentWidth = getContentAreaWidth();
- int contentHeight = getContentAreaHeight();
-
- int contentAreaWidthSpec = MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY);
- int contentAreaHeightSpec = MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY);
+ mContent.setFixedSize(getContentAreaWidth(), getContentAreaHeight());
- mContent.setFixedSize(contentWidth, contentHeight);
- mContentWrapper.measure(contentAreaWidthSpec, contentAreaHeightSpec);
-
- if (mContent.getChildCount() > 0) {
- int cellIconGap = (mContent.getPageAt(0).getCellWidth()
- - mLauncher.getDeviceProfile().iconSizePx) / 2;
- mFooter.setPadding(mContent.getPaddingLeft() + cellIconGap,
- mFooter.getPaddingTop(),
- mContent.getPaddingRight() + cellIconGap,
- mFooter.getPaddingBottom());
- }
- mFooter.measure(contentAreaWidthSpec,
- MeasureSpec.makeMeasureSpec(mFooterHeight, MeasureSpec.EXACTLY));
-
- int folderWidth = getPaddingLeft() + getPaddingRight() + contentWidth;
- int folderHeight = getFolderHeight(contentHeight);
- setMeasuredDimension(folderWidth, folderHeight);
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
@@ -1115,7 +1374,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
View child = null;
// Move the item from the folder to the workspace, in the position of the folder
- if (getItemCount() == 1) {
+ if (!mInfo.hidden && getItemCount() == 1) {
ShortcutInfo finalItem = mInfo.contents.get(0);
child = mLauncher.createShortcut(cellLayout, finalItem);
LauncherModel.addOrMoveItemInDatabase(mLauncher, finalItem, mInfo.container,
@@ -1203,14 +1462,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
currentDragView = mContent.createAndAddViewForRank(si, mEmptyCellRank);
// Actually move the item in the database if it was an external drag. Call this
// before creating the view, so that ShortcutInfo is updated appropriately.
- LauncherModel.addOrMoveItemInDatabase(
- mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+ if (shouldUpdateContentsInDatabase()) {
+ LauncherModel.addOrMoveItemInDatabase(
+ mLauncher, si, mInfo.id, 0, si.cellX, si.cellY);
+ }
// We only need to update the locations if it doesn't get handled in #onDropCompleted.
if (d.dragSource != this) {
updateItemLocationsInDatabaseBatch();
}
- mIsExternalDrag = false;
} else {
currentDragView = mCurrentDragView;
mContent.addViewForRank(currentDragView, si, mEmptyCellRank);
@@ -1246,6 +1506,17 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// The animation has already been shown while opening the folder.
mInfo.setOption(FolderInfo.FLAG_MULTI_PAGE_ANIMATION, true, mLauncher);
}
+
+ if (mIsExternalDrag) {
+ // Delay the close animation to reduce jank
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mLauncher.closeFolder(Folder.this);
+ }
+ }, 100);
+ mIsExternalDrag = false;
+ }
}
// This is used so the item doesn't immediately appear in the folder when added. In one case
@@ -1267,8 +1538,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (mSuppressOnAdd) return;
mContent.createAndAddViewForRank(item, mContent.allocateRankForNewItem(item));
mItemsInvalidated = true;
- LauncherModel.addOrMoveItemInDatabase(
- mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+
+ if (shouldUpdateContentsInDatabase()) {
+ LauncherModel.addOrMoveItemInDatabase(
+ mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
+ }
}
public void onRemove(ShortcutInfo item) {
@@ -1276,8 +1550,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// If this item is being dragged from this open folder, we have already handled
// the work associated with removing the item, so we don't have to do anything here.
if (item == mCurrentDragInfo) return;
- View v = getViewForInfo(item);
- mContent.removeItem(v);
+ mContent.removeItem(getViewForInfo(item));
if (mState == STATE_ANIMATING) {
mRearrangeOnClose = true;
} else {
@@ -1288,14 +1561,68 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
}
- private View getViewForInfo(final ShortcutInfo item) {
- return mContent.iterateOverItems(new ItemOperator() {
+ @Override
+ public void onRemoveAll() {
+ // Clear the UX after folder contents are removed from the DB
+ removeViewsForItems(null);
+ mLauncher.closeFolder(this);
+ replaceFolderWithFinalItem();
+ }
- @Override
- public boolean evaluate(ItemInfo info, View view, View parent) {
- return info == item;
+ @Override
+ public void onRemoveAll(ArrayList<ShortcutInfo> items) {
+ removeViewsForItems(items);
+ if (mState == STATE_ANIMATING) {
+ mRearrangeOnClose = true;
+ } else {
+ rearrangeChildren();
+ }
+ if (mInfo.contents.isEmpty()) {
+ mLauncher.closeFolder(this);
+ }
+ replaceFolderWithFinalItem();
+ }
+
+ /**
+ * Remove all the supplied item views from this folder.
+ * @param items info of views to remove, or null if all views should be removed.
+ */
+ protected void removeViewsForItems(ArrayList<ShortcutInfo> items) {
+ mItemsInvalidated = true;
+ if (items == null) {
+ mContent.removeAllItems();
+ } else {
+ for (ShortcutInfo item : items) {
+ mContent.removeItem(getViewForInfo(item));
}
- });
+ }
+ }
+
+ /**
+ * Update the view tied to this shortcut.
+ * @param info updated info to be applied to view.
+ * @return true if view for info was found, false otherwise.
+ */
+ @SuppressWarnings("unused")
+ public boolean updateViewForInfo(final ShortcutInfo info) {
+ View v = getViewForInfo(info);
+ if (v != null & v instanceof BubbleTextView) {
+ ((BubbleTextView) v).reapplyItemInfo(info);
+
+ mItemsInvalidated = true;
+ return true;
+ }
+
+ return false;
+ }
+
+ public View getViewForInfo(ShortcutInfo item) {
+ View v = mContent.getChildAtRank(item.rank);
+ if (v != null && v.getTag() == item) {
+ return v;
+ }
+
+ return null;
}
public void onItemsChanged() {
@@ -1338,6 +1665,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
outRect.right += mScrollAreaOffset;
}
+ public View getViewFromPosition(int position) {
+ return mItemsInReadingOrder.get(position);
+ }
+
@Override
public void fillInLaunchSourceData(Bundle sourceData) {
// Fill in from the folder icon's launch source provider first
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 8d534d2fe..356c2754b 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -39,6 +39,7 @@ import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.RelativeLayout;
import android.widget.TextView;
import com.android.launcher3.DropTarget.DragObject;
@@ -60,17 +61,17 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private StylusEventHelper mStylusEventHelper;
// The number of icons to display in the
- public static final int NUM_ITEMS_IN_PREVIEW = 3;
+ public static final int NUM_ITEMS_IN_PREVIEW = 4;
private static final int CONSUMPTION_ANIMATION_DURATION = 100;
private static final int DROP_IN_ANIMATION_DURATION = 400;
private static final int INITIAL_ITEM_ANIMATION_DURATION = 350;
private static final int FINAL_ITEM_ANIMATION_DURATION = 200;
// The degree to which the inner ring grows when accepting drop
- private static final float INNER_RING_GROWTH_FACTOR = 0.15f;
+ private static final float INNER_RING_GROWTH_FACTOR = 0.0f;
// The degree to which the outer ring is scaled in its natural state
- private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
+ private static final float OUTER_RING_GROWTH_FACTOR = 0.1f;
// The amount of vertical spread between items in the stack [0...1]
private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
@@ -90,7 +91,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
public static Drawable sSharedFolderLeaveBehind = null;
- @Thunk ImageView mPreviewBackground;
+ @Thunk View mPreviewBackground;
@Thunk BubbleTextView mFolderName;
FolderRingAnimator mFolderRingAnimator = null;
@@ -161,11 +162,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
// Offset the preview background to center this view accordingly
- icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
+ icon.mPreviewBackground = icon.findViewById(R.id.preview_background);
lp = (FrameLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
- lp.topMargin = grid.folderBackgroundOffset;
- lp.width = grid.folderIconSizePx;
- lp.height = grid.folderIconSizePx;
+ lp.width = grid.iconSizePx;
+ lp.height = grid.iconSizePx;
+ icon.mPreviewBackground.setLayoutParams(lp);
icon.setTag(folderInfo);
icon.setOnClickListener(launcher);
@@ -173,7 +174,16 @@ public class FolderIcon extends FrameLayout implements FolderListener {
icon.mLauncher = launcher;
icon.setContentDescription(String.format(launcher.getString(R.string.folder_name_format),
folderInfo.title));
- Folder folder = Folder.fromXml(launcher);
+ Folder folder;
+ if (folderInfo.isRemote()) {
+ folder = launcher.getRemoteFolderManager().createRemoteFolder(icon, launcher.getDragLayer());
+ if (folder == null) {
+ LauncherModel.deleteItemFromDatabase(launcher, folderInfo);
+ return null;
+ }
+ } else {
+ folder = Folder.fromXml(launcher, launcher.getDragLayer());
+ }
folder.setDragController(launcher.getDragController());
folder.setFolderIcon(icon);
folder.bind(folderInfo);
@@ -183,6 +193,63 @@ public class FolderIcon extends FrameLayout implements FolderListener {
folderInfo.addListener(icon);
icon.setOnFocusChangeListener(launcher.mFocusHandler);
+ icon.setDrawingCacheEnabled(true);
+
+ // get dimen for the icon size
+ // padding is equal to 1/8 of icon size
+ // padding gets used at start and end accounting for 2/8
+ // small icons are separated by 1/2 padding
+ // Total padding equals 2.5/8 leaving 5.5/8 for icons
+ // 5.5/8 remaining, divided by 2 equals 2.75 for each small icon
+ int padding = grid.iconSizePx / 8;
+ int smallIconSize = (int) (padding * 2.75);
+
+ for (int i = NUM_ITEMS_IN_PREVIEW; i >= 0; i--) {
+ ImageView appIcon = null;
+ int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0;
+ switch(i) {
+ case 0:
+ appIcon = (ImageView) icon.findViewById(R.id.app_0);
+ marginLeft = padding;
+ marginTop = padding;
+ break;
+ case 1:
+ appIcon = (ImageView) icon.findViewById(R.id.app_1);
+ marginTop = padding;
+ marginRight = padding;
+ break;
+ case 2:
+ appIcon = (ImageView) icon.findViewById(R.id.app_2);
+ marginBottom = padding;
+ marginLeft = padding;
+ break;
+ case 3:
+ appIcon = (ImageView) icon.findViewById(R.id.app_3);
+ marginBottom = padding;
+ marginRight = padding;
+ break;
+ }
+
+ if (appIcon != null) {
+ RelativeLayout.LayoutParams layoutParams
+ = (RelativeLayout.LayoutParams) appIcon.getLayoutParams();
+
+ layoutParams.width = smallIconSize;
+ layoutParams.height = smallIconSize;
+ layoutParams.leftMargin = marginLeft;
+ layoutParams.rightMargin = marginRight;
+ layoutParams.topMargin = marginTop;
+ layoutParams.bottomMargin = marginBottom;
+
+ appIcon.setLayoutParams(layoutParams);
+ }
+ }
+
+ // Create an overlay badge if this FolderIcon is for a RemoteFolder
+ if (folderInfo.isRemote()) {
+ icon = RemoteFolderManager.addBadgeToFolderIcon(icon);
+ }
+
return icon;
}
@@ -220,11 +287,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
DeviceProfile grid = launcher.getDeviceProfile();
- sPreviewSize = grid.folderIconSizePx;
+ sPreviewSize = grid.iconSizePx;
sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
- sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer);
- sSharedInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_nolip);
- sSharedFolderLeaveBehind = res.getDrawable(R.drawable.portal_ring_rest);
+ sSharedOuterRingDrawable = res.getDrawable(R.drawable.folder_fill_highlight);
+ sSharedInnerRingDrawable = null;
+ sSharedFolderLeaveBehind = res.getDrawable(R.drawable.folder_bg);
sStaticValuesDirty = false;
}
}
@@ -324,14 +391,28 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
private boolean willAcceptItem(ItemInfo item) {
+ if (mInfo.isRemote()) return false;
+
final int itemType = item.itemType;
+
+ boolean hidden = false;
+ if (item instanceof FolderInfo){
+ if (((FolderInfo) item).isRemote()) return false;
+
+ hidden = ((FolderInfo) item).hidden;
+ }
return ((itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION ||
- itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT) &&
- !mFolder.isFull() && item != mInfo && !mInfo.opened);
+ itemType == LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT ||
+ itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) &&
+ !mFolder.isFull() && item != mInfo && !mInfo.opened &&
+ !hidden);
}
public boolean acceptDrop(Object dragInfo) {
final ItemInfo item = (ItemInfo) dragInfo;
+ if (mInfo.hidden) {
+ return false;
+ }
return !mFolder.isDestroyed() && willAcceptItem(item);
}
@@ -370,12 +451,21 @@ public class FolderIcon extends FrameLayout implements FolderListener {
item = ((AppInfo) mDragInfo).makeShortcut();
item.spanX = 1;
item.spanY = 1;
+ } else if (mDragInfo instanceof FolderInfo) {
+ return;
} else {
// ShortcutInfo
item = (ShortcutInfo) mDragInfo;
}
mFolder.beginExternalDrag(item);
- mLauncher.openFolder(FolderIcon.this);
+ mFolderRingAnimator.mCellLayout.hideFolderAccept(mFolderRingAnimator);
+
+ int[] folderTouchXY = new int[2];
+ mFolder.getLocationOnScreen(folderTouchXY);
+ int[] folderTouchXYOffset = {folderTouchXY[0] + mFolder.getWidth() / 2,
+ folderTouchXY[1] + mFolder.getHeight() / 2};
+
+ mLauncher.openFolder(FolderIcon.this, folderTouchXYOffset);
}
};
@@ -482,6 +572,15 @@ public class FolderIcon extends FrameLayout implements FolderListener {
if (d.dragInfo instanceof AppInfo) {
// Came from all apps -- make a copy
item = ((AppInfo) d.dragInfo).makeShortcut();
+ } else if (d.dragInfo instanceof FolderInfo) {
+ FolderInfo folder = (FolderInfo) d.dragInfo;
+ mFolder.notifyDrop();
+ for (ShortcutInfo fItem : folder.contents) {
+ onDrop(fItem, d.dragView, null, 1.0f, mInfo.contents.size(), d.postAnimationRunnable, d);
+ }
+ mLauncher.removeFolder(folder);
+ LauncherModel.deleteItemFromDatabase(mLauncher, folder);
+ return;
} else {
item = (ShortcutInfo) d.dragInfo;
}
@@ -511,7 +610,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mMaxPerspectiveShift = mBaselineIconSize * PERSPECTIVE_SHIFT_FACTOR;
mPreviewOffsetX = (mTotalWidth - mAvailableSpaceInPreview) / 2;
- mPreviewOffsetY = previewPadding + grid.folderBackgroundOffset;
+ mPreviewOffsetY = grid.folderBackgroundOffset;
}
}
@@ -605,7 +704,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
super.dispatchDraw(canvas);
if (mFolder == null) return;
- if (mFolder.getItemCount() == 0 && !mAnimating) return;
+ if (mFolder.getItemCount() == 0 && !mAnimating && !mInfo.isRemote()) return;
ArrayList<View> items = mFolder.getItemsInReadingOrder();
Drawable d;
@@ -614,21 +713,65 @@ public class FolderIcon extends FrameLayout implements FolderListener {
// Update our drawing parameters if necessary
if (mAnimating) {
computePreviewDrawingParams(mAnimParams.drawable);
- } else {
+ } else if (!items.isEmpty()) {
v = (TextView) items.get(0);
d = getTopDrawable(v);
- computePreviewDrawingParams(d);
+ if (d != null) computePreviewDrawingParams(d);
+ }
+
+ int ntemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
+
+ // Hidden folder - don't display Preview
+ View folderLock = findViewById(R.id.folder_lock_image);
+ folderLock.setVisibility(mInfo.hidden ? VISIBLE : INVISIBLE);
+ View appView = findViewById(R.id.app_0);
+ appView.setVisibility(mInfo.hidden ? INVISIBLE : VISIBLE);
+ appView = findViewById(R.id.app_1);
+ appView.setVisibility(mInfo.hidden ? INVISIBLE : VISIBLE);
+ appView = findViewById(R.id.app_2);
+ appView.setVisibility(mInfo.hidden ? INVISIBLE : VISIBLE);
+ appView = findViewById(R.id.app_3);
+ appView.setVisibility(mInfo.hidden ? INVISIBLE : VISIBLE);
+
+ if (mInfo.hidden) {
+ return;
}
- int nItemsInPreview = Math.min(items.size(), NUM_ITEMS_IN_PREVIEW);
if (!mAnimating) {
- for (int i = nItemsInPreview - 1; i >= 0; i--) {
- v = (TextView) items.get(i);
- if (!mHiddenItems.contains(v.getTag())) {
- d = getTopDrawable(v);
+ for (int i = 0; i < NUM_ITEMS_IN_PREVIEW; i++) {
+ d = null;
+ if (mInfo.isRemote()) {
+ d = mLauncher.getRemoteFolderManager().getFolderIconDrawable(items, i);
+ } else if (i < items.size()) {
+ v = (TextView) items.get(i);
+ if (!mHiddenItems.contains(v.getTag())) {
+ d = getTopDrawable(v);
+ }
+ }
+
+ if (d != null) {
mParams = computePreviewItemDrawingParams(i, mParams);
mParams.drawable = d;
- drawPreviewItem(canvas, mParams);
+ }
+
+ ImageView appIcon = null;
+ switch(i) {
+ case 0:
+ appIcon = (ImageView) findViewById(R.id.app_0);
+ break;
+ case 1:
+ appIcon = (ImageView) findViewById(R.id.app_1);
+ break;
+ case 2:
+ appIcon = (ImageView) findViewById(R.id.app_2);
+ break;
+ case 3:
+ appIcon = (ImageView) findViewById(R.id.app_3);
+ break;
+ }
+
+ if (appIcon != null) {
+ appIcon.setImageDrawable(d);
}
}
} else {
@@ -645,10 +788,9 @@ public class FolderIcon extends FrameLayout implements FolderListener {
final Runnable onCompleteRunnable) {
final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
- float iconSize = mLauncher.getDeviceProfile().iconSizePx;
- final float scale0 = iconSize / d.getIntrinsicWidth() ;
- final float transX0 = (mAvailableSpaceInPreview - iconSize) / 2;
- final float transY0 = (mAvailableSpaceInPreview - iconSize) / 2 + getPaddingTop();
+ final float scale0 = 1.0f;
+ final float transX0 = (mAvailableSpaceInPreview - d.getIntrinsicWidth()) / 2;
+ final float transY0 = (mAvailableSpaceInPreview - d.getIntrinsicHeight()) / 2 + getPaddingTop();
mAnimParams.drawable = d;
ValueAnimator va = LauncherAnimUtils.ofFloat(this, 0f, 1.0f);
@@ -675,7 +817,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
public void onAnimationEnd(Animator animation) {
mAnimating = false;
if (onCompleteRunnable != null) {
- onCompleteRunnable.run();
+ mLauncher.runOnUiThread(onCompleteRunnable);
}
}
});
@@ -710,6 +852,18 @@ public class FolderIcon extends FrameLayout implements FolderListener {
requestLayout();
}
+ @Override
+ public void onRemoveAll() {
+ invalidate();
+ requestLayout();
+ }
+
+ @Override
+ public void onRemoveAll(ArrayList<ShortcutInfo> items) {
+ invalidate();
+ requestLayout();
+ }
+
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(String.format(getContext().getString(R.string.folder_name_format),
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index aea21c95b..7969d627b 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -28,6 +28,7 @@ import java.util.Arrays;
* Represents a folder containing shortcuts or apps.
*/
public class FolderInfo extends ItemInfo {
+ public static final int REMOTE_SUBTYPE = 1;
public static final int NO_FLAGS = 0x00000000;
@@ -50,13 +51,15 @@ public class FolderInfo extends ItemInfo {
* Whether this folder has been opened
*/
boolean opened;
+ int subType;
public int options;
/**
- * The apps and shortcuts
+ * The apps and shortcuts and hidden status
*/
public ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>();
+ public Boolean hidden = false;
ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
@@ -91,6 +94,54 @@ public class FolderInfo extends ItemInfo {
itemsChanged();
}
+ /**
+ * Remove all apps and shortcuts. Does not change the DB unless
+ * LauncherModel.deleteFolderContentsFromDatabase(Context, FolderInfo) is called first.
+ */
+ public void removeAll() {
+ if (contents.isEmpty()) return;
+
+ contents.clear();
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onRemoveAll();
+ }
+ itemsChanged();
+ }
+
+ /**
+ * Remove all supplied shortcuts. Does not change the DB unless
+ * LauncherModel.deleteFolderContentsFromDatabase(Context, FolderInfo) is called first.
+ * @param items the shortcuts to remove.
+ */
+ public void removeAll(ArrayList<ShortcutInfo> items) {
+ if (items.isEmpty()) return;
+
+ contents.removeAll(items);
+ for (int i = 0; i < listeners.size(); i++) {
+ listeners.get(i).onRemoveAll(items);
+ }
+ itemsChanged();
+ }
+
+ /**
+ * @return true if this info represents a remote folder, false otherwise
+ */
+ public boolean isRemote() {
+ return (subType & REMOTE_SUBTYPE) != 0;
+ }
+
+ /**
+ * Set flag indicating whether this folder is remote
+ * @param remote true if folder is remote, false otherwise
+ */
+ public void setRemote(final boolean remote) {
+ if (remote) {
+ subType |= REMOTE_SUBTYPE;
+ } else {
+ subType &= ~REMOTE_SUBTYPE;
+ }
+ }
+
public void setTitle(CharSequence title) {
this.title = title;
for (int i = 0; i < listeners.size(); i++) {
@@ -103,7 +154,8 @@ public class FolderInfo extends ItemInfo {
super.onAddToDatabase(context, values);
values.put(LauncherSettings.Favorites.TITLE, title.toString());
values.put(LauncherSettings.Favorites.OPTIONS, options);
-
+ values.put(LauncherSettings.Favorites.HIDDEN, hidden ? 1 : 0);
+ values.put(LauncherSettings.BaseLauncherColumns.SUBTYPE, subType);
}
void addListener(FolderListener listener) {
@@ -129,18 +181,21 @@ public class FolderInfo extends ItemInfo {
}
interface FolderListener {
- public void onAdd(ShortcutInfo item);
- public void onRemove(ShortcutInfo item);
- public void onTitleChanged(CharSequence title);
- public void onItemsChanged();
+ void onAdd(ShortcutInfo item);
+ void onRemove(ShortcutInfo item);
+ void onRemoveAll();
+ void onRemoveAll(ArrayList<ShortcutInfo> items);
+ void onTitleChanged(CharSequence title);
+ void onItemsChanged();
}
@Override
public String toString() {
- return "FolderInfo(id=" + this.id + " type=" + this.itemType
+ return "FolderInfo(id=" + this.id + " type=" + this.itemType + " subtype=" + this.subType
+ " container=" + this.container + " screen=" + screenId
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
+ + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ + " hidden=" + hidden + ")";
}
public boolean hasOption(int optionFlag) {
diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java
index cc9c5738a..5d5ac3d5a 100644
--- a/src/com/android/launcher3/FolderPagedView.java
+++ b/src/com/android/launcher3/FolderPagedView.java
@@ -30,6 +30,7 @@ import android.view.animation.OvershootInterpolator;
import com.android.launcher3.FocusHelper.PagedFolderKeyEventListener;
import com.android.launcher3.PageIndicator.PageMarkerResources;
import com.android.launcher3.Workspace.ItemOperator;
+import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -113,7 +114,7 @@ public class FolderPagedView extends PagedView {
* The grid size is calculated such that countY <= countX and countX = ceil(sqrt(count)) while
* maintaining the restrictions of {@link #mMaxCountX} &amp; {@link #mMaxCountY}.
*/
- private void setupContentDimensions(int count) {
+ public void setupContentDimensions(int count) {
mAllocatedContentSize = count;
boolean done;
if (count >= mMaxItemsPerPage) {
@@ -214,6 +215,11 @@ public class FolderPagedView extends PagedView {
textView.setOnClickListener(mFolder);
textView.setOnLongClickListener(mFolder);
textView.setOnFocusChangeListener(mFocusIndicatorView);
+ if (SettingsProvider.getBoolean(mFolder.mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default)) {
+ textView.setTextVisibility(false);
+ }
textView.setOnKeyListener(mKeyListener);
textView.setLayoutParams(new CellLayout.LayoutParams(
@@ -268,6 +274,51 @@ public class FolderPagedView extends PagedView {
}
}
+ public void removeAllItems() {
+ for (int i = 0; i < getChildCount(); i++) {
+ getPageAt(i).removeAllViews();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (getChildCount() == 0) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ } else {
+ // We should only be as large as our pages, so measure all of them first.
+ View page = null;
+ for (int i = 0; i < getChildCount(); i++) {
+ page = getChildAt(i);
+ page.measure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ // And then set ourselves to their size.
+ int width = getPaddingLeft() + page.getMeasuredWidth() + getPaddingRight();
+ int height = getPaddingTop() + page.getMeasuredHeight() + getPaddingBottom();
+ mViewport.set(0, 0, width, height);
+ setMeasuredDimension(width, height);
+ }
+ }
+
+ /**
+ * Find the child view for the given rank.
+ * @param rank sorted index of child.
+ * @return view of child at given rank.
+ */
+ public View getChildAtRank(int rank) {
+ int pagePos = rank % mMaxItemsPerPage;
+ int pageNo = rank / mMaxItemsPerPage;
+ int cellX = pagePos % mGridCountX;
+ int cellY = pagePos / mGridCountX;
+
+ CellLayout page = getPageAt(pageNo);
+ if (page != null) {
+ return page.getChildAt(cellX, cellY);
+ } else {
+ return null;
+ }
+ }
+
/**
* Updates position and rank of all the children in the view.
* It essentially removes all views from all the pages and then adds them again in appropriate
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 59ab8397d..ca5545df0 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -29,6 +29,7 @@ import android.content.res.Resources;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteReadOnlyDatabaseException;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
@@ -52,8 +53,10 @@ import com.android.launcher3.util.Thunk;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Set;
import java.util.Stack;
@@ -92,7 +95,7 @@ public class IconCache {
private final HashMap<ComponentKey, CacheEntry> mCache =
new HashMap<ComponentKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
private final int mIconDpi;
- @Thunk final IconDB mIconDb;
+ @Thunk IconDB mIconDb;
@Thunk final Handler mWorkerHandler;
@@ -198,6 +201,31 @@ public class IconCache {
}
/**
+ * Empty out the cache.
+ */
+ public synchronized void flush() {
+ mCache.clear();
+ if (mIconDb != null) {
+ mIconDb.close();
+ }
+ mIconDb = new IconDB(mContext);
+ }
+
+ /**
+ * Empty out the cache that aren't of the correct grid size
+ */
+ public synchronized void flushInvalidIcons(DeviceProfile deviceProfile) {
+ Iterator<Map.Entry<ComponentKey, CacheEntry>> it = mCache.entrySet().iterator();
+ while (it.hasNext()) {
+ final CacheEntry e = it.next().getValue();
+ if ((e.icon != null) && (e.icon.getWidth() < deviceProfile.iconSizePx
+ || e.icon.getHeight() < deviceProfile.iconSizePx)) {
+ it.remove();
+ }
+ }
+ }
+
+ /**
* Remove any records for the supplied package name from memory.
*/
private void removeFromMemCacheLocked(String packageName, UserHandleCompat user) {
@@ -363,8 +391,12 @@ public class IconCache {
values.put(IconDB.COLUMN_USER, userSerial);
values.put(IconDB.COLUMN_LAST_UPDATED, info.lastUpdateTime);
values.put(IconDB.COLUMN_VERSION, info.versionCode);
- mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
- SQLiteDatabase.CONFLICT_REPLACE);
+ try {
+ mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values,
+ SQLiteDatabase.CONFLICT_REPLACE);
+ } catch (SQLiteReadOnlyDatabaseException e) {
+ Log.e(TAG, "Can't add icon to read only db", e);
+ }
}
@Thunk ContentValues updateCacheAndGetContentValues(LauncherActivityInfoCompat app,
diff --git a/src/com/android/launcher3/InsettableFrameLayout.java b/src/com/android/launcher3/InsettableFrameLayout.java
index 7343bf686..770d1de1a 100644
--- a/src/com/android/launcher3/InsettableFrameLayout.java
+++ b/src/com/android/launcher3/InsettableFrameLayout.java
@@ -24,10 +24,14 @@ public class InsettableFrameLayout extends FrameLayout implements
if (child instanceof Insettable) {
((Insettable) child).setInsets(newInsets);
} else if (!lp.ignoreInsets) {
- lp.topMargin += (newInsets.top - oldInsets.top);
+ if (!lp.ignoreTopInsets) {
+ lp.topMargin += (newInsets.top - oldInsets.top);
+ }
lp.leftMargin += (newInsets.left - oldInsets.left);
lp.rightMargin += (newInsets.right - oldInsets.right);
- lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
+ if (!lp.ignoreBottomInsets) {
+ lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
+ }
}
child.setLayoutParams(lp);
}
@@ -65,6 +69,8 @@ public class InsettableFrameLayout extends FrameLayout implements
public static class LayoutParams extends FrameLayout.LayoutParams {
boolean ignoreInsets = false;
+ boolean ignoreTopInsets = false;
+ boolean ignoreBottomInsets = false;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
@@ -72,6 +78,10 @@ public class InsettableFrameLayout extends FrameLayout implements
R.styleable.InsettableFrameLayout_Layout);
ignoreInsets = a.getBoolean(
R.styleable.InsettableFrameLayout_Layout_layout_ignoreInsets, false);
+ ignoreTopInsets = a.getBoolean(
+ R.styleable.InsettableFrameLayout_Layout_layout_ignoreTopInsets, false);
+ ignoreBottomInsets = a.getBoolean(
+ R.styleable.InsettableFrameLayout_Layout_layout_ignoreBottomInsets, false);
a.recycle();
}
@@ -91,6 +101,7 @@ public class InsettableFrameLayout extends FrameLayout implements
@Override
public void onChildViewRemoved(View parent, View child) {
+ setFrameLayoutChildInsets(child, new Rect(), mInsets);
}
}
diff --git a/src/com/android/launcher3/InsettableLinearLayout.java b/src/com/android/launcher3/InsettableLinearLayout.java
new file mode 100644
index 000000000..8f64713e5
--- /dev/null
+++ b/src/com/android/launcher3/InsettableLinearLayout.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+
+public class InsettableLinearLayout extends LinearLayout implements
+ ViewGroup.OnHierarchyChangeListener, Insettable {
+
+ protected Rect mInsets = new Rect();
+
+ public InsettableLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setOnHierarchyChangeListener(this);
+ }
+
+ public void setLinearLayoutChildInsets(View child, Rect newInsets, Rect oldInsets) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ if (child instanceof Insettable) {
+ ((Insettable) child).setInsets(newInsets);
+ } else if (!lp.ignoreInsets) {
+ if (!lp.ignoreTopInsets) {
+ lp.topMargin += (newInsets.top - oldInsets.top);
+ }
+ lp.leftMargin += (newInsets.left - oldInsets.left);
+ lp.rightMargin += (newInsets.right - oldInsets.right);
+ if (!lp.ignoreBottomInsets) {
+ lp.bottomMargin += (newInsets.bottom - oldInsets.bottom);
+ }
+ }
+ child.setLayoutParams(lp);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ final int n = getChildCount();
+ for (int i = 0; i < n; i++) {
+ final View child = getChildAt(i);
+ setLinearLayoutChildInsets(child, insets, mInsets);
+ }
+ mInsets.set(insets);
+ }
+
+ @Override
+ public LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+ }
+
+ // Override to allow type-checking of LayoutParams.
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams;
+ }
+
+ @Override
+ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return new LayoutParams(p);
+ }
+
+ public static class LayoutParams extends LinearLayout.LayoutParams {
+ boolean ignoreInsets = false;
+ boolean ignoreTopInsets = false;
+ boolean ignoreBottomInsets = false;
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+ TypedArray a = c.obtainStyledAttributes(attrs,
+ R.styleable.InsettableLinearLayout_Layout);
+ ignoreInsets = a.getBoolean(
+ R.styleable.InsettableLinearLayout_Layout_layout_ignoreInsets, false);
+ ignoreTopInsets = a.getBoolean(
+ R.styleable.InsettableLinearLayout_Layout_layout_ignoreTopInsets, false);
+ ignoreBottomInsets = a.getBoolean(
+ R.styleable.InsettableLinearLayout_Layout_layout_ignoreBottomInsets, false);
+ a.recycle();
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(ViewGroup.LayoutParams lp) {
+ super(lp);
+ }
+ }
+
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ setLinearLayoutChildInsets(child, mInsets, new Rect());
+ }
+
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ }
+
+}
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index ae204c40c..e21a070ad 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -24,6 +24,7 @@ import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
+import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.Thunk;
import java.util.ArrayList;
@@ -32,6 +33,38 @@ import java.util.Comparator;
public class InvariantDeviceProfile {
+ public enum GridSize {
+ Comfortable(0),
+ Cozy(1),
+ Condensed(2),
+ Custom(3);
+
+ private final int mValue;
+ GridSize(int value) {
+ mValue = value;
+ }
+
+ public int getValue() {
+ return mValue;
+ }
+
+ public static GridSize getModeForValue(int value) {
+ switch (value) {
+ case 1:
+ return Cozy;
+ case 2:
+ return Condensed;
+ case 3:
+ return Custom;
+ default :
+ return Comfortable;
+ }
+ }
+ }
+
+ public final static int GRID_SIZE_MAX = 3;
+ public final static int GRID_SIZE_MIN = 2;
+
// This is a static that we use for the default icon size on a 4/5-inch phone
private static float DEFAULT_ICON_SIZE_DP = 60;
@@ -55,6 +88,8 @@ public class InvariantDeviceProfile {
*/
public int numRows;
public int numColumns;
+ public int numRowsBase;
+ public int numColumnsBase;
/**
* The minimum number of predicted apps in all apps.
@@ -132,7 +167,8 @@ public class InvariantDeviceProfile {
minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y), dm);
ArrayList<InvariantDeviceProfile> closestProfiles =
- findClosestDeviceProfiles(minWidthDps, minHeightDps, getPredefinedDeviceProfiles());
+ findClosestDeviceProfiles(minWidthDps, minHeightDps,
+ getPredefinedDeviceProfiles(context));
InvariantDeviceProfile interpolatedDeviceProfileOut =
invDistWeightedInterpolate(minWidthDps, minHeightDps, closestProfiles);
@@ -146,6 +182,29 @@ public class InvariantDeviceProfile {
numFolderColumns = closestProfile.numFolderColumns;
minAllAppsPredictionColumns = closestProfile.minAllAppsPredictionColumns;
+ numRowsBase = numRows;
+ int gridResize = SettingsProvider.getIntCustomDefault(context,
+ SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0);
+ if (GridSize.getModeForValue(gridResize) != GridSize.Custom) {
+ numRows += gridResize;
+ } else {
+ int iTempNumberOfRows = SettingsProvider.getIntCustomDefault(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, numRows);
+ if (iTempNumberOfRows > 0) {
+ numRows = iTempNumberOfRows;
+ }
+ }
+ numColumnsBase = numColumns;
+ if (GridSize.getModeForValue(gridResize) != GridSize.Custom) {
+ numColumns += gridResize;
+ } else {
+ int iTempNumberOfColumns = SettingsProvider.getIntCustomDefault(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, numColumns);
+ if (iTempNumberOfColumns > 0) {
+ numColumns = iTempNumberOfColumns;
+ }
+ }
+
iconSize = interpolatedDeviceProfileOut.iconSize;
iconBitmapSize = Utilities.pxFromDp(iconSize, dm);
iconTextSize = interpolatedDeviceProfileOut.iconTextSize;
@@ -169,35 +228,52 @@ public class InvariantDeviceProfile {
smallSide, largeSide, false /* isLandscape */);
}
- ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles() {
+ ArrayList<InvariantDeviceProfile> getPredefinedDeviceProfiles(Context context) {
+ boolean useLargeIcons = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_GENERAL_ICONS_LARGE,
+ R.bool.preferences_interface_general_icons_large_default);
ArrayList<InvariantDeviceProfile> predefinedDeviceProfiles = new ArrayList<>();
// width, height, #rows, #columns, #folder rows, #folder columns,
// iconSize, iconTextSize, #hotseat, #hotseatIconSize, defaultLayoutId.
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Super Short Stubby",
- 255, 300, 2, 3, 2, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+ 255, 300, 2, 3, 2, 3, 3, (useLargeIcons? 58 : 46), 13, 3,
+ (useLargeIcons? 58 : 46), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Shorter Stubby",
- 255, 400, 3, 3, 3, 3, 3, 48, 13, 3, 48, R.xml.default_workspace_4x4));
+ 255, 400, 3, 3, 3, 3, 3, (useLargeIcons? 58 : 46), 13, 3,
+ (useLargeIcons? 58 : 46), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Short Stubby",
- 275, 420, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+ 275, 420, 3, 4, 3, 4, 4, (useLargeIcons? 58 : 46), 13, 5,
+ (useLargeIcons? 58 : 46), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Stubby",
- 255, 450, 3, 4, 3, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+ 255, 450, 3, 4, 3, 4, 4, (useLargeIcons? 58 : 46), 13, 5,
+ (useLargeIcons? 58 : 46), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus S",
- 296, 491.33f, 4, 4, 4, 4, 4, 48, 13, 5, 48, R.xml.default_workspace_4x4));
+ 296, 491.33f, 4, 4, 4, 4, 4, (useLargeIcons? 58 : 46), 13, 5,
+ (useLargeIcons? 58 : 46), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 4",
- 335, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+ 335, 567, 4, 4, 4, 4, 4, (useLargeIcons ? DEFAULT_ICON_SIZE_DP : 52), 13, 5,
+ (useLargeIcons? 60 : 50), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 5",
- 359, 567, 4, 4, 4, 4, 4, DEFAULT_ICON_SIZE_DP, 13, 5, 56, R.xml.default_workspace_4x4));
+ 359, 567, 4, 4, 4, 4, 4, (useLargeIcons ? DEFAULT_ICON_SIZE_DP : 52), 13, 5,
+ (useLargeIcons? 60 : 50), R.xml.default_workspace_4x4));
+ predefinedDeviceProfiles.add(new InvariantDeviceProfile("Bacon",
+ 336, 592, 4, 4, 4, 4, 4, (useLargeIcons ? DEFAULT_ICON_SIZE_DP : 52), 13, 5,
+ (useLargeIcons? 60 : 48), R.xml.default_workspace_4x4));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Large Phone",
- 406, 694, 5, 5, 4, 4, 4, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5));
+ 406, 694, 5, 5, 4, 4, 4, (useLargeIcons ? 68 : 56), 14.4f, 5,
+ (useLargeIcons ? 60 : 48), R.xml.default_workspace_5x5));
// The tablet profile is odd in that the landscape orientation
// also includes the nav bar on the side
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 7",
- 575, 904, 5, 6, 4, 5, 4, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6));
+ 575, 904, 5, 6, 4, 5, 4, (useLargeIcons ? 76 : 60), 14.4f, 7,
+ (useLargeIcons ? 60 : 48), R.xml.default_workspace_5x6));
// Larger tablet profiles always have system bars on the top & bottom
predefinedDeviceProfiles.add(new InvariantDeviceProfile("Nexus 10",
- 727, 1207, 5, 6, 4, 5, 4, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6));
+ 727, 1207, 5, 6, 4, 5, 4, (useLargeIcons ? 80 : 64), 14.4f, 7,
+ (useLargeIcons ? 68 : 56), R.xml.default_workspace_5x6));
predefinedDeviceProfiles.add(new InvariantDeviceProfile("20-inch Tablet",
- 1527, 2527, 7, 7, 6, 6, 4, 100, 20, 7, 72, R.xml.default_workspace_4x4));
+ 1527, 2527, 7, 7, 6, 6, 4, (useLargeIcons ? 104 : 80), 20, 7,
+ (useLargeIcons ? 76 : 64), R.xml.default_workspace_4x4));
return predefinedDeviceProfiles;
}
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index f7e0ea488..b30c954f5 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -20,6 +20,7 @@ import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -115,12 +116,24 @@ public class ItemInfo {
public CharSequence contentDescription;
/**
+ * Indicates that this item has had it's position changed
+ * because the grid size was made smaller and it could no longer fit.
+ */
+ public boolean wasMovedDueToReducedSpace = false;
+
+ /**
* The position of the item in a drag-and-drop operation.
*/
public int[] dropPos = null;
public UserHandleCompat user;
+ /**
+ * A custom drawable to use for the icon. Not persisted to the database because
+ * it is not guaranteed to be a bitmap (could be a vector).
+ */
+ Drawable customDrawable;
+
public ItemInfo() {
user = UserHandleCompat.myUserHandle();
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 1f843cb70..b135fefd9 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,6 +16,7 @@
package com.android.launcher3;
+import android.Manifest;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -28,6 +29,9 @@ import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.AlertDialog;
+import android.app.Fragment;
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -53,6 +57,7 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
+import android.graphics.drawable.AnimationDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -103,7 +108,10 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.list.SettingsPinnedHeaderAdapter;
import com.android.launcher3.model.WidgetsModel;
+import com.android.launcher3.settings.SettingsProvider;
+import com.android.launcher3.stats.LauncherStats;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
@@ -150,12 +158,22 @@ public class Launcher extends Activity
private static final int REQUEST_BIND_APPWIDGET = 11;
private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
+ private static final int REQUEST_PERMISSION_CALL_PHONE = 13;
+
+ public static final int REQUEST_OPEN_PROTECTED_FOLDER = 14;
+ public static final int REQUEST_PROTECT_FOLDER = 15;
+ public static final int REQUEST_UNPROTECT_FOLDER = 16;
+
private static final int WORKSPACE_BACKGROUND_GRADIENT = 0;
private static final int WORKSPACE_BACKGROUND_TRANSPARENT = 1;
private static final int WORKSPACE_BACKGROUND_BLACK = 2;
private static final float BOUNCE_ANIMATION_TENSION = 1.3f;
+
+ public static final String LONGPRESS_CHANGE = "wallpaper_changed_by_longpress";
+
+
/**
* IntentStarter uses request codes starting with this. This must be greater than all activity
* request codes used internally.
@@ -238,6 +256,9 @@ public class Launcher extends Activity
private final BroadcastReceiver mCloseSystemDialogsReceiver
= new CloseSystemDialogsIntentReceiver();
+ @Thunk final NetworkConnectionReceiver mConnectionReceiver = new NetworkConnectionReceiver();
+ @Thunk final DeviceUnlockedReceiver mDeviceUnlockedReceiver = new DeviceUnlockedReceiver();
+
private LayoutInflater mInflater;
@Thunk Workspace mWorkspace;
@@ -246,6 +267,9 @@ public class Launcher extends Activity
@Thunk DragLayer mDragLayer;
private DragController mDragController;
private View mWeightWatcher;
+ private DynamicGridSizeFragment mDynamicGridSizeFragment;
+
+ protected static RemoteFolderManager sRemoteFolderManager;
private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
@@ -256,8 +280,14 @@ public class Launcher extends Activity
private int[] mTmpAddItemCellCoordinates = new int[2];
+ protected FolderIcon mHiddenFolderIcon;
+ private boolean mHiddenFolderLockStateChanged = false;
+ private boolean mHiddenFolderAuth = false;
+
@Thunk Hotseat mHotseat;
- private ViewGroup mOverviewPanel;
+ private VerticalSlidingPanel mOverviewPanel;
+ private View mDarkPanel;
+ OverviewSettingsPanel mOverviewSettingsPanel;
private View mAllAppsButton;
private View mWidgetsButton;
@@ -294,7 +324,7 @@ public class Launcher extends Activity
private Bundle mSavedInstanceState;
- private LauncherModel mModel;
+ protected LauncherModel mModel;
private IconCache mIconCache;
@Thunk boolean mUserPresent = true;
private boolean mVisible = false;
@@ -345,10 +375,63 @@ public class Launcher extends Activity
private DeviceProfile mDeviceProfile;
+ private boolean mUseScrubber = true;
+
+ private boolean mIsDrawerSearchBarEnabled;
+
// This is set to the view that launched the activity that navigated the user away from
// launcher. Since there is no callback for when the activity has finished launching, enable
// the press state and keep this reference to reset the press state when we return to launcher.
- private BubbleTextView mWaitingForResume;
+ BubbleTextView mWaitingForResume;
+
+ private long mDefaultScreenId;
+
+ public Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {
+ @Override
+ public void onAnimationStart(Animator arg0) {}
+ @Override
+ public void onAnimationRepeat(Animator arg0) {}
+ @Override
+ public void onAnimationEnd(Animator arg0) {
+ mDarkPanel.setVisibility(View.GONE);
+ }
+ @Override
+ public void onAnimationCancel(Animator arg0) {}
+ };
+
+ Runnable mReloadLauncherRunnable = new Runnable() {
+ @Override
+ public void run() {
+ reloadLauncher(mWorkspace.getRestorePage(), true, false);
+ }
+ };
+
+ private BroadcastReceiver protectedAppsChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Update the workspace
+ if (waitUntilResume(mReloadLauncherRunnable, true)) {
+ return;
+ }
+
+ reloadLauncher(mWorkspace.getRestorePage(), true, false);
+ }
+ };
+
+ private BroadcastReceiver searchBarVisibilityChangedReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Update the workspace
+ if (waitUntilResume(mReloadLauncherRunnable, true)) {
+ return;
+ }
+
+ reloadLauncher(mWorkspace.getRestorePage(), true, false);
+ }
+ };
+
+ // Preferences
+ private boolean mHideIconLabels;
protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
new HashMap<String, CustomAppWidget>();
@@ -425,17 +508,11 @@ public class Launcher extends Activity
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
- // Load configuration-specific DeviceProfile
- mDeviceProfile = getResources().getConfiguration().orientation
- == Configuration.ORIENTATION_LANDSCAPE ?
- app.getInvariantDeviceProfile().landscapeProfile
- : app.getInvariantDeviceProfile().portraitProfile;
+ initializeDeviceProfile(app);
mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
Context.MODE_PRIVATE);
mIsSafeModeEnabled = getPackageManager().isSafeMode();
- mModel = app.setLauncher(this);
- mIconCache = app.getIconCache();
mDragController = new DragController(this);
mInflater = getLayoutInflater();
@@ -448,6 +525,12 @@ public class Launcher extends Activity
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
+ if (sRemoteFolderManager == null) {
+ sRemoteFolderManager = new RemoteFolderManager(this);
+ } else {
+ sRemoteFolderManager.onRecreateLauncher(this);
+ }
+
// If we are getting an onCreate, we can actually preempt onResume and unset mPaused here,
// this also ensures that any synchronous binding below doesn't re-trigger another
// LauncherModel load.
@@ -491,6 +574,12 @@ public class Launcher extends Activity
IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
registerReceiver(mCloseSystemDialogsReceiver, filter);
+ filter = new IntentFilter(NetworkConnectionReceiver.INTENT_ACTION);
+ registerReceiver(mConnectionReceiver, filter);
+
+ filter = new IntentFilter(DeviceUnlockedReceiver.INTENT_ACTION);
+ registerReceiver(mDeviceUnlockedReceiver, filter);
+
mRotationEnabled = Utilities.isRotationAllowedForDevice(getApplicationContext());
// In case we are on a device with locked rotation, we should look at preferences to check
// if the user has specifically allowed rotation.
@@ -519,11 +608,20 @@ public class Launcher extends Activity
showFirstRunActivity();
showFirstRunClings();
}
+
+ IntentFilter protectedAppsFilter = new IntentFilter(
+ cyanogenmod.content.Intent.ACTION_PROTECTED_CHANGED);
+ registerReceiver(protectedAppsChangedReceiver, protectedAppsFilter,
+ cyanogenmod.platform.Manifest.permission.PROTECTED_APP, null);
+
+ IntentFilter searchBarVisibilityFilter = new IntentFilter(
+ SettingsPinnedHeaderAdapter.ACTION_SEARCH_BAR_VISIBILITY_CHANGED);
+ registerReceiver(searchBarVisibilityChangedReceiver, searchBarVisibilityFilter);
}
@Override
public void onSettingsChanged(String settings, boolean value) {
- if (Utilities.ALLOW_ROTATION_PREFERENCE_KEY.equals(settings)) {
+ if (SettingsProvider.SETTINGS_UI_ALLOW_ROTATION.equals(settings)) {
mRotationEnabled = value;
if (!waitUntilResume(mUpdateOrientationRunnable, true)) {
mUpdateOrientationRunnable.run();
@@ -744,6 +842,7 @@ public class Launcher extends Activity
// When the user has granted permission to bind widgets, we should check to see if
// we can inflate the default search bar widget.
getOrCreateQsbBar();
+ showWorkspace(false);
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -751,6 +850,30 @@ public class Launcher extends Activity
showWorkspace(false);
}
return;
+ } else if (requestCode == REQUEST_OPEN_PROTECTED_FOLDER) {
+ mHiddenFolderAuth = resultCode == RESULT_OK;
+ if (mHiddenFolderIcon != null && mHiddenFolderAuth) {
+ openFolder(mHiddenFolderIcon, null);
+ } else {
+ mHiddenFolderAuth = false;
+ }
+ return;
+ } else if (requestCode == REQUEST_PROTECT_FOLDER) {
+ mHiddenFolderAuth = resultCode == RESULT_OK;
+ if (mHiddenFolderIcon != null && mHiddenFolderAuth) {
+ mHiddenFolderIcon.getFolder().saveHiddenFolderState(true);
+ mHiddenFolderLockStateChanged = true;
+ } else {
+ mHiddenFolderAuth = false;
+ }
+ } else if (requestCode == REQUEST_UNPROTECT_FOLDER) {
+ mHiddenFolderAuth = resultCode == RESULT_OK;
+ if (mHiddenFolderIcon != null && mHiddenFolderAuth) {
+ mHiddenFolderIcon.getFolder().saveHiddenFolderState(false);
+ mHiddenFolderLockStateChanged = true;
+ } else {
+ mHiddenFolderAuth = false;
+ }
}
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
@@ -867,6 +990,24 @@ public class Launcher extends Activity
/** @Override for MNC */
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
+ if (requestCode == REQUEST_PERMISSION_CALL_PHONE && sPendingAddItem != null
+ && sPendingAddItem.requestCode == REQUEST_PERMISSION_CALL_PHONE) {
+ View v = null;
+ CellLayout layout = getCellLayout(sPendingAddItem.container, sPendingAddItem.screenId);
+ if (layout != null) {
+ v = layout.getChildAt(sPendingAddItem.cellX, sPendingAddItem.cellY);
+ }
+ Intent intent = sPendingAddItem.intent;
+ sPendingAddItem = null;
+ if (grantResults.length > 0
+ && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ startActivity(v, intent, null);
+ } else {
+ // TODO: Show a snack bar with link to settings
+ Toast.makeText(this, getString(R.string.msg_no_phone_permission,
+ getString(R.string.app_name)), Toast.LENGTH_SHORT).show();
+ }
+ }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onRequestPermissionsResult(requestCode, permissions,
grantResults);
@@ -984,6 +1125,7 @@ public class Launcher extends Activity
// switch to another app, otherwise, if it was
showAppsView(false /* animated */, false /* resetListToTop */,
!launchedFromApp /* updatePredictedApps */, false /* focusSearchBar */);
+ mWorkspace.setVisibility(View.INVISIBLE);
} else if (mOnResumeState == State.WIDGETS) {
showWidgetsView(false, false);
}
@@ -1067,6 +1209,14 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onResume();
}
+
+ // Close out fragments
+ Fragment gridFragment = getFragmentManager().findFragmentByTag(
+ DynamicGridSizeFragment.DYNAMIC_GRID_SIZE_FRAGMENT);
+ if (gridFragment != null) {
+ mDynamicGridSizeFragment.setSize();
+ unlockScreenOrientation(true);
+ }
}
@Override
@@ -1368,7 +1518,13 @@ public class Launcher extends Activity
mHotseat.setOnLongClickListener(this);
}
- mOverviewPanel = (ViewGroup) findViewById(R.id.overview_panel);
+ // Setup the overview panel
+ mOverviewPanel = (VerticalSlidingPanel) findViewById(R.id.overview_panel);
+ mOverviewSettingsPanel = new OverviewSettingsPanel(this);
+ mOverviewSettingsPanel.initializeAdapter();
+
+ mDarkPanel = mOverviewPanel.findViewById(R.id.dark_panel);
+
mWidgetsButton = findViewById(R.id.widget_button);
mWidgetsButton.setOnClickListener(new OnClickListener() {
@Override
@@ -1406,6 +1562,35 @@ public class Launcher extends Activity
settingsButton.setVisibility(View.GONE);
}
+ View defaultScreenButton = findViewById(R.id.default_screen_button);
+ defaultScreenButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ if (!mWorkspace.isSwitchingState()) {
+ onClickDefaultScreenButton(arg0);
+ }
+ }
+ });
+ defaultScreenButton.setOnTouchListener(getHapticFeedbackTouchListener());
+
+ mOverviewPanel.setPanelSlideListener(new SettingsPanelSlideListener());
+ mOverviewPanel.setEnableDragViewTouchEvents(true);
+
+ View settingsPaneHeader = mOverviewPanel.findViewById(R.id.settings_pane_header);
+ if (settingsPaneHeader != null) {
+ mOverviewPanel.setDragView(settingsPaneHeader);
+ settingsPaneHeader.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if (mOverviewPanel.isExpanded()) {
+ mOverviewPanel.collapsePane();
+ } else {
+ mOverviewPanel.expandPane();
+ }
+ }
+ });
+ }
+
mOverviewPanel.setAlpha(0f);
// Setup the workspace
@@ -1421,11 +1606,7 @@ public class Launcher extends Activity
// Setup Apps and Widgets
mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
mWidgetsView = (WidgetsContainerView) findViewById(R.id.widgets_view);
- if (mLauncherCallbacks != null && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
- mAppsView.setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
- } else {
- mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
- }
+ setupSearchBar(this);
// Setup the drag controller (drop targets have to be added in reverse order in priority)
dragController.setDragScoller(mWorkspace);
@@ -1451,6 +1632,8 @@ public class Launcher extends Activity
boolean show = shouldShowWeightWatcher();
mWeightWatcher.setVisibility(show ? View.VISIBLE : View.GONE);
}
+
+ sRemoteFolderManager.onSetupViews();
}
/**
@@ -1489,6 +1672,7 @@ public class Launcher extends Activity
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(R.layout.app_icon,
parent, false);
favorite.applyFromShortcutInfo(info, mIconCache);
+ favorite.setTextVisibility(!mHideIconLabels);
favorite.setCompoundDrawablePadding(mDeviceProfile.iconDrawablePaddingPx);
favorite.setOnClickListener(this);
favorite.setOnFocusChangeListener(mFocusHandler);
@@ -1632,6 +1816,161 @@ public class Launcher extends Activity
}
};
+ /**
+ * Initializes the device profile based off of the launcher app state and screen orientation
+ * @param app The launcher app state
+ */
+ public void initializeDeviceProfile(LauncherAppState app) {
+ // Load configuration-specific DeviceProfile
+ mDeviceProfile = getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE ?
+ app.getInvariantDeviceProfile().landscapeProfile
+ : app.getInvariantDeviceProfile().portraitProfile;
+
+ mHideIconLabels = SettingsProvider.getBoolean(this,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default);
+ mDefaultScreenId = SettingsProvider.getLongCustomDefault(this,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID,
+ R.integer.preferences_interface_homescreen_id_default);
+
+ mModel = app.setLauncher(this);
+ mIconCache = app.getIconCache();
+ mIconCache.flush();
+ }
+
+ /**
+ * Re-initializes the device profile and layout and reloads the workspace and app drawer as
+ * appropriate
+ * @param resizeGrid Indicates whether the grid should be resized
+ * @param reloadAppDrawer Indicates whether the app drawer should be reloaded
+ */
+ public void reloadLauncher(boolean resizeGrid, boolean reloadAppDrawer) {
+ reloadLauncher(mWorkspace.getCurrentPage(), resizeGrid, reloadAppDrawer);
+ }
+
+ /**
+ * Re-initializes the device profile and layout and reloads the workspace and app drawer as
+ * appropriate
+ * @param page The page to bind to
+ * @param resizeGrid Indicates whether the grid should be resized
+ * @param reloadAppDrawer Indicates whether the app drawer should be reloaded
+ */
+ private void reloadLauncher(int page, boolean resizeGrid, boolean reloadAppDrawer)
+ {
+ // Re-initialize device profile
+ LauncherAppState app = LauncherAppState.getInstance();
+ app.initInvariantDeviceProfile();
+ initializeDeviceProfile(app);
+
+ mDeviceProfile.layout(this);
+
+ // Reload
+ mModel.resetLoadedState(true, true);
+ int flag = resizeGrid ? LauncherModel.LOADER_FLAG_RESIZE_GRID :
+ LauncherModel.LOADER_FLAG_NONE;
+ mModel.startLoader(page, flag);
+
+ mWorkspace.updateCustomContentVisibility();
+
+ mSearchDropTargetBar.setupQsb(this);
+
+ if (reloadAppDrawer) {
+ reloadAppDrawer();
+ }
+ }
+
+ public void reloadAppDrawer() {
+ List<AppInfo> addedApps = mAppsView.getApps();
+ mDragLayer.removeView(mAppsView);
+ mAppsView = (AllAppsContainerView)LayoutInflater
+ .from(this).inflate(R.layout.all_apps, mDragLayer, false);
+ mDragLayer.addView(mAppsView, mDragLayer.getChildCount() - 1);
+ mAppsView.setVisibility(View.INVISIBLE);
+ setupSearchBar(this);
+ mAppsView.addApps(addedApps);
+ tryAndUpdatePredictedApps();
+ mAppsView.onReloadAppDrawer();
+ sRemoteFolderManager.onReloadAppDrawer();
+ }
+
+ public void reloadWidgetView() {
+ WidgetsModel model = mWidgetsView.getWidgets();
+ mDragLayer.removeView(mWidgetsView);
+ mWidgetsView = (WidgetsContainerView)LayoutInflater
+ .from(this).inflate(R.layout.widgets_view, mDragLayer, false);
+ mDragLayer.addView(mWidgetsView, mDragLayer.getChildCount() - 2);
+ mWidgetsView.setVisibility(View.INVISIBLE);
+ if (model != null) {
+ mWidgetsView.addWidgets(model);
+ }
+ mWidgetsView.reset();
+ }
+
+ /**
+ * Replaces currently added fragments in the launcher layout with a
+ * {@link DynamicGridSizeFragment}.
+ */
+ public void onClickDynamicGridSizeButton() {
+ FragmentManager fragmentManager = getFragmentManager();
+ FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
+ lockScreenOrientation();
+ mDynamicGridSizeFragment = new DynamicGridSizeFragment();
+ fragmentTransaction.replace(R.id.launcher, mDynamicGridSizeFragment,
+ DynamicGridSizeFragment.DYNAMIC_GRID_SIZE_FRAGMENT);
+ fragmentTransaction.commit();
+ }
+
+ /**
+ * If the new grid size is different from the current grid size, the launcher will be reloaded
+ * and the overview settings panel updated with the new grid size value.
+ * @param size The new grid size to set the workspace to.
+ */
+ public void setDynamicGridSize(InvariantDeviceProfile.GridSize size) {
+ int gridSize = SettingsProvider.getIntCustomDefault(this,
+ SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0);
+ boolean customValuesChanged = false;
+ if (gridSize == size.getValue() && size == InvariantDeviceProfile.GridSize.Custom) {
+ int tempRows = SettingsProvider.getIntCustomDefault(this,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, mDeviceProfile.inv.numRows);
+ int tempColumns = SettingsProvider.getIntCustomDefault(this,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, mDeviceProfile.inv.numColumns);
+ if (tempColumns != mDeviceProfile.inv.numColumns ||
+ tempRows != mDeviceProfile.inv.numRows) {
+ customValuesChanged = true;
+ }
+ }
+
+ if (gridSize != size.getValue() || customValuesChanged) {
+ SettingsProvider.putInt(this,
+ SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, size.getValue());
+ reloadLauncher(false, true);
+ }
+
+ // Must be called after reload and before settings invalidation.
+ sRemoteFolderManager.onGridSizeChanged();
+
+ mOverviewSettingsPanel.notifyDataSetInvalidated();
+
+ FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
+ Configuration config = getResources().getConfiguration();
+ if(config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ fragmentTransaction
+ .setCustomAnimations(0, R.anim.exit_out_left);
+ } else {
+ fragmentTransaction
+ .setCustomAnimations(0, R.anim.exit_out_right);
+ }
+ fragmentTransaction
+ .remove(mDynamicGridSizeFragment).commit();
+ unlockScreenOrientation(true);
+ mDarkPanel.setVisibility(View.VISIBLE);
+ ObjectAnimator anim = ObjectAnimator.ofFloat(
+ mDarkPanel, "alpha", 0.3f, 0.0f);
+ anim.start();
+ anim.addListener(mAnimatorListener);
+ }
+
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
@@ -1794,6 +2133,9 @@ public class Launcher extends Activity
public void removeAppWidget(LauncherAppWidgetInfo launcherInfo) {
removeWidgetToAutoAdvance(launcherInfo.hostView);
launcherInfo.hostView = null;
+ AppWidgetProviderInfo info = mAppWidgetManager.getAppWidgetInfo(launcherInfo.appWidgetId);
+ String packageName = info.providerInfo.packageName;
+ LauncherApplication.getLauncherStats().sendWidgetRemoveEvent(packageName);
}
public void showOutOfSpaceMessage(boolean isHotseatLayout) {
@@ -1817,14 +2159,26 @@ public class Launcher extends Activity
return mWorkspace;
}
+ public RemoteFolderManager getRemoteFolderManager() {
+ return sRemoteFolderManager;
+ }
+
public Hotseat getHotseat() {
return mHotseat;
}
+ public View getPageIndicator() {
+ return mPageIndicators;
+ }
+
public ViewGroup getOverviewPanel() {
return mOverviewPanel;
}
+ public View getDarkPanel() {
+ return mDarkPanel;
+ }
+
public SearchDropTargetBar getSearchDropTargetBar() {
return mSearchDropTargetBar;
}
@@ -1955,7 +2309,9 @@ public class Launcher extends Activity
outState.putInt(RUNTIME_STATE, mState.ordinal());
// We close any open folder since it will not be re-opened, and we need to make sure
// this state is reflected.
- closeFolder();
+ if (mHiddenFolderIcon == null) {
+ closeFolder();
+ }
if (mPendingAddInfo.container != ItemInfo.NO_ID && mPendingAddInfo.screenId > -1 &&
mWaitingForResult) {
@@ -2009,6 +2365,8 @@ public class Launcher extends Activity
TextKeyListener.getInstance().release();
unregisterReceiver(mCloseSystemDialogsReceiver);
+ unregisterReceiver(mConnectionReceiver);
+ unregisterReceiver(mDeviceUnlockedReceiver);
mDragLayer.clearAllResizeFrames();
((ViewGroup) mWorkspace.getParent()).removeAllViews();
@@ -2021,12 +2379,32 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onDestroy();
}
+
+ unregisterReceiver(protectedAppsChangedReceiver);
+ unregisterReceiver(searchBarVisibilityChangedReceiver);
}
public DragController getDragController() {
return mDragController;
}
+ public void validateLockForHiddenFolders(FolderIcon info, int action) {
+ mHiddenFolderIcon = info;
+ // Validate Lock Pattern
+ Intent lockPatternActivity = new Intent();
+ lockPatternActivity.setClassName(
+ "com.android.settings",
+ "com.android.settings.applications.LockPatternActivity");
+ startActivityForResult(lockPatternActivity, action);
+ mHiddenFolderAuth = false;
+ }
+
+ public void notifyFolderNameChanged() {
+ // Reload
+ mModel.resetLoadedState(true, true);
+ mModel.startLoader(mWorkspace.getCurrentPage(), LauncherModel.LOADER_FLAG_NONE);
+ }
+
@Override
public void startActivityForResult(Intent intent, int requestCode) {
onStartForResult(requestCode);
@@ -2159,7 +2537,7 @@ public class Launcher extends Activity
super.onPrepareOptionsMenu(menu);
if (!isOnCustomContent()) {
// Close any open folders
- closeFolder();
+ closeFolder(false);
// Stop resizing any widgets
mWorkspace.exitWidgetResizeMode();
if (!mWorkspace.isInOverviewMode()) {
@@ -2251,6 +2629,8 @@ public class Launcher extends Activity
completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
appWidgetInfo);
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
+ String packageName = appWidgetInfo.providerInfo.packageName;
+ LauncherApplication.getLauncherStats().sendWidgetAddEvent(packageName);
}
}
@@ -2361,10 +2741,14 @@ public class Launcher extends Activity
}
FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
- int cellY) {
- final FolderInfo folderInfo = new FolderInfo();
+ int cellY) {
+ FolderInfo folderInfo = new FolderInfo();
folderInfo.title = getText(R.string.folder_name);
+ return addFolder(layout, container, screenId, cellX, cellY, folderInfo);
+ }
+ FolderIcon addFolder(CellLayout layout, long container, final long screenId, int cellX,
+ int cellY, FolderInfo folderInfo) {
// Update the model
LauncherModel.addItemToDatabase(Launcher.this, folderInfo, container, screenId,
cellX, cellY);
@@ -2373,6 +2757,9 @@ public class Launcher extends Activity
// Create the view
FolderIcon newFolder =
FolderIcon.fromXml(R.layout.folder_icon, this, layout, folderInfo, mIconCache);
+ if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ newFolder.setTextVisible(!mHideIconLabels);
+ }
mWorkspace.addInScreen(newFolder, container, screenId, cellX, cellY, 1, 1,
isWorkspaceLocked());
// Force measure the new folder icon
@@ -2424,7 +2811,18 @@ public class Launcher extends Activity
} else if (isWidgetsViewVisible()) {
showOverviewMode(true);
} else if (mWorkspace.isInOverviewMode()) {
- showWorkspace(true);
+ Fragment gridFragment = getFragmentManager().findFragmentByTag(
+ DynamicGridSizeFragment.DYNAMIC_GRID_SIZE_FRAGMENT);
+ if (gridFragment != null) {
+ mDynamicGridSizeFragment.setSize();
+ unlockScreenOrientation(true);
+ }
+ else {
+ showWorkspace(true);
+ // Background was set to gradient in onPause(), restore to black if in all apps.
+ setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT
+ : WORKSPACE_BACKGROUND_TRANSPARENT);
+ }
} else if (mWorkspace.getOpenFolder() != null) {
Folder openFolder = mWorkspace.getOpenFolder();
if (openFolder.isEditingName()) {
@@ -2440,6 +2838,7 @@ public class Launcher extends Activity
}
}
+
/**
* Re-listen when widget host is reset.
*/
@@ -2489,7 +2888,15 @@ public class Launcher extends Activity
} else if (v == mAllAppsButton) {
onClickAllAppsButton(v);
} else if (tag instanceof AppInfo) {
+ AppInfo info = (AppInfo) tag;
startAppShortcutOrInfoActivity(v);
+ LauncherApplication.getLauncherStats().sendAppLaunchEvent(
+ LauncherStats.ORIGIN_APPDRAWER, info.componentName.getPackageName());
+ String packageName = info.getIntent().getComponent().getPackageName();
+ if (LauncherStats.SETTINGS_PACKAGE_NAME.equals(packageName)) {
+ LauncherApplication.getLauncherStats()
+ .sendSettingsOpenedEvent(LauncherStats.ORIGIN_APPDRAWER);
+ }
} else if (tag instanceof LauncherAppWidgetInfo) {
if (v instanceof PendingAppWidgetHostView) {
onClickPendingWidget((PendingAppWidgetHostView) v);
@@ -2638,6 +3045,17 @@ public class Launcher extends Activity
// Start activities
startAppShortcutOrInfoActivity(v);
+ ComponentName componentName = intent.getComponent();
+ if (componentName != null) {
+ String packageName = intent.getComponent().getPackageName();
+ LauncherApplication.getLauncherStats()
+ .sendAppLaunchEvent(LauncherStats.ORIGIN_HOMESCREEN,
+ packageName);
+ if (LauncherStats.SETTINGS_PACKAGE_NAME.equals(packageName)) {
+ LauncherApplication.getLauncherStats().sendSettingsOpenedEvent(
+ LauncherStats.ORIGIN_HOMESCREEN);
+ }
+ }
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onClickAppShortcut(v);
@@ -2688,6 +3106,11 @@ public class Launcher extends Activity
final FolderInfo info = folderIcon.getFolderInfo();
Folder openFolder = mWorkspace.getFolderForTag(info);
+ int[] folderTouchXY = new int[2];
+ v.getLocationOnScreen(folderTouchXY);
+ int[] folderTouchXYOffset = {folderTouchXY[0] + v.getWidth() / 2,
+ folderTouchXY[1] + v.getHeight() / 2};
+
// If the folder info reports that the associated folder is open, then verify that
// it is actually opened. There have been a few instances where this gets out of sync.
if (info.opened && openFolder == null) {
@@ -2700,7 +3123,7 @@ public class Launcher extends Activity
// Close any open folder
closeFolder();
// Open the requested folder
- openFolder(folderIcon);
+ openFolder(folderIcon, folderTouchXYOffset);
} else {
// Find the open folder...
int folderScreen;
@@ -2712,7 +3135,7 @@ public class Launcher extends Activity
// Close any folder open on the current screen
closeFolder();
// Pull the folder onto this screen
- openFolder(folderIcon);
+ openFolder(folderIcon, folderTouchXYOffset);
}
}
}
@@ -2761,7 +3184,33 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onClickSettingsButton(v);
} else {
- startActivity(new Intent(this, SettingsActivity.class));
+ if (mOverviewPanel.isExpanded()) {
+ mOverviewPanel.collapsePane();
+ } else {
+ mOverviewPanel.expandPane();
+ }
+ }
+ }
+
+ protected void onClickDefaultScreenButton(View v) {
+ if (LOGD) Log.d(TAG, "onClickDefaultScreenButton");
+
+ if (!mWorkspace.isInOverviewMode()) return;
+
+ mDefaultScreenId = mWorkspace.getScreenIdForPageIndex(mWorkspace.getPageNearestToCenterOfScreen());
+ updateDefaultScreenButton();
+ SettingsProvider.get(this).edit()
+ .putLong(SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID,
+ mDefaultScreenId)
+ .commit();
+ }
+
+ protected void updateDefaultScreenButton() {
+ if (mOverviewPanel != null) {
+ View defaultPageButton = mOverviewPanel.findViewById(R.id.default_screen_button);
+ defaultPageButton.setActivated(
+ mWorkspace.getScreenIdForPageIndex(mWorkspace.getPageNearestToCenterOfScreen())
+ == mDefaultScreenId);
}
}
@@ -2869,8 +3318,9 @@ public class Launcher extends Activity
}
private boolean startActivity(View v, Intent intent, Object tag) {
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
try {
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
@@ -2925,7 +3375,23 @@ public class Launcher extends Activity
intent.getSourceBounds(), optsBundle);
}
return true;
- } catch (SecurityException e) {
+ } catch (SecurityException | NullPointerException e) {
+ if (Utilities.ATLEAST_MARSHMALLOW && tag instanceof ItemInfo) {
+ // Due to legacy reasons, direct call shortcuts require Launchers to have the
+ // corresponding permission. Show the appropriate permission prompt if that
+ // is the case.
+ if (intent.getComponent() == null
+ && Intent.ACTION_CALL.equals(intent.getAction())
+ && checkSelfPermission(Manifest.permission.CALL_PHONE) !=
+ PackageManager.PERMISSION_GRANTED) {
+ // TODO: Rename sPendingAddItem to a generic name.
+ sPendingAddItem = preparePendingAddArgs(REQUEST_PERMISSION_CALL_PHONE, intent,
+ 0, (ItemInfo) tag);
+ requestPermissions(new String[]{Manifest.permission.CALL_PHONE},
+ REQUEST_PERMISSION_CALL_PHONE);
+ return false;
+ }
+ }
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
Log.e(TAG, "Launcher does not have the permission to launch " + intent +
". Make sure to create a MAIN intent-filter for the corresponding activity " +
@@ -3061,10 +3527,15 @@ public class Launcher extends Activity
* is animated relative to the specified View. If the View is null, no animation
* is played.
*
- * @param folderInfo The FolderInfo describing the folder to open.
+ * @param folderIcon The FolderIcon describing the folder to open.
*/
- public void openFolder(FolderIcon folderIcon) {
+ public void openFolder(FolderIcon folderIcon, int[] folderTouch) {
Folder folder = folderIcon.getFolder();
+
+ if (folder.getState() == Folder.STATE_ANIMATING) {
+ return;
+ }
+
Folder openFolder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
if (openFolder != null && openFolder != folder) {
// Close any open folder before opening a folder.
@@ -3073,6 +3544,11 @@ public class Launcher extends Activity
FolderInfo info = folder.mInfo;
+ if (info.hidden && !mHiddenFolderAuth) {
+ folder.startHiddenFolderManager(REQUEST_OPEN_PROTECTED_FOLDER);
+ return;
+ }
+
info.opened = true;
// While the folder is open, the position of the icon cannot change.
@@ -3086,9 +3562,10 @@ public class Launcher extends Activity
} else {
Log.w(TAG, "Opening folder (" + folder + ") which already has a parent (" +
folder.getParent() + ").");
+ return;
}
folder.animateOpen();
- growAndFadeOutFolderIcon(folderIcon);
+ /*growAndFadeOutFolderIcon(folderIcon);*/
// Notify the accessibility manager that this folder "window" has appeared and occluded
// the workspace items
@@ -3097,31 +3574,45 @@ public class Launcher extends Activity
}
public void closeFolder() {
+ closeFolder(true);
+ }
+
+ public void closeFolder(boolean animate) {
Folder folder = mWorkspace != null ? mWorkspace.getOpenFolder() : null;
if (folder != null) {
if (folder.isEditingName()) {
folder.dismissEditingName();
}
- closeFolder(folder);
+ closeFolder(folder, animate);
}
}
public void closeFolder(Folder folder) {
- folder.getInfo().opened = false;
+ closeFolder(folder, true);
+ }
+
+ public void closeFolder(Folder folder, boolean animate) {
+ final FolderInfo info = folder.getInfo();
+ info.opened = false;
+ if (info.hidden) {
+ mHiddenFolderAuth = false;
+ }
ViewGroup parent = (ViewGroup) folder.getParent().getParent();
if (parent != null) {
FolderIcon fi = (FolderIcon) mWorkspace.getViewForTag(folder.mInfo);
- shrinkAndFadeInFolderIcon(fi);
+ /*shrinkAndFadeInFolderIcon(fi);*/
if (fi != null) {
((CellLayout.LayoutParams) fi.getLayoutParams()).canReorder = true;
}
}
- folder.animateClosed();
+ folder.animateClosed(mHiddenFolderLockStateChanged ? false : animate);
// Notify the accessibility manager that this folder "window" has disappeard and no
// longer occludeds the workspace items
getDragLayer().sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ mHiddenFolderIcon = null;
+ mHiddenFolderLockStateChanged = false;
}
public boolean onLongClick(View v) {
@@ -3276,6 +3767,10 @@ public class Launcher extends Activity
boolean changed = mState != State.WORKSPACE ||
mWorkspace.getState() != Workspace.State.NORMAL;
if (changed) {
+ // Close overview mode if open
+ if (mWorkspace.isInOverviewMode()) {
+ mWorkspace.exitOverviewMode();
+ }
mWorkspace.setVisibility(View.VISIBLE);
mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(),
Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable);
@@ -3288,6 +3783,8 @@ public class Launcher extends Activity
// Change the state *after* we've called all the transition code
mState = State.WORKSPACE;
+ setWorkspaceBackground(mState == State.WORKSPACE ? WORKSPACE_BACKGROUND_GRADIENT
+ : WORKSPACE_BACKGROUND_TRANSPARENT);
// Resume the auto-advance of widgets
mUserPresent = true;
@@ -3321,6 +3818,8 @@ public class Launcher extends Activity
tryAndUpdatePredictedApps();
}
showAppsOrWidgets(State.APPS, animated, focusSearchBar);
+
+ sRemoteFolderManager.onAppDrawerOpened();
}
/**
@@ -3440,10 +3939,15 @@ public class Launcher extends Activity
* resumed.
*/
private void tryAndUpdatePredictedApps() {
- if (mLauncherCallbacks != null) {
- List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
- if (apps != null) {
- mAppsView.setPredictedApps(apps);
+ boolean mRemoteDrawerEnabled = SettingsProvider.getBoolean(this,
+ SettingsProvider.SETTINGS_UI_DRAWER_REMOTE_APPS,
+ R.bool.preferences_interface_drawer_remote_apps_default);
+ if (!mRemoteDrawerEnabled) {
+ if (mLauncherCallbacks != null) {
+ List<ComponentKey> apps = mLauncherCallbacks.getPredictedApps();
+ if (apps != null) {
+ mAppsView.setPredictedAppComponents(apps);
+ }
}
}
}
@@ -3662,6 +4166,10 @@ public class Launcher extends Activity
mWorkspace.createCustomContentContainer();
populateCustomContentContainer();
}
+
+ LauncherModel.saveWidgetCount(this);
+ LauncherModel.savePageCount(this);
+
}
@Override
@@ -3730,6 +4238,7 @@ public class Launcher extends Activity
if (addedApps != null && mAppsView != null) {
mAppsView.addApps(addedApps);
+ sRemoteFolderManager.onBindAddApps(addedApps);
}
}
@@ -3753,7 +4262,7 @@ public class Launcher extends Activity
final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
final Collection<Animator> bounceAnims = new ArrayList<Animator>();
final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
- Workspace workspace = mWorkspace;
+ final Workspace workspace = mWorkspace;
long newShortcutsScreenId = -1;
for (int i = start; i < end; i++) {
final ItemInfo item = shortcuts.get(i);
@@ -3793,6 +4302,10 @@ public class Launcher extends Activity
view = FolderIcon.fromXml(R.layout.folder_icon, this,
(ViewGroup) workspace.getChildAt(workspace.getCurrentPage()),
(FolderInfo) item, mIconCache);
+ if (view == null) {
+ continue;
+ }
+ ((FolderIcon) view).setTextVisible(!mHideIconLabels);
break;
default:
throw new RuntimeException("Invalid Item Type");
@@ -4049,6 +4562,10 @@ public class Launcher extends Activity
if (mLauncherCallbacks != null) {
mLauncherCallbacks.finishBindingItems(false);
}
+
+ mWorkspace.stripEmptyScreens();
+
+ sRemoteFolderManager.bindFinished();
}
private void sendLoadingCompleteBroadcastIfNecessary() {
@@ -4248,7 +4765,7 @@ public class Launcher extends Activity
for (AppInfo info : appInfos) {
removedComponents.add(info.componentName);
}
- if (!packageNames.isEmpty()) {
+ if (packageNames != null && !packageNames.isEmpty()) {
mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!removedComponents.isEmpty()) {
@@ -4719,6 +5236,64 @@ public class Launcher extends Activity
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
}
+
+ private void setupSearchBar(Context context) {
+ mIsDrawerSearchBarEnabled = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_DRAWER_SEARCH,
+ R.bool.preferences_interface_homescreen_search_default);
+
+ if (mIsDrawerSearchBarEnabled) {
+ if (mLauncherCallbacks != null
+ && mLauncherCallbacks.getAllAppsSearchBarController() != null) {
+ mAppsView
+ .setSearchBarController(mLauncherCallbacks.getAllAppsSearchBarController());
+ } else {
+ mAppsView.setSearchBarController(mAppsView.newDefaultAppSearchController());
+ }
+ } else {
+ mAppsView.setSearchBarController(null);
+ mAppsView.setHasSearchBar(mIsDrawerSearchBarEnabled);
+ mAppsView.setSearchBarContainerViewVisibility(
+ mIsDrawerSearchBarEnabled ? View.VISIBLE : View.GONE);
+ mAppsView.updateBackgroundAndPaddings();
+ }
+ }
+
+ class SettingsPanelSlideListener extends VerticalSlidingPanel.SimplePanelSlideListener {
+ ImageView mAnimatedArrow;
+
+ public SettingsPanelSlideListener() {
+ super();
+ mAnimatedArrow = (ImageView) mOverviewPanel.findViewById(R.id.settings_drag_arrow);
+ }
+
+ @Override
+ public void onPanelCollapsed(View panel) {
+ mAnimatedArrow.setBackgroundResource(R.drawable.transition_arrow_reverse);
+
+ AnimationDrawable frameAnimation = (AnimationDrawable) mAnimatedArrow.getBackground();
+ frameAnimation.start();
+ }
+
+ @Override
+ public void onPanelExpanded(View panel) {
+ mAnimatedArrow.setBackgroundResource(R.drawable.transition_arrow);
+
+ AnimationDrawable frameAnimation = (AnimationDrawable) mAnimatedArrow.getBackground();
+ frameAnimation.start();
+
+ LauncherApplication.getLauncherStats().sendSettingsOpenedEvent(
+ LauncherStats.ORIGIN_TREB_LONGPRESS);
+ }
+
+ @Override
+ public void onPanelShown(View panel) {
+ mAnimatedArrow.setBackgroundResource(R.drawable.transition_arrow_reverse);
+
+ AnimationDrawable frameAnimation = (AnimationDrawable) mAnimatedArrow.getBackground();
+ frameAnimation.start();
+ }
+ }
}
interface DebugIntents {
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index d87ad67e5..d2a9b2641 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -124,6 +124,10 @@ public class LauncherAppState {
mModel.startLoaderFromBackground();
}
+ public void recreateWidgetPreviewDb() {
+ mWidgetCache.recreateWidgetPreviewDb();
+ }
+
LauncherModel setLauncher(Launcher launcher) {
getLauncherProvider().setLauncherProviderChangeListener(launcher);
mModel.initialize(launcher);
@@ -174,6 +178,10 @@ public class LauncherAppState {
return mInvariantDeviceProfile;
}
+ public void initInvariantDeviceProfile() {
+ mInvariantDeviceProfile = new InvariantDeviceProfile(sContext);
+ }
+
public static boolean isDogfoodBuild() {
return getInstance().mBuildInfo.isDogfoodBuild();
}
diff --git a/src/com/android/launcher3/LauncherApplication.java b/src/com/android/launcher3/LauncherApplication.java
new file mode 100644
index 000000000..4bbcec073
--- /dev/null
+++ b/src/com/android/launcher3/LauncherApplication.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.Application;
+
+import com.android.launcher3.stats.LauncherStats;
+import com.android.launcher3.stats.internal.service.AggregationIntentService;
+
+public class LauncherApplication extends Application {
+
+ private static LauncherStats sLauncherStats = null;
+
+ /**
+ * Get the reference handle for LauncherStats commands
+ *
+ * @return {@link LauncherStats}
+ */
+ public static LauncherStats getLauncherStats() {
+ return sLauncherStats;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sLauncherStats = LauncherStats.getInstance(this);
+ AggregationIntentService.scheduleService(this);
+ }
+
+}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index b5922c6a3..e90ea1765 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -28,6 +28,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -62,6 +63,10 @@ import com.android.launcher3.util.CursorIconInfo;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.ManagedProfileHeuristic;
import com.android.launcher3.util.Thunk;
+import cyanogenmod.providers.CMSettings;
+
+import com.android.launcher3.settings.SettingsProvider;
+import com.android.launcher3.stats.internal.service.AggregationIntentService;
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
@@ -94,6 +99,7 @@ public class LauncherModel extends BroadcastReceiver
public static final int LOADER_FLAG_NONE = 0;
public static final int LOADER_FLAG_CLEAR_WORKSPACE = 1 << 0;
public static final int LOADER_FLAG_MIGRATE_SHORTCUTS = 1 << 1;
+ public static final int LOADER_FLAG_RESIZE_GRID = 1 << 2;
private static final int ITEMS_CHUNK = 6; // batch size for the workspace icons
private static final long INVALID_SCREEN_ID = -1L;
@@ -108,6 +114,8 @@ public class LauncherModel extends BroadcastReceiver
@Thunk boolean mIsLoaderTaskRunning;
@Thunk boolean mHasLoaderCompletedOnce;
+ private volatile boolean mFlushingWorkerThread;
+
private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
@Thunk static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
@@ -433,6 +441,33 @@ public class LauncherModel extends BroadcastReceiver
ArrayList<Long> workspaceScreens,
ArrayList<Long> addedWorkspaceScreensFinal,
int spanX, int spanY) {
+
+ // Preferred screen is the next one after the default.
+ long preferredScreenId = SettingsProvider.getLongCustomDefault(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID,
+ R.integer.preferences_interface_homescreen_id_default);
+ int preferredScreenIndex = 0;
+ for (int i = 0; i < workspaceScreens.size(); i++) {
+ if (workspaceScreens.get(i) == preferredScreenId) {
+ preferredScreenIndex = i + 1;
+ break;
+ }
+ }
+
+ return findSpaceForItem(context, workspaceScreens, addedWorkspaceScreensFinal,
+ spanX, spanY, preferredScreenIndex);
+ }
+
+ /**
+ * Find a position on the screen for the given size or adds a new screen. Checks
+ * preferredScreen first, and if no space is found then starts searching from the left.
+ * @return screenId and the coordinates for the item.
+ */
+ @Thunk Pair<Long, int[]> findSpaceForItem(
+ Context context,
+ ArrayList<Long> workspaceScreens,
+ ArrayList<Long> addedWorkspaceScreensFinal,
+ int spanX, int spanY, int preferredScreenIndex) {
LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
// Use sBgItemsIdMap as all the items are already loaded.
@@ -450,6 +485,12 @@ public class LauncherModel extends BroadcastReceiver
}
}
+ // If we have a zero-id screen then we skip over it.
+ boolean hasZero = false;
+ if (!workspaceScreens.isEmpty() && workspaceScreens.get(0) == 0) {
+ hasZero = true;
+ }
+
// Find appropriate space for the item.
long screenId = 0;
int[] cordinates = new int[2];
@@ -457,7 +498,6 @@ public class LauncherModel extends BroadcastReceiver
int screenCount = workspaceScreens.size();
// First check the preferred screen.
- int preferredScreenIndex = workspaceScreens.isEmpty() ? 0 : 1;
if (preferredScreenIndex < screenCount) {
screenId = workspaceScreens.get(preferredScreenIndex);
found = findNextAvailableIconSpaceInScreen(
@@ -466,7 +506,8 @@ public class LauncherModel extends BroadcastReceiver
if (!found) {
// Search on any of the screens starting from the first screen.
- for (int screen = 1; screen < screenCount; screen++) {
+ int firstScreen = hasZero ? 1 : 0;
+ for (int screen = firstScreen; screen < screenCount; screen++) {
screenId = workspaceScreens.get(screen);
if (findNextAvailableIconSpaceInScreen(
screenItems.get(screenId), cordinates, spanX, spanY)) {
@@ -650,27 +691,27 @@ public class LauncherModel extends BroadcastReceiver
modelShortcut.spanX == shortcut.spanX &&
modelShortcut.spanY == shortcut.spanY &&
((modelShortcut.dropPos == null && shortcut.dropPos == null) ||
- (modelShortcut.dropPos != null &&
- shortcut.dropPos != null &&
- modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
- modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
+ (modelShortcut.dropPos != null &&
+ shortcut.dropPos != null &&
+ modelShortcut.dropPos[0] == shortcut.dropPos[0] &&
+ modelShortcut.dropPos[1] == shortcut.dropPos[1]))) {
// For all intents and purposes, this is the same object
return;
}
- }
- // the modelItem needs to match up perfectly with item if our model is
- // to be consistent with the database-- for now, just require
- // modelItem == item or the equality check above
- String msg = "item: " + ((item != null) ? item.toString() : "null") +
- "modelItem: " +
- ((modelItem != null) ? modelItem.toString() : "null") +
- "Error: ItemInfo passed to checkItemInfo doesn't match original";
- RuntimeException e = new RuntimeException(msg);
- if (stackTrace != null) {
- e.setStackTrace(stackTrace);
+ // the modelItem needs to match up perfectly with item if our model is
+ // to be consistent with the database-- for now, just require
+ // modelItem == item or the equality check above
+ String msg = "item: " + ((item != null) ? item.toString() : "null") +
+ "modelItem: " +
+ ((modelItem != null) ? modelItem.toString() : "null") +
+ "Error: ItemInfo passed to checkItemInfo doesn't match original";
+ RuntimeException e = new RuntimeException(msg);
+ if (stackTrace != null) {
+ e.setStackTrace(stackTrace);
+ }
+ throw e;
}
- throw e;
}
}
@@ -774,6 +815,35 @@ public class LauncherModel extends BroadcastReceiver
}
}
+ public void flushWorkerThread() {
+ mFlushingWorkerThread = true;
+ Runnable waiter = new Runnable() {
+ public void run() {
+ synchronized (this) {
+ notifyAll();
+ mFlushingWorkerThread = false;
+ }
+ }
+ };
+
+ synchronized(waiter) {
+ runOnWorkerThread(waiter);
+ if (mLoaderTask != null) {
+ synchronized(mLoaderTask) {
+ mLoaderTask.notify();
+ }
+ }
+ boolean success = false;
+ while (!success) {
+ try {
+ waiter.wait();
+ success = true;
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
/**
* Move an item in the DB to a new <container, screen, cellX, cellY>
*/
@@ -950,6 +1020,8 @@ public class LauncherModel extends BroadcastReceiver
final int cellXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLX);
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int optionsIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.OPTIONS);
+ final int hiddenIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.HIDDEN);
+ final int subType = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE);
FolderInfo folderInfo = null;
switch (c.getInt(itemTypeIndex)) {
@@ -966,6 +1038,8 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.cellX = c.getInt(cellXIndex);
folderInfo.cellY = c.getInt(cellYIndex);
folderInfo.options = c.getInt(optionsIndex);
+ folderInfo.hidden = c.getInt(hiddenIndex) > 0;
+ folderInfo.subType = subType;
return folderInfo;
}
@@ -977,10 +1051,22 @@ public class LauncherModel extends BroadcastReceiver
}
/**
+ * Saves the total widget count to a shared preference
+ *
+ * @param context {@link Context}
+ */
+ /* package */ static void saveWidgetCount(Context context) {
+ int widgetCount = LauncherModel.sBgAppWidgets.size();
+ SharedPreferences prefs = context.getSharedPreferences(LauncherAppState
+ .getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ prefs.edit().putInt(AggregationIntentService.PREF_KEY_WIDGET_COUNT, widgetCount).apply();
+ }
+
+ /**
* Add an item to the database in a specified container. Sets the container, screen, cellX and
* cellY fields of the item. Also assigns an ID to the item.
*/
- public static void addItemToDatabase(Context context, final ItemInfo item, final long container,
+ public static void addItemToDatabase(final Context context, final ItemInfo item, final long container,
final long screenId, final int cellX, final int cellY) {
item.container = container;
item.cellX = cellX;
@@ -1030,6 +1116,7 @@ public class LauncherModel extends BroadcastReceiver
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
sBgAppWidgets.add((LauncherAppWidgetInfo) item);
+ saveWidgetCount(context);
break;
}
}
@@ -1080,9 +1167,9 @@ public class LauncherModel extends BroadcastReceiver
/**
* Removes the specified items from the database
* @param context
- * @param item
+ * @param items
*/
- static void deleteItemsFromDatabase(Context context, final ArrayList<? extends ItemInfo> items) {
+ static void deleteItemsFromDatabase(final Context context, final ArrayList<? extends ItemInfo> items) {
final ContentResolver cr = context.getContentResolver();
Runnable r = new Runnable() {
public void run() {
@@ -1112,6 +1199,7 @@ public class LauncherModel extends BroadcastReceiver
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
+ saveWidgetCount(context);
break;
}
sBgItemsIdMap.remove(item.id);
@@ -1123,10 +1211,22 @@ public class LauncherModel extends BroadcastReceiver
}
/**
+ * Saves the count of workspace pages
+ *
+ * @param context {@link Context}
+ */
+ /* package */ static void savePageCount(Context context) {
+ int pageCount = LauncherModel.sBgWorkspaceScreens.size();
+ SharedPreferences prefs = context.getSharedPreferences(LauncherAppState
+ .getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ prefs.edit().putInt(AggregationIntentService.PREF_KEY_PAGE_COUNT, pageCount).apply();
+ }
+
+ /**
* 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.
*/
- public void updateWorkspaceScreenOrder(Context context, final ArrayList<Long> screens) {
+ public void updateWorkspaceScreenOrder(final 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;
@@ -1164,6 +1264,7 @@ public class LauncherModel extends BroadcastReceiver
synchronized (sBgLock) {
sBgWorkspaceScreens.clear();
sBgWorkspaceScreens.addAll(screensCopy);
+ savePageCount(context);
}
}
};
@@ -1612,7 +1713,7 @@ 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,
- ArrayList<Long> workspaceScreens) {
+ ArrayList<Long> workspaceScreens, boolean shouldResizeAndUpdateDB) {
LauncherAppState app = LauncherAppState.getInstance();
InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
final int countX = profile.numColumns;
@@ -1668,26 +1769,67 @@ public class LauncherModel extends BroadcastReceiver
return true;
}
- if (!occupied.containsKey(item.screenId)) {
- ItemInfo[][] items = new ItemInfo[countX + 1][countY + 1];
- occupied.put(item.screenId, items);
+ // If the current item's position lies outside of the bounds
+ // of the current grid size, attempt to place it in the next
+ // available position.
+ if (item.cellX < 0 || item.cellY < 0 || item.cellX + item.spanX > countX
+ || item.cellY + item.spanY > countY) {
+ // If we won't be resizing the grid, then just return, this item does not fit.
+ if (!shouldResizeAndUpdateDB) {
+ Log.e(TAG, "Error loading shortcut " + item
+ + " into cell (" + containerIndex + "-" + item.screenId + ":"
+ + item.cellX + "," + item.cellY
+ + ") out of screen bounds ( " + countX + "x" + countY + ")");
+ return false;
+ }
+
+ if (item.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET) {
+ // Place the item at 0 0 of screen 1
+ // if items overlap here, they will be moved later on
+ item.cellX = 0;
+ item.cellY = 0;
+ item.screenId = 1;
+ item.wasMovedDueToReducedSpace = true;
+ item.requiresDbUpdate = true;
+ } else {
+ // see if widget can be shrunk to fit a screen, if not, just remove it
+ if (item.minSpanX > countX || item.minSpanY > countY) {
+ return false;
+ }
+ // if the widget is larger than the grid, shrink it down
+ if (item.cellX + item.spanX > countX) {
+ item.cellX = 0;
+ item.spanY = (item.spanY / 2) > 0 ? item.spanY / 2 : 1;
+ item.spanX = item.minSpanX;
+ item.requiresDbUpdate = true;
+ item.wasMovedDueToReducedSpace = true;
+ }
+ if (item.cellY + item.spanY > countY) {
+ item.cellY = 0;
+ item.spanY = countY;
+ item.requiresDbUpdate = true;
+ item.wasMovedDueToReducedSpace = true;
+ }
+ if (item.cellY + item.spanY == countY && item.cellX + item.spanX == countX) {
+ // if the widget is the size of the grid, make a screen all it's own.
+ item.screenId = sBgWorkspaceScreens.size() + 1;
+ }
+ }
+ } else {
+ item.wasMovedDueToReducedSpace = false;
+ item.requiresDbUpdate = true;
}
- final ItemInfo[][] screens = occupied.get(item.screenId);
- if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
- item.cellX < 0 || item.cellY < 0 ||
- item.cellX + item.spanX > countX || item.cellY + item.spanY > countY) {
- Log.e(TAG, "Error loading shortcut " + item
- + " into cell (" + containerIndex + "-" + item.screenId + ":"
- + item.cellX + "," + item.cellY
- + ") out of screen bounds ( " + countX + "x" + countY + ")");
- return false;
+ if (!occupied.containsKey(item.screenId)) {
+ ItemInfo[][] items = new ItemInfo[countX][countY];
+ occupied.put(item.screenId, items);
}
+ ItemInfo[][] screens = occupied.get(item.screenId);
// Check if any workspace icons overlap with each other
for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
- if (screens[x][y] != null) {
+ if (!shouldResizeAndUpdateDB && screens[x][y] != null) {
Log.e(TAG, "Error loading shortcut " + item
+ " into cell (" + containerIndex + "-" + item.screenId + ":"
+ x + "," + y
@@ -1700,6 +1842,16 @@ public class LauncherModel extends BroadcastReceiver
for (int x = item.cellX; x < (item.cellX+item.spanX); x++) {
for (int y = item.cellY; y < (item.cellY+item.spanY); y++) {
screens[x][y] = item;
+ if (item.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
+ && shouldResizeAndUpdateDB) {
+ // fill up the entire grid where the widget technically is
+ for (int spanX = x; spanX < item.spanX; spanX++) {
+ screens[spanX][y] = item;
+ for (int spanY = y; spanY < item.spanX; spanY++) {
+ screens[spanX][spanY] = item;
+ }
+ }
+ }
}
}
@@ -1733,6 +1885,8 @@ public class LauncherModel extends BroadcastReceiver
int countX = profile.numColumns;
int countY = profile.numRows;
+ boolean shouldResize = ((mFlags & LOADER_FLAG_RESIZE_GRID) != 0);
+
if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) {
long migrationStartTime = System.currentTimeMillis();
Log.v(TAG, "Starting workspace migration after restore");
@@ -1816,6 +1970,9 @@ public class LauncherModel extends BroadcastReceiver
LauncherSettings.Favorites.PROFILE_ID);
final int optionsIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.OPTIONS);
+ final int hiddenIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.HIDDEN);
+ final int subTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SUBTYPE);
final CursorIconInfo cursorIconInfo = new CursorIconInfo(c);
final LongSparseArray<UserHandleCompat> allUsers = new LongSparseArray<>();
@@ -2044,7 +2201,8 @@ public class LauncherModel extends BroadcastReceiver
}
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens)) {
+ if (!checkItemPlacement(occupied, info, sBgWorkspaceScreens,
+ shouldResize)) {
itemsToRemove.add(id);
break;
}
@@ -2093,9 +2251,12 @@ public class LauncherModel extends BroadcastReceiver
folderInfo.spanX = 1;
folderInfo.spanY = 1;
folderInfo.options = c.getInt(optionsIndex);
+ folderInfo.hidden = c.getInt(hiddenIndex) > 0;
+ folderInfo.subType = c.getInt(subTypeIndex);
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens)) {
+ if (!checkItemPlacement(occupied, folderInfo, sBgWorkspaceScreens,
+ shouldResize)) {
itemsToRemove.add(id);
break;
}
@@ -2222,7 +2383,8 @@ public class LauncherModel extends BroadcastReceiver
appWidgetInfo.container = container;
// check & update map of what's occupied
- if (!checkItemPlacement(occupied, appWidgetInfo, sBgWorkspaceScreens)) {
+ if (!checkItemPlacement(occupied, appWidgetInfo,
+ sBgWorkspaceScreens, shouldResize)) {
itemsToRemove.add(id);
break;
}
@@ -2327,6 +2489,17 @@ public class LauncherModel extends BroadcastReceiver
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
}
+ // If any items have been shifted and require a DB update, update them in the DB.
+ if (shouldResize) {
+ for (ItemInfo info : sBgWorkspaceItems) {
+ if (info != null && info.requiresDbUpdate) {
+ info.requiresDbUpdate = false;
+ LauncherModel.modifyItemInDatabase(mContext, info, info.container,
+ info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
+ }
+ }
+ }
+
if (DEBUG_LOADERS) {
Log.d(TAG, "loaded workspace in " + (SystemClock.uptimeMillis()-t) + "ms");
Log.d(TAG, "workspace layout: ");
@@ -2488,6 +2661,111 @@ public class LauncherModel extends BroadcastReceiver
runOnMainThread(r);
}
+ private void removeHiddenAppsWorkspaceItems(
+ final ArrayList<ItemInfo> workspaceItems,
+ final ArrayList<LauncherAppWidgetInfo> appWidgets,
+ final LongArrayMap<FolderInfo> folders) {
+
+ // Get hidden apps
+ ArrayList<ComponentName> mHiddenApps = new ArrayList<ComponentName>();
+ ArrayList<String> mHiddenAppsPackages = new ArrayList<String>();
+ Context context = mApp.getContext();
+ String protectedComponents = CMSettings.Secure.getString(context.getContentResolver(),
+ CMSettings.Secure.PROTECTED_COMPONENTS);
+ protectedComponents = protectedComponents == null ? "" : protectedComponents;
+ String[] flattened = protectedComponents.split("\\|");
+
+ for (String flat : flattened) {
+ ComponentName cmp = ComponentName.unflattenFromString(flat);
+ if (cmp != null) {
+ mHiddenApps.add(cmp);
+ mHiddenAppsPackages.add(cmp.getPackageName());
+ }
+ }
+
+ // Shortcuts
+ int N = workspaceItems.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ final ItemInfo item = workspaceItems.get(i);
+ if (item instanceof ShortcutInfo) {
+ ShortcutInfo shortcut = (ShortcutInfo)item;
+ if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
+ if (mHiddenApps.contains(shortcut.intent.getComponent())) {
+ LauncherModel.deleteItemFromDatabase(mContext, shortcut);
+ workspaceItems.remove(i);
+ }
+ }
+ } else {
+ // Only remove items from folders that aren't hidden
+ final FolderInfo folder = (FolderInfo)item;
+ List<ShortcutInfo> shortcuts = folder.contents;
+
+ int NN = shortcuts.size() - 1;
+ for (int j = NN; j >= 0; j--) {
+ final ShortcutInfo sci = shortcuts.get(j);
+ if (sci.intent != null && sci.intent.getComponent() != null) {
+ if (!folder.hidden){
+ if (mHiddenApps.contains(sci.intent.getComponent())) {
+ LauncherModel.deleteItemFromDatabase(mContext, sci);
+ Runnable r = new Runnable() {
+ public void run() {
+ folder.remove(sci);
+ }
+ };
+ runOnMainThread(r);
+ }
+ } else {
+ if (!mHiddenApps.contains(sci.intent.getComponent())) {
+ LauncherModel.deleteItemFromDatabase(mContext, sci);
+ Runnable r = new Runnable() {
+ public void run() {
+ folder.remove(sci);
+ }
+ };
+ runOnMainThread(r);
+ }
+ }
+
+ }
+ }
+
+ if (folder.contents.size() == 1 && !folder.hidden) {
+ ShortcutInfo finalItem = folder.contents.get(0);
+ finalItem.container = folder.container;
+ LauncherModel.deleteItemFromDatabase(mContext, folder);
+ // only replace this item back on the workspace if it's not protected
+ // and not a remote folder.
+ if (!mHiddenApps.contains(finalItem.intent.getComponent()) &&
+ !folder.isRemote()) {
+ LauncherModel.addOrMoveItemInDatabase(mContext, finalItem,
+ folder.container, folder.screenId, folder.cellX, folder.cellY);
+ workspaceItems.add(finalItem);
+ }
+ workspaceItems.remove(i);
+ folders.remove(Long.valueOf(item.id));
+
+ // Remote folders are always empty on bind.
+ } else if (folder.contents.size() == 0 && !folder.isRemote()) {
+ LauncherModel.deleteFolderContentsFromDatabase(mContext, folder);
+ workspaceItems.remove(i);
+ folders.remove(Long.valueOf(item.id));
+ }
+ }
+ }
+
+ // AppWidgets
+ N = appWidgets.size() - 1;
+ for (int i = N; i >= 0; i--) {
+ final LauncherAppWidgetInfo item = appWidgets.get(i);
+ if (item.providerName != null) {
+ if (mHiddenAppsPackages.contains(item.providerName.getPackageName())) {
+ LauncherModel.deleteItemFromDatabase(mContext, item);
+ appWidgets.remove(i);
+ }
+ }
+ }
+ }
+
private void bindWorkspaceItems(final Callbacks oldCallbacks,
final ArrayList<ItemInfo> workspaceItems,
final ArrayList<LauncherAppWidgetInfo> appWidgets,
@@ -2496,6 +2774,8 @@ public class LauncherModel extends BroadcastReceiver
final boolean postOnMainThread = (deferredBindRunnables != null);
+ removeHiddenAppsWorkspaceItems(workspaceItems, appWidgets, folders);
+
// Bind the workspace items
int N = workspaceItems.size();
for (int i = 0; i < N; i += ITEMS_CHUNK) {
@@ -3305,9 +3585,12 @@ public class LauncherModel extends BroadcastReceiver
// Refresh widget list, if there is any newly added widget
PackageManager pm = context.getPackageManager();
for (String pkg : mPackages) {
- needToRefresh |= !pm.queryBroadcastReceivers(
+ List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(
new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
- .setPackage(pkg), 0).isEmpty();
+ .setPackage(pkg), 0);
+ if (resolveInfos != null) {
+ needToRefresh |= !resolveInfos.isEmpty();
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 8791e9e57..022ccaccd 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -63,12 +63,13 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
private static final boolean LOGD = false;
- private static final int DATABASE_VERSION = 26;
+ private static final int DATABASE_VERSION = 28;
public static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -537,8 +538,10 @@ public class LauncherProvider extends ContentProvider {
"modified INTEGER NOT NULL DEFAULT 0," +
"restored INTEGER NOT NULL DEFAULT 0," +
"profileId INTEGER DEFAULT " + userSerialNumber + "," +
+ "hidden INTEGER DEFAULT 0" + "," +
"rank INTEGER NOT NULL DEFAULT 0," +
- "options INTEGER NOT NULL DEFAULT 0" +
+ "options INTEGER NOT NULL DEFAULT 0," +
+ "subType INTEGER DEFAULT 0" +
");");
addWorkspacesTable(db);
@@ -568,7 +571,8 @@ public class LauncherProvider extends ContentProvider {
setFlagEmptyDbCreated();
// When a new DB is created, remove all previously stored managed profile information.
- ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(), mContext);
+ ManagedProfileHeuristic.processAllUsers(Collections.<UserHandleCompat>emptyList(),
+ mContext);
}
private void addWorkspacesTable(SQLiteDatabase db) {
@@ -673,6 +677,42 @@ public class LauncherProvider extends ContentProvider {
}
}
case 16: {
+
+ Log.w(TAG, "Found pre-11 Trebuchet, preparing update");
+
+ // With the new shrink-wrapped and re-orderable workspaces, it makes sense
+ // to persist workspace screens and their relative order.
+ mMaxScreenId = 0;
+
+ addWorkspacesTable(db);
+
+ Cursor c = null;
+ long screenId = getMaxId(db, TABLE_FAVORITES);
+
+
+ db.beginTransaction();
+ try {
+ // Insert new column for holding widget provider name
+ db.execSQL("ALTER TABLE favorites " +
+ "ADD COLUMN appWidgetProvider TEXT;");
+ addIntegerColumn(db, "modified", 0);
+ // Create workspaces for the migrated things
+ if (screenId > 0) {
+ for (int sId = 0; sId <= screenId; sId++) {
+ db.execSQL("INSERT INTO workspaceScreens (_id, screenRank) " +
+ "VALUES (" + (sId+1) + ", " + sId + ")");
+ }
+ }
+ // Adjust hotseat format
+ db.execSQL("UPDATE favorites SET screen=cellX WHERE container=-101;");
+ db.setTransactionSuccessful();
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(TAG, ex.getMessage(), ex);
+ } finally {
+ db.endTransaction();
+ }
+
// We use the db version upgrade here to identify users who may not have seen
// clings yet (because they weren't available), but for whom the clings are now
// available (tablet users). Because one of the possible cling flows (migration)
@@ -711,6 +751,10 @@ public class LauncherProvider extends ContentProvider {
// Old version remains, which means we wipe old data
break;
}
+ if (!ensureRankColumn(db) || !updateFolderItemsRank(db, false)) {
+ // Old version remains, which means we wipe old data
+ break;
+ }
}
case 23:
// No-op
@@ -718,9 +762,38 @@ public class LauncherProvider extends ContentProvider {
ManagedProfileHeuristic.markExistingUsersForNoFolderCreation(mContext);
case 25:
convertShortcutsToLauncherActivities(db);
- case 26: {
- // DB Upgraded successfully
- return;
+ case 26:
+ if (!ensureHiddenColumn(db)) {
+ // Old version remains, which means we wipe old data
+ break;
+ }
+ case 27: {
+ migrateLauncherFavorite(db, "com.android.dialer", "com.cyngn.dialer",
+ "com.android.dialer.DialtactsActivity",
+ "com.android.dialer.DialtactsActivity");
+ migrateLauncherFavorite(db, "com.android.mms", "com.android.messaging",
+ "com.android.mms.ui.ConversationList",
+ "com.android.messaging.ui.conversationlist.ConversationListActivity");
+ migrateLauncherFavorite(db, "com.android.camera2", "org.cyanogenmod.snap",
+ "com.android.camera.CameraLauncher",
+ "com.android.camera.CameraLauncher");
+ migrateLauncherFavorite(db, "org.cyanogenmod.snap", "com.android.camera2",
+ "com.android.camera.CameraLauncher",
+ "com.android.camera.CameraLauncher");
+ }
+ case 28: {
+ db.beginTransaction();
+ try {
+ db.execSQL("ALTER TABLE favorites " +
+ "ADD COLUMN subType INTEGER DEFAULT 0;");
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ return;
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(TAG, ex.getMessage(), ex);
+ db.endTransaction();
+ }
}
}
@@ -863,12 +936,12 @@ public class LauncherProvider extends ContentProvider {
Cursor c = db.rawQuery("SELECT container, MAX(cellX) FROM favorites"
+ " WHERE container IN (SELECT _id FROM favorites WHERE itemType = ?)"
+ " GROUP BY container;",
- new String[] {Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)});
+ new String[]{Integer.toString(LauncherSettings.Favorites.ITEM_TYPE_FOLDER)});
while (c.moveToNext()) {
db.execSQL("UPDATE favorites SET rank=cellX+(cellY*?) WHERE "
- + "container=? AND cellX IS NOT NULL AND cellY IS NOT NULL;",
- new Object[] {c.getLong(1) + 1, c.getLong(0)});
+ + "container=? AND cellX IS NOT NULL AND cellY IS NOT NULL;",
+ new Object[]{c.getLong(1) + 1, c.getLong(0)});
}
c.close();
@@ -883,6 +956,36 @@ public class LauncherProvider extends ContentProvider {
return true;
}
+ @Thunk boolean ensureRankColumn(SQLiteDatabase db) {
+ try {
+ // Make sure rank exists
+ Cursor c = db.rawQuery("SELECT rank FROM favorites;", null);
+ if (c != null) {
+ c.close();
+ }
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(TAG, ex.getMessage(), ex);
+ return addIntegerColumn(db, Favorites.RANK, 0);
+ }
+ return true;
+ }
+
+ @Thunk boolean ensureHiddenColumn(SQLiteDatabase db) {
+ try {
+ // Make sure hidden exists
+ Cursor c = db.rawQuery("SELECT hidden FROM favorites;", null);
+ if (c != null) {
+ c.close();
+ }
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(TAG, ex.getMessage(), ex);
+ return addIntegerColumn(db, Favorites.HIDDEN, 0);
+ }
+ return true;
+ }
+
private boolean addProfileColumn(SQLiteDatabase db) {
UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
// Default to the serial number of this user, for older
@@ -892,6 +995,71 @@ public class LauncherProvider extends ContentProvider {
return addIntegerColumn(db, Favorites.PROFILE_ID, userSerialNumber);
}
+ private void migrateLauncherFavorite(SQLiteDatabase db, String originalPackageName,
+ String newPackageName, String originalClassName, String newClassName) {
+ if (!Utilities.isPackageInstalled(mContext, newPackageName)) {
+ return;
+ }
+
+ final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
+ new int[]{Favorites.ITEM_TYPE_SHORTCUT, Favorites.ITEM_TYPE_APPLICATION});
+ Cursor c = null;
+ db.beginTransaction();
+
+ try {
+ // Select and iterate through each matching widget
+ c = db.query(TABLE_FAVORITES,
+ new String[] { Favorites._ID, Favorites.INTENT },
+ selectWhere, null, null, null, null);
+ if (c == null) return;
+
+ while (c.moveToNext()) {
+ long favoriteId = c.getLong(0);
+ final String intentUri = c.getString(1);
+ if (intentUri != null) {
+ try {
+ final Intent intent = Intent.parseUri(intentUri, 0);
+ final ComponentName componentName = intent.getComponent();
+ final Set<String> categories = intent.getCategories();
+
+ if (Intent.ACTION_MAIN.equals(intent.getAction()) &&
+ componentName != null &&
+ originalPackageName.equals(componentName.getPackageName()) &&
+ originalClassName.equals(componentName.getClassName()) &&
+ categories != null &&
+ categories.contains(Intent.CATEGORY_LAUNCHER)) {
+
+ final ComponentName newName = new ComponentName(newPackageName,
+ newClassName);
+ intent.setComponent(newName);
+ final ContentValues values = new ContentValues();
+ values.put(Favorites.INTENT, intent.toUri(0));
+
+ String updateWhere = Favorites._ID + "=" + favoriteId;
+ db.update(TABLE_FAVORITES, values, updateWhere, null);
+ if (LOGD) {
+ Log.i(TAG, "Updated " + componentName + " to " + newName);
+ }
+ }
+ } catch (RuntimeException ex) {
+ Log.e(TAG, "Problem moving " + originalClassName + " activity", ex);
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Problem moving " + originalClassName + " activity", e);
+ }
+ }
+ }
+
+ db.setTransactionSuccessful();
+ } catch (SQLException ex) {
+ Log.w(TAG, "Problem while upgrading " + originalClassName + " icon", ex);
+ } finally {
+ db.endTransaction();
+ if (c != null) {
+ c.close();
+ }
+ }
+ }
+
private boolean addIntegerColumn(SQLiteDatabase db, String columnName, long defaultValue) {
db.beginTransaction();
try {
@@ -1391,6 +1559,21 @@ public class LauncherProvider extends ContentProvider {
return id;
}
+ /**
+ * Build a query string that will match any row where the column matches
+ * anything in the values list.
+ */
+ private static String buildOrWhereString(String column, int[] values) {
+ StringBuilder selectWhere = new StringBuilder();
+ for (int i = values.length - 1; i >= 0; i--) {
+ selectWhere.append(column).append("=").append(values[i]);
+ if (i > 0) {
+ selectWhere.append(" OR ");
+ }
+ }
+ return selectWhere.toString();
+ }
+
static class SqlArguments {
public final String table;
public final String where;
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 8a5804f34..20428670b 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -42,6 +42,15 @@ public class LauncherSettings {
public static final String TITLE = "title";
/**
+ * Folder Hidden status
+ */
+ public static final String HIDDEN = "hidden";
+
+ /**
+ * Folder subtype, used for Remote Folders
+ */
+ static final String SUBTYPE = "subType";
+ /**
* The Intent URL of the gesture, describing what it points to. This
* value is given to {@link android.content.Intent#parseUri(String, int)} to create
* an Intent that can be launched.
@@ -309,6 +318,12 @@ public class LauncherSettings {
* <p>Type: INTEGER</p>
*/
public static final String OPTIONS = "options";
+
+ /**
+ * Stores whether the item is hidden.
+ * <p>Type: INTEGER</p>
+ */
+ public static final String HIDDEN = "hidden";
}
/**
diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
index cdde8c13f..2bdee3790 100644
--- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java
+++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java
@@ -24,6 +24,7 @@ import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.annotation.SuppressLint;
import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
@@ -168,6 +169,11 @@ public class LauncherStateTransitionAnimation {
public float getMaterialRevealViewFinalAlpha(View revealView) {
return 0.3f;
}
+
+ @Override
+ void onTransitionComplete() {
+ toView.setScrubberVisibility(View.VISIBLE);
+ }
};
mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState,
Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(),
@@ -282,6 +288,14 @@ public class LauncherStateTransitionAnimation {
// Setup the animation for the content view
contentView.setVisibility(View.VISIBLE);
+ /*
+ * Because the contentView and revealView overlap, if their backgrounds have alpha,
+ * it will blend and cause a flicker. So get the current alpha of the contentView (for
+ * later) and set to 0. When the animation is done, reset the alpha.
+ */
+ final Drawable contentBackground = contentView.getBackground();
+ final int alpha = contentBackground.getAlpha();
+ contentBackground.setAlpha(0);
contentView.setAlpha(0f);
contentView.setTranslationY(revealViewToYDrift);
layerViews.put(contentView, BUILD_AND_SET_LAYER);
@@ -322,6 +336,7 @@ public class LauncherStateTransitionAnimation {
// Hide the reveal view
revealView.setVisibility(View.INVISIBLE);
+ contentBackground.setAlpha(alpha);
// Disable all necessary layers
for (View v : layerViews.keySet()) {
@@ -453,6 +468,7 @@ public class LauncherStateTransitionAnimation {
final Workspace.State toWorkspaceState, final int toWorkspacePage,
final boolean animated, final Runnable onCompleteRunnable) {
final WidgetsContainerView widgetsView = mLauncher.getWidgetsView();
+ widgetsView.setScrubberVisibility(View.INVISIBLE);
PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() {
@Override
float getMaterialRevealViewFinalAlpha(View revealView) {
@@ -486,6 +502,9 @@ public class LauncherStateTransitionAnimation {
final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet();
final Resources res = mLauncher.getResources();
final boolean material = Utilities.ATLEAST_LOLLIPOP;
+ final boolean doReveal = !((mLauncher.mState == Launcher.State.APPS ||
+ mLauncher.mState == Launcher.State.WIDGETS) &&
+ toWorkspaceState == Workspace.State.OVERVIEW);
final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime);
final int itemsAlphaStagger =
res.getInteger(R.integer.config_overlayItemsAlphaStagger);
@@ -515,13 +534,17 @@ public class LauncherStateTransitionAnimation {
animation.play(workspaceAnim);
}
+ final Drawable contentBackground = contentView.getBackground();
+ final int alpha = contentBackground.getAlpha();
+
// hideAppsCustomizeHelper is called in some cases when it is already hidden
// don't perform all these no-op animations. In particularly, this was causing
// the all-apps button to pop in and out.
- if (fromView.getVisibility() == View.VISIBLE) {
+ if (doReveal && fromView.getVisibility() == View.VISIBLE) {
int width = revealView.getMeasuredWidth();
int height = revealView.getMeasuredHeight();
float revealRadius = (float) Math.hypot(width / 2, height / 2);
+ contentBackground.setAlpha(0);
revealView.setVisibility(View.VISIBLE);
revealView.setAlpha(1f);
revealView.setTranslationY(0);
@@ -618,6 +641,8 @@ public class LauncherStateTransitionAnimation {
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionPrepare(toView, animated, true);
+ } else {
+ fromView.setVisibility(View.GONE);
}
animation.addListener(new AnimatorListenerAdapter() {
@@ -644,6 +669,7 @@ public class LauncherStateTransitionAnimation {
contentView.setTranslationX(0);
contentView.setTranslationY(0);
contentView.setAlpha(1);
+ contentBackground.setAlpha(alpha);
}
if (overlaySearchBarView != null) {
overlaySearchBarView.setAlpha(1f);
diff --git a/src/com/android/launcher3/NetworkConnectionReceiver.java b/src/com/android/launcher3/NetworkConnectionReceiver.java
new file mode 100644
index 000000000..0b49c208d
--- /dev/null
+++ b/src/com/android/launcher3/NetworkConnectionReceiver.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.ConnectivityManager;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class NetworkConnectionReceiver extends BroadcastReceiver {
+ public static final String INTENT_ACTION = ConnectivityManager.CONNECTIVITY_ACTION;
+
+ private final Set<NetworkStateChangeListener> mListeners;
+
+ interface NetworkStateChangeListener {
+ void onNetworkConnected();
+ void onNetworkDisconnected();
+ }
+
+ public NetworkConnectionReceiver() {
+ mListeners = new HashSet<NetworkStateChangeListener>();
+ }
+
+ public void registerListener(final NetworkStateChangeListener listener) {
+ mListeners.add(listener);
+ }
+
+ public void deregisterListener(final NetworkStateChangeListener listener) {
+ mListeners.remove(listener);
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!intent.getAction().equals(INTENT_ACTION)) return;
+
+ boolean connected = Utilities.isNetworkConnected(context);
+ for (NetworkStateChangeListener listener: mListeners) {
+ if (connected) {
+ listener.onNetworkConnected();
+ } else {
+ listener.onNetworkDisconnected();
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/OverviewPanel.java b/src/com/android/launcher3/OverviewPanel.java
new file mode 100644
index 000000000..2fdca6330
--- /dev/null
+++ b/src/com/android/launcher3/OverviewPanel.java
@@ -0,0 +1,32 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+public class OverviewPanel extends VerticalSlidingPanel implements Insettable {
+ public OverviewPanel(Context context) {
+ super(context);
+ }
+
+ public OverviewPanel(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public OverviewPanel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void setInsets(Rect insets) {
+ LinearLayout layout = (LinearLayout)
+ findViewById(R.id.settings_container);
+ FrameLayout.LayoutParams lp =
+ (FrameLayout.LayoutParams) layout.getLayoutParams();
+ lp.bottomMargin = insets.bottom;
+ layout.setLayoutParams(lp);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/OverviewSettingsPanel.java b/src/com/android/launcher3/OverviewSettingsPanel.java
new file mode 100644
index 000000000..8f1b435e8
--- /dev/null
+++ b/src/com/android/launcher3/OverviewSettingsPanel.java
@@ -0,0 +1,107 @@
+package com.android.launcher3;
+
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.database.MatrixCursor;
+import android.widget.ListView;
+import com.android.launcher3.list.PinnedHeaderListView;
+import com.android.launcher3.list.SettingsPinnedHeaderAdapter;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+public class OverviewSettingsPanel {
+ public static final String ANDROID_SETTINGS = "com.android.settings";
+ public static final String ANDROID_PROTECTED_APPS =
+ "com.android.settings.applications.ProtectedAppsActivity";
+ public static final int HOME_SETTINGS_POSITION = 0;
+ public static final int DRAWER_SETTINGS_POSITION = 1;
+ public static final int APP_SETTINGS_POSITION = 2;
+
+ private Launcher mLauncher;
+ private SettingsPinnedHeaderAdapter mSettingsAdapter;
+ private PinnedHeaderListView mListView;
+
+ OverviewSettingsPanel(Launcher launcher) {
+ mLauncher = launcher;
+ }
+
+ // One time initialization of the SettingsPinnedHeaderAdapter
+ public void initializeAdapter() {
+ // Settings pane Listview
+ mListView = (PinnedHeaderListView) mLauncher
+ .findViewById(R.id.settings_home_screen_listview);
+ mListView.setOverScrollMode(ListView.OVER_SCROLL_NEVER);
+ Resources res = mLauncher.getResources();
+ String[] headers = new String[] {
+ res.getString(R.string.home_screen_settings),
+ res.getString(R.string.drawer_settings),
+ res.getString(R.string.app_settings)};
+
+ String[] valuesApp = new String[] {
+ res.getString(R.string.larger_icons_text),
+ res.getString(R.string.protected_app_settings)};
+
+ mSettingsAdapter = new SettingsPinnedHeaderAdapter(mLauncher);
+ mSettingsAdapter.setHeaders(headers);
+ mSettingsAdapter.addPartition(false, true);
+ mSettingsAdapter.addPartition(false, true);
+ mSettingsAdapter.addPartition(false, true);
+ mSettingsAdapter.mPinnedHeaderCount = headers.length;
+
+ mSettingsAdapter.changeCursor(HOME_SETTINGS_POSITION,
+ createCursor(headers[0], getValuesHome()));
+ mSettingsAdapter.changeCursor(DRAWER_SETTINGS_POSITION,
+ createCursor(headers[1], getValuesDrawer()));
+ mSettingsAdapter.changeCursor(APP_SETTINGS_POSITION, createCursor(headers[2], valuesApp));
+ mListView.setAdapter(mSettingsAdapter);
+ }
+
+ private Cursor createCursor(String header, String[] values) {
+ MatrixCursor cursor = new MatrixCursor(new String[]{"_id", header});
+ int count = values.length;
+ for (int i = 0; i < count; i++) {
+ cursor.addRow(new Object[]{i, values[i]});
+ }
+ return cursor;
+ }
+
+ private String[] getValuesHome() {
+ Resources res = mLauncher.getResources();
+ ArrayList<String> values = new ArrayList<String>(Arrays.asList(new String[]{
+ res.getString(R.string.home_screen_search_text),
+ res.getString(R.string.icon_labels),
+ res.getString(R.string.scrolling_wallpaper),
+ res.getString(R.string.grid_size_text),
+ res.getString(R.string.allow_rotation_title)}));
+
+ // Add additional external settings.
+ RemoteFolderManager.onInitializeHomeSettings(values, mLauncher);
+
+ String[] valuesArr = new String[values.size()];
+ values.toArray(valuesArr);
+ return valuesArr;
+ }
+
+ private String[] getValuesDrawer() {
+ Resources res = mLauncher.getResources();
+ ArrayList<String> values = new ArrayList<String>(Arrays.asList(new String[]{
+ res.getString(R.string.icon_labels),
+ res.getString(R.string.app_drawer_style),
+ res.getString(R.string.app_drawer_color),
+ res.getString(R.string.fast_scroller),
+ res.getString(R.string.fast_scroller_type),
+ res.getString(R.string.home_screen_search_text)}));
+
+ // Add additional external settings.
+ RemoteFolderManager.onInitializeDrawerSettings(values, mLauncher);
+
+ String[] valuesArr = new String[values.size()];
+ values.toArray(valuesArr);
+ return valuesArr;
+ }
+
+ public void notifyDataSetInvalidated() {
+ mSettingsAdapter.notifyDataSetInvalidated();
+ }
+}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 05f0a0553..fe940c182 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -169,7 +169,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
@Thunk PageIndicator mPageIndicator;
// The viewport whether the pages are to be contained (the actual view may be larger than the
// viewport)
- private Rect mViewport = new Rect();
+ protected Rect mViewport = new Rect();
// Reordering
// We use the min scale to determine how much to expand the actually PagedView measured
@@ -1238,15 +1238,24 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
// XXX-RTL: This will be fixed in a future CL
if (mCurrentPage >= 0 && mCurrentPage < getPageCount()) {
- getPageAt(mCurrentPage).addFocusables(views, direction, focusableMode);
+ View page = getPageAt(mCurrentPage);
+ if (page != null) {
+ page.addFocusables(views, direction, focusableMode);
+ }
}
if (direction == View.FOCUS_LEFT) {
if (mCurrentPage > 0) {
- getPageAt(mCurrentPage - 1).addFocusables(views, direction, focusableMode);
+ View page = getPageAt(mCurrentPage - 1);
+ if (page != null) {
+ page.addFocusables(views, direction, focusableMode);
+ }
}
} else if (direction == View.FOCUS_RIGHT){
if (mCurrentPage < getPageCount() - 1) {
- getPageAt(mCurrentPage + 1).addFocusables(views, direction, focusableMode);
+ View page = getPageAt(mCurrentPage + 1);
+ if (page != null) {
+ page.addFocusables(views, direction, focusableMode);
+ }
}
}
}
diff --git a/src/com/android/launcher3/ProtectedComponentsHelper.java b/src/com/android/launcher3/ProtectedComponentsHelper.java
new file mode 100644
index 000000000..12ec96da4
--- /dev/null
+++ b/src/com/android/launcher3/ProtectedComponentsHelper.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.ComponentName;
+import android.content.Context;
+import cyanogenmod.providers.CMSettings;
+
+import java.util.ArrayList;
+
+public class ProtectedComponentsHelper {
+ private static final int FILTER_APPS_SYSTEM_FLAG = 1;
+ private static final int FILTER_APPS_DOWNLOADED_FLAG = 2;
+ private static int sFilterApps = FILTER_APPS_SYSTEM_FLAG | FILTER_APPS_DOWNLOADED_FLAG;
+
+ private static ArrayList<ComponentName> sProtectedApps = new ArrayList<ComponentName>();
+ private static ArrayList<String> sProtectedPackages = new ArrayList<String>();
+
+ /**
+ * Gets the list of protected components from {@link CMSettings} and updates the existing list
+ * of protected apps and packages
+ * @param context Context
+ */
+ public static void updateProtectedComponentsLists(Context context) {
+ String protectedComponents = CMSettings.Secure.getString(context.getContentResolver(),
+ CMSettings.Secure.PROTECTED_COMPONENTS);
+ protectedComponents = protectedComponents == null ? "" : protectedComponents;
+ String [] flattened = protectedComponents.split("\\|");
+ sProtectedApps = new ArrayList<ComponentName>(flattened.length);
+ sProtectedPackages = new ArrayList<String>(flattened.length);
+ for (String flat : flattened) {
+ ComponentName cmp = ComponentName.unflattenFromString(flat);
+ if (cmp != null) {
+ sProtectedApps.add(cmp);
+ sProtectedPackages.add(cmp.getPackageName());
+ }
+ }
+ }
+
+ /**
+ * Checks if the given combination of {@link ComponentName} and flags is for a protected app
+ */
+ public static boolean isProtectedApp(int flags, ComponentName componentName) {
+ boolean system = isSystemFlag(flags);
+ return sProtectedApps.contains(componentName) || (system && !getShowSystemApps()) ||
+ (!system && !getShowDownloadedApps());
+ }
+
+ /**
+ * Checks if the given combination of package name and flags is for a protected package
+ */
+ public static boolean isProtectedPackage(int flags, String packageName) {
+ boolean system = isSystemFlag(flags);
+ return (sProtectedPackages.contains(packageName) || (system && !getShowSystemApps()) ||
+ (!system && !getShowDownloadedApps()));
+ }
+
+ private static boolean isSystemFlag(int flags) {
+ return (flags & AppInfo.DOWNLOADED_FLAG) == 0;
+ }
+
+ private static boolean getShowSystemApps() {
+ return (sFilterApps & FILTER_APPS_SYSTEM_FLAG) != 0;
+ }
+
+ private static boolean getShowDownloadedApps() {
+ return (sFilterApps & FILTER_APPS_DOWNLOADED_FLAG) != 0;
+ }
+}
diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java
index 772a334b9..182d741ae 100644
--- a/src/com/android/launcher3/SearchDropTargetBar.java
+++ b/src/com/android/launcher3/SearchDropTargetBar.java
@@ -64,6 +64,7 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
private static final AccelerateInterpolator sAccelerateInterpolator =
new AccelerateInterpolator();
+ private Launcher mLauncher;
private State mState = State.SEARCH_BAR;
@Thunk View mQSB;
@Thunk View mDropTargetBar;
@@ -84,6 +85,8 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
public void setup(Launcher launcher, DragController dragController) {
+ mLauncher = launcher;
+
dragController.addDragListener(this);
dragController.setFlingToDeleteDropTarget(mDeleteDropTarget);
@@ -134,24 +137,39 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
});
}
+ public void setupQsb(Launcher launcher) {
+ mLauncher = launcher;
+ mQSB = launcher.getOrCreateQsbBar();
+ }
+
public void setQsbSearchBar(View qsb) {
+ float alpha = 1f;
+ int visibility = View.VISIBLE;
+ if (mQSB != null) {
+ alpha = mQSB.getAlpha();
+ visibility = mQSB.getVisibility();
+ }
+
mQSB = qsb;
if (mQSB != null) {
- // Update the search ber animation
+ mQSB.setAlpha(alpha);
+ mQSB.setVisibility(visibility);
+
+ // Update the search bar 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) {
+ if (mQSB != null && isSearchBarVisible()) {
mQSB.setVisibility(View.VISIBLE);
}
}
@Override
public void onAnimationEnd(Animator animation) {
- if (mQSB != null) {
+ if (mQSB != null && isSearchBarVisible()) {
AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled);
}
}
@@ -173,9 +191,10 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
AccessibilityManager am = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
mAccessibilityEnabled = am.isEnabled();
-
- animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
- duration);
+ if (mLauncher.getDeviceProfile().searchBarVisible) {
+ animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(),
+ duration);
+ }
animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(),
duration);
}
@@ -245,8 +264,15 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D
}
}
+ public boolean isSearchBarVisible() {
+ if (mLauncher != null) {
+ return mLauncher.getDeviceProfile().searchBarVisible;
+ }
+ return true;
+ }
+
public void enableAccessibleDrag(boolean enable) {
- if (mQSB != null) {
+ if (mQSB != null && isSearchBarVisible()) {
mQSB.setVisibility(enable ? View.GONE : View.VISIBLE);
}
mInfoDropTarget.enableAccessibleDrag(enable);
diff --git a/src/com/android/launcher3/SettingsActivity.java b/src/com/android/launcher3/SettingsActivity.java
deleted file mode 100644
index dab71c862..000000000
--- a/src/com/android/launcher3/SettingsActivity.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.app.Activity;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.Preference.OnPreferenceChangeListener;
-import android.preference.PreferenceFragment;
-import android.preference.SwitchPreference;
-
-/**
- * Settings activity for Launcher. Currently implements the following setting: Allow rotation
- */
-public class SettingsActivity extends Activity {
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- // Display the fragment as the main content.
- getFragmentManager().beginTransaction()
- .replace(android.R.id.content, new LauncherSettingsFragment())
- .commit();
- }
-
- /**
- * This fragment shows the launcher preferences.
- */
- public static class LauncherSettingsFragment extends PreferenceFragment
- implements OnPreferenceChangeListener {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- addPreferencesFromResource(R.xml.launcher_preferences);
-
- SwitchPreference pref = (SwitchPreference) findPreference(
- Utilities.ALLOW_ROTATION_PREFERENCE_KEY);
- pref.setPersistent(false);
-
- Bundle extras = new Bundle();
- extras.putBoolean(LauncherSettings.Settings.EXTRA_DEFAULT_VALUE, false);
- Bundle value = getActivity().getContentResolver().call(
- LauncherSettings.Settings.CONTENT_URI,
- LauncherSettings.Settings.METHOD_GET_BOOLEAN,
- Utilities.ALLOW_ROTATION_PREFERENCE_KEY, extras);
- pref.setChecked(value.getBoolean(LauncherSettings.Settings.EXTRA_VALUE));
-
- pref.setOnPreferenceChangeListener(this);
- }
-
- @Override
- public boolean onPreferenceChange(Preference preference, Object newValue) {
- Bundle extras = new Bundle();
- extras.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, (Boolean) newValue);
- getActivity().getContentResolver().call(
- LauncherSettings.Settings.CONTENT_URI,
- LauncherSettings.Settings.METHOD_SET_BOOLEAN,
- preference.getKey(), extras);
- return true;
- }
- }
-}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 5766cf2f2..b1a787bfc 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -100,7 +100,7 @@ public class ShortcutInfo extends ItemInfo {
/**
* The application icon.
*/
- private Bitmap mIcon;
+ Bitmap mIcon;
/**
* Indicates that the icon is disabled due to safe mode restrictions.
@@ -265,6 +265,14 @@ public class ShortcutInfo extends ItemInfo {
return (status & flag) != 0;
}
+ /**
+ * Check if this shortcut has a specific flag.
+ * @param flag flag to check.
+ * @return true if the flag is present, false otherwise.
+ */
+ public boolean hasFlag(int flag) {
+ return (flags & flag) != 0;
+ }
public final boolean isPromise() {
return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
diff --git a/src/com/android/launcher3/ThemeChangedReceiver.java b/src/com/android/launcher3/ThemeChangedReceiver.java
new file mode 100644
index 000000000..3f4f3af38
--- /dev/null
+++ b/src/com/android/launcher3/ThemeChangedReceiver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.io.File;
+import java.util.ArrayList;
+
+public class ThemeChangedReceiver extends BroadcastReceiver {
+ private static final String EXTRA_COMPONENTS = "components";
+
+ public static final String MODIFIES_ICONS = "mods_icons";
+ public static final String MODIFIES_FONTS = "mods_fonts";
+ public static final String MODIFIES_OVERLAYS = "mods_overlays";
+
+ public void onReceive(Context context, Intent intent) {
+ // components is a string array of the components that changed
+ ArrayList<String> components = intent.getStringArrayListExtra(EXTRA_COMPONENTS);
+ if (isInterestingThemeChange(components)) {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ LauncherAppState.setApplicationContext(context);
+ app = LauncherAppState.getInstance();
+ }
+ clearAppIconCache(context);
+ clearWidgetPreviewCache(context);
+ app.recreateWidgetPreviewDb();
+ app.getIconCache().flush();
+ app.getModel().forceReload();
+
+ if (Launcher.sRemoteFolderManager != null) {
+ Launcher.sRemoteFolderManager.onThemeChanged();
+ }
+ }
+ }
+
+ /**
+ * We consider this an "interesting" theme change if it modifies icons, overlays, or fonts.
+ * @param components
+ * @return
+ */
+ private boolean isInterestingThemeChange(ArrayList<String> components) {
+ if (components != null) {
+ for (String component : components) {
+ if (component.equals(MODIFIES_ICONS) ||
+ component.equals(MODIFIES_FONTS) ||
+ component.equals(MODIFIES_OVERLAYS)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private void clearWidgetPreviewCache(Context context) {
+ context.deleteDatabase(LauncherFiles.WIDGET_PREVIEWS_DB);
+ }
+
+ private void clearAppIconCache(Context context) {
+ context.deleteDatabase(LauncherFiles.APP_ICONS_DB);
+ }
+}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index adedd33b2..bf11aa19e 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -25,7 +25,6 @@ import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
@@ -44,6 +43,8 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Process;
@@ -56,6 +57,8 @@ import android.util.TypedValue;
import android.view.View;
import android.widget.Toast;
+import com.android.launcher3.settings.SettingsProvider;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
@@ -110,22 +113,19 @@ public final class Utilities {
private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate";
private static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
- public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
-
public static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
public static boolean isAllowRotationPrefEnabled(Context context, boolean multiProcess) {
- SharedPreferences sharedPrefs = context.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE | (multiProcess ?
- Context.MODE_MULTI_PROCESS : 0));
- boolean allowRotationPref = sharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false);
+ boolean allowRotationPref = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_ALLOW_ROTATION,
+ R.bool.preferences_interface_allow_rotation);
return sForceEnableRotation || allowRotationPref;
}
public static boolean isRotationAllowedForDevice(Context context) {
- return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation);
+ return sForceEnableRotation || context.getResources().getBoolean(R.bool.preferences_interface_allow_rotation);
}
public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) {
@@ -695,8 +695,7 @@ public final class Utilities {
}
public static float dpiFromPx(int size, DisplayMetrics metrics){
- float densityRatio = (float) metrics.densityDpi / DisplayMetrics.DENSITY_DEFAULT;
- return (size / densityRatio);
+ return (size / metrics.density);
}
public static int pxFromDp(float size, DisplayMetrics metrics) {
return (int) Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
@@ -710,4 +709,33 @@ public final class Utilities {
public static String createDbSelectionQuery(String columnName, Iterable<?> values) {
return String.format(Locale.ENGLISH, "%s IN (%s)", columnName, TextUtils.join(", ", values));
}
+
+ public static boolean searchActivityExists(Context context) {
+ final SearchManager searchManager =
+ (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+ ComponentName globalSearchActivity = searchManager.getGlobalSearchActivity();
+
+ return globalSearchActivity != null;
+ }
+
+ public static boolean isPackageInstalled(Context context, String pkg) {
+ PackageManager packageManager = context.getPackageManager();
+ try {
+ PackageInfo pi = packageManager.getPackageInfo(pkg, 0);
+ return pi.applicationInfo.enabled;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ public static boolean isNetworkConnected(Context context) {
+ ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo activeNetwork = connectivityManager.getActiveNetworkInfo();
+ return activeNetwork != null && activeNetwork.isConnected();
+ }
+
+ public static boolean isConnectedToWiFi(Context context) {
+ ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ return connectivityManager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected();
+ }
}
diff --git a/src/com/android/launcher3/VerticalSlidingPanel.java b/src/com/android/launcher3/VerticalSlidingPanel.java
new file mode 100644
index 000000000..88a52392c
--- /dev/null
+++ b/src/com/android/launcher3/VerticalSlidingPanel.java
@@ -0,0 +1,1330 @@
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.v4.view.MotionEventCompat;
+import android.support.v4.view.ViewCompat;
+import android.support.v4.widget.ViewDragHelper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.SoundEffectConstants;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+
+public class VerticalSlidingPanel extends ViewGroup {
+ private static final String TAG = VerticalSlidingPanel.class.getSimpleName();
+
+ /**
+ * Default peeking out panel height
+ */
+ private static final int DEFAULT_PANEL_HEIGHT = 68; // dp;
+
+ /**
+ * Default height of the shadow above the peeking out panel
+ */
+ private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp;
+
+ /**
+ * If no fade color is given by default it will fade to 80% gray.
+ */
+ private static final int DEFAULT_FADE_COLOR = 0x99000000;
+
+ /**
+ * Default Minimum velocity that will be detected as a fling
+ */
+ private static final int DEFAULT_MIN_FLING_VELOCITY = 400; // dips per second
+ /**
+ * Default is set to false because that is how it was written
+ */
+ private static final boolean DEFAULT_OVERLAY_FLAG = false;
+ /**
+ * Default attributes for layout
+ */
+ private static final int[] DEFAULT_ATTRS = new int[] {
+ android.R.attr.gravity
+ };
+
+ /**
+ * Minimum velocity that will be detected as a fling
+ */
+ private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY;
+
+ /**
+ * The fade color used for the panel covered by the slider. 0 = no fading.
+ */
+ private int mCoveredFadeColor = DEFAULT_FADE_COLOR;
+
+ /**
+ * Default parallax length of the main view
+ */
+ private static final int DEFAULT_PARALLAX_OFFSET = 0;
+
+ /**
+ * The paint used to dim the main layout when sliding
+ */
+ private final Paint mCoveredFadePaint = new Paint();
+
+ /**
+ * Drawable used to draw the shadow between panes.
+ */
+ private final Drawable mShadowDrawable;
+
+ /**
+ * The size of the overhang in pixels.
+ */
+ private int mPanelHeight = -1;
+
+ /**
+ * The size of the shadow in pixels.
+ */
+ private int mShadowHeight = -1;
+
+ /**
+ * Parallax offset
+ */
+ private int mParallaxOffset = -1;
+
+ /**
+ * True if the collapsed panel should be dragged up.
+ */
+ private boolean mIsSlidingUp;
+
+ /**
+ * True if a panel can slide with the current measurements
+ */
+ private boolean mCanSlide;
+
+ /**
+ * Panel overlays the windows instead of putting it underneath it.
+ */
+ private boolean mOverlayContent = DEFAULT_OVERLAY_FLAG;
+
+ /**
+ * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
+ * used for dragging.
+ */
+ private View mDragView;
+
+ /**
+ * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be
+ * used for dragging.
+ */
+ private int mDragViewResId = -1;
+
+ /**
+ * The child view that can slide, if any.
+ */
+ private View mSlideableView;
+
+ /**
+ * The main view
+ */
+ private View mMainView;
+
+ /**
+ * Current state of the slideable view.
+ */
+ private enum SlideState {
+ EXPANDED,
+ COLLAPSED,
+ ANCHORED
+ }
+ private SlideState mSlideState = SlideState.COLLAPSED;
+
+ /**
+ * How far the panel is offset from its expanded position.
+ * range [0, 1] where 0 = expanded, 1 = collapsed.
+ */
+ private float mSlideOffset;
+
+ /**
+ * How far in pixels the slideable panel may move.
+ */
+ private int mSlideRange;
+
+ /**
+ * A panel view is locked into internal scrolling or another condition that
+ * is preventing a drag.
+ */
+ private boolean mIsUnableToDrag;
+
+ /**
+ * Flag indicating that sliding feature is enabled\disabled
+ */
+ private boolean mIsSlidingEnabled;
+
+ /**
+ * Flag indicating if a drag view can have its own touch events. If set
+ * to true, a drag view can scroll horizontally and have its own click listener.
+ *
+ * Default is set to false.
+ */
+ private boolean mIsUsingDragViewTouchEvents;
+
+ /**
+ * Threshold to tell if there was a scroll touch event.
+ */
+ private final int mScrollTouchSlop;
+
+ private float mInitialMotionX;
+ private float mInitialMotionY;
+ private float mAnchorPoint = 0.f;
+ private TranslateAnimation mAnimation;
+
+ private PanelSlideListener mPanelSlideListener;
+
+ private final ViewDragHelper mDragHelper;
+
+ /**
+ * Stores whether or not the pane was expanded the last time it was slideable.
+ * If expand/collapse operations are invoked this state is modified. Used by
+ * instance state save/restore.
+ */
+ private boolean mFirstLayout = true;
+
+ private final Rect mTmpRect = new Rect();
+
+ /**
+ * Listener for monitoring events about sliding panes.
+ */
+ public interface PanelSlideListener {
+ /**
+ * Called when a sliding pane's position changes.
+ * @param panel The child view that was moved
+ * @param slideOffset The new offset of this sliding pane within its range, from 0-1
+ */
+ public void onPanelSlide(View panel, float slideOffset);
+ /**
+ * Called when a sliding pane becomes slid completely collapsed. The pane may or may not
+ * be interactive at this point depending on if it's shown or hidden
+ * @param panel The child view that was slid to an collapsed position, revealing other panes
+ */
+ public void onPanelCollapsed(View panel);
+
+ /**
+ * Called when a sliding pane becomes slid completely expanded. The pane is now guaranteed
+ * to be interactive. It may now obscure other views in the layout.
+ * @param panel The child view that was slid to a expanded position
+ */
+ public void onPanelExpanded(View panel);
+
+ public void onPanelAnchored(View panel);
+
+ public void onPanelShown(View panel);
+ }
+
+ /**
+ * No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset
+ * of the listener methods you can extend this instead of implement the full interface.
+ */
+ public static class SimplePanelSlideListener implements PanelSlideListener {
+ @Override
+ public void onPanelSlide(View panel, float slideOffset) {
+ }
+ @Override
+ public void onPanelCollapsed(View panel) {
+ }
+ @Override
+ public void onPanelExpanded(View panel) {
+ }
+ @Override
+ public void onPanelAnchored(View panel) {
+ }
+ @Override
+ public void onPanelShown(View panel) {
+ }
+ }
+
+ public VerticalSlidingPanel(Context context) {
+ this(context, null);
+ }
+
+ public VerticalSlidingPanel(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public VerticalSlidingPanel(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ if (attrs != null) {
+ TypedArray defAttrs = context.obtainStyledAttributes(attrs, DEFAULT_ATTRS);
+
+ if (defAttrs != null) {
+ int gravity = defAttrs.getInt(0, Gravity.NO_GRAVITY);
+ if (gravity != Gravity.TOP && gravity != Gravity.BOTTOM) {
+ throw new IllegalArgumentException("gravity must be set to either top or bottom");
+ }
+ mIsSlidingUp = gravity == Gravity.BOTTOM;
+ }
+
+ defAttrs.recycle();
+
+ TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.VerticalSlidingPanel);
+
+ if (ta != null) {
+ mPanelHeight = ta.getDimensionPixelSize(R.styleable.VerticalSlidingPanel_panelHeight, -1);
+ mShadowHeight = ta.getDimensionPixelSize(R.styleable.VerticalSlidingPanel_shadowHeight, -1);
+ mParallaxOffset = ta.getDimensionPixelSize(R.styleable.VerticalSlidingPanel_parallaxOffset, -1);
+
+ mMinFlingVelocity = ta.getInt(R.styleable.VerticalSlidingPanel_flingVelocity, DEFAULT_MIN_FLING_VELOCITY);
+ mCoveredFadeColor = ta.getColor(R.styleable.VerticalSlidingPanel_fadeColor, DEFAULT_FADE_COLOR);
+
+ mDragViewResId = ta.getResourceId(R.styleable.VerticalSlidingPanel_dragView, -1);
+
+ mOverlayContent = ta.getBoolean(R.styleable.VerticalSlidingPanel_overlay,DEFAULT_OVERLAY_FLAG);
+ }
+
+ ta.recycle();
+ }
+
+ final float density = context.getResources().getDisplayMetrics().density;
+ if (mPanelHeight == -1) {
+ mPanelHeight = (int) (DEFAULT_PANEL_HEIGHT * density + 0.5f);
+ }
+ if (mShadowHeight == -1) {
+ mShadowHeight = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f);
+ }
+ if (mParallaxOffset == -1) {
+ mParallaxOffset = (int) (DEFAULT_PARALLAX_OFFSET * density);
+ }
+ // If the shadow height is zero, don't show the shadow
+ if (mShadowHeight > 0) {
+ if (mIsSlidingUp) {
+ mShadowDrawable = getResources().getDrawable(R.drawable.above_shadow,
+ context.getTheme());
+ } else {
+ mShadowDrawable = getResources().getDrawable(R.drawable.below_shadow,
+ context.getTheme());
+ }
+
+ } else {
+ mShadowDrawable = null;
+ }
+
+ setWillNotDraw(false);
+
+ mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback());
+ mDragHelper.setMinVelocity(mMinFlingVelocity * density);
+
+ mCanSlide = true;
+ mIsSlidingEnabled = true;
+
+ ViewConfiguration vc = ViewConfiguration.get(context);
+ mScrollTouchSlop = vc.getScaledTouchSlop();
+ }
+
+ /**
+ * Set the Drag View after the view is inflated
+ */
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ if (mDragViewResId != -1) {
+ mDragView = findViewById(mDragViewResId);
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (mPanelSlideListener != null && visibility == VISIBLE) {
+ mPanelSlideListener.onPanelShown(changedView);
+ }
+ }
+
+ /**
+ * Set the color used to fade the pane covered by the sliding pane out when the pane
+ * will become fully covered in the expanded state.
+ *
+ * @param color An ARGB-packed color value
+ */
+ public void setCoveredFadeColor(int color) {
+ mCoveredFadeColor = color;
+ invalidate();
+ }
+
+ /**
+ * @return The ARGB-packed color value used to fade the fixed pane
+ */
+ public int getCoveredFadeColor() {
+ return mCoveredFadeColor;
+ }
+
+ /**
+ * Set sliding enabled flag
+ * @param enabled flag value
+ */
+ public void setSlidingEnabled(boolean enabled) {
+ mIsSlidingEnabled = enabled;
+ }
+
+ /**
+ * Set the collapsed panel height in pixels
+ *
+ * @param val A height in pixels
+ */
+ public void setPanelHeight(int val) {
+ mPanelHeight = val;
+ requestLayout();
+ }
+
+ /**
+ * @return The current collapsed panel height
+ */
+ public int getPanelHeight() {
+ return mPanelHeight;
+ }
+
+ /**
+ * @return The current parallax offset
+ */
+ public int getCurrentParallaxOffset() {
+ int offset = (int)(mParallaxOffset * (1 - mSlideOffset));
+ return mIsSlidingUp ? -offset : offset;
+ }
+
+ /**
+ * Sets the panel slide listener
+ * @param listener
+ */
+ public void setPanelSlideListener(PanelSlideListener listener) {
+ mPanelSlideListener = listener;
+ }
+
+ /**
+ * Set the draggable view portion. Use to null, to allow the whole panel to be draggable
+ *
+ * @param dragView A view that will be used to drag the panel.
+ */
+ public void setDragView(View dragView) {
+ mDragView = dragView;
+ }
+
+ /**
+ * Set an anchor point where the panel can stop during sliding
+ *
+ * @param anchorPoint A value between 0 and 1, determining the position of the anchor point
+ * starting from the top of the layout.
+ */
+ public void setAnchorPoint(float anchorPoint) {
+ if (anchorPoint > 0 && anchorPoint < 1)
+ mAnchorPoint = anchorPoint;
+ }
+
+ /**
+ * Sets whether or not the panel overlays the content
+ * @param overlayed
+ */
+ public void setOverlayed(boolean overlayed) {
+ mOverlayContent = overlayed;
+ }
+
+ /**
+ * Check if the panel is set as an overlay.
+ */
+ public boolean isOverlayed() {
+ return mOverlayContent;
+ }
+
+ void dispatchOnPanelSlide(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelSlide(panel, mSlideOffset);
+ }
+ }
+
+ void dispatchOnPanelExpanded(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelExpanded(panel);
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ void dispatchOnPanelCollapsed(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelCollapsed(panel);
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ void dispatchOnPanelAnchored(View panel) {
+ if (mPanelSlideListener != null) {
+ mPanelSlideListener.onPanelAnchored(panel);
+ }
+ sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
+ }
+
+ void updateObscuredViewVisibility() {
+ if (getChildCount() == 0) {
+ return;
+ }
+ final int leftBound = getPaddingLeft();
+ final int rightBound = getWidth() - getPaddingRight();
+ final int topBound = getPaddingTop();
+ final int bottomBound = getHeight() - getPaddingBottom();
+ final int left;
+ final int right;
+ final int top;
+ final int bottom;
+ if (mSlideableView != null && hasOpaqueBackground(mSlideableView)) {
+ left = mSlideableView.getLeft();
+ right = mSlideableView.getRight();
+ top = mSlideableView.getTop();
+ bottom = mSlideableView.getBottom();
+ } else {
+ left = right = top = bottom = 0;
+ }
+ View child = getChildAt(0);
+ final int clampedChildLeft = Math.max(leftBound, child.getLeft());
+ final int clampedChildTop = Math.max(topBound, child.getTop());
+ final int clampedChildRight = Math.min(rightBound, child.getRight());
+ final int clampedChildBottom = Math.min(bottomBound, child.getBottom());
+ final int vis;
+ if (clampedChildLeft >= left && clampedChildTop >= top &&
+ clampedChildRight <= right && clampedChildBottom <= bottom) {
+ vis = INVISIBLE;
+ } else {
+ vis = VISIBLE;
+ }
+ child.setVisibility(vis);
+ }
+
+ void setAllChildrenVisible() {
+ for (int i = 0, childCount = getChildCount(); i < childCount; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == INVISIBLE) {
+ child.setVisibility(VISIBLE);
+ }
+ }
+ }
+
+ private static boolean hasOpaqueBackground(View v) {
+ final Drawable bg = v.getBackground();
+ if (bg != null) {
+ return bg.getOpacity() == PixelFormat.OPAQUE;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mFirstLayout = true;
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mFirstLayout = true;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ if (widthMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException("Width must have an exact value or MATCH_PARENT");
+ } else if (heightMode != MeasureSpec.EXACTLY) {
+ throw new IllegalStateException("Height must have an exact value or MATCH_PARENT");
+ }
+
+ int layoutHeight = heightSize - getPaddingTop() - getPaddingBottom();
+ int panelHeight = mPanelHeight;
+
+ final int childCount = getChildCount();
+
+ if (childCount > 2) {
+ Log.e(TAG, "onMeasure: More than two child views are not supported.");
+ } else if (getChildAt(1) != null && getChildAt(1).getVisibility() == GONE) {
+ panelHeight = 0;
+ }
+
+ // We'll find the current one below.
+ mSlideableView = null;
+ mCanSlide = false;
+
+ // First pass. Measure based on child LayoutParams width/height.
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+
+ int height = layoutHeight;
+ if (child.getVisibility() == GONE) {
+ lp.dimWhenOffset = false;
+ continue;
+ }
+
+ if (i == 1) {
+ lp.slideable = true;
+ lp.dimWhenOffset = true;
+ mSlideableView = child;
+ mCanSlide = true;
+ } else {
+ if (!mOverlayContent) {
+ height -= panelHeight;
+ }
+ mMainView = child;
+ }
+
+ int childWidthSpec;
+ if (lp.width == LayoutParams.WRAP_CONTENT) {
+ childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
+ } else if (lp.width == LayoutParams.MATCH_PARENT) {
+ childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
+ } else {
+ childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
+ }
+
+ int childHeightSpec;
+ if (lp.height == LayoutParams.WRAP_CONTENT) {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
+ } else if (lp.height == LayoutParams.MATCH_PARENT) {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+ } else {
+ childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
+ }
+
+ child.measure(childWidthSpec, childHeightSpec);
+ }
+
+ setMeasuredDimension(widthSize, heightSize);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ final int paddingLeft = getPaddingLeft();
+ final int paddingTop = getPaddingTop();
+ final int slidingTop = getSlidingTop();
+
+ final int childCount = getChildCount();
+
+ if (mFirstLayout) {
+ switch (mSlideState) {
+ case EXPANDED:
+ mSlideOffset = mCanSlide ? 0.f : 1.f;
+ break;
+ case ANCHORED:
+ mSlideOffset = mCanSlide ? mAnchorPoint : 1.f;
+ break;
+ default:
+ mSlideOffset = 1.f;
+ break;
+ }
+ }
+
+ for (int i = 0; i < childCount; i++) {
+ final View child = getChildAt(i);
+
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ final int childHeight = child.getMeasuredHeight();
+
+ if (lp.slideable) {
+ mSlideRange = childHeight - mPanelHeight;
+ }
+
+ int childTop;
+ if (mIsSlidingUp) {
+ childTop = lp.slideable ? slidingTop + (int) (mSlideRange * mSlideOffset) : paddingTop;
+ } else {
+ childTop = lp.slideable ? slidingTop - (int) (mSlideRange * mSlideOffset) : paddingTop;
+ if (!lp.slideable && !mOverlayContent) {
+ childTop += mPanelHeight;
+ }
+ }
+ final int childBottom = childTop + childHeight;
+ final int childLeft = paddingLeft;
+ final int childRight = childLeft + child.getMeasuredWidth();
+
+ child.layout(childLeft, childTop, childRight, childBottom);
+ }
+
+ if (mFirstLayout) {
+ updateObscuredViewVisibility();
+ }
+
+ mFirstLayout = false;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Recalculate sliding panes and their details
+ if (h != oldh) {
+ mFirstLayout = true;
+ }
+ }
+
+ /**
+ * Set if the drag view can have its own touch events. If set
+ * to true, a drag view can scroll horizontally and have its own click listener.
+ *
+ * Default is set to false.
+ */
+ public void setEnableDragViewTouchEvents(boolean enabled) {
+ mIsUsingDragViewTouchEvents = enabled;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ final int action = MotionEventCompat.getActionMasked(ev);
+
+ if (mAnimation != null || !mCanSlide || !mIsSlidingEnabled || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) {
+ mDragHelper.cancel();
+ return super.onInterceptTouchEvent(ev);
+ }
+
+ if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
+ mDragHelper.cancel();
+ return false;
+ }
+
+ final float x = ev.getX();
+ final float y = ev.getY();
+ boolean interceptTap = false;
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN: {
+ mIsUnableToDrag = false;
+ mInitialMotionX = x;
+ mInitialMotionY = y;
+ if (isDragViewUnder((int) x, (int) y) && !mIsUsingDragViewTouchEvents) {
+ interceptTap = true;
+ }
+ break;
+ }
+
+ case MotionEvent.ACTION_MOVE: {
+ final float adx = Math.abs(x - mInitialMotionX);
+ final float ady = Math.abs(y - mInitialMotionY);
+ final int dragSlop = mDragHelper.getTouchSlop();
+
+ // Handle any horizontal scrolling on the drag view.
+ if (mIsUsingDragViewTouchEvents) {
+ if (adx > mScrollTouchSlop && ady < mScrollTouchSlop) {
+ return super.onInterceptTouchEvent(ev);
+ }
+ // Intercept the touch if the drag view has any vertical scroll.
+ // onTouchEvent will determine if the view should drag vertically.
+ else if (ady > mScrollTouchSlop) {
+ interceptTap = isDragViewUnder((int) x, (int) y);
+ }
+ }
+
+ if ((ady > dragSlop && adx > ady) || !isDragViewUnder((int) x, (int) y)) {
+ mDragHelper.cancel();
+ mIsUnableToDrag = true;
+ return false;
+ }
+ break;
+ }
+ }
+
+ final boolean interceptForDrag = mDragHelper.shouldInterceptTouchEvent(ev);
+
+ return interceptForDrag;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (!mCanSlide || !mIsSlidingEnabled || mAnimation != null) {
+ return super.onTouchEvent(ev);
+ }
+
+ mDragHelper.processTouchEvent(ev);
+
+ final int action = ev.getAction();
+ boolean wantTouchEvents = true;
+
+ switch (action & MotionEventCompat.ACTION_MASK) {
+ case MotionEvent.ACTION_DOWN: {
+ final float x = ev.getX();
+ final float y = ev.getY();
+
+ //Fix to allow both SettingPanel Drag and Workspace Drag
+ if (mSlideState == SlideState.COLLAPSED) {
+ if (y < mSlideableView.getTop()) {
+ return false;
+ }
+ }
+
+ mInitialMotionX = x;
+ mInitialMotionY = y;
+ break;
+ }
+
+ case MotionEvent.ACTION_UP: {
+ final float x = ev.getX();
+ final float y = ev.getY();
+ final float dx = x - mInitialMotionX;
+ final float dy = y - mInitialMotionY;
+ final int slop = mDragHelper.getTouchSlop();
+ View dragView = mDragView != null ? mDragView : mSlideableView;
+ if (dx * dx + dy * dy < slop * slop &&
+ isDragViewUnder((int) x, (int) y)) {
+ dragView.playSoundEffect(SoundEffectConstants.CLICK);
+ if (!isExpanded() && !isAnchored()) {
+ expandPane(mAnchorPoint);
+ } else {
+ collapsePane();
+ }
+ break;
+ }
+ break;
+ }
+ }
+
+ return wantTouchEvents;
+ }
+
+ private boolean isDragViewUnder(int x, int y) {
+ View dragView = mDragView != null ? mDragView : mSlideableView;
+ if (dragView == null) return false;
+ int[] viewLocation = new int[2];
+ dragView.getLocationOnScreen(viewLocation);
+ int[] parentLocation = new int[2];
+ this.getLocationOnScreen(parentLocation);
+ int screenX = parentLocation[0] + x;
+ int screenY = parentLocation[1] + y;
+ return screenX >= viewLocation[0] && screenX < viewLocation[0] + dragView.getWidth() &&
+ screenY >= viewLocation[1] && screenY < viewLocation[1] + dragView.getHeight();
+ }
+
+ private boolean expandPane(View pane, int initialVelocity, float mSlideOffset) {
+ if (mFirstLayout || smoothSlideTo(mSlideOffset, initialVelocity)) {
+ return true;
+ }
+ return false;
+ }
+
+ private boolean collapsePane(View pane, int initialVelocity) {
+ if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) {
+ return true;
+ }
+ return false;
+ }
+
+ private int getSlidingTop() {
+ if (mSlideableView != null) {
+ return mIsSlidingUp
+ ? getMeasuredHeight() - getPaddingBottom() - mSlideableView.getMeasuredHeight()
+ : getPaddingTop();
+ }
+
+ return getMeasuredHeight() - getPaddingBottom();
+ }
+
+ /**
+ * Collapse the sliding pane if it is currently slideable. If first layout
+ * has already completed this will animate.
+ *
+ * @return true if the pane was slideable and is now collapsed/in the process of collapsing
+ */
+ public boolean collapsePane() {
+ return collapsePane(mSlideableView, 0);
+ }
+
+ /**
+ * Expand the sliding pane if it is currently slideable. If first layout
+ * has already completed this will animate.
+ *
+ * @return true if the pane was slideable and is now expanded/in the process of expading
+ */
+ public boolean expandPane() {
+ return expandPane(0);
+ }
+
+ /**
+ * Partially expand the sliding pane up to a specific offset
+ *
+ * @param mSlideOffset Value between 0 and 1, where 0 is completely expanded.
+ * @return true if the pane was slideable and is now expanded/in the process of expading
+ */
+ public boolean expandPane(float mSlideOffset) {
+ if (!isPaneVisible()) {
+ showPane();
+ }
+ return expandPane(mSlideableView, 0, mSlideOffset);
+ }
+
+ /**
+ * Check if the layout is completely expanded.
+ *
+ * @return true if sliding panels are completely expanded
+ */
+ public boolean isExpanded() {
+ return mSlideState == SlideState.EXPANDED;
+ }
+
+ /**
+ * Check if the layout is anchored in an intermediate point.
+ *
+ * @return true if sliding panels are anchored
+ */
+ public boolean isAnchored() {
+ return mSlideState == SlideState.ANCHORED;
+ }
+
+ /**
+ * Check if the content in this layout cannot fully fit side by side and therefore
+ * the content pane can be slid back and forth.
+ *
+ * @return true if content in this layout can be expanded
+ */
+ public boolean isSlideable() {
+ return mCanSlide;
+ }
+
+ public boolean isPaneVisible() {
+ if (getChildCount() < 2) {
+ return false;
+ }
+ View slidingPane = getChildAt(1);
+ return slidingPane.getVisibility() == View.VISIBLE;
+ }
+
+ public void showPane() {
+ if (getChildCount() < 2) {
+ return;
+ }
+ final View slidingPane = getChildAt(1);
+ mAnimation = new TranslateAnimation(0, 0, (mIsSlidingUp ? 1 : -1) * getPanelHeight(), 0);
+ mAnimation.setDuration(400);
+ mAnimation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ slidingPane.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ requestLayout();
+ mAnimation = null;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+ });
+ slidingPane.startAnimation(mAnimation);
+ }
+
+ public void hidePane() {
+ if (mSlideableView == null) {
+ return;
+ }
+ mAnimation = new TranslateAnimation(0, 0, 0, (mIsSlidingUp ? 1 : -1) * getPanelHeight());
+ mAnimation.setDuration(500);
+ mAnimation.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ mSlideableView.setVisibility(View.GONE);
+ requestLayout();
+ mAnimation = null;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+
+ }
+ });
+ mSlideableView.startAnimation(mAnimation);
+ }
+
+ private void onPanelDragged(int newTop) {
+ final int topBound = getSlidingTop();
+ mSlideOffset = mIsSlidingUp
+ ? (float) (newTop - topBound) / mSlideRange
+ : (float) (topBound - newTop) / mSlideRange;
+ dispatchOnPanelSlide(mSlideableView);
+
+ if (mParallaxOffset > 0) {
+ int mainViewOffset = getCurrentParallaxOffset();
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
+ mMainView.setTranslationY(mainViewOffset);
+ } else {
+ mMainView.animate().translationY(mainViewOffset);
+ }
+ }
+ }
+
+ @Override
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ final LayoutParams lp = (LayoutParams) child.getLayoutParams();
+ boolean result;
+ final int save = canvas.save(Canvas.CLIP_SAVE_FLAG);
+
+ boolean drawScrim = false;
+
+ if (mCanSlide && !lp.slideable && mSlideableView != null) {
+ // Clip against the slider; no sense drawing what will immediately be covered,
+ // Unless the panel is set to overlay content
+ if (!mOverlayContent) {
+ canvas.getClipBounds(mTmpRect);
+ if (mIsSlidingUp) {
+ mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop());
+ } else {
+ mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom());
+ }
+ canvas.clipRect(mTmpRect);
+ }
+ if (mSlideOffset < 1) {
+ drawScrim = true;
+ }
+ }
+
+ result = super.drawChild(canvas, child, drawingTime);
+ canvas.restoreToCount(save);
+
+ if (drawScrim) {
+ final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24;
+ final int imag = (int) (baseAlpha * (1 - mSlideOffset));
+ final int color = imag << 24 | (mCoveredFadeColor & 0xffffff);
+ mCoveredFadePaint.setColor(color);
+ canvas.drawRect(mTmpRect, mCoveredFadePaint);
+ }
+
+ return result;
+ }
+
+ /**
+ * Smoothly animate mDraggingPane to the target X position within its range.
+ *
+ * @param slideOffset position to animate to
+ * @param velocity initial velocity in case of fling, or 0.
+ */
+ boolean smoothSlideTo(float slideOffset, int velocity) {
+ if (!mCanSlide) {
+ // Nothing to do.
+ return false;
+ }
+
+ final int topBound = getSlidingTop();
+ int y = mIsSlidingUp
+ ? (int) (topBound + slideOffset * mSlideRange)
+ : (int) (topBound - slideOffset * mSlideRange);
+
+ if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), y)) {
+ setAllChildrenVisible();
+ ViewCompat.postInvalidateOnAnimation(this);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public void computeScroll() {
+ if (mDragHelper.continueSettling(true)) {
+ if (!mCanSlide) {
+ mDragHelper.abort();
+ return;
+ }
+
+ ViewCompat.postInvalidateOnAnimation(this);
+ }
+ }
+
+ @Override
+ public void draw(Canvas c) {
+ super.draw(c);
+
+ if (mSlideableView == null) {
+ // No need to draw a shadow if we don't have one.
+ return;
+ }
+
+ final int right = mSlideableView.getRight();
+ final int top;
+ final int bottom;
+ if (mIsSlidingUp) {
+ top = mSlideableView.getTop() - mShadowHeight;
+ bottom = mSlideableView.getTop();
+ } else {
+ top = mSlideableView.getBottom();
+ bottom = mSlideableView.getBottom() + mShadowHeight;
+ }
+ final int left = mSlideableView.getLeft();
+
+ if (mShadowDrawable != null) {
+ mShadowDrawable.setBounds(left, top, right, bottom);
+ mShadowDrawable.draw(c);
+ }
+ }
+
+ /**
+ * Tests scrollability within child views of v given a delta of dx.
+ *
+ * @param v View to test for horizontal scrollability
+ * @param checkV Whether the view v passed should itself be checked for scrollability (true),
+ * or just its children (false).
+ * @param dx Delta scrolled in pixels
+ * @param x X coordinate of the active touch point
+ * @param y Y coordinate of the active touch point
+ * @return true if child views of v can be scrolled by delta of dx.
+ */
+ protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {
+ if (v instanceof ViewGroup) {
+ final ViewGroup group = (ViewGroup) v;
+ final int scrollX = v.getScrollX();
+ final int scrollY = v.getScrollY();
+ final int count = group.getChildCount();
+ // Count backwards - let topmost views consume scroll distance first.
+ for (int i = count - 1; i >= 0; i--) {
+ final View child = group.getChildAt(i);
+ if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&
+ y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&
+ canScroll(child, true, dx, x + scrollX - child.getLeft(),
+ y + scrollY - child.getTop())) {
+ return true;
+ }
+ }
+ }
+ return checkV && ViewCompat.canScrollHorizontally(v, -dx);
+ }
+
+
+ @Override
+ protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
+ return new LayoutParams();
+ }
+
+ @Override
+ protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof MarginLayoutParams
+ ? new LayoutParams((MarginLayoutParams) p)
+ : new LayoutParams(p);
+ }
+
+ @Override
+ protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+ return p instanceof LayoutParams && super.checkLayoutParams(p);
+ }
+
+ @Override
+ public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
+ return new LayoutParams(getContext(), attrs);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ SavedState ss = new SavedState(superState);
+ ss.mSlideState = mSlideState;
+
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+ mSlideState = ss.mSlideState;
+ }
+
+ private class DragHelperCallback extends ViewDragHelper.Callback {
+
+ @Override
+ public boolean tryCaptureView(View child, int pointerId) {
+ if (mIsUnableToDrag) {
+ return false;
+ }
+
+ return ((LayoutParams) child.getLayoutParams()).slideable;
+ }
+
+ @Override
+ public void onViewDragStateChanged(int state) {
+ int anchoredTop = (int)(mAnchorPoint*mSlideRange);
+
+ if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) {
+ if (mSlideOffset == 0) {
+ if (mSlideState != SlideState.EXPANDED) {
+ updateObscuredViewVisibility();
+ dispatchOnPanelExpanded(mSlideableView);
+ mSlideState = SlideState.EXPANDED;
+ }
+ } else if (mSlideOffset == (float)anchoredTop/(float)mSlideRange) {
+ if (mSlideState != SlideState.ANCHORED) {
+ updateObscuredViewVisibility();
+ dispatchOnPanelAnchored(mSlideableView);
+ mSlideState = SlideState.ANCHORED;
+ }
+ } else if (mSlideState != SlideState.COLLAPSED) {
+ dispatchOnPanelCollapsed(mSlideableView);
+ mSlideState = SlideState.COLLAPSED;
+ }
+ }
+ }
+
+ @Override
+ public void onViewCaptured(View capturedChild, int activePointerId) {
+ // Make all child views visible in preparation for sliding things around
+ setAllChildrenVisible();
+ }
+
+ @Override
+ public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
+ onPanelDragged(top);
+ invalidate();
+ }
+
+ @Override
+ public void onViewReleased(View releasedChild, float xvel, float yvel) {
+ int top = mIsSlidingUp
+ ? getSlidingTop()
+ : getSlidingTop() - mSlideRange;
+
+ if (mAnchorPoint != 0) {
+ int anchoredTop;
+ float anchorOffset;
+ if (mIsSlidingUp) {
+ anchoredTop = (int)(mAnchorPoint*mSlideRange);
+ anchorOffset = (float)anchoredTop/(float)mSlideRange;
+ } else {
+ anchoredTop = mPanelHeight - (int)(mAnchorPoint*mSlideRange);
+ anchorOffset = (float)(mPanelHeight - anchoredTop)/(float)mSlideRange;
+ }
+
+ if (yvel > 0 || (yvel == 0 && mSlideOffset >= (1f+anchorOffset)/2)) {
+ top += mSlideRange;
+ } else if (yvel == 0 && mSlideOffset < (1f+anchorOffset)/2
+ && mSlideOffset >= anchorOffset/2) {
+ top += mSlideRange * mAnchorPoint;
+ }
+
+ } else if (yvel > 0 || (yvel == 0 && mSlideOffset > 0.5f)) {
+ top += mSlideRange;
+ }
+
+ mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top);
+ invalidate();
+ }
+
+ @Override
+ public int getViewVerticalDragRange(View child) {
+ return mSlideRange;
+ }
+
+ @Override
+ public int clampViewPositionVertical(View child, int top, int dy) {
+ final int topBound;
+ final int bottomBound;
+ if (mIsSlidingUp) {
+ topBound = getSlidingTop();
+ bottomBound = topBound + mSlideRange;
+ } else {
+ bottomBound = getPaddingTop();
+ topBound = bottomBound - mSlideRange;
+ }
+
+ return Math.min(Math.max(top, topBound), bottomBound);
+ }
+ }
+
+ public static class LayoutParams extends ViewGroup.MarginLayoutParams {
+ private static final int[] ATTRS = new int[] {
+ android.R.attr.layout_weight
+ };
+
+ /**
+ * True if this pane is the slideable pane in the layout.
+ */
+ boolean slideable;
+
+ /**
+ * True if this view should be drawn dimmed
+ * when it's been offset from its default position.
+ */
+ boolean dimWhenOffset;
+
+ Paint dimPaint;
+
+ public LayoutParams() {
+ super(MATCH_PARENT, MATCH_PARENT);
+ }
+
+ public LayoutParams(int width, int height) {
+ super(width, height);
+ }
+
+ public LayoutParams(android.view.ViewGroup.LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(MarginLayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(LayoutParams source) {
+ super(source);
+ }
+
+ public LayoutParams(Context c, AttributeSet attrs) {
+ super(c, attrs);
+
+ final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS);
+ a.recycle();
+ }
+
+ }
+
+ static class SavedState extends BaseSavedState {
+ SlideState mSlideState;
+
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ private SavedState(Parcel in) {
+ super(in);
+ try {
+ mSlideState = Enum.valueOf(SlideState.class, in.readString());
+ } catch (IllegalArgumentException e) {
+ mSlideState = SlideState.COLLAPSED;
+ }
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeString(mSlideState.toString());
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR =
+ new Parcelable.Creator<SavedState>() {
+ @Override
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ @Override
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+}
diff --git a/src/com/android/launcher3/WallpaperChangedReceiver.java b/src/com/android/launcher3/WallpaperChangedReceiver.java
index 2d5612f12..0a6a7efa5 100644
--- a/src/com/android/launcher3/WallpaperChangedReceiver.java
+++ b/src/com/android/launcher3/WallpaperChangedReceiver.java
@@ -20,10 +20,25 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import com.android.launcher3.stats.LauncherStats;
+
public class WallpaperChangedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent data) {
LauncherAppState.setApplicationContext(context.getApplicationContext());
LauncherAppState appState = LauncherAppState.getInstance();
appState.onWallpaperChanged();
+ SharedPreferences prefs = context.getSharedPreferences(LauncherAppState
+ .getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ boolean fromSelf = prefs.getBoolean(Launcher.LONGPRESS_CHANGE, false);
+ if (fromSelf) {
+ prefs.edit().putBoolean(Launcher.LONGPRESS_CHANGE, false).apply();
+ LauncherApplication.getLauncherStats().sendWallpaperChangedEvent(
+ LauncherStats.ORIGIN_TREB_LONGPRESS);
+ } else {
+ LauncherApplication.getLauncherStats().sendWallpaperChangedEvent(
+ LauncherStats.ORIGIN_CHOOSER);
+ }
+
}
}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 346055566..056bdec53 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -66,7 +66,7 @@ public class WidgetPreviewLoader {
private final IconCache mIconCache;
private final UserManagerCompat mUserManager;
private final AppWidgetManagerCompat mManager;
- private final CacheDb mDb;
+ private CacheDb mDb;
private final int mProfileBadgeMargin;
private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -83,6 +83,13 @@ public class WidgetPreviewLoader {
.getDimensionPixelSize(R.dimen.profile_badge_margin);
}
+ public void recreateWidgetPreviewDb() {
+ if (mDb != null) {
+ mDb.close();
+ }
+ mDb = new CacheDb(mContext);
+ }
+
/**
* Generates the widget preview on {@link AsyncTask#THREAD_POOL_EXECUTOR}. Must be
* called on UI thread
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 856e3b88a..4364e6b1b 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -68,6 +68,7 @@ import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.LongArrayMap;
import com.android.launcher3.util.Thunk;
import com.android.launcher3.util.WallpaperUtils;
@@ -77,6 +78,8 @@ import com.android.launcher3.widget.PendingAddWidgetInfo;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -217,6 +220,7 @@ public class Workspace extends PagedView
private boolean mWorkspaceFadeInAdjacentScreens;
WallpaperOffsetInterpolator mWallpaperOffset;
+ private boolean mScrollWallpaper;
@Thunk boolean mWallpaperIsLiveWallpaper;
@Thunk int mNumPagesForWallpaperParallax;
@Thunk float mLastSetWallpaperOffsetSteps = 0;
@@ -290,6 +294,8 @@ public class Workspace extends PagedView
}
};
+ private boolean mHideIconLabels;
+
/**
* Used to inflate the Workspace from XML.
*
@@ -312,6 +318,10 @@ public class Workspace extends PagedView
mOutlineHelper = HolographicOutlineHelper.obtain(context);
+ mHideIconLabels = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default);
+
mLauncher = (Launcher) context;
mStateTransitionAnimation = new WorkspaceStateTransitionAnimation(mLauncher, this);
final Resources res = getResources();
@@ -351,6 +361,13 @@ public class Workspace extends PagedView
}
}
+ /**
+ * @return A {@link List} of {@link Long}s representing ids of the workspace screens
+ */
+ public List<Long> getWorkspaceScreenIds() {
+ return mScreenOrder;
+ }
+
// estimate the size of a widget with spans hSpan, vSpan. return MAX_VALUE for each
// dimension if unsuccessful
public int[] estimateItemSize(ItemInfo itemInfo, boolean springLoaded) {
@@ -566,6 +583,11 @@ public class Workspace extends PagedView
mScreenOrder.add(insertIndex, screenId);
addView(newScreen, insertIndex);
+ if (getDefaultScreenId() == screenId) {
+ int defaultPage = getPageIndexForScreenId(screenId);
+ moveToScreen(defaultPage, false);
+ }
+
LauncherAccessibilityDelegate delegate =
LauncherAppState.getInstance().getAccessibilityDelegate();
if (delegate != null && delegate.isInAccessibleDrag()) {
@@ -964,6 +986,8 @@ public class Workspace extends PagedView
*/
void addInScreen(View child, long container, long screenId, int x, int y, int spanX, int spanY,
boolean insert, boolean computeXYFromRank) {
+ reloadSettings();
+
if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (getScreenWithId(screenId) == null) {
Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
@@ -994,9 +1018,10 @@ public class Workspace extends PagedView
screenId = mLauncher.getHotseat().getOrderInHotseat(x, y);
}
} else {
- // Show folder title if not in the hotseat
if (child instanceof FolderIcon) {
- ((FolderIcon) child).setTextVisible(true);
+ ((FolderIcon) child).setTextVisible(!mHideIconLabels);
+ } else if (child instanceof BubbleTextView) {
+ ((BubbleTextView) child).setTextVisibility(!mHideIconLabels);
}
layout = getScreenWithId(screenId);
child.setOnKeyListener(new IconKeyEventListener());
@@ -1362,6 +1387,7 @@ public class Workspace extends PagedView
// Don't use all the wallpaper for parallax until you have at least this many pages
private final int MIN_PARALLAX_PAGE_SPAN = 3;
int mNumScreens;
+ boolean mCompletedInitialOffset;
public WallpaperOffsetInterpolator() {
mChoreographer = Choreographer.getInstance();
@@ -1376,7 +1402,8 @@ public class Workspace extends PagedView
private void updateOffset(boolean force) {
if (mWaitingForUpdate || force) {
mWaitingForUpdate = false;
- if (computeScrollOffset() && mWindowToken != null) {
+ if ((!mCompletedInitialOffset || computeScrollOffset()) && mWindowToken != null) {
+ mCompletedInitialOffset = true;
try {
mWallpaperManager.setWallpaperOffsets(mWindowToken,
mWallpaperOffset.getCurrX(), 0.5f);
@@ -1533,7 +1560,12 @@ public class Workspace extends PagedView
@Override
public void computeScroll() {
super.computeScroll();
- mWallpaperOffset.syncWithScroll();
+
+ if (mScrollWallpaper) mWallpaperOffset.syncWithScroll();
+
+ if (isInOverviewMode() && !isReordering(true)) {
+ mLauncher.updateDefaultScreenButton();
+ }
}
@Override
@@ -1702,6 +1734,8 @@ public class Workspace extends PagedView
}
}
+ setScrollingWallpaper();
+
// Update wallpaper dimensions if they were changed since last onResume
// (we also always set the wallpaper dimensions in the constructor)
if (LauncherAppState.getInstance().hasWallpaperChangedSinceLastCheck()) {
@@ -1715,7 +1749,8 @@ public class Workspace extends PagedView
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
- if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
+ if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()
+ && mScrollWallpaper) {
mWallpaperOffset.syncWithScroll();
mWallpaperOffset.jumpToFinal();
}
@@ -1970,12 +2005,33 @@ public class Workspace extends PagedView
// Re-enable auto layout transitions for page deletion.
enableLayoutTransitions();
+
+ // Show the default screen button
+ mLauncher.updateDefaultScreenButton();
}
public boolean isInOverviewMode() {
return mState == State.OVERVIEW;
}
+ public boolean enterOverviewMode() {
+ if (mTouchState != TOUCH_STATE_REST) {
+ return false;
+ }
+ enableOverviewMode();
+ return true;
+ }
+
+ public void exitOverviewMode() {
+ ((OverviewPanel) mLauncher.getOverviewPanel()).collapsePane();
+ reloadSettings();
+ }
+
+ private void enableOverviewMode() {
+ reloadSettings();
+ }
+
+
int getOverviewModeTranslationY() {
DeviceProfile grid = mLauncher.getDeviceProfile();
Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources()));
@@ -3523,6 +3579,7 @@ public class Workspace extends PagedView
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
view = FolderIcon.fromXml(R.layout.folder_icon, mLauncher, cellLayout,
(FolderInfo) info, mIconCache);
+ ((FolderIcon) view).setTextVisible(!mHideIconLabels);
break;
default:
throw new IllegalStateException("Unknown item type: " + info.itemType);
@@ -3761,6 +3818,11 @@ public class Workspace extends PagedView
return;
}
+ // Drop is finished, so we should use our actual cell coordinates now.
+ if (mDragInfo != null) {
+ ((CellLayout.LayoutParams) mDragInfo.cell.getLayoutParams()).useTmpCoords = false;
+ }
+
boolean beingCalledAfterUninstall = mDeferredAction != null;
if (success && !(beingCalledAfterUninstall && !mUninstallSuccessful)) {
@@ -4420,7 +4482,7 @@ public class Workspace extends PagedView
}
void moveToDefaultScreen(boolean animate) {
- moveToScreen(mDefaultPage, animate);
+ moveToScreen(getPageIndexForScreenId(getDefaultScreenId()), animate);
}
void moveToCustomContentScreen(boolean animate) {
@@ -4482,6 +4544,34 @@ public class Workspace extends PagedView
sourceData.putInt(Stats.SOURCE_EXTRA_CONTAINER_PAGE, getCurrentPage());
}
+ private void reloadSettings() {
+ mHideIconLabels = SettingsProvider.getBoolean(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default);
+
+ setScrollingWallpaper();
+ }
+
+ /**
+ * Gets the preference for whether to apply scrolling wallpaper effect or not and applies the
+ * preference.
+ */
+ private void setScrollingWallpaper() {
+ mScrollWallpaper = SettingsProvider.getBoolean(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL,
+ R.bool.preferences_interface_homescreen_scrolling_wallpaper_scroll_default);
+ if (!mScrollWallpaper) {
+ if (mWindowToken != null) mWallpaperManager.setWallpaperOffsets(mWindowToken, 0f, 0.5f);
+ } else {
+ mWallpaperOffset.syncWithScroll();
+ }
+ }
+
+ private long getDefaultScreenId() {
+ return SettingsProvider.getLongCustomDefault(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID, 1);
+ }
+
/**
* Used as a workaround to ensure that the AppWidgetService receives the
* PACKAGE_ADDED broadcast before updating widgets.
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 88c6acada..8e4491180 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -35,6 +35,7 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
@@ -47,6 +48,7 @@ import com.android.launcher3.LauncherTransitionable;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
+import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.Thunk;
@@ -55,74 +57,6 @@ import java.nio.charset.CharsetEncoder;
import java.util.ArrayList;
import java.util.List;
-
-
-/**
- * A merge algorithm that merges every section indiscriminately.
- */
-final class FullMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
-
- @Override
- public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
- AlphabeticalAppsList.SectionInfo withSection,
- int sectionAppCount, int numAppsPerRow, int mergeCount) {
- // Don't merge the predicted apps
- if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
- return false;
- }
- // Otherwise, merge every other section
- return true;
- }
-}
-
-/**
- * The logic we use to merge multiple sections. We only merge sections when their final row
- * contains less than a certain number of icons, and stop at a specified max number of merges.
- * In addition, we will try and not merge sections that identify apps from different scripts.
- */
-final class SimpleSectionMergeAlgorithm implements AlphabeticalAppsList.MergeAlgorithm {
-
- private int mMinAppsPerRow;
- private int mMinRowsInMergedSection;
- private int mMaxAllowableMerges;
- private CharsetEncoder mAsciiEncoder;
-
- public SimpleSectionMergeAlgorithm(int minAppsPerRow, int minRowsInMergedSection, int maxNumMerges) {
- mMinAppsPerRow = minAppsPerRow;
- mMinRowsInMergedSection = minRowsInMergedSection;
- mMaxAllowableMerges = maxNumMerges;
- mAsciiEncoder = Charset.forName("US-ASCII").newEncoder();
- }
-
- @Override
- public boolean continueMerging(AlphabeticalAppsList.SectionInfo section,
- AlphabeticalAppsList.SectionInfo withSection,
- int sectionAppCount, int numAppsPerRow, int mergeCount) {
- // Don't merge the predicted apps
- if (section.firstAppItem.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
- return false;
- }
-
- // Continue merging if the number of hanging apps on the final row is less than some
- // fixed number (ragged), the merged rows has yet to exceed some minimum row count,
- // and while the number of merged sections is less than some fixed number of merges
- int rows = sectionAppCount / numAppsPerRow;
- int cols = sectionAppCount % numAppsPerRow;
-
- // Ensure that we do not merge across scripts, currently we only allow for english and
- // native scripts so we can test if both can just be ascii encoded
- boolean isCrossScript = false;
- if (section.firstAppItem != null && withSection.firstAppItem != null) {
- isCrossScript = mAsciiEncoder.canEncode(section.firstAppItem.sectionName) !=
- mAsciiEncoder.canEncode(withSection.firstAppItem.sectionName);
- }
- return (0 < cols && cols < mMinAppsPerRow) &&
- rows < mMinRowsInMergedSection &&
- mergeCount < mMaxAllowableMerges &&
- !isCrossScript;
- }
-}
-
/**
* The all apps view container.
*/
@@ -130,8 +64,11 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
LauncherTransitionable, View.OnTouchListener, View.OnLongClickListener,
AllAppsSearchBarController.Callbacks {
- private static final int MIN_ROWS_IN_MERGED_SECTION_PHONE = 3;
- private static final int MAX_NUM_MERGES_PHONE = 2;
+ public static final int SECTION_STRATEGY_GRID = 1;
+ public static final int SECTION_STRATEGY_RAGGED = 2;
+
+ public static final int GRID_THEME_LIGHT = 1;
+ public static final int GRID_THEME_DARK = 2;
@Thunk Launcher mLauncher;
@Thunk AlphabeticalAppsList mApps;
@@ -148,6 +85,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
private View mSearchBarView;
private SpannableStringBuilder mSearchQueryBuilder = null;
+ private int mSectionStrategy = SECTION_STRATEGY_RAGGED;
+ private int mGridTheme = GRID_THEME_DARK;
+ private int mLastGridTheme = -1;
+
private int mSectionNamesMargin;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
@@ -157,6 +98,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// This coordinate is relative to its parent
private final Point mIconLastTouchPos = new Point();
+ private boolean mReloadDrawer = false;
+
private View.OnClickListener mSearchClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
@@ -178,9 +121,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
Resources res = context.getResources();
mLauncher = (Launcher) context;
- mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
mApps = new AlphabeticalAppsList(context);
- mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this);
+ mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher,
+ this);
mApps.setAdapter(mAdapter);
mLayoutManager = mAdapter.getLayoutManager();
mItemDecoration = mAdapter.getItemDecoration();
@@ -191,11 +134,33 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
Selection.setSelection(mSearchQueryBuilder, 0);
}
+ public int getNumPredictedAppsPerRow() {
+ return mNumPredictedAppsPerRow;
+ }
+
+ /**
+ * Sets the current set of predicted apps by component.
+ * Only usable when custom predicted apps are disabled.
+ */
+ public void setPredictedAppComponents(List<ComponentKey> apps) {
+ mApps.setPredictedAppComponents(apps);
+ updateScrubber();
+ }
+
/**
- * Sets the current set of predicted apps.
+ * Sets the current set of predicted apps by info.
+ * Only usable when custom predicated apps are enabled.
*/
- public void setPredictedApps(List<ComponentKey> apps) {
+ public void setPredictedApps(List<AppInfo> apps) {
mApps.setPredictedApps(apps);
+ updateScrubber();
+ }
+
+ /**
+ * Set whether the predicted apps row will have a customized selection of apps.
+ */
+ public void setCustomPredictedAppsEnabled(boolean enabled) {
+ mApps.mCustomPredictedAppsEnabled = enabled;
}
/**
@@ -203,6 +168,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
*/
public void setApps(List<AppInfo> apps) {
mApps.setApps(apps);
+ updateScrubber();
}
/**
@@ -210,6 +176,17 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
*/
public void addApps(List<AppInfo> apps) {
mApps.addApps(apps);
+ updateScrubber();
+ }
+
+ /**
+ * Reloads the existing apps in the list
+ */
+ public void onReloadAppDrawer() {
+ mReloadDrawer = true;
+ List<AppInfo> apps = mApps.getApps();
+ updateApps(apps);
+ requestLayout();
}
/**
@@ -217,6 +194,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
*/
public void updateApps(List<AppInfo> apps) {
mApps.updateApps(apps);
+ updateScrubber();
}
/**
@@ -224,12 +202,55 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
*/
public void removeApps(List<AppInfo> apps) {
mApps.removeApps(apps);
+ updateScrubber();
+ }
+
+ private void updateScrubber() {
+ if (useScroller() && useScrubber()) {
+ mScrubber.updateSections();
+ }
+ }
+
+ public List<AppInfo> getApps() {
+ return mApps.getApps();
+ }
+
+ public int getSectionStrategy() {
+ return mSectionStrategy;
+ }
+
+ private void updateSectionStrategy() {
+ Context context = getContext();
+ Resources res = context.getResources();
+ boolean useCompactGrid = SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_DRAWER_STYLE_USE_COMPACT,
+ R.bool.preferences_interface_drawer_compact_default);
+ mSectionStrategy = useCompactGrid ? SECTION_STRATEGY_GRID : SECTION_STRATEGY_RAGGED;
+ mSectionNamesMargin = useCompactGrid ?
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin) :
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin_with_sections);
+ mAdapter.setSectionStrategy(mSectionStrategy);
+ mAppsRecyclerView.setSectionStrategy(mSectionStrategy);
+ }
+
+ private void updateGridTheme() {
+ Context context = getContext();
+ boolean useDarkColor= SettingsProvider.getBoolean(context,
+ SettingsProvider.SETTINGS_UI_DRAWER_DARK,
+ R.bool.preferences_interface_drawer_dark_default);
+ mGridTheme = useDarkColor ? GRID_THEME_DARK : GRID_THEME_LIGHT;
+ mAdapter.setGridTheme(mGridTheme);
+ updateBackgroundAndPaddings(true);
}
/**
* Sets the search bar that shows above the a-z list.
*/
public void setSearchBarController(AllAppsSearchBarController searchController) {
+ if (searchController == null) {
+ mSearchBarController = null;
+ return;
+ }
if (mSearchBarController != null) {
throw new RuntimeException("Expected search bar controller to only be set once");
}
@@ -241,11 +262,16 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mSearchBarContainerView.addView(searchBarView);
mSearchBarContainerView.setVisibility(View.VISIBLE);
mSearchBarView = searchBarView;
- setHasSearchBar();
+ setHasSearchBar(true);
updateBackgroundAndPaddings();
}
+ public void setSearchBarContainerViewVisibility(int visibility) {
+ mSearchBarContainerView.setVisibility(visibility);
+ updateBackgroundAndPaddings();
+ }
+
/**
* Scrolls this list view to the top.
*/
@@ -322,7 +348,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
if (mItemDecoration != null) {
mAppsRecyclerView.addItemDecoration(mItemDecoration);
}
-
+ setScroller();
+ updateGridTheme();
+ updateSectionStrategy();
updateBackgroundAndPaddings();
}
@@ -337,26 +365,27 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
int availableWidth = !mContentBounds.isEmpty() ? mContentBounds.width() :
MeasureSpec.getSize(widthMeasureSpec);
DeviceProfile grid = mLauncher.getDeviceProfile();
- grid.updateAppsViewNumCols(getResources(), availableWidth);
+ grid.updateAppsViewNumCols(getResources(), availableWidth,
+ mSectionStrategy);
if (mNumAppsPerRow != grid.allAppsNumCols ||
mNumPredictedAppsPerRow != grid.allAppsNumPredictiveCols) {
mNumAppsPerRow = grid.allAppsNumCols;
mNumPredictedAppsPerRow = grid.allAppsNumPredictiveCols;
- // If there is a start margin to draw section names, determine how we are going to merge
- // app sections
- boolean mergeSectionsFully = mSectionNamesMargin == 0 || !grid.isPhone;
- AlphabeticalAppsList.MergeAlgorithm mergeAlgorithm = mergeSectionsFully ?
- new FullMergeAlgorithm() :
- new SimpleSectionMergeAlgorithm((int) Math.ceil(mNumAppsPerRow / 2f),
- MIN_ROWS_IN_MERGED_SECTION_PHONE, MAX_NUM_MERGES_PHONE);
-
mAppsRecyclerView.setNumAppsPerRow(grid, mNumAppsPerRow);
mAdapter.setNumAppsPerRow(mNumAppsPerRow);
- mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeAlgorithm);
+
+ boolean mergeSections = mSectionStrategy == SECTION_STRATEGY_GRID;
+ mApps.setNumAppsPerRow(mNumAppsPerRow, mNumPredictedAppsPerRow, mergeSections);
+
+ mLauncher.getRemoteFolderManager().onMeasureDrawer(mNumPredictedAppsPerRow);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mReloadDrawer) {
+ updateBackgroundAndPaddings(true);
+ mReloadDrawer = false;
+ }
}
/**
@@ -369,8 +398,10 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
boolean isRtl = Utilities.isRtl(getResources());
// TODO: Use quantum_panel instead of quantum_panel_shape
+ int bgRes = mGridTheme == GRID_THEME_DARK ? R.drawable.quantum_panel_shape_dark :
+ R.drawable.quantum_panel_shape;
InsetDrawable background = new InsetDrawable(
- getResources().getDrawable(R.drawable.quantum_panel_shape), padding.left, 0,
+ getResources().getDrawable(bgRes), padding.left, 0,
padding.right, 0);
Rect bgPadding = new Rect();
background.getPadding(bgPadding);
@@ -389,12 +420,23 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
// names)
int startInset = Math.max(mSectionNamesMargin, mAppsRecyclerView.getMaxScrollbarWidth());
int topBottomPadding = mRecyclerViewTopBottomPadding;
+ final boolean useScrollerScrubber = useScroller() && useScrubber();
if (isRtl) {
mAppsRecyclerView.setPadding(padding.left + mAppsRecyclerView.getMaxScrollbarWidth(),
- topBottomPadding, padding.right + startInset, topBottomPadding);
+ topBottomPadding, padding.right + startInset, useScrollerScrubber ?
+ mScrubberHeight + topBottomPadding : topBottomPadding);
+ if (useScrollerScrubber) {
+ mScrubberContainerView.setPadding(padding.left +
+ mAppsRecyclerView.getMaxScrollbarWidth(), 0, padding.right, 0);
+ }
} else {
mAppsRecyclerView.setPadding(padding.left + startInset, topBottomPadding,
- padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), topBottomPadding);
+ padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), useScrollerScrubber ?
+ mScrubberHeight + topBottomPadding : topBottomPadding);
+ if (useScrollerScrubber) {
+ mScrubberContainerView.setPadding(padding.left, 0,
+ padding.right + mAppsRecyclerView.getMaxScrollbarWidth(), 0);
+ }
}
// Inset the search bar to fit its bounds above the container
@@ -407,7 +449,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
mSearchBarContainerView.getLayoutParams();
lp.leftMargin = searchBarBounds.left - backgroundPadding.left;
lp.topMargin = searchBarBounds.top - backgroundPadding.top;
- lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right) - backgroundPadding.right;
+ lp.rightMargin = (getMeasuredWidth() - searchBarBounds.right)
+ - backgroundPadding.right;
mSearchBarContainerView.requestLayout();
}
}
@@ -416,7 +459,8 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
public boolean dispatchKeyEvent(KeyEvent event) {
// Determine if the key event was actual text, if so, focus the search bar and then dispatch
// the key normally so that it can process this key event
- if (!mSearchBarController.isSearchFieldFocused() &&
+ if (mSearchBarController != null &&
+ !mSearchBarController.isSearchFieldFocused() &&
event.getAction() == KeyEvent.ACTION_DOWN) {
final int unicodeChar = event.getUnicodeChar();
final boolean isKeyNotWhitespace = unicodeChar > 0 &&
@@ -556,7 +600,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
if (toWorkspace) {
// Reset the search bar and base recycler view after transitioning home
- mSearchBarController.reset();
+ if (hasSearchBar()) {
+ mSearchBarController.reset();
+ }
mAppsRecyclerView.reset();
}
}
@@ -614,6 +660,12 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
public void onSearchResult(String query, ArrayList<ComponentKey> apps) {
if (apps != null) {
mApps.setOrderedFilter(apps);
+ if (mGridTheme != GRID_THEME_LIGHT) {
+ mLastGridTheme = mGridTheme;
+ mGridTheme = GRID_THEME_LIGHT;
+ updateBackgroundAndPaddings(true);
+ mAdapter.setGridTheme(mGridTheme);
+ }
mAdapter.setLastSearchQuery(query);
mAppsRecyclerView.onSearchResultsChanged();
}
@@ -623,10 +675,20 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc
public void clearSearchResult() {
mApps.setOrderedFilter(null);
mAppsRecyclerView.onSearchResultsChanged();
-
+ if (mLastGridTheme != -1 && mLastGridTheme != GRID_THEME_LIGHT) {
+ mGridTheme = mLastGridTheme;
+ updateBackgroundAndPaddings(true);
+ mAdapter.setGridTheme(mGridTheme);
+ mLastGridTheme = -1;
+ }
// Clear the search query
mSearchQueryBuilder.clear();
mSearchQueryBuilder.clearSpans();
Selection.setSelection(mSearchQueryBuilder, 0);
}
+
+ @Override
+ protected BaseRecyclerView getRecyclerView() {
+ return mAppsRecyclerView;
+ }
}
diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
index 1f95133d4..643182588 100644
--- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
+++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java
@@ -24,7 +24,6 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.support.v4.view.accessibility.AccessibilityRecordCompat;
import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.net.Uri;
@@ -38,10 +37,14 @@ import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.TextView;
import com.android.launcher3.AppInfo;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar.FastScrollFocusApplicator;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar.FastScrollFocusable;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
+import com.android.launcher3.RemoteFolderManager;
import com.android.launcher3.Utilities;
+import com.android.launcher3.settings.SettingsProvider;
import com.android.launcher3.util.Thunk;
import java.util.HashMap;
@@ -68,6 +71,17 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
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;
+ // Section header for customized predicated apps.
+ public static final int CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE = 6;
+ // Additional spacing between predicted apps and regular apps.
+ public static final int CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE = 7;
+
+ private boolean mIconsDimmed = false;
+
+ private int mGridTheme;
+
+
+ private AlphabeticalAppsList.SectionInfo mFocusedSection;
/**
* ViewHolder for each icon.
@@ -142,7 +156,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
public class GridItemDecoration extends RecyclerView.ItemDecoration {
private static final boolean DEBUG_SECTION_MARGIN = false;
- private static final boolean FADE_OUT_SECTIONS = false;
+ private static final boolean FADE_OUT_SECTIONS = true;
private HashMap<String, PointF> mCachedSectionBounds = new HashMap<>();
private Rect mTmpBounds = new Rect();
@@ -181,36 +195,34 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mPredictedAppsDividerPaint);
hasDrawnPredictedAppsDivider = true;
- } else if (showSectionNames && shouldDrawItemSection(holder, i, items)) {
- // At this point, we only draw sections for each section break;
+ // Only customized predicted apps will draw a section name.
+ if (!mApps.mCustomPredictedAppsEnabled) continue;
+ }
+
+ if (showSectionNames && shouldDrawItemSection(holder, items)) {
+ // Draw the section name for the first visible item
int viewTopOffset = (2 * child.getPaddingTop());
int pos = holder.getPosition();
AlphabeticalAppsList.AdapterItem item = items.get(pos);
AlphabeticalAppsList.SectionInfo sectionInfo = item.sectionInfo;
-
- // Draw all the sections for this index
String lastSectionName = item.sectionName;
- for (int j = item.sectionAppIndex; j < sectionInfo.numApps; j++, pos++) {
- AlphabeticalAppsList.AdapterItem nextItem = items.get(pos);
- String sectionName = nextItem.sectionName;
- if (nextItem.sectionInfo != sectionInfo) {
- break;
- }
- if (j > item.sectionAppIndex && sectionName.equals(lastSectionName)) {
- continue;
- }
+ // Find the section name bounds
+ PointF sectionBounds = getAndCacheSectionBounds(lastSectionName);
- // Find the section name bounds
- PointF sectionBounds = getAndCacheSectionBounds(sectionName);
+ // Calculate where to draw the section
+ int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
+ int x = mIsRtl ?
+ parent.getWidth() - mBackgroundPadding.left - mSectionNamesMargin :
+ mBackgroundPadding.left;
+ x += (int) ((mSectionNamesMargin - sectionBounds.x) / 2f);
- // Calculate where to draw the section
- int sectionBaseline = (int) (viewTopOffset + sectionBounds.y);
- int x = mIsRtl ?
- parent.getWidth() - mBackgroundPadding.left - mSectionNamesMargin :
- mBackgroundPadding.left;
- x += (int) ((mSectionNamesMargin - sectionBounds.x) / 2f);
- int y = child.getTop() + sectionBaseline;
+ int y;
+ boolean fixedToRow = false;
+ if (item.viewType == PREDICTION_ICON_VIEW_TYPE) {
+ y = child.getTop() - (int) mSectionTextPaint.getTextSize() / 2;
+ } else {
+ y = child.getTop() + sectionBaseline;
// Determine whether this is the last row with apps in that section, if
// so, then fix the section to the row allowing it to scroll past the
@@ -219,7 +231,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
int nextRowPos = Math.min(items.size() - 1,
pos + mAppsPerRow - (appIndexInSection % mAppsPerRow));
AlphabeticalAppsList.AdapterItem nextRowItem = items.get(nextRowPos);
- boolean fixedToRow = !sectionName.equals(nextRowItem.sectionName);
+ fixedToRow = !lastSectionName.equals(nextRowItem.sectionName);
if (!fixedToRow) {
y = Math.max(sectionBaseline, y);
}
@@ -229,22 +241,21 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
if (lastSectionHeight > 0 && y <= (lastSectionTop + lastSectionHeight)) {
y += lastSectionTop - y + lastSectionHeight;
}
+ }
- // Draw the section header
- if (FADE_OUT_SECTIONS) {
- int alpha = 255;
- if (fixedToRow) {
- alpha = Math.min(255,
- (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
- }
- mSectionTextPaint.setAlpha(alpha);
+ // Draw the section header
+ if (FADE_OUT_SECTIONS) {
+ int alpha = 255;
+ if (fixedToRow) {
+ alpha = Math.min(255,
+ (int) (255 * (Math.max(0, y) / (float) sectionBaseline)));
}
- c.drawText(sectionName, x, y, mSectionTextPaint);
-
- lastSectionTop = y;
- lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset);
- lastSectionName = sectionName;
+ mSectionTextPaint.setAlpha(alpha);
}
+ c.drawText(lastSectionName, x, y, mSectionTextPaint);
+
+ lastSectionTop = y;
+ lastSectionHeight = (int) (sectionBounds.y + mSectionHeaderOffset);
i += (sectionInfo.numApps - item.sectionAppIndex);
}
}
@@ -304,21 +315,22 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
/**
* Returns whether to draw the section for the given child.
*/
- private boolean shouldDrawItemSection(ViewHolder holder, int childIndex,
+ private boolean shouldDrawItemSection(ViewHolder holder,
List<AlphabeticalAppsList.AdapterItem> items) {
int pos = holder.getPosition();
AlphabeticalAppsList.AdapterItem item = items.get(pos);
// Ensure it's an icon
- if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE) {
+ if (item.viewType != ICON_VIEW_TYPE && item.viewType != PREDICTION_ICON_VIEW_TYPE) {
return false;
}
- // Draw the section header for the first item in each section
- return (childIndex == 0) ||
- (items.get(pos - 1).viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE);
+
+ return true;
}
}
+ private final RemoteFolderManager mRemoteFolderManager;
+
private Launcher mLauncher;
private LayoutInflater mLayoutInflater;
@Thunk AlphabeticalAppsList mApps;
@@ -349,9 +361,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
// Section drawing
@Thunk int mSectionNamesMargin;
@Thunk int mSectionHeaderOffset;
+ @Thunk int mSectionStrategy;
@Thunk Paint mSectionTextPaint;
@Thunk Paint mPredictedAppsDividerPaint;
+ private int mAllAppsTextColor;
+
+ private int mCustomPredictedAppsHeaderHeight;
+ private int mCustomPredictedAppsFooterHeight;
+
public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps,
View.OnTouchListener touchListener, View.OnClickListener iconClickListener,
View.OnLongClickListener iconLongClickListener) {
@@ -367,13 +385,24 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mTouchListener = touchListener;
mIconClickListener = iconClickListener;
mIconLongClickListener = iconLongClickListener;
- mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin);
+ mSectionNamesMargin = mSectionStrategy ==
+ AllAppsContainerView.SECTION_STRATEGY_GRID ?
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin) :
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin_with_sections);
+
+ mAllAppsTextColor = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
+ res.getColor(R.color.quantum_panel_text_color_dark) :
+ res.getColor(R.color.quantum_panel_text_color);
+
mSectionHeaderOffset = res.getDimensionPixelSize(R.dimen.all_apps_grid_section_y_offset);
mSectionTextPaint = new Paint();
mSectionTextPaint.setTextSize(res.getDimensionPixelSize(
R.dimen.all_apps_grid_section_text_size));
- mSectionTextPaint.setColor(res.getColor(R.color.all_apps_grid_section_text_color));
+ int sectionTextColorId = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
+ R.color.all_apps_grid_section_text_color_dark :
+ R.color.all_apps_grid_section_text_color;
+ mSectionTextPaint.setColor(res.getColor(sectionTextColorId));
mSectionTextPaint.setAntiAlias(true);
mPredictedAppsDividerPaint = new Paint();
@@ -381,8 +410,7 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mPredictedAppsDividerPaint.setColor(0x1E000000);
mPredictedAppsDividerPaint.setAntiAlias(true);
mPredictionBarDividerOffset =
- (-res.getDimensionPixelSize(R.dimen.all_apps_prediction_icon_bottom_padding) +
- res.getDimensionPixelSize(R.dimen.all_apps_icon_top_bottom_padding)) / 2;
+ res.getDimensionPixelSize(R.dimen.all_apps_prediction_bar_divider_offset);
// Resolve the market app handling additional searches
PackageManager pm = launcher.getPackageManager();
@@ -391,6 +419,8 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
if (marketInfo != null) {
mMarketAppName = marketInfo.loadLabel(pm).toString();
}
+
+ mRemoteFolderManager = launcher.getRemoteFolderManager();
}
/**
@@ -408,6 +438,15 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
mIsRtl = rtl;
}
+ public void setSectionStrategy(int sectionStrategy) {
+ Resources res = mLauncher.getResources();
+ mSectionStrategy = sectionStrategy;
+ mSectionNamesMargin = mSectionStrategy ==
+ AllAppsContainerView.SECTION_STRATEGY_GRID ?
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin) :
+ res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin_with_sections);
+ }
+
/**
* 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.
@@ -449,30 +488,50 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ boolean hideIconLabels = SettingsProvider.getBoolean(mLauncher,
+ SettingsProvider.SETTINGS_UI_DRAWER_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_drawer_hide_icon_labels_default);
switch (viewType) {
case SECTION_BREAK_VIEW_TYPE:
return new ViewHolder(new View(parent.getContext()));
case ICON_VIEW_TYPE: {
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_icon, parent, false);
+ if (hideIconLabels) {
+ icon.setTextVisibility(!hideIconLabels);
+ }
icon.setOnTouchListener(mTouchListener);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
.getLongPressTimeout());
icon.setFocusable(true);
+ FastScrollFocusApplicator.createApplicator(icon,
+ FastScrollFocusable.FAST_SCROLL_FOCUS_DIMMABLE |
+ FastScrollFocusable.FAST_SCROLL_FOCUS_SCALABLE);
+
return new ViewHolder(icon);
}
case PREDICTION_ICON_VIEW_TYPE: {
BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.all_apps_prediction_bar_icon, parent, false);
+ if (hideIconLabels) {
+ icon.setTextVisibility(!hideIconLabels);
+ }
icon.setOnTouchListener(mTouchListener);
icon.setOnClickListener(mIconClickListener);
icon.setOnLongClickListener(mIconLongClickListener);
icon.setLongPressTimeout(ViewConfiguration.get(parent.getContext())
.getLongPressTimeout());
icon.setFocusable(true);
- return new ViewHolder(icon);
+ FastScrollFocusApplicator.createApplicator(icon,
+ FastScrollFocusable.FAST_SCROLL_FOCUS_DIMMABLE |
+ FastScrollFocusable.FAST_SCROLL_FOCUS_SCALABLE);
+
+ ViewHolder holder = new ViewHolder(icon);
+ mRemoteFolderManager.onCreateViewHolder(holder, viewType);
+
+ return holder;
}
case EMPTY_SEARCH_VIEW_TYPE:
return new ViewHolder(mLayoutInflater.inflate(R.layout.all_apps_empty_search,
@@ -490,6 +549,22 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
}
});
return new ViewHolder(searchMarketView);
+ case CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE: {
+ View v = mLayoutInflater.inflate(
+ R.layout.custom_predicted_apps_header, parent, false);
+ FastScrollFocusApplicator.createApplicator(v,
+ FastScrollFocusable.FAST_SCROLL_FOCUS_DIMMABLE);
+ ViewHolder holder = new ViewHolder(v);
+ mRemoteFolderManager.onCreateViewHolder(holder, viewType);
+ return holder;
+ }
+ case CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE: {
+ View v = mLayoutInflater.inflate(R.layout.custom_predicted_apps_footer,
+ parent, false);
+ ViewHolder holder = new ViewHolder(v);
+ mRemoteFolderManager.onCreateViewHolder(holder, viewType);
+ return holder;
+ }
default:
throw new RuntimeException("Unexpected view type");
}
@@ -497,17 +572,38 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
+ boolean hideIconLabels = SettingsProvider.getBoolean(mLauncher,
+ SettingsProvider.SETTINGS_UI_DRAWER_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_drawer_hide_icon_labels_default);
+ FastScrollFocusApplicator.setFastScrollDimmed(holder.mContent, false, false);
+ FastScrollFocusApplicator.setFastScrollFocused(holder.mContent, false, false);
switch (holder.getItemViewType()) {
case ICON_VIEW_TYPE: {
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.mContent;
+ icon.setTextColor(mAllAppsTextColor);
+ if (hideIconLabels) {
+ icon.setTextVisibility(!hideIconLabels);
+ }
icon.applyFromApplicationInfo(info);
+ FastScrollFocusApplicator.setFastScrollDimmed(icon, shouldDimPosition(position),
+ !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollFocused(icon, false, !mIconsDimmed);
break;
}
case PREDICTION_ICON_VIEW_TYPE: {
AppInfo info = mApps.getAdapterItems().get(position).appInfo;
BubbleTextView icon = (BubbleTextView) holder.mContent;
+ icon.setTextColor(mAllAppsTextColor);
+ if (hideIconLabels) {
+ icon.setTextVisibility(!hideIconLabels);
+ }
icon.applyFromApplicationInfo(info);
+ FastScrollFocusApplicator.setFastScrollDimmed(icon, shouldDimPosition(position),
+ !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollFocused(icon, false, !mIconsDimmed);
+
+ mRemoteFolderManager.onBindViewHolder(holder, info);
break;
}
case EMPTY_SEARCH_VIEW_TYPE:
@@ -528,7 +624,42 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
searchView.setVisibility(View.GONE);
}
break;
+ case CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE: {
+ TextView title = (TextView) holder.mContent.findViewById(R.id.title);
+ title.setTextColor(mAllAppsTextColor);
+ FastScrollFocusApplicator.setFastScrollDimmed(holder.mContent,
+ shouldDimPosition(position), !mIconsDimmed);
+ FastScrollFocusApplicator.setFastScrollFocused(holder.mContent, false, !mIconsDimmed);
+
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) holder.mContent.getLayoutParams();
+ mCustomPredictedAppsHeaderHeight = holder.mContent.getHeight() +
+ lp.topMargin + lp.bottomMargin;
+ break;
+ }
+ case CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE:
+ ViewGroup.MarginLayoutParams lp =
+ (ViewGroup.MarginLayoutParams) holder.mContent.getLayoutParams();
+ mCustomPredictedAppsFooterHeight = holder.mContent.getHeight() +
+ lp.topMargin + lp.bottomMargin;
+ }
+ }
+
+ private boolean shouldDimPosition(int position) {
+ if (mFocusedSection != null && mIconsDimmed) {
+ if (position >= mFocusedSection.firstAppItem.position &&
+ position < mFocusedSection.firstAppItem.position +
+ mFocusedSection.numApps) {
+ return false;
+ }
}
+ return mIconsDimmed;
+ }
+
+ public int getCustomPredictedAppsOffset(int rowIndex) {
+ int offset = mCustomPredictedAppsHeaderHeight;
+ if (rowIndex > 0) offset += mCustomPredictedAppsFooterHeight;
+ return offset;
}
@Override
@@ -542,6 +673,34 @@ public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.
return item.viewType;
}
+ public void setIconsDimmed(boolean iconsDimmed) {
+ if (mIconsDimmed != iconsDimmed) {
+ mIconsDimmed = iconsDimmed;
+ notifyDataSetChanged();
+ }
+ }
+
+ public void setFocusedSection(
+ AlphabeticalAppsList.SectionInfo focusedSection) {
+ mFocusedSection = focusedSection;
+ }
+
+ public void setGridTheme(int gridTheme) {
+ mGridTheme = gridTheme;
+ int sectionTextColorId = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
+ R.color.all_apps_grid_section_text_color_dark :
+ R.color.all_apps_grid_section_text_color;
+ mSectionTextPaint.setColor(mLauncher.getColor(sectionTextColorId));
+
+ mAllAppsTextColor = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
+ mLauncher.getColor(R.color.quantum_panel_text_color_dark) :
+ mLauncher.getColor(R.color.quantum_panel_text_color);
+
+ int mPredictedAppsDividerColorId = mGridTheme == AllAppsContainerView.GRID_THEME_DARK ?
+ R.color.drawer_divider_dark : R.color.drawer_divider_light;
+ mPredictedAppsDividerPaint.setColor(mLauncher.getColor(mPredictedAppsDividerColorId));
+ }
+
/**
* Creates a new market search intent.
*/
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
index 2f66e2cad..63acba5b2 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java
@@ -15,7 +15,6 @@
*/
package com.android.launcher3.allapps;
-import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
@@ -27,13 +26,14 @@ import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
-import com.android.launcher3.BaseRecyclerViewFastScrollBar;
import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.BaseRecyclerViewFastScrollBar.FastScrollFocusApplicator;
import com.android.launcher3.R;
import com.android.launcher3.Stats;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Thunk;
+import java.util.ArrayList;
import java.util.List;
/**
@@ -50,9 +50,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private AlphabeticalAppsList mApps;
private int mNumAppsPerRow;
+ private int mSectionStrategy = AllAppsContainerView.SECTION_STRATEGY_RAGGED;
- @Thunk BaseRecyclerViewFastScrollBar.FastScrollFocusableView mLastFastScrollFocusedView;
+ @Thunk ArrayList<View> mLastFastScrollFocusedViews = new ArrayList();
@Thunk int mPrevFastScrollFocusedPosition;
+ @Thunk AlphabeticalAppsList.SectionInfo mPrevFastScrollFocusedSection;
@Thunk int mFastScrollFrameIndex;
@Thunk final int[] mFastScrollFrames = new int[10];
@@ -60,6 +62,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
private final int mScrollBarMode = FAST_SCROLL_BAR_MODE_DISTRIBUTE_BY_ROW;
private ScrollPositionState mScrollPosState = new ScrollPositionState();
+ private boolean mFastScrollDragging;
private AllAppsBackgroundDrawable mEmptySearchBackground;
private int mEmptySearchBackgroundTopOffset;
@@ -81,9 +84,30 @@ public class AllAppsRecyclerView extends BaseRecyclerView
super(context, attrs, defStyleAttr);
Resources res = getResources();
- mScrollbar.setDetachThumbOnFastScroll();
+ if (mUseScrollbar) {
+ mScrollbar.setDetachThumbOnFastScroll();
+ }
mEmptySearchBackgroundTopOffset = res.getDimensionPixelSize(
R.dimen.all_apps_empty_search_bg_top_offset);
+
+ addOnScrollListener(new FocusScrollListener());
+ }
+
+ private class FocusScrollListener extends OnScrollListener {
+ public FocusScrollListener() { }
+
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ switch (newState) {
+ case SCROLL_STATE_IDLE:
+ // Don't change anything if we've stopped touching the scroll bar.
+ if (mFastScrollDragging) {
+ // Animation completed, set the fast scroll state on the target views
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
+ }
+ }
+ }
}
/**
@@ -107,15 +131,23 @@ public class AllAppsRecyclerView extends BaseRecyclerView
pool.setMaxRecycledViews(AllAppsGridAdapter.ICON_VIEW_TYPE, approxRows * mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE, mNumAppsPerRow);
pool.setMaxRecycledViews(AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE, approxRows);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE, 1);
+ pool.setMaxRecycledViews(AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE, 1);
+ }
+
+ public void setSectionStrategy(int sectionStrategy) {
+ mSectionStrategy = sectionStrategy;
}
/**
* 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();
+ if (mUseScrollbar) {
+ // Ensure we reattach the scrollbar if it was previously detached while fast-scrolling
+ if (mScrollbar.isThumbDetached()) {
+ mScrollbar.reattachThumbToScroll();
+ }
}
scrollToPosition(0);
}
@@ -226,36 +258,30 @@ public class AllAppsRecyclerView extends BaseRecyclerView
throw new RuntimeException("Unexpected scroll bar mode");
}
- // Map the touch position back to the scroll of the recycler view
- getCurScrollState(mScrollPosState);
- int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
- LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
- if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
- layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
+ // Reset the last focused section
+ if (mPrevFastScrollFocusedSection != lastInfo.sectionInfo) {
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, true, true);
+ clearSectionFocusedItems();
}
- if (mPrevFastScrollFocusedPosition != lastInfo.fastScrollToItem.position) {
- mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position;
+ mPrevFastScrollFocusedPosition = lastInfo.fastScrollToItem.position;
+ mPrevFastScrollFocusedSection = lastInfo.sectionInfo;
- // Reset the last focused view
- if (mLastFastScrollFocusedView != null) {
- mLastFastScrollFocusedView.setFastScrollFocused(false, true);
- mLastFastScrollFocusedView = null;
- }
+ getCurScrollState(mScrollPosState);
+ if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) {
+ smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState);
+
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
+ // Map the touch position back to the scroll of the recycler view
+ int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight);
+ LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
+ layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction));
- if (mFastScrollMode == FAST_SCROLL_MODE_JUMP_TO_FIRST_ICON) {
- smoothSnapToPosition(mPrevFastScrollFocusedPosition, mScrollPosState);
- } else if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) {
- final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
- if (vh != null &&
- vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView) {
- mLastFastScrollFocusedView =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- mLastFastScrollFocusedView.setFastScrollFocused(true, true);
- }
- } else {
- throw new RuntimeException("Unexpected fast scroll mode");
- }
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ } else {
+ throw new RuntimeException("Unexpected fast scroll mode");
}
return lastInfo.sectionName;
}
@@ -263,12 +289,11 @@ public class AllAppsRecyclerView extends BaseRecyclerView
@Override
public void onFastScrollCompleted() {
super.onFastScrollCompleted();
- // Reset and clean up the last focused view
- if (mLastFastScrollFocusedView != null) {
- mLastFastScrollFocusedView.setFastScrollFocused(false, true);
- mLastFastScrollFocusedView = null;
- }
+
+ // Reset and clean up the last focused views
+ clearSectionFocusedItems();
mPrevFastScrollFocusedPosition = -1;
+ mPrevFastScrollFocusedSection = null;
}
/**
@@ -276,6 +301,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView
*/
@Override
public void onUpdateScrollbar(int dy) {
+ if (!mUseScrollbar) {
+ return;
+ }
List<AlphabeticalAppsList.AdapterItem> items = mApps.getAdapterItems();
// Skip early if there are no items or we haven't been measured
@@ -294,7 +322,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
// Only show the scrollbar if there is height to be scrolled
int availableScrollBarHeight = getAvailableScrollBarHeight();
- int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows(), mScrollPosState.rowHeight);
+ int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows(),
+ mScrollPosState.rowHeight);
if (availableScrollHeight <= 0) {
mScrollbar.setThumbOffset(-1, -1);
return;
@@ -303,8 +332,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
// Calculate the current scroll position, the scrollY of the recycler view accounts for the
// view padding, while the scrollBarY is drawn right up to the background padding (ignoring
// padding)
- int scrollY = getPaddingTop() +
- (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset;
+ int scrollY = getCurrentScroll(mScrollPosState);
int scrollBarY = mBackgroundPadding.top +
(int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight);
@@ -354,6 +382,88 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
}
+ @Override
+ public String scrollToSection(String sectionName) {
+ List<AlphabeticalAppsList.FastScrollSectionInfo> scrollSectionInfos =
+ mApps.getFastScrollerSections();
+ if (scrollSectionInfos != null) {
+ for (int i = 0; i < scrollSectionInfos.size(); i++) {
+ AlphabeticalAppsList.FastScrollSectionInfo info = scrollSectionInfos.get(i);
+ if (info.sectionName.equals(sectionName)) {
+ scrollToPositionAtProgress(info.touchFraction);
+ return info.sectionName;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String[] getSectionNames() {
+ List<AlphabeticalAppsList.FastScrollSectionInfo> scrollSectionInfos =
+ mApps.getFastScrollerSections();
+ if (scrollSectionInfos != null) {
+ String[] sectionNames = new String[scrollSectionInfos.size()];
+ for (int i = 0; i < scrollSectionInfos.size(); i++) {
+ AlphabeticalAppsList.FastScrollSectionInfo info = scrollSectionInfos.get(i);
+ sectionNames[i] = info.sectionName;
+ }
+
+ return sectionNames;
+ }
+ return new String[0];
+ }
+
+ private void setSectionFastScrollFocused(int position) {
+ if (mPrevFastScrollFocusedSection != null) {
+ ((AllAppsGridAdapter)getAdapter()).setFocusedSection(mPrevFastScrollFocusedSection);
+ int size = mPrevFastScrollFocusedSection.numApps +
+ mPrevFastScrollFocusedSection.numOtherViews;
+ for (int i = 0; i < size; i++) {
+ int sectionPosition = position+i;
+ final ViewHolder vh = findViewHolderForAdapterPosition(sectionPosition);
+ if (vh != null) {
+ FastScrollFocusApplicator.setFastScrollFocused(vh.itemView, true, true);
+ mLastFastScrollFocusedViews.add(vh.itemView);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void setPreviousSectionFastScrollFocused() {
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
+ }
+
+ private void setSectionFastScrollDimmed(int position, boolean dimmed, boolean animate) {
+ if (mPrevFastScrollFocusedSection != null) {
+ int size = mPrevFastScrollFocusedSection.numApps +
+ mPrevFastScrollFocusedSection.numOtherViews;
+ for (int i = 0; i < size; i++) {
+ int sectionPosition = position+i;
+ final ViewHolder vh = findViewHolderForAdapterPosition(sectionPosition);
+ if (vh != null) {
+ FastScrollFocusApplicator.setFastScrollDimmed(vh.itemView, dimmed, animate);
+ }
+ }
+ }
+ }
+
+ private void clearSectionFocusedItems() {
+ final int N = mLastFastScrollFocusedViews.size();
+ for (int i = 0; i < N; i++) {
+ View view = mLastFastScrollFocusedViews.get(i);
+ FastScrollFocusApplicator.setFastScrollFocused(view, false, true);
+ }
+ mLastFastScrollFocusedViews.clear();
+ }
+
+ @Override
+ public void setFastScrollDragging(boolean dragging) {
+ ((AllAppsGridAdapter) getAdapter()).setIconsDimmed(dragging);
+ mFastScrollDragging = dragging;
+ }
+
/**
* This runnable runs a single frame of the smooth scroll animation and posts the next frame
* if necessary.
@@ -362,19 +472,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView
@Override
public void run() {
if (mFastScrollFrameIndex < mFastScrollFrames.length) {
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, true);
scrollBy(0, mFastScrollFrames[mFastScrollFrameIndex]);
mFastScrollFrameIndex++;
postOnAnimation(mSmoothSnapNextFrameRunnable);
} else {
- // Animation completed, set the fast scroll state on the target view
- final ViewHolder vh = findViewHolderForPosition(mPrevFastScrollFocusedPosition);
- if (vh != null &&
- vh.itemView instanceof BaseRecyclerViewFastScrollBar.FastScrollFocusableView &&
- mLastFastScrollFocusedView != vh.itemView) {
- mLastFastScrollFocusedView =
- (BaseRecyclerViewFastScrollBar.FastScrollFocusableView) vh.itemView;
- mLastFastScrollFocusedView.setFastScrollFocused(true, true);
- }
+ setSectionFastScrollDimmed(mPrevFastScrollFocusedPosition, false, false);
+ setSectionFastScrollFocused(mPrevFastScrollFocusedPosition);
}
}
};
@@ -388,8 +492,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView
// Calculate the full animation from the current scroll position to the final scroll
// position, and then run the animation for the duration.
- int curScrollY = getPaddingTop() +
- (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset;
+ int curScrollY = getCurrentScroll(scrollPosState);
int newScrollY = getScrollAtPosition(position, scrollPosState.rowHeight);
int numFrames = mFastScrollFrames.length;
for (int i = 0; i < numFrames; i++) {
@@ -423,7 +526,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
stateOut.rowIndex = item.rowIndex;
- stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child);
+ stateOut.rowTopOffset = getLayoutManager().getDecoratedTop(child) -
+ getAdditionalScrollHeight(stateOut.rowIndex);
stateOut.rowHeight = child.getHeight();
break;
}
@@ -431,6 +535,16 @@ public class AllAppsRecyclerView extends BaseRecyclerView
}
}
+ @Override
+ protected int getAvailableScrollHeight(int rowCount, int rowHeight) {
+ return super.getAvailableScrollHeight(rowCount, rowHeight) +
+ getAdditionalScrollHeight(mApps.getAdapterItems().size());
+ }
+
+ private int getAdditionalScrollHeight(int rowIndex) {
+ return ((AllAppsGridAdapter) getAdapter()).getCustomPredictedAppsOffset(rowIndex);
+ }
+
/**
* Returns the scrollY for the given position in the adapter.
*/
@@ -439,6 +553,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView
if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE ||
item.viewType == AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
int offset = item.rowIndex > 0 ? getPaddingTop() : 0;
+ offset += ((AllAppsGridAdapter) getAdapter()).
+ getCustomPredictedAppsOffset(item.rowIndex);
return offset + item.rowIndex * rowHeight;
} else {
return 0;
diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
index 14e2a1863..d5ebdab07 100644
--- a/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsRecyclerViewContainerView.java
@@ -17,9 +17,7 @@ package com.android.launcher3.allapps;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.util.AttributeSet;
-import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.launcher3.BubbleTextView;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index dac0df12a..9d30f81ee 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -21,14 +21,17 @@ import android.util.Log;
import com.android.launcher3.AppInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ProtectedComponentsHelper;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.model.AppNameComparator;
import com.android.launcher3.util.ComponentKey;
+import cyanogenmod.providers.CMSettings;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -46,6 +49,9 @@ public class AlphabeticalAppsList {
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 static final String CUSTOM_PREDICTIONS_SCRUBBER = "★";
+ private static final String CUSTOM_PREDICTIONS_HEADER = "☆";
+
private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS;
/**
@@ -54,6 +60,8 @@ public class AlphabeticalAppsList {
public static class SectionInfo {
// The number of applications in this section
public int numApps;
+ // The number of drawn (non-app) adapter items in this section.
+ public int numOtherViews;
// The section break AdapterItem for this section
public AdapterItem sectionBreakItem;
// The first app AdapterItem for this section
@@ -61,19 +69,21 @@ public class AlphabeticalAppsList {
}
/**
- * Info about a fast scroller section, depending if sections are merged, the fast scroller
- * sections will not be the same set as the section headers.
+ * Info about a fast scroller section.
*/
public static class FastScrollSectionInfo {
// The section name
public String sectionName;
+ // Info for this section
+ public SectionInfo sectionInfo;
// The AdapterItem to scroll to for this section
public AdapterItem fastScrollToItem;
// The touch fraction that should map to this fast scroll section info
public float touchFraction;
- public FastScrollSectionInfo(String sectionName) {
+ public FastScrollSectionInfo(String sectionName, SectionInfo sectionInfo) {
this.sectionName = sectionName;
+ this.sectionInfo = sectionInfo;
}
}
@@ -155,14 +165,20 @@ public class AlphabeticalAppsList {
item.position = pos;
return item;
}
- }
- /**
- * Common interface for different merging strategies.
- */
- public interface MergeAlgorithm {
- boolean continueMerging(SectionInfo section, SectionInfo withSection,
- int sectionAppCount, int numAppsPerRow, int mergeCount);
+ public static AdapterItem asCustomPredictedAppsHeader(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
+
+ public static AdapterItem asPredictedAppsSpacer(int pos) {
+ AdapterItem item = new AdapterItem();
+ item.viewType = AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_FOOTER_VIEW_TYPE;
+ item.position = pos;
+ return item;
+ }
}
private Launcher mLauncher;
@@ -177,7 +193,7 @@ public class AlphabeticalAppsList {
private List<AdapterItem> mAdapterItems = new ArrayList<>();
// The set of sections for the apps with the current filter
private List<SectionInfo> mSections = new ArrayList<>();
- // The set of sections that we allow fast-scrolling to (includes non-merged sections)
+ // The set of sections that we allow fast-scrolling to
private List<FastScrollSectionInfo> mFastScrollerSections = new ArrayList<>();
// The set of predicted app component names
private List<ComponentKey> mPredictedAppComponents = new ArrayList<>();
@@ -189,11 +205,13 @@ public class AlphabeticalAppsList {
private RecyclerView.Adapter mAdapter;
private AlphabeticIndexCompat mIndexer;
private AppNameComparator mAppNameComparator;
- private MergeAlgorithm mMergeAlgorithm;
+ private boolean mMergeSections;
private int mNumAppsPerRow;
private int mNumPredictedAppsPerRow;
private int mNumAppRowsInAdapter;
+ boolean mCustomPredictedAppsEnabled;
+
public AlphabeticalAppsList(Context context) {
mLauncher = (Launcher) context;
mIndexer = new AlphabeticIndexCompat(context);
@@ -204,10 +222,10 @@ public class AlphabeticalAppsList {
* Sets the number of apps per row.
*/
public void setNumAppsPerRow(int numAppsPerRow, int numPredictedAppsPerRow,
- MergeAlgorithm mergeAlgorithm) {
+ boolean mergeSections) {
mNumAppsPerRow = numAppsPerRow;
mNumPredictedAppsPerRow = numPredictedAppsPerRow;
- mMergeAlgorithm = mergeAlgorithm;
+ mMergeSections = mergeSections;
updateAdapterItems();
}
@@ -289,13 +307,33 @@ public class AlphabeticalAppsList {
* Sets the current set of predicted apps. Since this can be called before we get the full set
* of applications, we should merge the results only in onAppsUpdated() which is idempotent.
*/
- public void setPredictedApps(List<ComponentKey> apps) {
+ public void setPredictedAppComponents(List<ComponentKey> apps) {
+ if (!mCustomPredictedAppsEnabled) {
+ throw new IllegalStateException("Unable to set predicted app components when adapter " +
+ "is set to accept a custom predicted apps list.");
+ }
+
mPredictedAppComponents.clear();
mPredictedAppComponents.addAll(apps);
onAppsUpdated();
}
/**
+ * Sets the current set of predicted apps. This uses the info directly, so we do not
+ * merge data in {@link #onAppsUpdated()}, but go directly to {@link #updateAdapterItems()}.
+ */
+ public void setPredictedApps(List<AppInfo> apps) {
+ if (!mCustomPredictedAppsEnabled) {
+ throw new IllegalStateException("Unable to set predicted apps directly when adapter " +
+ "is not set to accept a custom predicted apps list.");
+ }
+
+ mPredictedApps.clear();
+ mPredictedApps.addAll(apps);
+ updateAdapterItems();
+ }
+
+ /**
* Sets the current set of apps.
*/
public void setApps(List<AppInfo> apps) {
@@ -412,27 +450,48 @@ public class AlphabeticalAppsList {
}
// Process the predicted app components
- mPredictedApps.clear();
- if (mPredictedAppComponents != null && !mPredictedAppComponents.isEmpty() && !hasFilter()) {
- for (ComponentKey ck : mPredictedAppComponents) {
- AppInfo info = mComponentToAppMap.get(ck);
- if (info != null) {
- mPredictedApps.add(info);
- } else {
- if (LauncherAppState.isDogfoodBuild()) {
- Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
+ boolean hasPredictedApps;
+
+ // We haven't measured yet. Skip this for now. We will set properly after measure.
+ if (mNumPredictedAppsPerRow == 0) {
+ hasPredictedApps = false;
+ } else if (mCustomPredictedAppsEnabled) {
+ hasPredictedApps = !mPredictedApps.isEmpty();
+ } else {
+ mPredictedApps.clear();
+ hasPredictedApps = mPredictedAppComponents != null &&
+ !mPredictedAppComponents.isEmpty();
+ }
+
+ if (hasPredictedApps && !hasFilter()) {
+ if (!mCustomPredictedAppsEnabled) {
+ for (ComponentKey ck : mPredictedAppComponents) {
+ AppInfo info = mComponentToAppMap.get(ck);
+ if (info != null) {
+ mPredictedApps.add(info);
+ } else {
+ if (LauncherAppState.isDogfoodBuild()) {
+ Log.e(TAG, "Predicted app not found: " + ck.flattenToString(mLauncher));
+ }
+ }
+ // Stop at the number of predicted apps
+ if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
+ break;
}
}
- // Stop at the number of predicted apps
- if (mPredictedApps.size() == mNumPredictedAppsPerRow) {
- break;
+ } else {
+ // Shrink to column count.
+ if (mPredictedApps.size() > mNumPredictedAppsPerRow) {
+ mPredictedApps.subList(mNumAppsPerRow, mPredictedApps.size()).clear();
}
}
if (!mPredictedApps.isEmpty()) {
// Add a section for the predictions
lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo("");
+ String text = mCustomPredictedAppsEnabled ? CUSTOM_PREDICTIONS_SCRUBBER : " ";
+ lastFastScrollerSectionInfo =
+ new FastScrollSectionInfo(text, lastSectionInfo);
AdapterItem sectionItem = AdapterItem.asSectionBreak(position++, lastSectionInfo);
mSections.add(lastSectionInfo);
mFastScrollerSections.add(lastFastScrollerSectionInfo);
@@ -440,8 +499,9 @@ public class AlphabeticalAppsList {
// Add the predicted app items
for (AppInfo info : mPredictedApps) {
+ text = mCustomPredictedAppsEnabled ? CUSTOM_PREDICTIONS_HEADER : " ";
AdapterItem appItem = AdapterItem.asPredictedApp(position++, lastSectionInfo,
- "", lastSectionInfo.numApps++, info, appIndex++);
+ text, lastSectionInfo.numApps++, info, appIndex++);
if (lastSectionInfo.firstAppItem == null) {
lastSectionInfo.firstAppItem = appItem;
lastFastScrollerSectionInfo.fastScrollToItem = appItem;
@@ -449,19 +509,30 @@ public class AlphabeticalAppsList {
mAdapterItems.add(appItem);
mFilteredApps.add(info);
}
+
+ if (mCustomPredictedAppsEnabled) {
+ position = mLauncher.getRemoteFolderManager().onUpdateAdapterItems(
+ mAdapterItems, lastFastScrollerSectionInfo, lastSectionInfo, position);
+ }
}
}
+ ProtectedComponentsHelper.updateProtectedComponentsLists(mLauncher);
+
// Recreate the filtered and sectioned apps (for convenience for the grid layout) from the
// ordered set of sections
for (AppInfo info : getFiltersAppInfos()) {
+ if (ProtectedComponentsHelper.isProtectedApp(info.flags, info.componentName)) {
+ continue;
+ }
+
String sectionName = getAndUpdateCachedSectionName(info.title);
// Create a new section if the section names do not match
if (lastSectionInfo == null || !sectionName.equals(lastSectionName)) {
lastSectionName = sectionName;
lastSectionInfo = new SectionInfo();
- lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName);
+ lastFastScrollerSectionInfo = new FastScrollSectionInfo(sectionName, lastSectionInfo);
mSections.add(lastSectionInfo);
mFastScrollerSections.add(lastFastScrollerSectionInfo);
@@ -493,7 +564,7 @@ public class AlphabeticalAppsList {
mAdapterItems.add(AdapterItem.asMarketSearch(position++));
}
- // Merge multiple sections together as requested by the merge strategy for this device
+ // Merge multiple sections together as needed.
mergeSections();
if (mNumAppsPerRow != 0) {
@@ -527,7 +598,8 @@ public class AlphabeticalAppsList {
for (FastScrollSectionInfo info : mFastScrollerSections) {
AdapterItem item = info.fastScrollToItem;
if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
- item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE &&
+ item.viewType != AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE) {
info.touchFraction = 0f;
continue;
}
@@ -542,7 +614,8 @@ public class AlphabeticalAppsList {
for (FastScrollSectionInfo info : mFastScrollerSections) {
AdapterItem item = info.fastScrollToItem;
if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE &&
- item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) {
+ item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE &&
+ item.viewType != AllAppsGridAdapter.CUSTOM_PREDICTED_APPS_HEADER_VIEW_TYPE) {
info.touchFraction = 0f;
continue;
}
@@ -579,52 +652,18 @@ public class AlphabeticalAppsList {
*/
private void mergeSections() {
// Ignore merging until we have an algorithm and a valid row size
- if (mMergeAlgorithm == null || mNumAppsPerRow == 0) {
+ if (!mMergeSections || mNumAppsPerRow == 0 || hasFilter()) {
return;
}
- // Go through each section and try and merge some of the sections
- if (!hasFilter()) {
- int sectionAppCount = 0;
- for (int i = 0; i < mSections.size() - 1; i++) {
- SectionInfo section = mSections.get(i);
- sectionAppCount = section.numApps;
- int mergeCount = 1;
-
- // Merge rows based on the current strategy
- while (i < (mSections.size() - 1) &&
- mMergeAlgorithm.continueMerging(section, mSections.get(i + 1),
- sectionAppCount, mNumAppsPerRow, mergeCount)) {
- SectionInfo nextSection = mSections.remove(i + 1);
-
- // Remove the next section break
- mAdapterItems.remove(nextSection.sectionBreakItem);
- int pos = mAdapterItems.indexOf(section.firstAppItem);
-
- // Point the section for these new apps to the merged section
- int nextPos = pos + section.numApps;
- for (int j = nextPos; j < (nextPos + nextSection.numApps); j++) {
- AdapterItem item = mAdapterItems.get(j);
- item.sectionInfo = section;
- item.sectionAppIndex += section.numApps;
- }
-
- // Update the following adapter items of the removed section item
- pos = mAdapterItems.indexOf(nextSection.firstAppItem);
- for (int j = pos; j < mAdapterItems.size(); j++) {
- AdapterItem item = mAdapterItems.get(j);
- item.position--;
- }
- section.numApps += nextSection.numApps;
- sectionAppCount += nextSection.numApps;
-
- if (DEBUG) {
- Log.d(TAG, "Merging: " + nextSection.firstAppItem.sectionName +
- " to " + section.firstAppItem.sectionName +
- " mergedNumRows: " + (sectionAppCount / mNumAppsPerRow));
- }
- mergeCount++;
- }
+ Iterator<AdapterItem> iter = mAdapterItems.iterator();
+ int positionShift = 0;
+ while (iter.hasNext()) {
+ AdapterItem item = iter.next();
+ item.position -= positionShift;
+ if (item.viewType == AllAppsGridAdapter.SECTION_BREAK_VIEW_TYPE) {
+ iter.remove();
+ positionShift++;
}
}
}
diff --git a/src/com/android/launcher3/list/AutoScrollListView.java b/src/com/android/launcher3/list/AutoScrollListView.java
new file mode 100644
index 000000000..733a753c1
--- /dev/null
+++ b/src/com/android/launcher3/list/AutoScrollListView.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.list;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ListView;
+
+/**
+ * A ListView that can be asked to scroll (smoothly or otherwise) to a specific
+ * position. This class takes advantage of similar functionality that exists
+ * in {@link ListView} and enhances it.
+ */
+public class AutoScrollListView extends ListView {
+
+ /**
+ * Position the element at about 1/3 of the list height
+ */
+ private static final float PREFERRED_SELECTION_OFFSET_FROM_TOP = 0.33f;
+
+ private int mRequestedScrollPosition = -1;
+ private boolean mSmoothScrollRequested;
+
+ public AutoScrollListView(Context context) {
+ super(context);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public AutoScrollListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ /**
+ * Brings the specified position to view by optionally performing a jump-scroll maneuver:
+ * first it jumps to some position near the one requested and then does a smooth
+ * scroll to the requested position. This creates an impression of full smooth
+ * scrolling without actually traversing the entire list. If smooth scrolling is
+ * not requested, instantly positions the requested item at a preferred offset.
+ */
+ public void requestPositionToScreen(int position, boolean smoothScroll) {
+ mRequestedScrollPosition = position;
+ mSmoothScrollRequested = smoothScroll;
+ requestLayout();
+ }
+
+ @Override
+ protected void layoutChildren() {
+ super.layoutChildren();
+ if (mRequestedScrollPosition == -1) {
+ return;
+ }
+
+ final int position = mRequestedScrollPosition;
+ mRequestedScrollPosition = -1;
+
+ int firstPosition = getFirstVisiblePosition() + 1;
+ int lastPosition = getLastVisiblePosition();
+ if (position >= firstPosition && position <= lastPosition) {
+ return; // Already on screen
+ }
+
+ final int offset = (int) (getHeight() * PREFERRED_SELECTION_OFFSET_FROM_TOP);
+ if (!mSmoothScrollRequested) {
+ setSelectionFromTop(position, offset);
+
+ // Since we have changed the scrolling position, we need to redo child layout
+ // Calling "requestLayout" in the middle of a layout pass has no effect,
+ // so we call layoutChildren explicitly
+ super.layoutChildren();
+
+ } else {
+ // We will first position the list a couple of screens before or after
+ // the new selection and then scroll smoothly to it.
+ int twoScreens = (lastPosition - firstPosition) * 2;
+ int preliminaryPosition;
+ if (position < firstPosition) {
+ preliminaryPosition = position + twoScreens;
+ if (preliminaryPosition >= getCount()) {
+ preliminaryPosition = getCount() - 1;
+ }
+ if (preliminaryPosition < firstPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ } else {
+ preliminaryPosition = position - twoScreens;
+ if (preliminaryPosition < 0) {
+ preliminaryPosition = 0;
+ }
+ if (preliminaryPosition > lastPosition) {
+ setSelection(preliminaryPosition);
+ super.layoutChildren();
+ }
+ }
+
+
+ smoothScrollToPositionFromTop(position, offset);
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/list/CompositeCursorAdapter.java b/src/com/android/launcher3/list/CompositeCursorAdapter.java
new file mode 100644
index 000000000..b163c501c
--- /dev/null
+++ b/src/com/android/launcher3/list/CompositeCursorAdapter.java
@@ -0,0 +1,532 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.list;
+
+import android.content.Context;
+import android.database.Cursor;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.util.ArrayList;
+
+/**
+ * A general purpose adapter that is composed of multiple cursors. It just
+ * appends them in the order they are added.
+ */
+public abstract class CompositeCursorAdapter extends BaseAdapter {
+
+ private static final int INITIAL_CAPACITY = 2;
+
+ public static class Partition {
+ boolean showIfEmpty;
+ boolean hasHeader;
+
+ Cursor cursor;
+ int idColumnIndex;
+ int count;
+
+ public Partition(boolean showIfEmpty, boolean hasHeader) {
+ this.showIfEmpty = showIfEmpty;
+ this.hasHeader = hasHeader;
+ }
+
+ /**
+ * True if the directory should be shown even if no contacts are found.
+ */
+ public boolean getShowIfEmpty() {
+ return showIfEmpty;
+ }
+
+ public boolean getHasHeader() {
+ return hasHeader;
+ }
+ }
+
+ private final Context mContext;
+ private ArrayList<Partition> mPartitions;
+ private int mCount = 0;
+ private boolean mCacheValid = true;
+ private boolean mNotificationsEnabled = true;
+ private boolean mNotificationNeeded;
+
+ public CompositeCursorAdapter(Context context) {
+ this(context, INITIAL_CAPACITY);
+ }
+
+ public CompositeCursorAdapter(Context context, int initialCapacity) {
+ mContext = context;
+ mPartitions = new ArrayList<Partition>();
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ /**
+ * Registers a partition. The cursor for that partition can be set later.
+ * Partitions should be added in the order they are supposed to appear in the
+ * list.
+ */
+ public void addPartition(boolean showIfEmpty, boolean hasHeader) {
+ addPartition(new Partition(showIfEmpty, hasHeader));
+ }
+
+ public void addPartition(Partition partition) {
+ mPartitions.add(partition);
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ public void addPartition(int location, Partition partition) {
+ mPartitions.add(location, partition);
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ public void removePartition(int partitionIndex) {
+ Cursor cursor = mPartitions.get(partitionIndex).cursor;
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ mPartitions.remove(partitionIndex);
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Removes cursors for all partitions.
+ */
+ // TODO: Is this really what this is supposed to do? Just remove the cursors? Not close them?
+ // Not remove the partitions themselves? Isn't this leaking?
+
+ public void clearPartitions() {
+ for (Partition partition : mPartitions) {
+ partition.cursor = null;
+ }
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ /**
+ * Closes all cursors and removes all partitions.
+ */
+ public void close() {
+ for (Partition partition : mPartitions) {
+ Cursor cursor = partition.cursor;
+ if (cursor != null && !cursor.isClosed()) {
+ cursor.close();
+ }
+ }
+ mPartitions.clear();
+ invalidate();
+ notifyDataSetChanged();
+ }
+
+ public void setHasHeader(int partitionIndex, boolean flag) {
+ mPartitions.get(partitionIndex).hasHeader = flag;
+ invalidate();
+ }
+
+ public void setShowIfEmpty(int partitionIndex, boolean flag) {
+ mPartitions.get(partitionIndex).showIfEmpty = flag;
+ invalidate();
+ }
+
+ public Partition getPartition(int partitionIndex) {
+ return mPartitions.get(partitionIndex);
+ }
+
+ protected void invalidate() {
+ mCacheValid = false;
+ }
+
+ public int getPartitionCount() {
+ return mPartitions.size();
+ }
+
+ protected void ensureCacheValid() {
+ if (mCacheValid) {
+ return;
+ }
+
+ mCount = 0;
+ for (Partition partition : mPartitions) {
+ Cursor cursor = partition.cursor;
+ int count = cursor != null ? cursor.getCount() : 0;
+ if (partition.hasHeader) {
+ if (count != 0 || partition.showIfEmpty) {
+ count++;
+ }
+ }
+ partition.count = count;
+ mCount += count;
+ }
+
+ mCacheValid = true;
+ }
+
+ /**
+ * Returns true if the specified partition was configured to have a header.
+ */
+ public boolean hasHeader(int partition) {
+ return mPartitions.get(partition).hasHeader;
+ }
+
+ /**
+ * Returns the total number of list items in all partitions.
+ */
+ public int getCount() {
+ ensureCacheValid();
+ return mCount;
+ }
+
+ /**
+ * Returns the cursor for the given partition
+ */
+ public Cursor getCursor(int partition) {
+ return mPartitions.get(partition).cursor;
+ }
+
+ /**
+ * Changes the cursor for an individual partition.
+ */
+ public void changeCursor(int partition, Cursor cursor) {
+ Cursor prevCursor = mPartitions.get(partition).cursor;
+ if (prevCursor != cursor) {
+ if (prevCursor != null && !prevCursor.isClosed()) {
+ prevCursor.close();
+ }
+ mPartitions.get(partition).cursor = cursor;
+ if (cursor != null) {
+ mPartitions.get(partition).idColumnIndex = cursor.getColumnIndex("_id");
+ }
+ invalidate();
+ notifyDataSetChanged();
+ }
+ }
+
+ /**
+ * Returns true if the specified partition has no cursor or an empty cursor.
+ */
+ public boolean isPartitionEmpty(int partition) {
+ Cursor cursor = mPartitions.get(partition).cursor;
+ return cursor == null || cursor.getCount() == 0;
+ }
+
+ /**
+ * Given a list position, returns the index of the corresponding partition.
+ */
+ public int getPartitionForPosition(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0, n = mPartitions.size(); i < n; i++) {
+ int end = start + mPartitions.get(i).count;
+ if (position >= start && position < end) {
+ return i;
+ }
+ start = end;
+ }
+ return -1;
+ }
+
+ /**
+ * Given a list position, return the offset of the corresponding item in its
+ * partition. The header, if any, will have offset -1.
+ */
+ public int getOffsetInPartition(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (Partition partition : mPartitions) {
+ int end = start + partition.count;
+ if (position >= start && position < end) {
+ int offset = position - start;
+ if (partition.hasHeader) {
+ offset--;
+ }
+ return offset;
+ }
+ start = end;
+ }
+ return -1;
+ }
+
+ /**
+ * Returns the first list position for the specified partition.
+ */
+ public int getPositionForPartition(int partition) {
+ ensureCacheValid();
+ int position = 0;
+ for (int i = 0; i < partition; i++) {
+ position += mPartitions.get(i).count;
+ }
+ return position;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return getItemViewTypeCount() + 1;
+ }
+
+ /**
+ * Returns the overall number of item view types across all partitions. An
+ * implementation of this method needs to ensure that the returned count is
+ * consistent with the values returned by {@link #getItemViewType(int,int)}.
+ */
+ public int getItemViewTypeCount() {
+ return 1;
+ }
+
+ /**
+ * Returns the view type for the list item at the specified position in the
+ * specified partition.
+ */
+ protected int getItemViewType(int partition, int position) {
+ return 1;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0, n = mPartitions.size(); i < n; i++) {
+ int end = start + mPartitions.get(i).count;
+ if (position >= start && position < end) {
+ int offset = position - start;
+ if (mPartitions.get(i).hasHeader) {
+ offset--;
+ }
+ if (offset == -1) {
+ return IGNORE_ITEM_VIEW_TYPE;
+ } else {
+ return getItemViewType(i, offset);
+ }
+ }
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0, n = mPartitions.size(); i < n; i++) {
+ int end = start + mPartitions.get(i).count;
+ if (position >= start && position < end) {
+ int offset = position - start;
+ if (mPartitions.get(i).hasHeader) {
+ offset--;
+ }
+ View view;
+ if (offset == -1) {
+ view = getHeaderView(i, mPartitions.get(i).cursor, convertView, parent);
+ } else {
+ if (!mPartitions.get(i).cursor.moveToPosition(offset)) {
+ throw new IllegalStateException("Couldn't move cursor to position "
+ + offset);
+ }
+ view = getView(i, mPartitions.get(i).cursor, offset, convertView, parent);
+ }
+ if (view == null) {
+ throw new NullPointerException("View should not be null, partition: " + i
+ + " position: " + offset);
+ }
+ return view;
+ }
+ start = end;
+ }
+
+ throw new ArrayIndexOutOfBoundsException(position);
+ }
+
+ /**
+ * Returns the header view for the specified partition, creating one if needed.
+ */
+ protected View getHeaderView(int partition, Cursor cursor, View convertView,
+ ViewGroup parent) {
+ View view = convertView != null
+ ? convertView
+ : newHeaderView(mContext, partition, cursor, parent);
+ bindHeaderView(view, partition, cursor);
+ return view;
+ }
+
+ /**
+ * Creates the header view for the specified partition.
+ */
+ protected View newHeaderView(Context context, int partition, Cursor cursor,
+ ViewGroup parent) {
+ return null;
+ }
+
+ /**
+ * Binds the header view for the specified partition.
+ */
+ protected void bindHeaderView(View view, int partition, Cursor cursor) {
+ }
+
+ /**
+ * Returns an item view for the specified partition, creating one if needed.
+ */
+ protected View getView(int partition, Cursor cursor, int position, View convertView,
+ ViewGroup parent) {
+ View view;
+ if (convertView != null) {
+ view = convertView;
+ } else {
+ view = newView(mContext, partition, cursor, position, parent);
+ }
+ bindView(view, partition, cursor, position);
+ return view;
+ }
+
+ /**
+ * Creates an item view for the specified partition and position. Position
+ * corresponds directly to the current cursor position.
+ */
+ protected abstract View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent);
+
+ /**
+ * Binds an item view for the specified partition and position. Position
+ * corresponds directly to the current cursor position.
+ */
+ protected abstract void bindView(View v, int partition, Cursor cursor, int position);
+
+ /**
+ * Returns a pre-positioned cursor for the specified list position.
+ */
+ public Object getItem(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (Partition mPartition : mPartitions) {
+ int end = start + mPartition.count;
+ if (position >= start && position < end) {
+ int offset = position - start;
+ if (mPartition.hasHeader) {
+ offset--;
+ }
+ if (offset == -1) {
+ return null;
+ }
+ Cursor cursor = mPartition.cursor;
+ cursor.moveToPosition(offset);
+ return cursor;
+ }
+ start = end;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the item ID for the specified list position.
+ */
+ public long getItemId(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (Partition mPartition : mPartitions) {
+ int end = start + mPartition.count;
+ if (position >= start && position < end) {
+ int offset = position - start;
+ if (mPartition.hasHeader) {
+ offset--;
+ }
+ if (offset == -1) {
+ return 0;
+ }
+ if (mPartition.idColumnIndex == -1) {
+ return 0;
+ }
+
+ Cursor cursor = mPartition.cursor;
+ if (cursor == null || cursor.isClosed() || !cursor.moveToPosition(offset)) {
+ return 0;
+ }
+ return cursor.getLong(mPartition.idColumnIndex);
+ }
+ start = end;
+ }
+
+ return 0;
+ }
+
+ /**
+ * Returns false if any partition has a header.
+ */
+ @Override
+ public boolean areAllItemsEnabled() {
+ for (Partition mPartition : mPartitions) {
+ if (mPartition.hasHeader) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns true for all items except headers.
+ */
+ @Override
+ public boolean isEnabled(int position) {
+ ensureCacheValid();
+ int start = 0;
+ for (int i = 0, n = mPartitions.size(); i < n; i++) {
+ int end = start + mPartitions.get(i).count;
+ if (position >= start && position < end) {
+ int offset = position - start;
+ if (mPartitions.get(i).hasHeader && offset == 0) {
+ return false;
+ } else {
+ return isEnabled(i, offset);
+ }
+ }
+ start = end;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns true if the item at the specified offset of the specified
+ * partition is selectable and clickable.
+ */
+ protected boolean isEnabled(int partition, int position) {
+ return true;
+ }
+
+ /**
+ * Enable or disable data change notifications. It may be a good idea to
+ * disable notifications before making changes to several partitions at once.
+ */
+ public void setNotificationsEnabled(boolean flag) {
+ mNotificationsEnabled = flag;
+ if (flag && mNotificationNeeded) {
+ notifyDataSetChanged();
+ }
+ }
+
+ @Override
+ public void notifyDataSetChanged() {
+ if (mNotificationsEnabled) {
+ mNotificationNeeded = false;
+ super.notifyDataSetChanged();
+ } else {
+ mNotificationNeeded = true;
+ }
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/list/PinnedHeaderListAdapter.java b/src/com/android/launcher3/list/PinnedHeaderListAdapter.java
new file mode 100644
index 000000000..cc053e18f
--- /dev/null
+++ b/src/com/android/launcher3/list/PinnedHeaderListAdapter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.list;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A subclass of {@link CompositeCursorAdapter} that manages pinned partition headers.
+ */
+public abstract class PinnedHeaderListAdapter extends CompositeCursorAdapter
+ implements PinnedHeaderListView.PinnedHeaderAdapter {
+
+ public static final int PARTITION_HEADER_TYPE = 0;
+
+ private boolean mPinnedPartitionHeadersEnabled;
+ private boolean mHeaderVisibility[];
+
+ public PinnedHeaderListAdapter(Context context) {
+ super(context);
+ }
+
+ public PinnedHeaderListAdapter(Context context, int initialCapacity) {
+ super(context, initialCapacity);
+ }
+
+ public boolean getPinnedPartitionHeadersEnabled() {
+ return mPinnedPartitionHeadersEnabled;
+ }
+
+ public void setPinnedPartitionHeadersEnabled(boolean flag) {
+ this.mPinnedPartitionHeadersEnabled = flag;
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ if (mPinnedPartitionHeadersEnabled) {
+ return getPartitionCount();
+ } else {
+ return 0;
+ }
+ }
+
+ protected boolean isPinnedPartitionHeaderVisible(int partition) {
+ return getPinnedPartitionHeadersEnabled() && hasHeader(partition)
+ && !isPartitionEmpty(partition);
+ }
+
+ /**
+ * The default implementation creates the same type of view as a normal
+ * partition header.
+ */
+ @Override
+ public View getPinnedHeaderView(int partition, View convertView, ViewGroup parent) {
+ if (hasHeader(partition)) {
+ View view = null;
+ if (convertView != null) {
+ Integer headerType = (Integer)convertView.getTag();
+ if (headerType != null && headerType == PARTITION_HEADER_TYPE) {
+ view = convertView;
+ }
+ }
+ if (view == null) {
+ view = newHeaderView(getContext(), partition, null, parent);
+ view.setTag(PARTITION_HEADER_TYPE);
+ view.setFocusable(false);
+ view.setEnabled(false);
+ }
+ bindHeaderView(view, partition, getCursor(partition));
+ view.setLayoutDirection(parent.getLayoutDirection());
+ return view;
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void configurePinnedHeaders(PinnedHeaderListView listView) {
+ if (!getPinnedPartitionHeadersEnabled()) {
+ return;
+ }
+
+ int size = getPartitionCount();
+ boolean unCached = false;
+ // Cache visibility bits, because we will need them several times later on
+ if (mHeaderVisibility == null || mHeaderVisibility.length != size) {
+ mHeaderVisibility = new boolean[size];
+ unCached = true;
+ }
+ for (int i = 0; i < size; i++) {
+ boolean visible = isPinnedPartitionHeaderVisible(i);
+ mHeaderVisibility[i] = visible;
+ if (!visible) {
+ listView.setHeaderInvisible(i, true);
+ }
+ }
+
+ int headerViewsCount = listView.getHeaderViewsCount();
+
+ // Starting at the top, find and pin headers for partitions preceding the visible one(s)
+ int topHeaderHeight = 0;
+ for (int i = 0; i < size; i++) {
+ if (mHeaderVisibility[i]) {
+ int position = listView.getPositionAt(topHeaderHeight) - headerViewsCount;
+ int partition = getPartitionForPosition(position);
+ if (i > partition) {
+ break;
+ }
+
+ if (!unCached){
+ listView.setHeaderPinnedAtTop(i, topHeaderHeight, false);
+ topHeaderHeight += listView.getPinnedHeaderHeight(i);
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public int getScrollPositionForHeader(int viewIndex) {
+ return getPositionForPartition(viewIndex);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/list/PinnedHeaderListView.java b/src/com/android/launcher3/list/PinnedHeaderListView.java
new file mode 100644
index 000000000..58e8791ab
--- /dev/null
+++ b/src/com/android/launcher3/list/PinnedHeaderListView.java
@@ -0,0 +1,565 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.list;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AbsListView;
+import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.ListAdapter;
+
+/**
+ * A ListView that maintains a header pinned at the top of the list. The
+ * pinned header can be pushed up and dissolved as needed.
+ */
+public class PinnedHeaderListView extends AutoScrollListView
+ implements OnScrollListener, OnItemSelectedListener {
+
+ /**
+ * Adapter interface. The list adapter must implement this interface.
+ */
+ public interface PinnedHeaderAdapter {
+
+ /**
+ * Returns the overall number of pinned headers, visible or not.
+ */
+ int getPinnedHeaderCount();
+
+ /**
+ * Creates or updates the pinned header view.
+ */
+ View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent);
+
+ /**
+ * Configures the pinned headers to match the visible list items. The
+ * adapter should call {@link PinnedHeaderListView#setHeaderPinnedAtTop},
+ * {@link PinnedHeaderListView#setHeaderPinnedAtBottom},
+ * {@link PinnedHeaderListView#setFadingHeader} or
+ * {@link PinnedHeaderListView#setHeaderInvisible}, for each header that
+ * needs to change its position or visibility.
+ */
+ void configurePinnedHeaders(PinnedHeaderListView listView);
+
+ /**
+ * Returns the list position to scroll to if the pinned header is touched.
+ * Return -1 if the list does not need to be scrolled.
+ */
+ int getScrollPositionForHeader(int viewIndex);
+ }
+
+ private static final int MAX_ALPHA = 255;
+ private static final int TOP = 0;
+ private static final int BOTTOM = 1;
+ private static final int FADING = 2;
+
+ private static final int DEFAULT_ANIMATION_DURATION = 20;
+
+ private static final int DEFAULT_SMOOTH_SCROLL_DURATION = 100;
+
+ private static final class PinnedHeader {
+ View view;
+ boolean visible;
+ int y;
+ int height;
+ int alpha;
+ int state;
+
+ boolean animating;
+ boolean targetVisible;
+ int sourceY;
+ int targetY;
+ long targetTime;
+ }
+
+ private PinnedHeaderAdapter mAdapter;
+ private int mSize;
+ private PinnedHeader[] mHeaders;
+ private RectF mBounds = new RectF();
+ private Rect mClipRect = new Rect();
+ private OnScrollListener mOnScrollListener;
+ private OnItemSelectedListener mOnItemSelectedListener;
+ private int mScrollState;
+
+ private boolean mScrollToSectionOnHeaderTouch = false;
+ private boolean mHeaderTouched = false;
+
+ private int mAnimationDuration = DEFAULT_ANIMATION_DURATION;
+ private boolean mAnimating;
+ private long mAnimationTargetTime;
+ private int mHeaderPaddingStart;
+ private int mHeaderWidth;
+
+ public PinnedHeaderListView(Context context) {
+ this(context, null, android.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs) {
+ this(context, attrs, android.R.attr.listViewStyle);
+ }
+
+ public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ super.setOnScrollListener(this);
+ super.setOnItemSelectedListener(this);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
+ mHeaderPaddingStart = getPaddingStart();
+ mHeaderWidth = r - l - mHeaderPaddingStart - getPaddingEnd();
+ }
+
+ public void setPinnedHeaderAnimationDuration(int duration) {
+ mAnimationDuration = duration;
+ }
+
+ @Override
+ public void setAdapter(ListAdapter adapter) {
+ mAdapter = (PinnedHeaderAdapter)adapter;
+ super.setAdapter(adapter);
+ }
+
+ @Override
+ public void setOnScrollListener(OnScrollListener onScrollListener) {
+ mOnScrollListener = onScrollListener;
+ super.setOnScrollListener(this);
+ }
+
+ @Override
+ public void setOnItemSelectedListener(OnItemSelectedListener listener) {
+ mOnItemSelectedListener = listener;
+ super.setOnItemSelectedListener(this);
+ }
+
+ public void setScrollToSectionOnHeaderTouch(boolean value) {
+ mScrollToSectionOnHeaderTouch = value;
+ }
+
+ @Override
+ public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
+ int totalItemCount) {
+ if (mAdapter != null) {
+ int count = mAdapter.getPinnedHeaderCount();
+ if (count != mSize) {
+ mSize = count;
+ if (mHeaders == null) {
+ mHeaders = new PinnedHeader[mSize];
+ } else if (mHeaders.length < mSize) {
+ PinnedHeader[] headers = mHeaders;
+ mHeaders = new PinnedHeader[mSize];
+ System.arraycopy(headers, 0, mHeaders, 0, headers.length);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i] == null) {
+ mHeaders[i] = new PinnedHeader();
+ }
+ mHeaders[i].view = mAdapter.getPinnedHeaderView(i, mHeaders[i].view, this);
+ }
+
+ mAnimationTargetTime = System.currentTimeMillis() + mAnimationDuration;
+ mAdapter.configurePinnedHeaders(this);
+ invalidateIfAnimating();
+
+ }
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScroll(this, firstVisibleItem, visibleItemCount, totalItemCount);
+ }
+ }
+
+ @Override
+ protected float getTopFadingEdgeStrength() {
+ // Disable vertical fading at the top when the pinned header is present
+ return mSize > 0 ? 0 : super.getTopFadingEdgeStrength();
+ }
+
+ @Override
+ public void onScrollStateChanged(AbsListView view, int scrollState) {
+ mScrollState = scrollState;
+ if (mOnScrollListener != null) {
+ mOnScrollListener.onScrollStateChanged(this, scrollState);
+ }
+ }
+
+ /**
+ * Ensures that the selected item is positioned below the top-pinned headers
+ * and above the bottom-pinned ones.
+ */
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+ int height = getHeight();
+
+ int windowTop = 0;
+ int windowBottom = height;
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ if (header.state == TOP) {
+ windowTop = header.y + header.height;
+ } else if (header.state == BOTTOM) {
+ windowBottom = header.y;
+ break;
+ }
+ }
+ }
+
+ View selectedView = getSelectedView();
+ if (selectedView != null) {
+ if (selectedView.getTop() < windowTop) {
+ setSelectionFromTop(position, windowTop);
+ } else if (selectedView.getBottom() > windowBottom) {
+ setSelectionFromTop(position, windowBottom - selectedView.getHeight());
+ }
+ }
+
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onItemSelected(parent, view, position, id);
+ }
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ if (mOnItemSelectedListener != null) {
+ mOnItemSelectedListener.onNothingSelected(parent);
+ }
+ }
+
+ public int getPinnedHeaderHeight(int viewIndex) {
+ ensurePinnedHeaderLayout(viewIndex);
+ return mHeaders[viewIndex].view.getHeight();
+ }
+
+ /**
+ * Set header to be pinned at the top.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtTop(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.y = y;
+ header.state = TOP;
+
+ // TODO perhaps we should animate at the top as well
+ header.animating = false;
+ }
+
+ /**
+ * Set header to be pinned at the bottom.
+ *
+ * @param viewIndex index of the header view
+ * @param y is position of the header in pixels.
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderPinnedAtBottom(int viewIndex, int y, boolean animate) {
+ ensurePinnedHeaderLayout(viewIndex);
+ PinnedHeader header = mHeaders[viewIndex];
+ header.state = BOTTOM;
+ if (header.animating) {
+ header.targetTime = mAnimationTargetTime;
+ header.sourceY = header.y;
+ header.targetY = y;
+ } else if (animate && (header.y != y || !header.visible)) {
+ if (header.visible) {
+ header.sourceY = header.y;
+ } else {
+ header.visible = true;
+ header.sourceY = y + header.height;
+ }
+ header.animating = true;
+ header.targetVisible = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetY = y;
+ } else {
+ header.visible = true;
+ header.y = y;
+ }
+ }
+
+ /**
+ * Set header to be pinned at the top of the first visible item.
+ *
+ * @param viewIndex index of the header view
+ * @param position is position of the header in pixels.
+ */
+ public void setFadingHeader(int viewIndex, int position, boolean fade) {
+ ensurePinnedHeaderLayout(viewIndex);
+
+ View child = getChildAt(position - getFirstVisiblePosition());
+ if (child == null) return;
+
+ PinnedHeader header = mHeaders[viewIndex];
+ header.visible = true;
+ header.state = FADING;
+ header.alpha = MAX_ALPHA;
+ header.animating = false;
+
+ int top = getTotalTopPinnedHeaderHeight();
+ header.y = top;
+ if (fade) {
+ int bottom = child.getBottom() - top;
+ int headerHeight = header.height;
+ if (bottom < headerHeight) {
+ int portion = bottom - headerHeight;
+ header.alpha = MAX_ALPHA * (headerHeight + portion) / headerHeight;
+ header.y = top + portion;
+ }
+ }
+ }
+
+ /**
+ * Makes header invisible.
+ *
+ * @param viewIndex index of the header view
+ * @param animate true if the transition to the new coordinate should be animated
+ */
+ public void setHeaderInvisible(int viewIndex, boolean animate) {
+ PinnedHeader header = mHeaders[viewIndex];
+ if (header.visible && (animate || header.animating) && header.state == BOTTOM) {
+ header.sourceY = header.y;
+ if (!header.animating) {
+ header.visible = true;
+ header.targetY = getBottom() + header.height;
+ }
+ header.animating = true;
+ header.targetTime = mAnimationTargetTime;
+ header.targetVisible = false;
+ } else {
+ header.visible = false;
+ }
+ }
+
+ private void ensurePinnedHeaderLayout(int viewIndex) {
+ View view = mHeaders[viewIndex].view;
+ if (view.isLayoutRequested()) {
+ int widthSpec = View.MeasureSpec.makeMeasureSpec(mHeaderWidth, View.MeasureSpec.EXACTLY);
+ int heightSpec;
+ ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
+ if (layoutParams != null && layoutParams.height > 0) {
+ heightSpec = View.MeasureSpec
+ .makeMeasureSpec(layoutParams.height, View.MeasureSpec.EXACTLY);
+ } else {
+ heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
+ }
+ view.measure(widthSpec, heightSpec);
+ int height = view.getMeasuredHeight();
+ mHeaders[viewIndex].height = height;
+ view.layout(0, 0, mHeaderWidth, height);
+ }
+ }
+
+ /**
+ * Returns the sum of heights of headers pinned to the top.
+ */
+ public int getTotalTopPinnedHeaderHeight() {
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == TOP) {
+ return header.y + header.height;
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the list item position at the specified y coordinate.
+ */
+ public int getPositionAt(int y) {
+ do {
+ int position = pointToPosition(getPaddingLeft() + 1, y);
+ if (position != -1) {
+ return position;
+ }
+ // If position == -1, we must have hit a separator. Let's examine
+ // a nearby pixel
+ y--;
+ } while (y > 0);
+ return 0;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent ev) {
+ mHeaderTouched = false;
+ if (super.onInterceptTouchEvent(ev)) {
+ return true;
+ }
+
+ if (mScrollState == SCROLL_STATE_IDLE) {
+ final int y = (int)ev.getY();
+ final int x = (int)ev.getX();
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ // For RTL layouts, this also takes into account that the scrollbar is on the left
+ // side.
+ final int padding = getPaddingLeft();
+ if (header.visible && header.y <= y && header.y + header.height > y &&
+ x >= padding && padding + mHeaderWidth >= x) {
+ mHeaderTouched = true;
+ if (mScrollToSectionOnHeaderTouch &&
+ ev.getAction() == MotionEvent.ACTION_DOWN) {
+ return smoothScrollToPartition(i);
+ } else {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ if (mHeaderTouched) {
+ if (ev.getAction() == MotionEvent.ACTION_UP) {
+ mHeaderTouched = false;
+ }
+ return true;
+ }
+ return super.onTouchEvent(ev);
+ };
+
+ private boolean smoothScrollToPartition(int partition) {
+ final int position = mAdapter.getScrollPositionForHeader(partition);
+ if (position == -1) {
+ return false;
+ }
+
+ int offset = 0;
+ for (int i = 0; i < partition; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ offset += header.height;
+ }
+ }
+ smoothScrollToPositionFromTop(position + getHeaderViewsCount(), offset,
+ DEFAULT_SMOOTH_SCROLL_DURATION);
+ return true;
+ }
+
+ private void invalidateIfAnimating() {
+ mAnimating = false;
+ for (int i = 0; i < mSize; i++) {
+ if (mHeaders[i].animating) {
+ mAnimating = true;
+ invalidate();
+ return;
+ }
+ }
+ }
+
+ @Override
+ protected void dispatchDraw(Canvas canvas) {
+ long currentTime = mAnimating ? System.currentTimeMillis() : 0;
+
+ int top = 0;
+ int bottom = getBottom();
+ boolean hasVisibleHeaders = false;
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible) {
+ hasVisibleHeaders = true;
+ if (header.state == BOTTOM && header.y < bottom) {
+ bottom = header.y;
+ } else if (header.state == TOP || header.state == FADING) {
+ int newTop = header.y + header.height;
+ if (newTop > top) {
+ top = newTop;
+ }
+ }
+ }
+ }
+
+ if (hasVisibleHeaders) {
+ canvas.save();
+ mClipRect.set(0, top, getWidth(), bottom);
+ canvas.clipRect(mClipRect);
+ }
+
+ super.dispatchDraw(canvas);
+
+ if (hasVisibleHeaders) {
+ canvas.restore();
+
+ // First draw top headers, then the bottom ones to handle the Z axis correctly
+ for (int i = mSize; --i >= 0;) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && (header.state == TOP || header.state == FADING)) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+
+ for (int i = 0; i < mSize; i++) {
+ PinnedHeader header = mHeaders[i];
+ if (header.visible && header.state == BOTTOM) {
+ drawHeader(canvas, header, currentTime);
+ }
+ }
+ }
+
+ invalidateIfAnimating();
+ }
+
+ private void drawHeader(Canvas canvas, PinnedHeader header, long currentTime) {
+ if (header.animating) {
+ int timeLeft = (int)(header.targetTime - currentTime);
+ if (timeLeft <= 0) {
+ header.y = header.targetY;
+ header.visible = header.targetVisible;
+ header.animating = false;
+ } else {
+ header.y = header.targetY + (header.sourceY - header.targetY) * timeLeft
+ / mAnimationDuration;
+ }
+ }
+ if (header.visible) {
+ View view = header.view;
+ int saveCount = canvas.save();
+ canvas.translate(isLayoutRtl() ?
+ getWidth() - mHeaderPaddingStart - mHeaderWidth : mHeaderPaddingStart,
+ header.y);
+ if (header.state == FADING) {
+ mBounds.set(0, 0, mHeaderWidth, view.getHeight());
+ canvas.saveLayerAlpha(mBounds, header.alpha, Canvas.ALL_SAVE_FLAG);
+ }
+ view.draw(canvas);
+ canvas.restoreToCount(saveCount);
+ }
+ }
+
+ /**
+ * Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
+ */
+ public boolean isLayoutRtl() {
+ return (getLayoutDirection() == LAYOUT_DIRECTION_RTL);
+ }
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java
new file mode 100644
index 000000000..1ccd2daa0
--- /dev/null
+++ b/src/com/android/launcher3/list/SettingsPinnedHeaderAdapter.java
@@ -0,0 +1,434 @@
+package com.android.launcher3.list;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Switch;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.android.launcher3.InvariantDeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.OverviewSettingsPanel;
+import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
+import com.android.launcher3.settings.SettingsProvider;
+
+public class SettingsPinnedHeaderAdapter extends PinnedHeaderListAdapter {
+ public static final String ACTION_SEARCH_BAR_VISIBILITY_CHANGED =
+ "cyanogenmod.intent.action.SEARCH_BAR_VISIBILITY_CHANGED";
+
+ private Launcher mLauncher;
+ private Context mContext;
+
+ class SettingsPosition {
+ int partition = 0;
+ int position = 0;
+
+ SettingsPosition (int partition, int position) {
+ this.partition = partition;
+ this.position = position;
+ }
+ }
+
+ public SettingsPinnedHeaderAdapter(Context context) {
+ super(context);
+ mLauncher = (Launcher) context;
+ mContext = context;
+ }
+
+ private String[] mHeaders;
+ public int mPinnedHeaderCount;
+
+ public void setHeaders(String[] headers) {
+ this.mHeaders = headers;
+ }
+
+ @Override
+ protected View newHeaderView(Context context, int partition, Cursor cursor,
+ ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(R.layout.settings_pane_list_header, null);
+ }
+
+ @Override
+ protected void bindHeaderView(View view, int partition, Cursor cursor) {
+ TextView textView = (TextView) view.findViewById(R.id.item_name);
+ textView.setText(mHeaders[partition]);
+ }
+
+ @Override
+ protected View newView(Context context, int partition, Cursor cursor, int position,
+ ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ return inflater.inflate(R.layout.settings_pane_list_item, null);
+ }
+
+ @Override
+ protected void bindView(View v, int partition, Cursor cursor, int position) {
+ TextView nameView = (TextView)v.findViewById(R.id.item_name);
+ TextView stateView = (TextView)v.findViewById(R.id.item_state);
+ Switch settingSwitch = (Switch)v.findViewById(R.id.setting_switch);
+ settingSwitch.setClickable(false);
+
+ // RTL
+ Configuration config = mLauncher.getResources().getConfiguration();
+ if (config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ nameView.setGravity(Gravity.RIGHT);
+ }
+
+ String title = cursor.getString(1);
+ nameView.setText(title);
+
+ v.setTag(new SettingsPosition(partition, position));
+
+ Resources res = mLauncher.getResources();
+
+ boolean current;
+ String state;
+
+ switch (partition) {
+ case OverviewSettingsPanel.HOME_SETTINGS_POSITION:
+ switch (position) {
+ case 0:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH,
+ R.bool.preferences_interface_homescreen_search_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ case 1:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default);
+ // Reversed logic here. Boolean is hideLabels, where setting is show labels
+ setSettingSwitch(stateView, settingSwitch, !current);
+ break;
+ case 2:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL,
+ R.bool.preferences_interface_homescreen_scrolling_wallpaper_scroll_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ case 3:
+ updateDynamicGridSizeSettingsItem(stateView, settingSwitch);
+ break;
+ case 4:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_ALLOW_ROTATION,
+ R.bool.preferences_interface_allow_rotation);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ case 5:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER,
+ R.bool.preferences_interface_homescreen_remote_folder_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ default:
+ hideStates(stateView, settingSwitch);
+ }
+ break;
+ case OverviewSettingsPanel.DRAWER_SETTINGS_POSITION:
+ switch (position) {
+ case 0:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_DRAWER_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_drawer_hide_icon_labels_default);
+ // Reversed logic here. Boolean is hideLabels, where setting is show labels
+ setSettingSwitch(stateView, settingSwitch, !current);
+ break;
+ case 1:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_DRAWER_STYLE_USE_COMPACT,
+ R.bool.preferences_interface_drawer_compact_default);
+ state = current ? res.getString(R.string.app_drawer_style_compact)
+ : res.getString(R.string.app_drawer_style_sections);
+ setStateText(stateView, settingSwitch, state);
+ break;
+ case 2:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_DRAWER_DARK,
+ R.bool.preferences_interface_drawer_dark_default);
+ state = current ? res.getString(R.string.app_drawer_color_dark)
+ : res.getString(R.string.app_drawer_color_light);
+ setStateText(stateView, settingSwitch, state);
+ break;
+ case 3:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_USE_SCROLLER,
+ R.bool.preferences_interface_use_scroller_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ case 4:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_USE_HORIZONTAL_SCRUBBER,
+ R.bool.preferences_interface_use_horizontal_scrubber_default);
+ state = current ? res.getString(R.string.fast_scroller_type_horizontal)
+ : res.getString(R.string.fast_scroller_type_vertical);
+ setStateText(stateView, settingSwitch, state);
+ break;
+ case 5:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_DRAWER_SEARCH,
+ R.bool.preferences_interface_drawer_search_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ case 6:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_DRAWER_REMOTE_APPS,
+ R.bool.preferences_interface_drawer_remote_apps_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ default:
+ hideStates(stateView, settingSwitch);
+ }
+ break;
+ case OverviewSettingsPanel.APP_SETTINGS_POSITION:
+ switch (position) {
+ case 0:
+ current = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_GENERAL_ICONS_LARGE,
+ R.bool.preferences_interface_general_icons_large_default);
+ setSettingSwitch(stateView, settingSwitch, current);
+ break;
+ default:
+ hideStates(stateView, settingSwitch);
+ }
+ }
+
+ v.setOnClickListener(mSettingsItemListener);
+ }
+
+ @Override
+ public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = LayoutInflater.from(getContext());
+ View view = inflater.inflate(R.layout.settings_pane_list_header, parent, false);
+ view.setFocusable(false);
+ view.setEnabled(false);
+ bindHeaderView(view, viewIndex, null);
+ return view;
+ }
+
+ @Override
+ public int getPinnedHeaderCount() {
+ return mPinnedHeaderCount;
+ }
+
+ public void updateDynamicGridSizeSettingsItem(TextView stateView, Switch settingSwitch) {
+ InvariantDeviceProfile.GridSize gridSize = InvariantDeviceProfile.GridSize.getModeForValue(
+ SettingsProvider.getIntCustomDefault(mLauncher,
+ SettingsProvider.SETTINGS_UI_DYNAMIC_GRID_SIZE, 0));
+ String state = "";
+
+ switch (gridSize) {
+ case Comfortable:
+ state = mLauncher.getResources().getString(R.string.grid_size_comfortable);
+ break;
+ case Cozy:
+ state = mLauncher.getResources().getString(R.string.grid_size_cozy);
+ break;
+ case Condensed:
+ state = mLauncher.getResources().getString(R.string.grid_size_condensed);
+ break;
+ case Custom:
+ int rows = SettingsProvider.getIntCustomDefault(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_ROWS, 0);
+ int columns = SettingsProvider.getIntCustomDefault(mLauncher,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_COLUMNS, 0);
+ state = rows + " " + "\u00d7" + " " + columns;
+ break;
+ }
+ setStateText(stateView, settingSwitch, state);
+ }
+
+ OnClickListener mSettingsItemListener = new OnClickListener() {
+
+ @Override
+ public void onClick(View v) {
+ int partition = ((SettingsPosition) v.getTag()).partition;
+ int position = ((SettingsPosition) v.getTag()).position;
+
+ switch (partition) {
+ case OverviewSettingsPanel.HOME_SETTINGS_POSITION:
+ switch (position) {
+ case 0:
+ updateSearchBarVisibility(v);
+ break;
+ case 1:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_homescreen_hide_icon_labels_default,
+ true);
+ mLauncher.reloadLauncher(false, false);
+ break;
+ case 2:
+ onSettingsBooleanChanged(v,
+ SettingsProvider
+ .SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL,
+ R.bool.preferences_interface_homescreen_scrolling_wallpaper_scroll_default,
+ false);
+ mLauncher.reloadLauncher(false, false);
+ break;
+ case 3:
+ mLauncher.onClickDynamicGridSizeButton();
+ break;
+ case 4:
+ String key = SettingsProvider.SETTINGS_UI_ALLOW_ROTATION;
+ boolean newValue = onSettingsBooleanChanged(v, key,
+ R.bool.preferences_interface_allow_rotation, false);
+
+ Bundle extras = new Bundle();
+ extras.putBoolean(LauncherSettings.Settings.EXTRA_VALUE, newValue);
+
+ // Required for system to pickup rotation change.
+ mContext.getContentResolver().call(
+ LauncherSettings.Settings.CONTENT_URI,
+ LauncherSettings.Settings.METHOD_SET_BOOLEAN,
+ key, extras);
+
+ break;
+ case 5:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER,
+ R.bool.preferences_interface_homescreen_remote_folder_default,
+ false);
+ mLauncher.getRemoteFolderManager().onSettingChanged();
+ break;
+ }
+ break;
+ case OverviewSettingsPanel.DRAWER_SETTINGS_POSITION:
+ switch (position) {
+ case 0:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_DRAWER_HIDE_ICON_LABELS,
+ R.bool.preferences_interface_drawer_hide_icon_labels_default,
+ true);
+ mLauncher.reloadAppDrawer();
+ break;
+ case 1:
+ onTextSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_DRAWER_STYLE_USE_COMPACT,
+ R.bool.preferences_interface_drawer_compact_default,
+ R.string.app_drawer_style_compact,
+ R.string.app_drawer_style_sections);
+ mLauncher.reloadAppDrawer();
+ break;
+ case 2:
+ onTextSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_DRAWER_DARK,
+ R.bool.preferences_interface_drawer_dark_default,
+ R.string.app_drawer_color_dark,
+ R.string.app_drawer_color_light);
+ mLauncher.reloadAppDrawer();
+ break;
+ case 3:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_USE_SCROLLER,
+ R.bool.preferences_interface_use_scroller_default, false);
+ mLauncher.reloadAppDrawer();
+ mLauncher.reloadWidgetView();
+ break;
+ case 4:
+ onTextSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_USE_HORIZONTAL_SCRUBBER,
+ R.bool.preferences_interface_use_horizontal_scrubber_default,
+ R.string.fast_scroller_type_horizontal,
+ R.string.fast_scroller_type_vertical);
+ mLauncher.reloadAppDrawer();
+ mLauncher.reloadWidgetView();
+ break;
+ case 5:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_DRAWER_SEARCH,
+ R.bool.preferences_interface_drawer_search_default, false);
+ mLauncher.reloadAppDrawer();
+ break;
+ case 6:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_DRAWER_REMOTE_APPS,
+ R.bool.preferences_interface_drawer_remote_apps_default, false);
+ mLauncher.getRemoteFolderManager().onSettingChanged();
+ break;
+ }
+ break;
+ case OverviewSettingsPanel.APP_SETTINGS_POSITION:
+ switch (position) {
+ case 0:
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_GENERAL_ICONS_LARGE,
+ R.bool.preferences_interface_general_icons_large_default, false);
+ mLauncher.reloadLauncher(true, true);
+ break;
+ case 1:
+ Intent intent = new Intent();
+ intent.setClassName(OverviewSettingsPanel.ANDROID_SETTINGS,
+ OverviewSettingsPanel.ANDROID_PROTECTED_APPS);
+ mLauncher.startActivity(intent);
+ break;
+ }
+ }
+
+ View defaultHome = mLauncher.findViewById(R.id.default_home_screen_panel);
+ defaultHome.setVisibility(getCursor(0).getCount() > 1 ? View.VISIBLE : View.GONE);
+ }
+ };
+
+ private void updateSearchBarVisibility(View v) {
+ boolean isSearchEnabled = SettingsProvider.getBoolean(mContext,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH,
+ R.bool.preferences_interface_homescreen_search_default);
+
+ if (!isSearchEnabled) {
+ if (!Utilities.searchActivityExists(mContext)) {
+ Toast.makeText(mContext, mContext.getString(R.string.search_activity_not_found),
+ Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+
+ onSettingsBooleanChanged(v,
+ SettingsProvider.SETTINGS_UI_HOMESCREEN_SEARCH,
+ R.bool.preferences_interface_homescreen_search_default, false);
+
+ Intent intent = new Intent(ACTION_SEARCH_BAR_VISIBILITY_CHANGED);
+ mContext.sendBroadcast(intent);
+ }
+
+ private boolean onSettingsBooleanChanged(View v, String key, int res, boolean invert) {
+ boolean newValue = SettingsProvider.changeBoolean(mContext, key, res);
+ ((Switch)v.findViewById(R.id.setting_switch)).setChecked(invert != newValue);
+ return newValue;
+ }
+
+ private void onTextSettingsBooleanChanged(View v, String key, int defRes,
+ int trueRes, int falseRes) {
+ boolean newValue = SettingsProvider.changeBoolean(mContext, key, defRes);
+ int state = newValue ? trueRes : falseRes;
+ ((TextView) v.findViewById(R.id.item_state)).setText(state);
+ }
+
+ private void setStateText(TextView stateView, Switch settingSwitch, String state) {
+ stateView.setText(state);
+ stateView.setVisibility(View.VISIBLE);
+ settingSwitch.setVisibility(View.INVISIBLE);
+ }
+
+ private void setSettingSwitch(TextView stateView, Switch settingSwitch, boolean isChecked) {
+ settingSwitch.setChecked(isChecked);
+ settingSwitch.setVisibility(View.VISIBLE);
+ stateView.setVisibility(View.INVISIBLE);
+ }
+
+ private void hideStates(TextView stateView, Switch settingSwitch) {
+ settingSwitch.setVisibility(View.INVISIBLE);
+ stateView.setVisibility(View.INVISIBLE);
+ }
+}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index eef4f9173..d0fdad32a 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -7,15 +7,18 @@ import android.content.pm.ResolveInfo;
import android.util.Log;
import com.android.launcher3.AppFilter;
+import com.android.launcher3.AppInfo;
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.ProtectedComponentsHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.compat.AlphabeticIndexCompat;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import com.android.launcher3.compat.UserHandleCompat;
+import cyanogenmod.providers.CMSettings;
import java.util.ArrayList;
import java.util.Collections;
@@ -41,6 +44,7 @@ public class WidgetsModel {
private ArrayList<Object> mRawList;
+ private Context mContext;
private final AppWidgetManagerCompat mAppWidgetMgr;
private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator;
private final Comparator<ItemInfo> mAppNameComparator;
@@ -49,6 +53,7 @@ public class WidgetsModel {
private AlphabeticIndexCompat mIndexer;
public WidgetsModel(Context context, IconCache iconCache, AppFilter appFilter) {
+ mContext = context;
mAppWidgetMgr = AppWidgetManagerCompat.getInstance(context);
mWidgetAndShortcutNameComparator = new WidgetsAndShortcutNameComparator(context);
mAppNameComparator = (new AppNameComparator(context)).getAppInfoComparator();
@@ -110,6 +115,7 @@ public class WidgetsModel {
mWidgetAndShortcutNameComparator.reset();
InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile();
+ ProtectedComponentsHelper.updateProtectedComponentsLists(mContext);
// add and update.
for (Object o: rawWidgetsShortcuts) {
@@ -158,11 +164,18 @@ public class WidgetsModel {
PackageItemInfo pInfo = tmpPackageItemInfos.get(packageName);
ArrayList<Object> widgetsShortcutsList = mWidgetsList.get(pInfo);
if (widgetsShortcutsList != null) {
+ if (pInfo != null && ProtectedComponentsHelper.isProtectedPackage(pInfo.flags,
+ packageName)) {
+ continue;
+ }
widgetsShortcutsList.add(o);
} else {
+ pInfo = new PackageItemInfo(packageName);
+ if (ProtectedComponentsHelper.isProtectedPackage(pInfo.flags, packageName)) {
+ continue;
+ }
widgetsShortcutsList = new ArrayList<>();
widgetsShortcutsList.add(o);
- pInfo = new PackageItemInfo(packageName);
mIconCache.getTitleAndIconForApp(packageName, userHandle,
true /* userLowResIcon */, pInfo);
pInfo.titleSectionName = mIndexer.computeSectionName(pInfo.title);
diff --git a/src/com/android/launcher3/settings/SettingsProvider.java b/src/com/android/launcher3/settings/SettingsProvider.java
new file mode 100644
index 000000000..e809a8a0d
--- /dev/null
+++ b/src/com/android/launcher3/settings/SettingsProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.settings;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+public final class SettingsProvider {
+ public static final String SETTINGS_KEY = "trebuchet_preferences";
+
+ public static final String SETTINGS_UI_HOMESCREEN_DEFAULT_SCREEN_ID = "ui_homescreen_default_screen_id";
+ public static final String SETTINGS_UI_HOMESCREEN_SEARCH = "ui_homescreen_search";
+ public static final String SETTINGS_UI_HOMESCREEN_HIDE_ICON_LABELS = "ui_homescreen_general_hide_icon_labels";
+ public static final String SETTINGS_UI_HOMESCREEN_SCROLLING_WALLPAPER_SCROLL = "ui_homescreen_scrolling_wallpaper_scroll";
+ public static final String SETTINGS_UI_HOMESCREEN_REMOTE_FOLDER = "ui_homescreen_remote_folder";
+ public static final String SETTINGS_UI_DRAWER_REMOTE_APPS = "ui_drawer_remote_apps";
+ public static final String SETTINGS_UI_DYNAMIC_GRID_SIZE = "ui_dynamic_grid_size";
+ public static final String SETTINGS_UI_HOMESCREEN_ROWS = "ui_homescreen_rows";
+ public static final String SETTINGS_UI_HOMESCREEN_COLUMNS = "ui_homescreen_columns";
+ public static final String SETTINGS_UI_DRAWER_HIDE_ICON_LABELS = "ui_drawer_hide_icon_labels";
+ public static final String SETTINGS_UI_DRAWER_STYLE_USE_COMPACT = "ui_drawer_style_compact";
+ public static final String SETTINGS_UI_DRAWER_DARK = "ui_drawer_dark";
+ public static final String SETTINGS_UI_USE_SCROLLER = "ui_scroller";
+ public static final String SETTINGS_UI_USE_HORIZONTAL_SCRUBBER = "ui_horizontal_scrubber";
+ public static final String SETTINGS_UI_DRAWER_SEARCH = "ui_drawer_search";
+ public static final String SETTINGS_UI_GENERAL_ICONS_LARGE = "ui_general_icons_large";
+ public static final String SETTINGS_UI_ALLOW_ROTATION = "ui_allow_rotation";
+
+ public static SharedPreferences get(Context context) {
+ return context.getSharedPreferences(SETTINGS_KEY, Context.MODE_PRIVATE);
+ }
+
+ public static int getIntCustomDefault(Context context, String key, int def) {
+ return get(context).getInt(key, def);
+ }
+
+ public static int getInt(Context context, String key, int resource) {
+ return getIntCustomDefault(context, key, context.getResources().getInteger(resource));
+ }
+
+ public static long getLongCustomDefault(Context context, String key, long def) {
+ return get(context).getLong(key, def);
+ }
+
+ public static long getLong(Context context, String key, int resource) {
+ return getLongCustomDefault(context, key, context.getResources().getInteger(resource));
+ }
+
+ public static boolean getBooleanCustomDefault(Context context, String key, boolean def) {
+ return get(context).getBoolean(key, def);
+ }
+
+ public static boolean getBoolean(Context context, String key, int resource) {
+ return getBooleanCustomDefault(context, key, context.getResources().getBoolean(resource));
+ }
+
+ public static String getStringCustomDefault(Context context, String key, String def) {
+ return get(context).getString(key, def);
+ }
+
+ public static String getString(Context context, String key, int resource) {
+ return getStringCustomDefault(context, key, context.getResources().getString(resource));
+ }
+
+ public static void putString(Context context, String key, String value) {
+ get(context).edit().putString(key, value).commit();
+ }
+
+ public static void putInt(Context context, String key, int value) {
+ get(context).edit().putInt(key, value).commit();
+ }
+
+ public static boolean changeBoolean(Context context, String key, int defaultRes) {
+ boolean def = context.getResources().getBoolean(defaultRes);
+ boolean val = !SettingsProvider.getBooleanCustomDefault(context, key, def);
+ putBoolean(context, key, val);
+ return val;
+ }
+
+ public static void putBoolean(Context context, String key, int res) {
+ boolean val = context.getResources().getBoolean(res);
+ putBoolean(context, key, val);
+ }
+
+ public static void putBoolean(Context context, String key, boolean value) {
+ get(context).edit().putBoolean(key, value).commit();
+ }
+}
diff --git a/src/com/android/launcher3/stats/LauncherStats.java b/src/com/android/launcher3/stats/LauncherStats.java
new file mode 100644
index 000000000..5e8cb83d5
--- /dev/null
+++ b/src/com/android/launcher3/stats/LauncherStats.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.launcher3.stats.internal.db.DatabaseHelper;
+import com.android.launcher3.stats.internal.model.TrackingEvent;
+
+/**
+ * <pre>
+ * Utility class made specifically for Launcher related events
+ * </pre>
+ */
+public class LauncherStats {
+
+ // Constants
+ private static final String TAG = LauncherStats.class.getSimpleName();
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final int MSG_STORE_EVENT = 1000;
+ public static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
+ public static final String ORIGIN_HOMESCREEN = "homescreen";
+ public static final String ORIGIN_APPDRAWER = "appdrawer";
+ public static final String ORIGIN_TREB_LONGPRESS = "trebuchet_longpress";
+ public static final String ORIGIN_CHOOSER = "theme_chooser";
+ public static final String ORIGIN_SETTINGS = "settings";
+ public static final String ORIGIN_DRAG_DROP = "drag_drop";
+ public static final String ORIGIN_FOLDER = "folder";
+
+ private static void log(String msg) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(msg)) {
+ throw new IllegalArgumentException("'msg' cannot be null or empty!");
+ }
+ if (DEBUG) {
+ Log.d(TAG, msg);
+ }
+ }
+
+ private static void loge(String msg) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(msg)) {
+ throw new IllegalArgumentException("'msg' cannot be null or empty!");
+ }
+ Log.e(TAG, msg);
+ }
+
+ /**
+ * <pre>
+ * This is a thread responsible for writing events to a database
+ * </pre>
+ *
+ * @see {@link HandlerThread}
+ */
+ private static class WriteHandlerThread extends HandlerThread {
+ public WriteHandlerThread() {
+ super(WriteHandlerThread.class.getSimpleName());
+ }
+ }
+
+ /**
+ * <pre>
+ * Handler for issuing db writes
+ * </pre>
+ *
+ * @see {@link Handler}
+ */
+ private static class WriteHandler extends Handler {
+
+ public WriteHandler() {
+ super(sHandlerThread.getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ log("Handling message: " + msg.what);
+ switch (msg.what) {
+ case MSG_STORE_EVENT:
+ handleStoreEvent((TrackingEvent) msg.obj);
+ break;
+ default:
+ super.handleMessage(msg);
+ }
+ }
+ }
+
+ // Instance
+ private static LauncherStats sInstance = null;
+
+ // Members
+ private static WriteHandlerThread sHandlerThread;
+ private static WriteHandler sWriteHandler;
+ private static DatabaseHelper sDatabaseHelper;
+
+ /**
+ * Send a message to the handler to store event data
+ *
+ * @param trackingEvent {@link TrackingEvent}
+ */
+ protected void sendStoreEventMessage(TrackingEvent trackingEvent) {
+ log("Sending tracking event to handler: " + trackingEvent);
+ Message msg = new Message();
+ msg.what = MSG_STORE_EVENT;
+ msg.obj = trackingEvent;
+ sWriteHandler.sendMessage(msg);
+ }
+
+ /**
+ * Handle the storing work
+ *
+ * @param trackingEvent {@link TrackingEvent}
+ */
+ private static void handleStoreEvent(TrackingEvent trackingEvent) {
+ log("Handling store event: " + trackingEvent);
+ if (trackingEvent != null) {
+ sDatabaseHelper.writeEvent(trackingEvent);
+ } else {
+ loge("Tracking event was null!");
+ }
+ }
+
+ /**
+ * Used only for overlay extensions
+ */
+ protected LauncherStats() { }
+
+ /**
+ * Constructor
+ *
+ * @param context {@link Context} not null!
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ private LauncherStats(Context context) throws IllegalArgumentException {
+ if (context == null) {
+ throw new IllegalArgumentException("'context' cannot be null!");
+ }
+ sDatabaseHelper = new DatabaseHelper(context);
+ sHandlerThread = new WriteHandlerThread();
+ sHandlerThread.start();
+ sWriteHandler = new WriteHandler();
+ }
+
+ /**
+ * Gets a singleton instance of the stats utility
+ *
+ * @param context {@link Context} not null!
+ * @return {@link LauncherStats}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public static LauncherStats getInstance(Context context)
+ throws IllegalArgumentException {
+ if (sInstance == null) {
+ sInstance = new LauncherStats(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Interface for posting a new widget add event
+ *
+ * @param pkg {@link String} package name of widget
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public void sendWidgetAddEvent(String pkg) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(pkg)) {
+ throw new IllegalArgumentException("'pkg' cannot be null!");
+ }
+ TrackingEvent trackingEvent = new TrackingEvent(TrackingEvent.Category.WIDGET_ADD);
+ trackingEvent.setMetaData(TrackingEvent.KEY_PACKAGE, pkg);
+ sendStoreEventMessage(trackingEvent);
+ }
+
+ /**
+ * Interface for posting a new widget removal event
+ *
+ * @param pkg {@link String} package name of widget
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public void sendWidgetRemoveEvent(String pkg) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(pkg)) {
+ throw new IllegalArgumentException("'pkg' cannot be null!");
+ }
+ TrackingEvent trackingEvent = new TrackingEvent(TrackingEvent.Category.WIDGET_REMOVE);
+ trackingEvent.setMetaData(TrackingEvent.KEY_PACKAGE, pkg);
+ sendStoreEventMessage(trackingEvent);
+ }
+
+ /**
+ * Interface for posting an app launch event
+ *
+ * @param origin {@link String} origin of application launch
+ * @param pkg {@link String} package of app launched
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public void sendAppLaunchEvent(String origin, String pkg) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(origin)) {
+ throw new IllegalArgumentException("'origin' cannot be null!");
+ }
+ if (TextUtils.isEmpty(pkg)) {
+ throw new IllegalArgumentException("'pkg' cannot be null!");
+ }
+ TrackingEvent trackingEvent = new TrackingEvent(TrackingEvent.Category.APP_LAUNCH);
+ trackingEvent.setMetaData(TrackingEvent.KEY_ORIGIN, origin);
+ trackingEvent.setMetaData(TrackingEvent.KEY_PACKAGE, pkg);
+ sendStoreEventMessage(trackingEvent);
+ }
+
+ /**
+ * Interface for sending a "settings opened" event
+ *
+ * @param origin {@link String} origin of the event
+ */
+ public void sendSettingsOpenedEvent(String origin) {
+ TrackingEvent trackingEvent = new TrackingEvent(TrackingEvent.Category.SETTINGS_OPEN);
+ trackingEvent.setMetaData(TrackingEvent.KEY_ORIGIN, origin);
+ sendStoreEventMessage(trackingEvent);
+ }
+
+ /**
+ * Interface for sending a "wallpaper changed" event
+ *
+ * @param origin {@link String} origin of the event
+ */
+ public void sendWallpaperChangedEvent(String origin) {
+ TrackingEvent trackingEvent = new TrackingEvent(TrackingEvent.Category.WALLPAPER_CHANGE);
+ trackingEvent.setMetaData(TrackingEvent.KEY_ORIGIN, origin);
+ sendStoreEventMessage(trackingEvent);
+ }
+
+}
diff --git a/src/com/android/launcher3/stats/external/StatsUtil.java b/src/com/android/launcher3/stats/external/StatsUtil.java
new file mode 100644
index 000000000..697df542c
--- /dev/null
+++ b/src/com/android/launcher3/stats/external/StatsUtil.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.external;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import com.android.launcher3.stats.util.Logger;
+
+/**
+ * StatsUtil
+ * <pre>
+ * Utility for interfacing with CyanogenStats
+ * </pre>
+ */
+public class StatsUtil {
+
+ // Tag and logging
+ private static final String TAG = StatsUtil.class.getSimpleName();
+
+ // Constants
+ private static final String KEY_TRACKING_ID = "tracking_id";
+ private static final String ANALYTIC_INTENT = "com.cyngn.stats.action.SEND_ANALYTIC_EVENT";
+ private static final String STATS_PACKAGE = "com.cyngn.stats";
+
+ /**
+ * Checks if stats collection is enabled
+ *
+ * @param context {@link android.content.Context}
+ * @return {@link java.lang.Boolean}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public static boolean isStatsCollectionEnabled(Context context)
+ throws IllegalArgumentException {
+ return isStatsPackageInstalledAndSystemApp(context);
+ }
+
+ /**
+ * Checks if the stats package is installed
+ *
+ * @param context {@link android.content.Context}
+ * @return {@link Boolean {@link Boolean {@link Boolean {@link Boolean}}}}
+ */
+ private static boolean isStatsPackageInstalledAndSystemApp(Context context)
+ throws IllegalArgumentException {
+ if (context == null) {
+ throw new IllegalArgumentException("'context' cannot be null!");
+ }
+ try {
+ PackageInfo pi = context.getPackageManager().getPackageInfo(STATS_PACKAGE, 0);
+ boolean isSystemApp = (pi.applicationInfo.flags &
+ (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0;
+ return pi.applicationInfo.enabled && isSystemApp;
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "stats not found!");
+ return false;
+ }
+ }
+
+ /**
+ * Send an event to CyangenStats
+ *
+ * @param context {@link Context} not null
+ * @param trackingBundle {@link Bundle}
+ * @throws IllegalArgumentException
+ */
+ public static void sendEvent(Context context, Bundle trackingBundle)
+ throws IllegalArgumentException {
+ if (context == null) {
+ throw new IllegalArgumentException("'context' cannot be null!");
+ }
+ if (trackingBundle == null) {
+ throw new IllegalArgumentException("'trackingBundle' cannot be null!");
+ }
+ if (!isStatsCollectionEnabled(context)) {
+ Logger.logd(TAG, "Stats collection: DISABLED!");
+ return;
+ }
+ Logger.logd(TAG, "Stats collection: ENABLED!");
+
+ Intent newIntent = new Intent(ANALYTIC_INTENT);
+
+ if (!trackingBundle.containsKey(KEY_TRACKING_ID)) {
+ Logger.logd(TAG, "No tracking id in bundle");
+ return;
+ } else {
+ if (trackingBundle.containsKey(TrackingBundle.KEY_EVENT_CATEGORY)
+ && trackingBundle.containsKey(TrackingBundle.KEY_EVENT_ACTION)) {
+ Logger.logd(TAG, trackingBundle.toString());
+ newIntent.putExtras(trackingBundle);
+ context.sendBroadcast(newIntent);
+ } else {
+ Logger.logd(TAG, "Not a valid tracking bundle");
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/launcher3/stats/external/TrackingBundle.java b/src/com/android/launcher3/stats/external/TrackingBundle.java
new file mode 100644
index 000000000..6ca5d971e
--- /dev/null
+++ b/src/com/android/launcher3/stats/external/TrackingBundle.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.external;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+
+/**
+ * <pre>
+ * Extension of a {@link Bundle} to provider streamline interfaces for
+ * the specific task of sending events
+ * </pre>
+ *
+ * @see {@link Bundle}
+ */
+public class TrackingBundle {
+
+ // Constants
+ public static final String KEY_TRACKING_ID = "tracking_id";
+ public static final String KEY_EVENT_CATEGORY = "category";
+ public static final String KEY_EVENT_ACTION = "action";
+ public static final String KEY_METADATA_VALUE = "value";
+ public static final String KEY_METADATA_ORIGIN = "origin";
+ public static final String KEY_METADATA_PACKAGE = "package";
+
+
+ /**
+ * Constructor
+ *
+ * @param trackingId {@link String}
+ * @param category {@link String}
+ * @param action {@link String}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public static Bundle createTrackingBundle(String trackingId, String category, String action)
+ throws IllegalArgumentException {
+ if (TextUtils.isEmpty(trackingId)) {
+ throw new IllegalArgumentException("'trackingId' cannot be null or empty!");
+ }
+ if (TextUtils.isEmpty(category)) {
+ throw new IllegalArgumentException("'category' cannot be null or empty!");
+ }
+ if (TextUtils.isEmpty(action)) {
+ throw new IllegalArgumentException("'action' cannot be null or empty!");
+ }
+ Bundle bundle = new Bundle();
+ bundle.putString(KEY_EVENT_CATEGORY, category);
+ bundle.putString(KEY_EVENT_ACTION, action);
+ bundle.putString(KEY_TRACKING_ID, trackingId);
+ return bundle;
+ }
+
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/stats/internal/db/DatabaseHelper.java b/src/com/android/launcher3/stats/internal/db/DatabaseHelper.java
new file mode 100644
index 000000000..7ffd509ff
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/db/DatabaseHelper.java
@@ -0,0 +1,159 @@
+package com.android.launcher3.stats.internal.db;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import com.android.launcher3.stats.internal.model.TrackingEvent;
+import com.android.launcher3.stats.util.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <pre>
+ * Helper for accessing the database
+ * </pre>
+ *
+ * @see {@link SQLiteOpenHelper}
+ */
+public class DatabaseHelper extends SQLiteOpenHelper {
+
+ // Constants
+ private static final String TAG = DatabaseHelper.class.getSimpleName();
+ private static final String DATABASE_NAME = "events";
+ private static final int DATABASE_VERSION = 1;
+
+ // Instance
+ private static DatabaseHelper sInstance = null;
+
+ /**
+ * Constructor
+ *
+ * @param context {@link Context}
+ * @return {@link DatabaseHelper}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public static DatabaseHelper createInstance(Context context) throws IllegalArgumentException {
+ if (sInstance == null) {
+ sInstance = new DatabaseHelper(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param context {@link Context}
+ */
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ /**
+ * Write an event to the database
+ *
+ * @param trackingEvent {@link TrackingEvent}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public void writeEvent(TrackingEvent trackingEvent)
+ throws IllegalArgumentException {
+ if (trackingEvent == null) {
+ throw new IllegalArgumentException("'trackingEvent' cannot be null!");
+ }
+ Logger.logd(TAG, "Event written to database: " + trackingEvent);
+ SQLiteDatabase db = getWritableDatabase();
+ ContentValues contentValues = trackingEvent.toContentValues();
+ db.insert(TrackingEventContract.EVENT_TABLE_NAME, null, contentValues);
+ db.close();
+ }
+
+ /**
+ * Get a list of tracking events
+ *
+ * @param instanceId {@link Integer}
+ * @return {@link List}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public List<TrackingEvent> getTrackingEventsByCategory(int instanceId,
+ TrackingEvent.Category category) throws IllegalArgumentException {
+ if (category == null) {
+ throw new IllegalArgumentException("'category' cannot be null!");
+ }
+
+ List<TrackingEvent> eventList = new ArrayList<TrackingEvent>();
+
+ // Get a writable database
+ SQLiteDatabase db = getWritableDatabase();
+
+ // Update unclaimed items for this instance
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(TrackingEventContract.EVENT_COLUMN_INSTANCE, instanceId);
+ String whereClause = TrackingEventContract.EVENT_COLUMN_INSTANCE + " IS NULL AND "
+ + TrackingEventContract.EVENT_COLUMN_CATEGORY + " = ? ";
+ String[] whereArgs = new String[] {
+ category.name(),
+ };
+ int cnt = db.update(TrackingEventContract.EVENT_TABLE_NAME, contentValues, whereClause,
+ whereArgs);
+
+ // Short circuit empty update
+ if (cnt < 1) {
+ return eventList;
+ }
+
+ // Select all tagged items
+ String selection = TrackingEventContract.EVENT_COLUMN_CATEGORY + " = ? AND "
+ + TrackingEventContract.EVENT_COLUMN_INSTANCE + " = ? ";
+ String[] selectionArgs = new String[]{
+ category.name(),
+ String.valueOf(instanceId),
+ };
+ Cursor c = db.query(TrackingEventContract.EVENT_TABLE_NAME, null, selection, selectionArgs,
+ null, null, null);
+
+ // Build return list
+ while (c != null && c.getCount() > 0 && c.moveToNext()) {
+ eventList.add(new TrackingEvent(c));
+ }
+
+ db.close();
+
+ return eventList;
+ }
+
+ /**
+ * Deletes events related to the instance
+ *
+ * @param instanceId {@link Integer}
+ * @return {@link Integer}
+ */
+ public int deleteEventsByInstanceId(int instanceId) {
+ SQLiteDatabase db = getWritableDatabase();
+ String whereClause = TrackingEventContract.EVENT_COLUMN_INSTANCE + " = ?";
+ String[] whereArgs = new String[]{
+ String.valueOf(instanceId),
+ };
+ int cnt = db.delete(TrackingEventContract.EVENT_TABLE_NAME, whereClause, whereArgs);
+ db.close();
+ return cnt;
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ db.execSQL(TrackingEventContract.CREATE_EVENT_TABLE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+
+ // [NOTE][MSB]: This will lose data, need to make sure this is handled if/when database
+ // schema changes
+
+ // db.execSQL("DROP TABLE IF EXISTS " + TrackingEventContract.EVENT_TABLE_NAME);
+ // onCreate(db);
+
+ }
+
+}
diff --git a/src/com/android/launcher3/stats/internal/db/TrackingEventContract.java b/src/com/android/launcher3/stats/internal/db/TrackingEventContract.java
new file mode 100644
index 000000000..481a43193
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/db/TrackingEventContract.java
@@ -0,0 +1,31 @@
+package com.android.launcher3.stats.internal.db;
+
+import android.provider.BaseColumns;
+
+/**
+ * <pre>
+ * Table contract definition
+ * </pre>
+ *
+ * @see {@link BaseColumns}
+ */
+public class TrackingEventContract implements BaseColumns {
+
+ // Constants
+ public static final String EVENT_TABLE_NAME = "event";
+
+ // Columns
+ public static final String EVENT_COLUMN_CATEGORY = "category";
+ public static final String EVENT_COLUMN_METADATA = "metadata";
+ public static final String EVENT_COLUMN_INSTANCE = "instance";
+
+ // SQL
+ public static final String CREATE_EVENT_TABLE = "CREATE TABLE " + EVENT_TABLE_NAME
+ + " ( "
+ + " `" + _ID + "` INTEGER PRIMARY KEY AUTOINCREMENT, "
+ + " `" + EVENT_COLUMN_CATEGORY + "` TEXT, "
+ + " `" + EVENT_COLUMN_METADATA + "` TEXT, "
+ + " `" + EVENT_COLUMN_INSTANCE + "` INTEGER "
+ + ");";
+
+}
diff --git a/src/com/android/launcher3/stats/internal/model/CountAction.java b/src/com/android/launcher3/stats/internal/model/CountAction.java
new file mode 100644
index 000000000..d509d4d26
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/model/CountAction.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.internal.model;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import com.android.launcher3.stats.external.TrackingBundle;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <pre>
+ * Handles the specific for sending a tracking event
+ * </pre>
+ *
+ * @see {@link ITrackingAction}
+ */
+public class CountAction implements ITrackingAction {
+
+ public static final String TRACKING_ACTION = "count";
+
+ @Override
+ public String toString() {
+ return TRACKING_ACTION;
+ }
+
+ @Override
+ public List<Bundle> createTrackingBundles(String trackingId, TrackingEvent.Category category,
+ List<TrackingEvent> eventList) {
+
+ Map<String, List<TrackingEvent>> eventPackageMap =
+ new HashMap<String, List<TrackingEvent>>();
+
+ for (TrackingEvent event : eventList) {
+ String pkg = event.getMetaData(TrackingEvent.KEY_PACKAGE);
+ pkg = (TextUtils.isEmpty(pkg)) ? trackingId : pkg;
+ if (!eventPackageMap.containsKey(pkg)) {
+ eventPackageMap.put(pkg, new ArrayList<TrackingEvent>());
+ }
+ eventPackageMap.get(pkg).add(event);
+ }
+
+ List<Bundle> bundleList = new ArrayList<Bundle>();
+ for (Map.Entry<String, List<TrackingEvent>> entry : eventPackageMap.entrySet()) {
+ Bundle bundle = TrackingBundle.createTrackingBundle(trackingId, category.name(),
+ TRACKING_ACTION);
+ bundle.putInt(TrackingBundle.KEY_METADATA_VALUE, entry.getValue().size());
+ String pkg = entry.getKey();
+ if (!pkg.equals(trackingId)) {
+ bundle.putString(TrackingBundle.KEY_METADATA_PACKAGE, pkg);
+ }
+ bundleList.add(bundle);
+ }
+ return bundleList;
+ }
+}
diff --git a/src/com/android/launcher3/stats/internal/model/CountOriginByPackageAction.java b/src/com/android/launcher3/stats/internal/model/CountOriginByPackageAction.java
new file mode 100644
index 000000000..fc04ca088
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/model/CountOriginByPackageAction.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.internal.model;
+
+import android.os.Bundle;
+import android.text.TextUtils;
+import com.android.launcher3.stats.external.TrackingBundle;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * <pre>
+ * This is an action to send a count of events with common origins
+ * </pre>
+ */
+public class CountOriginByPackageAction implements ITrackingAction {
+
+ public static final String TRACKING_ACTION = "count_by_origin";
+
+ @Override
+ public String toString() {
+ return TRACKING_ACTION;
+ }
+
+ @Override
+ public List<Bundle> createTrackingBundles(String trackingId, TrackingEvent.Category category,
+ List<TrackingEvent> eventList) {
+ // Make an origin mapper
+ Map<String, Map<String, List<TrackingEvent>>> originEventMap =
+ new HashMap<String, Map<String, List<TrackingEvent>>>();
+
+ // Parse the event list and categorize by origin
+ for (TrackingEvent event : eventList) {
+ // We are parsing for things with origin, if no origin is set, discard it!
+ if (TextUtils.isEmpty(event.getMetaData(TrackingEvent.KEY_ORIGIN))) {
+ continue;
+ }
+ String originKey = event.getMetaData(TrackingEvent.KEY_ORIGIN);
+ if (!originEventMap.containsKey(originKey)) {
+ HashMap<String, List<TrackingEvent>> newMap =
+ new HashMap<String, List<TrackingEvent>>();
+ originEventMap.put(originKey, newMap);
+ }
+ String packageName = event.getMetaData(TrackingEvent.KEY_PACKAGE);
+ // Set a default so our iteration picks it up and just discard package metadata
+ packageName = (TextUtils.isEmpty(packageName)) ? trackingId : packageName;
+ if (!originEventMap.get(originKey).containsKey(packageName)) {
+ originEventMap.get(originKey).put(packageName, new ArrayList<TrackingEvent>());
+ }
+ originEventMap.get(originKey).get(packageName).add(event);
+ }
+
+ // Start building result tracking bundles
+ List<Bundle> bundleList = new ArrayList<Bundle>();
+ for (Map.Entry<String, Map<String, List<TrackingEvent>>> entry :
+ originEventMap.entrySet()) {
+ String origin = entry.getKey();
+ for (Map.Entry<String, List<TrackingEvent>> entry2 : entry.getValue().entrySet()) {
+ String pkg = entry2.getKey();
+ List<TrackingEvent> events = entry2.getValue();
+ Bundle bundle = TrackingBundle.createTrackingBundle(trackingId, category.name(),
+ TRACKING_ACTION);
+ bundle.putString(TrackingBundle.KEY_METADATA_ORIGIN, origin);
+ bundle.putInt(TrackingBundle.KEY_METADATA_VALUE, events.size());
+ if (!trackingId.equals(pkg)) {
+ bundle.putString(TrackingBundle.KEY_METADATA_PACKAGE, pkg);
+ }
+ bundleList.add(bundle);
+ }
+ }
+ return bundleList;
+ }
+
+}
diff --git a/src/com/android/launcher3/stats/internal/model/ITrackingAction.java b/src/com/android/launcher3/stats/internal/model/ITrackingAction.java
new file mode 100644
index 000000000..b577ed2d0
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/model/ITrackingAction.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.internal.model;
+
+import android.os.Bundle;
+
+import java.util.List;
+
+/**
+ * <pre>
+ * This is an action we want to perfrom from a report.
+ *
+ * e.g.
+ * 1. I want to get the COUNT of widgets added
+ * 2. I want to get the origin of app launches
+ * </pre>
+ */
+public interface ITrackingAction {
+
+ /**
+ * Creates a new bundle used to tracking events
+ *
+ * @param trackingId {@link String}
+ * @param category {@link com.android.launcher3.stats.internal.model.TrackingEvent.Category}
+ * @param eventList {@link List}
+ * @return {@link List}
+ */
+ List<Bundle> createTrackingBundles(String trackingId, TrackingEvent.Category category,
+ List<TrackingEvent> eventList);
+
+}
diff --git a/src/com/android/launcher3/stats/internal/model/TrackingEvent.java b/src/com/android/launcher3/stats/internal/model/TrackingEvent.java
new file mode 100644
index 000000000..552dac729
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/model/TrackingEvent.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.internal.model;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.launcher3.stats.external.TrackingBundle;
+import com.android.launcher3.stats.internal.db.TrackingEventContract;
+import com.android.launcher3.stats.util.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * <pre>
+ * Model of an event to track
+ * </pre>
+ */
+public class TrackingEvent {
+
+ // Constants
+ private static final String TAG = TrackingEvent.class.getSimpleName();
+
+ // Members
+ private Category mCategory;
+ private final Map<String, String> mMetaData = new HashMap<String, String>();
+
+ public enum Category {
+ APP_LAUNCH,
+ WIDGET_ADD,
+ WIDGET_REMOVE,
+ SETTINGS_OPEN,
+ WALLPAPER_CHANGE,
+ HOMESCREEN_PAGE,
+ WIDGET,
+
+ // Remote folder specific
+ REMOTE_FOLDER_DISABLED,
+ REMOTE_DRAWER_DISABLED,
+ REMOTE_FOLDER_OPENED,
+ REMOTE_FOLDER_INFO_OPENED,
+ REMOTE_APP_OPENED,
+ REMOTE_APP_INSTALLED,
+ REMOTE_SYNC_TIME,
+ REMOTE_SYNC_ERROR,
+ REMOTE_SYNC_UNFILLED,
+ REMOTE_FEATURE_DISABLED_FEEDBACK
+ }
+
+ public static final String KEY_ORIGIN = TrackingBundle.KEY_METADATA_ORIGIN;
+ public static final String KEY_VALUE = TrackingBundle.KEY_METADATA_VALUE;
+ public static final String KEY_PACKAGE = TrackingBundle.KEY_METADATA_PACKAGE;
+
+ /**
+ * Constructor
+ *
+ * @param category {@link TrackingEvent.Category}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public TrackingEvent(Category category) throws IllegalArgumentException {
+ if (category == null) {
+ throw new IllegalArgumentException("'category' cannot be null or empty!");
+ }
+ mCategory = category;
+ }
+
+ /**
+ * Constructor
+ *
+ * @param cursor {@link Cursor}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public TrackingEvent(Cursor cursor) throws IllegalArgumentException {
+ if (cursor == null) {
+ throw new IllegalArgumentException("'cursor' cannot be null!");
+ }
+ mCategory = Category.valueOf(cursor.getString(cursor.getColumnIndex(
+ TrackingEventContract.EVENT_COLUMN_CATEGORY)));
+ String metadata = cursor.getString(cursor.getColumnIndex(
+ TrackingEventContract.EVENT_COLUMN_METADATA));
+ if (!TextUtils.isEmpty(metadata)) {
+ String[] parts = metadata.split(",");
+ for (String part : parts) {
+ try {
+ String key = part.split("=")[0];
+ String val = part.split("=")[1];
+ mMetaData.put(key, val);
+ } catch (IndexOutOfBoundsException e) {
+ Log.w(TAG, e.getMessage(), e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get the category
+ *
+ * @return {@link TrackingEvent.Category}
+ */
+ public Category getCategory() {
+ return mCategory;
+ }
+
+ /**
+ * Get the set of meta data keys
+ *
+ * @return {@link Set}
+ */
+ public Set<String> getMetaDataKeySet() {
+ return mMetaData.keySet();
+ }
+
+ /**
+ * Set some meta data
+ *
+ * @param key {@link String}
+ * @param value {@link String}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public void setMetaData(String key, String value) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("'key' cannot be null or empty!");
+ }
+ if (TextUtils.isEmpty(value)) {
+ throw new IllegalArgumentException("'value' cannot be null or empty!");
+ }
+ mMetaData.put(key, value);
+ }
+
+ /**
+ * Get some meta data value
+ *
+ * @param key {@link String}
+ * @return {@link String}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public String getMetaData(String key) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("'key' cannot be null or empty!");
+ }
+ if (mMetaData.containsKey(key)) {
+ return mMetaData.get(key);
+ }
+ return null;
+ }
+
+ /**
+ * Remove some meta data
+ *
+ * @param key {@link String}
+ * @return {@link String} or null
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public String removeMetaData(String key) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(key)) {
+ throw new IllegalArgumentException("'key' cannot be null or empty!");
+ }
+ if (mMetaData.containsKey(key)) {
+ return mMetaData.remove(key);
+ }
+ return null;
+ }
+
+ /**
+ * Converts this object into content values for use with sqlite
+ *
+ * @return {@link ContentValues}
+ */
+ public ContentValues toContentValues() {
+ ContentValues contentValues = new ContentValues();
+ contentValues.put(TrackingEventContract.EVENT_COLUMN_CATEGORY, mCategory.name());
+ StringBuilder sb = new StringBuilder();
+ for (String key : mMetaData.keySet()) {
+ sb.append(key).append("=").append(mMetaData.get(key)).append(",");
+ }
+ if (sb.length() > 0) {
+ String metadata = sb.toString();
+ metadata = metadata.substring(0, metadata.length() - 1);
+ Logger.logd(TAG, "MetaData: " + metadata);
+ contentValues.put(TrackingEventContract.EVENT_COLUMN_METADATA, metadata);
+ }
+ return contentValues;
+ }
+
+ /**
+ * Convert this object into a tracking bundle
+ *
+ * @param trackingId {@link String}
+ * @param action {@link ITrackingAction}
+ * @return {@link Bundle}
+ */
+ public Bundle toTrackingBundle(String trackingId, ITrackingAction action) {
+ Bundle bundle = TrackingBundle.createTrackingBundle(trackingId, mCategory.name(),
+ action.toString());
+ return bundle;
+ }
+
+}
diff --git a/src/com/android/launcher3/stats/internal/service/AggregationIntentService.java b/src/com/android/launcher3/stats/internal/service/AggregationIntentService.java
new file mode 100644
index 000000000..cd9eaf793
--- /dev/null
+++ b/src/com/android/launcher3/stats/internal/service/AggregationIntentService.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.internal.service;
+
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherApplication;
+import com.android.launcher3.stats.external.StatsUtil;
+import com.android.launcher3.stats.external.TrackingBundle;
+import com.android.launcher3.stats.internal.db.DatabaseHelper;
+import com.android.launcher3.stats.internal.model.CountAction;
+import com.android.launcher3.stats.internal.model.CountOriginByPackageAction;
+import com.android.launcher3.stats.internal.model.ITrackingAction;
+import com.android.launcher3.stats.internal.model.TrackingEvent;
+import com.android.launcher3.stats.util.Logger;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * <pre>
+ * Service that starts on a timer and handles aggregating events and sending them to
+ * CyanogenStats
+ * </pre>
+ *
+ * @see {@link IntentService}
+ */
+public class AggregationIntentService extends IntentService {
+
+ // Constants
+ private static final String TAG = AggregationIntentService.class.getSimpleName();
+ private static final String TRACKING_ID = "com.cyanogenmod.trebuchet";
+ public static final String ACTION_AGGREGATE_AND_TRACK =
+ "com.cyanogenmod.trebuchet.AGGREGATE_AND_TRACK";
+ private static final List<ITrackingAction> TRACKED_ACTIONS = new ArrayList<ITrackingAction>() {
+ {
+ add(new CountAction());
+ add(new CountOriginByPackageAction());
+ }
+ };
+ private static final int INVALID_COUNT = -1;
+ private static final String KEY_LAST_TIME_RAN = "last_time_stats_ran";
+ public static final String PREF_KEY_PAGE_COUNT = "page_count";
+ public static final String PREF_KEY_WIDGET_COUNT = "widget_count";
+
+ // Members
+ private DatabaseHelper mDatabaseHelper = null;
+ private int mInstanceId = -1;
+ private SharedPreferences mPrefs = null;
+
+ /**
+ * Creates an IntentService. Invoked by your subclass's constructor.
+ */
+ public AggregationIntentService() {
+ super(AggregationIntentService.class.getSimpleName());
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (!isTrebuchetDefaultLauncher()) {
+ // Cancel repeating schedule
+ unscheduleService();
+ // don't return b/c we still want to upload whatever metrics are left.
+ }
+ String action = intent.getAction();
+ if (ACTION_AGGREGATE_AND_TRACK.equals(action)) {
+ mPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
+ Context.MODE_PRIVATE);
+ mPrefs.edit().putLong(KEY_LAST_TIME_RAN, System.currentTimeMillis()).apply();
+ mInstanceId = (int) System.currentTimeMillis();
+ mDatabaseHelper = DatabaseHelper.createInstance(this);
+ performAggregation();
+ deleteTrackingEventsForInstance();
+ handleNonEventMetrics();
+ }
+ }
+
+ private void performAggregation() {
+
+ // Iterate available categories
+ for (TrackingEvent.Category category : TrackingEvent.Category.values()) {
+
+ // Fetch the events from the database based on the category
+ List<TrackingEvent> eventList =
+ mDatabaseHelper.getTrackingEventsByCategory(mInstanceId, category);
+
+ Logger.logd(TAG, "Event list size: " + eventList.size());
+ // Short circuit if no events for the category
+ if (eventList.size() < 1) {
+ continue;
+ }
+
+ // Now crunch the data into actionable events for the server
+ for (ITrackingAction action : TRACKED_ACTIONS) {
+ try {
+ for (Bundle bundle : action.createTrackingBundles(TRACKING_ID, category,
+ eventList)) {
+ performTrackingCall(bundle);
+ }
+ } catch (NullPointerException e) {
+ Log.e(TAG, "NPE fetching bundle list!", e);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Illegal argument!", e);
+ }
+ }
+
+ }
+ }
+
+ private void deleteTrackingEventsForInstance() {
+ mDatabaseHelper.deleteEventsByInstanceId(mInstanceId);
+ }
+
+ /**
+ * These are metrics that are not event based and need a snapshot every INTERVAL
+ */
+ private void handleNonEventMetrics() {
+ sendPageCountStats();
+ sendWidgetCountStats();
+
+ }
+
+ private void sendPageCountStats() {
+ int pageCount = mPrefs.getInt(PREF_KEY_PAGE_COUNT, INVALID_COUNT);
+ if (pageCount == INVALID_COUNT) {
+ return;
+ }
+ Bundle bundle = TrackingBundle
+ .createTrackingBundle(TRACKING_ID, TrackingEvent.Category.HOMESCREEN_PAGE.name(),
+ "count");
+ bundle.putString(TrackingEvent.KEY_VALUE, String.valueOf(pageCount));
+ StatsUtil.sendEvent(this, bundle);
+ }
+
+ private void sendWidgetCountStats() {
+ int widgetCount = mPrefs.getInt(PREF_KEY_WIDGET_COUNT, INVALID_COUNT);
+ if (widgetCount == INVALID_COUNT) {
+ return;
+ }
+ Bundle bundle = TrackingBundle
+ .createTrackingBundle(TRACKING_ID, TrackingEvent.Category.WIDGET.name(), "count");
+ bundle.putString(TrackingEvent.KEY_VALUE, String.valueOf(widgetCount));
+ StatsUtil.sendEvent(this, bundle);
+ }
+
+ private void performTrackingCall(Bundle bundle) throws IllegalArgumentException {
+ StatsUtil.sendEvent(this, bundle);
+ }
+
+ private void unscheduleService() {
+ Intent intent = new Intent(this, AggregationIntentService.class);
+ intent.setAction(ACTION_AGGREGATE_AND_TRACK);
+ PendingIntent pi = PendingIntent.getService(this, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ AlarmManager alarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.cancel(pi);
+ }
+
+ private boolean isTrebuchetDefaultLauncher() {
+ final IntentFilter filter = new IntentFilter(Intent.ACTION_MAIN);
+ filter.addCategory(Intent.CATEGORY_HOME);
+
+ List<IntentFilter> filters = new ArrayList<IntentFilter>();
+ filters.add(filter);
+
+ final String myPackageName = getPackageName();
+ List<ComponentName> activities = new ArrayList<ComponentName>();
+ final PackageManager packageManager = getPackageManager();
+
+ // You can use name of your package here as third argument
+ packageManager.getPreferredActivities(filters, activities, null);
+
+ for (ComponentName activity : activities) {
+ if (myPackageName.equals(activity.getPackageName())) {
+ Logger.logd(TAG, "Trebuchet IS default launcher!");
+ return true;
+ }
+ }
+ Logger.logd(TAG, "Trebuchet IS NOT default launcher!");
+ return false;
+ }
+
+ private static final long ALARM_INTERVAL = 86400000; // 1 day
+
+ /**
+ * Schedule an alarm service, will cancel existing
+ *
+ * @param context {@link Context}
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public static void scheduleService(Context context) throws IllegalArgumentException {
+ if (context == null) {
+ throw new IllegalArgumentException("'context' cannot be null!");
+ }
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ long lastTimeRan = prefs.getLong(KEY_LAST_TIME_RAN, 0);
+ Intent intent = new Intent(context, AggregationIntentService.class);
+ intent.setAction(ACTION_AGGREGATE_AND_TRACK);
+ PendingIntent pi = PendingIntent.getService(context, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ alarmManager.cancel(pi);
+ alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, lastTimeRan + ALARM_INTERVAL,
+ ALARM_INTERVAL, pi);
+ }
+
+}
diff --git a/src/com/android/launcher3/stats/util/Logger.java b/src/com/android/launcher3/stats/util/Logger.java
new file mode 100644
index 000000000..8d73f54ca
--- /dev/null
+++ b/src/com/android/launcher3/stats/util/Logger.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2015. The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.stats.util;
+
+import android.os.Build;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * <pre>
+ * Metrics debug logging
+ * </pre>
+ */
+public class Logger {
+
+ private static final String TAG = "TrebuchetStats";
+
+ /**
+ * Log a debug message
+ *
+ * @param tag {@link String}
+ * @param msg {@link String }
+ * @throws IllegalArgumentException {@link IllegalArgumentException}
+ */
+ public static void logd(String tag, String msg) throws IllegalArgumentException {
+ if (TextUtils.isEmpty(tag)) {
+ throw new IllegalArgumentException("'tag' cannot be empty!");
+ }
+ if (TextUtils.isEmpty(msg)) {
+ throw new IllegalArgumentException("'msg' cannot be empty!");
+ }
+ if (isDebugging()) {
+ Log.d(TAG, tag + " [ " + msg + " ]");
+ }
+ }
+
+ private static boolean isDebugging() {
+ return Log.isLoggable(TAG, Log.DEBUG);
+ }
+
+}
diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java
index 0c6ea31bb..fff60a1d5 100644
--- a/src/com/android/launcher3/widget/WidgetsContainerView.java
+++ b/src/com/android/launcher3/widget/WidgetsContainerView.java
@@ -29,6 +29,7 @@ import android.view.View;
import android.widget.Toast;
import com.android.launcher3.BaseContainerView;
+import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeleteDropTarget;
import com.android.launcher3.DeviceProfile;
@@ -114,6 +115,19 @@ public class WidgetsContainerView extends BaseContainerView
});
mPadding.set(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
getPaddingBottom());
+ setScroller();
+ updateBackgroundAndPaddings();
+ }
+
+ public void reset() {
+ updateScrubber();
+ updateBackgroundAndPaddings();
+ }
+
+ private void updateScrubber() {
+ if (useScroller() && useScrubber()) {
+ mScrubber.updateSections();
+ }
}
//
@@ -124,6 +138,12 @@ public class WidgetsContainerView extends BaseContainerView
return mView;
}
+ public void setScrubberVisibility(int visibility) {
+ if (mScrubberContainerView != null) {
+ mScrubberContainerView.setVisibility(visibility);
+ }
+ }
+
public View getRevealView() {
// TODO(hyunyoungs): temporarily use apps view transition.
return findViewById(R.id.widgets_reveal_view);
@@ -336,6 +356,7 @@ public class WidgetsContainerView extends BaseContainerView
@Override
protected void onUpdateBackgroundAndPaddings(Rect searchBarBounds, Rect padding) {
+ boolean isRtl = Utilities.isRtl(getResources());
// Apply the top-bottom padding to the content itself so that the launcher transition is
// clipped correctly
mContent.setPadding(0, padding.top, 0, padding.bottom);
@@ -349,6 +370,27 @@ public class WidgetsContainerView extends BaseContainerView
mView.setBackground(background);
getRevealView().setBackground(background.getConstantState().newDrawable());
mView.updateBackgroundPadding(bgPadding);
+
+ int startInset = mView.getMaxScrollbarWidth();
+ int topBottomPadding = getPaddingTop();
+ final boolean useScrollerScrubber = useScroller() && useScrubber();
+ if (isRtl) {
+ mView.setPadding(padding.left + mView.getMaxScrollbarWidth(),
+ topBottomPadding, padding.right + startInset, useScrollerScrubber ?
+ mScrubberHeight + topBottomPadding : topBottomPadding);
+ if (useScrollerScrubber) {
+ mScrubberContainerView.setPadding(padding.left, 0, padding.right, 0);
+ }
+ } else {
+ mView.setPadding(padding.left + startInset, topBottomPadding,
+ padding.right + mView.getMaxScrollbarWidth(), useScrollerScrubber ?
+ mScrubberHeight + topBottomPadding : topBottomPadding);
+ if (useScrollerScrubber) {
+ mScrubberContainerView.setPadding(padding.left, 0, padding.right, 0);
+ mScrubberContainerView.setEnabled(true);
+ mScrubberContainerView.bringToFront();
+ }
+ }
}
/**
@@ -358,6 +400,14 @@ public class WidgetsContainerView extends BaseContainerView
mView.setWidgets(model);
mAdapter.setWidgetsModel(model);
mAdapter.notifyDataSetChanged();
+ updateScrubber();
+ }
+
+ public WidgetsModel getWidgets() {
+ if (mView != null) {
+ return mView.getWidgets();
+ }
+ return null;
}
private WidgetPreviewLoader getWidgetPreviewLoader() {
@@ -366,4 +416,9 @@ public class WidgetsContainerView extends BaseContainerView
}
return mWidgetPreviewLoader;
}
+
+ @Override
+ protected BaseRecyclerView getRecyclerView() {
+ return mView;
+ }
} \ No newline at end of file
diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
index 884bdc418..6818f3f71 100644
--- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java
+++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java
@@ -20,13 +20,17 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.support.v7.widget.LinearLayoutManager;
+import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.Log;
import android.view.View;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.R;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetsModel;
+import java.util.ArrayList;
+
/**
* The widgets recycler view.
*/
@@ -70,7 +74,11 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
public void setWidgets(WidgetsModel widgets) {
mWidgets = widgets;
}
-
+
+ public WidgetsModel getWidgets() {
+ return mWidgets;
+ }
+
/**
* We need to override the draw to ensure that we don't draw the overscroll effect beyond the
* background bounds.
@@ -126,20 +134,66 @@ public class WidgetsRecyclerView extends BaseRecyclerView {
// Skip early if there are no widgets.
int rowCount = mWidgets.getPackageSize();
if (rowCount == 0) {
- mScrollbar.setThumbOffset(-1, -1);
+ if (mUseScrollbar) {
+ mScrollbar.setThumbOffset(-1, -1);
+ }
return;
}
// Skip early if, there no child laid out in the container.
getCurScrollState(mScrollPosState);
if (mScrollPosState.rowIndex < 0) {
- mScrollbar.setThumbOffset(-1, -1);
+ if (mUseScrollbar) {
+ mScrollbar.setThumbOffset(-1, -1);
+ }
return;
}
synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount);
}
+ @Override
+ public String scrollToSection(String sectionName) {
+ // 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 "";
+ }
+ for (int i = 0; i < rowCount; i++) {
+ PackageItemInfo packageItemInfo = mWidgets.getPackageItemInfo(i);
+ if (packageItemInfo != null && !TextUtils.isEmpty(packageItemInfo.titleSectionName) &&
+ packageItemInfo.titleSectionName.equals(sectionName)) {
+ LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager());
+ layoutManager.smoothScrollToPosition(this, null, i);
+ return packageItemInfo.titleSectionName;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String[] getSectionNames() {
+ if (mWidgets == null) {
+ return new String[0];
+ }
+ final int N = mWidgets.getPackageSize();
+ ArrayList<String> sections = new ArrayList<>();
+ String lastLetter = null;
+ for (int i = 0; i < N; i++) {
+ final String titleSectionName = mWidgets.getPackageItemInfo(i).titleSectionName;
+ if (!TextUtils.isEmpty(titleSectionName) && !titleSectionName.equals(lastLetter)) {
+ lastLetter = titleSectionName;
+ sections.add(titleSectionName);
+ }
+ }
+ return sections.toArray(new String[sections.size()]);
+ }
+
/**
* Returns the current scroll state.
*/
diff --git a/tests/Android.mk b/tests/Android.mk
index eba4ade48..e579af78b 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -12,26 +12,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-
-LOCAL_PATH := $(call my-dir)
-
-src_dirs := src
-res_dirs := res
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
-LOCAL_AAPT_FLAGS := --auto-add-overlay
-
-LOCAL_SDK_VERSION := 21
-
-LOCAL_PACKAGE_NAME := Launcher3Tests
-
-LOCAL_INSTRUMENTATION_FOR := Launcher3
-
-include $(BUILD_PACKAGE)
+#LOCAL_PATH := $(call my-dir)
+#include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
index 1bc7c1190..dc3e05e9d 100644
--- a/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
+++ b/tests/src/com/android/launcher3/InvariantDeviceProfileTest.java
@@ -42,7 +42,7 @@ public class InvariantDeviceProfileTest extends AndroidTestCase {
protected void setUp() throws Exception {
super.setUp();
mInvariantProfile = new InvariantDeviceProfile(getContext());
- mPredefinedDeviceProfiles = mInvariantProfile.getPredefinedDeviceProfiles();
+ mPredefinedDeviceProfiles = mInvariantProfile.getPredefinedDeviceProfiles(getContext());
}
@Override