summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk2
-rw-r--r--AndroidManifest.xml53
-rwxr-xr-xWallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.pngbin0 -> 4962 bytes
-rw-r--r--WallpaperPicker/res/values-am/strings.xml6
-rw-r--r--WallpaperPicker/res/values-th/strings.xml2
-rw-r--r--WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java49
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java62
-rw-r--r--WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java212
-rw-r--r--WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java3
-rw-r--r--proguard.flags5
-rw-r--r--res/anim/no_anim.xml (renamed from res/values-sw340dp-port/dimens.xml)8
-rw-r--r--res/anim/task_open_enter.xml34
-rw-r--r--res/drawable-hdpi/bg_cling1.pngbin659 -> 0 bytes
-rw-r--r--res/drawable-hdpi/bg_cling2.pngbin638 -> 0 bytes
-rw-r--r--res/drawable-hdpi/bg_cling3.pngbin769 -> 0 bytes
-rw-r--r--res/drawable-hdpi/bg_cling4.pngbin647 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling.9.pngbin284 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling_arrow_down.pngbin521 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling_arrow_left.pngbin493 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling_arrow_right.pngbin540 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling_arrow_up.pngbin512 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling_bg.9.pngbin0 -> 1352 bytes
-rw-r--r--res/drawable-hdpi/cling_button.9.pngbin450 -> 0 bytes
-rw-r--r--res/drawable-hdpi/cling_button_pressed.9.pngbin453 -> 0 bytes
-rw-r--r--res/drawable-hdpi/custom_content_page.pngbin1353 -> 0 bytes
-rw-r--r--res/drawable-hdpi/focused_bg.9.pngbin188 -> 0 bytes
-rw-r--r--res/drawable-hdpi/ic_allapps.pngbin10781 -> 5861 bytes
-rw-r--r--res/drawable-hdpi/ic_allapps_pressed.pngbin9985 -> 5972 bytes
-rw-r--r--res/drawable-hdpi/ic_pageindicator_add.pngbin729 -> 945 bytes
-rw-r--r--res/drawable-hdpi/ic_pageindicator_current.pngbin1174 -> 1013 bytes
-rw-r--r--res/drawable-hdpi/ic_pageindicator_default.pngbin668 -> 1046 bytes
-rw-r--r--res/drawable-hdpi/ic_setting.pngbin3282 -> 2286 bytes
-rw-r--r--res/drawable-hdpi/ic_setting_pressed.pngbin3642 -> 2293 bytes
-rw-r--r--res/drawable-hdpi/ic_wallpaper.pngbin2189 -> 1997 bytes
-rw-r--r--res/drawable-hdpi/ic_wallpaper_pressed.pngbin2474 -> 1882 bytes
-rw-r--r--res/drawable-hdpi/ic_widget.pngbin1560 -> 1580 bytes
-rw-r--r--res/drawable-hdpi/ic_widget_pressed.pngbin2332 -> 1524 bytes
-rw-r--r--res/drawable-hdpi/page_hover_left.9.pngbin0 -> 184 bytes
-rw-r--r--res/drawable-hdpi/page_hover_left_active.9.pngbin0 -> 183 bytes
-rw-r--r--res/drawable-hdpi/page_hover_left_holo.9.pngbin2916 -> 0 bytes
-rw-r--r--res/drawable-hdpi/page_hover_right.9.pngbin0 -> 174 bytes
-rw-r--r--res/drawable-hdpi/page_hover_right_active.9.pngbin0 -> 174 bytes
-rw-r--r--res/drawable-hdpi/page_hover_right_holo.9.pngbin243 -> 0 bytes
-rw-r--r--res/drawable-hdpi/quantum_panel.9.pngbin0 -> 744 bytes
-rw-r--r--res/drawable-hdpi/quantum_panel_dark.9.pngbin0 -> 646 bytes
-rw-r--r--res/drawable-hdpi/screenpanel.9.pngbin881 -> 218 bytes
-rw-r--r--res/drawable-hdpi/screenpanel_hover.9.pngbin1096 -> 218 bytes
-rw-r--r--res/drawable-hdpi/virtual_preload.9.pngbin0 -> 5153 bytes
-rw-r--r--res/drawable-hdpi/virtual_preload_folder.9.pngbin0 -> 3214 bytes
-rw-r--r--res/drawable-hdpi/widget_container_holo.9.pngbin369 -> 0 bytes
-rw-r--r--res/drawable-land-hdpi/bg_cling1.pngbin671 -> 0 bytes
-rw-r--r--res/drawable-land-hdpi/bg_cling2.pngbin639 -> 0 bytes
-rw-r--r--res/drawable-land-hdpi/bg_cling3.pngbin765 -> 0 bytes
-rw-r--r--res/drawable-land-mdpi/bg_cling1.pngbin509 -> 0 bytes
-rw-r--r--res/drawable-land-mdpi/bg_cling2.pngbin484 -> 0 bytes
-rw-r--r--res/drawable-land-mdpi/bg_cling3.pngbin621 -> 0 bytes
-rw-r--r--res/drawable-land-xhdpi/bg_cling1.pngbin815 -> 0 bytes
-rw-r--r--res/drawable-land-xhdpi/bg_cling2.pngbin794 -> 0 bytes
-rw-r--r--res/drawable-land-xhdpi/bg_cling3.pngbin1074 -> 0 bytes
-rw-r--r--res/drawable-land-xxhdpi/bg_cling1.pngbin1850 -> 0 bytes
-rw-r--r--res/drawable-land-xxhdpi/bg_cling2.pngbin1823 -> 0 bytes
-rw-r--r--res/drawable-land-xxhdpi/bg_cling3.pngbin2296 -> 0 bytes
-rw-r--r--res/drawable-mdpi/bg_cling1.pngbin535 -> 0 bytes
-rw-r--r--res/drawable-mdpi/bg_cling2.pngbin517 -> 0 bytes
-rw-r--r--res/drawable-mdpi/bg_cling3.pngbin617 -> 0 bytes
-rw-r--r--res/drawable-mdpi/bg_cling4.pngbin537 -> 0 bytes
-rw-r--r--res/drawable-mdpi/bg_cling5.pngbin178 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling.9.pngbin236 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling_arrow_down.pngbin485 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling_arrow_left.pngbin455 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling_arrow_right.pngbin486 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling_arrow_up.pngbin473 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling_bg.9.pngbin0 -> 987 bytes
-rw-r--r--res/drawable-mdpi/cling_button.9.pngbin356 -> 0 bytes
-rw-r--r--res/drawable-mdpi/cling_button_pressed.9.pngbin352 -> 0 bytes
-rw-r--r--res/drawable-mdpi/custom_content_page.pngbin1227 -> 0 bytes
-rw-r--r--res/drawable-mdpi/focused_bg.9.pngbin180 -> 0 bytes
-rw-r--r--res/drawable-mdpi/ic_allapps.pngbin6613 -> 3846 bytes
-rw-r--r--res/drawable-mdpi/ic_allapps_pressed.pngbin6048 -> 3979 bytes
-rw-r--r--res/drawable-mdpi/ic_pageindicator_add.pngbin537 -> 669 bytes
-rw-r--r--res/drawable-mdpi/ic_pageindicator_current.pngbin820 -> 738 bytes
-rw-r--r--res/drawable-mdpi/ic_pageindicator_default.pngbin503 -> 746 bytes
-rw-r--r--res/drawable-mdpi/ic_setting.pngbin2065 -> 1577 bytes
-rw-r--r--res/drawable-mdpi/ic_setting_pressed.pngbin2182 -> 1617 bytes
-rw-r--r--res/drawable-mdpi/ic_wallpaper.pngbin1494 -> 1496 bytes
-rw-r--r--res/drawable-mdpi/ic_wallpaper_pressed.pngbin1574 -> 1391 bytes
-rw-r--r--res/drawable-mdpi/ic_widget.pngbin1174 -> 1182 bytes
-rw-r--r--res/drawable-mdpi/ic_widget_pressed.pngbin1487 -> 1177 bytes
-rw-r--r--res/drawable-mdpi/page_hover_left.9.pngbin0 -> 172 bytes
-rw-r--r--res/drawable-mdpi/page_hover_left_active.9.pngbin0 -> 169 bytes
-rw-r--r--res/drawable-mdpi/page_hover_left_holo.9.pngbin2858 -> 0 bytes
-rw-r--r--res/drawable-mdpi/page_hover_right.9.pngbin0 -> 162 bytes
-rw-r--r--res/drawable-mdpi/page_hover_right_active.9.pngbin0 -> 162 bytes
-rw-r--r--res/drawable-mdpi/page_hover_right_holo.9.pngbin180 -> 0 bytes
-rw-r--r--res/drawable-mdpi/quantum_panel.9.pngbin0 -> 476 bytes
-rw-r--r--res/drawable-mdpi/quantum_panel_dark.9.pngbin0 -> 449 bytes
-rw-r--r--res/drawable-mdpi/screenpanel.9.pngbin900 -> 222 bytes
-rw-r--r--res/drawable-mdpi/screenpanel_hover.9.pngbin975 -> 220 bytes
-rw-r--r--res/drawable-mdpi/virtual_preload.9.pngbin0 -> 3395 bytes
-rw-r--r--res/drawable-mdpi/virtual_preload_folder.9.pngbin0 -> 2404 bytes
-rw-r--r--res/drawable-mdpi/widget_container_holo.9.pngbin285 -> 0 bytes
-rw-r--r--res/drawable-nodpi/ic_migration.pngbin0 -> 208114 bytes
-rw-r--r--res/drawable-xhdpi/bg_cling1.pngbin955 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/bg_cling2.pngbin949 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/bg_cling3.pngbin1059 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/bg_cling4.pngbin986 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling.9.pngbin327 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling_arrow_down.pngbin602 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling_arrow_left.pngbin618 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling_arrow_right.pngbin629 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling_arrow_up.pngbin602 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling_bg.9.pngbin0 -> 1829 bytes
-rw-r--r--res/drawable-xhdpi/cling_button.9.pngbin558 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/cling_button_pressed.9.pngbin558 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/custom_content_page.pngbin1297 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/focused_bg.9.pngbin194 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/ic_allapps.pngbin15138 -> 6809 bytes
-rw-r--r--res/drawable-xhdpi/ic_allapps_pressed.pngbin13782 -> 7239 bytes
-rw-r--r--res/drawable-xhdpi/ic_pageindicator_add.pngbin873 -> 1192 bytes
-rw-r--r--res/drawable-xhdpi/ic_pageindicator_current.pngbin1509 -> 1264 bytes
-rw-r--r--res/drawable-xhdpi/ic_pageindicator_default.pngbin828 -> 1301 bytes
-rw-r--r--res/drawable-xhdpi/ic_setting.pngbin4499 -> 2976 bytes
-rw-r--r--res/drawable-xhdpi/ic_setting_pressed.pngbin5300 -> 3013 bytes
-rw-r--r--res/drawable-xhdpi/ic_wallpaper.pngbin2875 -> 2401 bytes
-rw-r--r--res/drawable-xhdpi/ic_wallpaper_pressed.pngbin3504 -> 2209 bytes
-rw-r--r--res/drawable-xhdpi/ic_widget.pngbin1978 -> 1982 bytes
-rw-r--r--res/drawable-xhdpi/ic_widget_pressed.pngbin3360 -> 1883 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_left.9.pngbin0 -> 190 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_left_active.9.pngbin0 -> 189 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_left_holo.9.pngbin2956 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_right.9.pngbin0 -> 192 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_right_active.9.pngbin0 -> 192 bytes
-rw-r--r--res/drawable-xhdpi/page_hover_right_holo.9.pngbin272 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/quantum_panel.9.pngbin0 -> 928 bytes
-rw-r--r--res/drawable-xhdpi/quantum_panel_dark.9.pngbin0 -> 894 bytes
-rw-r--r--res/drawable-xhdpi/screenpanel.9.pngbin1403 -> 273 bytes
-rw-r--r--res/drawable-xhdpi/screenpanel_hover.9.pngbin1704 -> 281 bytes
-rw-r--r--res/drawable-xhdpi/virtual_preload.9.pngbin0 -> 6682 bytes
-rw-r--r--res/drawable-xhdpi/virtual_preload_folder.9.pngbin0 -> 4189 bytes
-rw-r--r--res/drawable-xhdpi/widget_container_holo.9.pngbin460 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/apps_customize_bg.pngbin0 -> 244 bytes
-rw-r--r--res/drawable-xxhdpi/bg_cling1.pngbin2219 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_cling2.pngbin2075 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_cling3.pngbin2274 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_cling4.pngbin2012 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_cling_home.pngbin2346 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_cling_nakasi3.pngbin3004 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling.9.pngbin1279 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling_arrow_down.pngbin1168 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling_arrow_left.pngbin1141 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling_arrow_right.pngbin1127 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling_arrow_up.pngbin1138 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling_bg.9.pngbin0 -> 2936 bytes
-rw-r--r--res/drawable-xxhdpi/cling_button.9.pngbin1597 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/cling_button_pressed.9.pngbin1580 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/focused_bg.9.pngbin2884 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/ic_allapps.pngbin18589 -> 11608 bytes
-rw-r--r--res/drawable-xxhdpi/ic_allapps_pressed.pngbin17006 -> 12588 bytes
-rw-r--r--res/drawable-xxhdpi/ic_pageindicator_add.pngbin1578 -> 1607 bytes
-rw-r--r--res/drawable-xxhdpi/ic_pageindicator_current.pngbin2227 -> 1865 bytes
-rw-r--r--res/drawable-xxhdpi/ic_pageindicator_default.pngbin1532 -> 1812 bytes
-rw-r--r--res/drawable-xxhdpi/ic_setting.pngbin4512 -> 4568 bytes
-rw-r--r--res/drawable-xxhdpi/ic_setting_pressed.pngbin7627 -> 4476 bytes
-rw-r--r--res/drawable-xxhdpi/ic_wallpaper.pngbin3422 -> 3563 bytes
-rw-r--r--res/drawable-xxhdpi/ic_wallpaper_pressed.pngbin5546 -> 3312 bytes
-rw-r--r--res/drawable-xxhdpi/ic_widget.pngbin2413 -> 2857 bytes
-rw-r--r--res/drawable-xxhdpi/ic_widget_pressed.pngbin5518 -> 2772 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_left.9.pngbin0 -> 216 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_left_active.9.pngbin0 -> 216 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_left_holo.9.pngbin3026 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_right.9.pngbin0 -> 233 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_right_active.9.pngbin0 -> 233 bytes
-rw-r--r--res/drawable-xxhdpi/page_hover_right_holo.9.pngbin3015 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/quantum_panel.9.pngbin0 -> 1439 bytes
-rw-r--r--res/drawable-xxhdpi/quantum_panel_dark.9.pngbin0 -> 1366 bytes
-rw-r--r--res/drawable-xxhdpi/screenpanel.9.pngbin5196 -> 346 bytes
-rw-r--r--res/drawable-xxhdpi/screenpanel_hover.9.pngbin2605 -> 334 bytes
-rw-r--r--res/drawable-xxhdpi/virtual_preload.9.pngbin0 -> 11343 bytes
-rw-r--r--res/drawable-xxhdpi/virtual_preload_folder.9.pngbin0 -> 6064 bytes
-rw-r--r--res/drawable-xxhdpi/widget_container_holo.9.pngbin1558 -> 0 bytes
-rw-r--r--res/drawable/bg_migration_cling.xml11
-rw-r--r--res/drawable/cling_arrow_start.xml19
-rw-r--r--res/drawable/cling_button_bg.xml20
-rw-r--r--res/drawable/focusable_view_bg.xml13
-rw-r--r--res/interpolator/decelerate_quart.xml21
-rw-r--r--res/interpolator/decelerate_quint.xml21
-rw-r--r--res/layout-land/first_run_cling.xml97
-rw-r--r--res/layout-land/folder_cling.xml65
-rw-r--r--res/layout-land/launcher.xml39
-rw-r--r--res/layout-land/longpress_cling.xml27
-rw-r--r--res/layout-land/migration_cling.xml141
-rw-r--r--res/layout-land/migration_workspace_cling.xml70
-rw-r--r--res/layout-land/workspace_cling.xml108
-rw-r--r--res/layout-port/first_run_cling.xml100
-rw-r--r--res/layout-port/folder_cling.xml65
-rw-r--r--res/layout-port/launcher.xml39
-rw-r--r--res/layout-port/longpress_cling.xml17
-rw-r--r--res/layout-port/migration_cling.xml136
-rw-r--r--res/layout-port/migration_workspace_cling.xml70
-rw-r--r--res/layout-port/workspace_cling.xml108
-rw-r--r--res/layout-sw600dp-port/first_run_cling.xml100
-rw-r--r--res/layout-sw600dp-port/longpress_cling.xml27
-rw-r--r--res/layout-sw600dp-port/migration_workspace_cling.xml70
-rw-r--r--res/layout-sw600dp/first_run_cling.xml100
-rw-r--r--res/layout-sw600dp/folder_cling.xml66
-rw-r--r--res/layout-sw600dp/migration_cling.xml98
-rw-r--r--res/layout-sw600dp/workspace_cling.xml65
-rw-r--r--res/layout-sw720dp/first_run_cling.xml98
-rw-r--r--res/layout-sw720dp/launcher.xml39
-rw-r--r--res/layout-sw720dp/migration_workspace_cling.xml70
-rw-r--r--res/layout/all_apps_button.xml3
-rw-r--r--res/layout/application.xml3
-rw-r--r--res/layout/apps_customize_application.xml11
-rw-r--r--res/layout/apps_customize_pane.xml71
-rw-r--r--res/layout/apps_customize_widget.xml51
-rw-r--r--res/layout/appwidget_not_ready.xml (renamed from res/values-sw340dp-land/dimens.xml)10
-rw-r--r--res/layout/custom_content_page_indicator_marker.xml39
-rw-r--r--res/layout/folder_application.xml (renamed from res/drawable/cling_arrow_end.xml)10
-rw-r--r--res/layout/folder_icon.xml3
-rw-r--r--res/layout/longpress_cling_content.xml43
-rw-r--r--res/layout/longpress_cling_welcome_content.xml54
-rw-r--r--res/layout/user_folder.xml2
-rw-r--r--res/values-af/strings.xml15
-rw-r--r--res/values-am/strings.xml17
-rw-r--r--res/values-ar/strings.xml15
-rw-r--r--res/values-bg/strings.xml15
-rw-r--r--res/values-bn-rBD/strings.xml121
-rw-r--r--res/values-ca/strings.xml15
-rw-r--r--res/values-cs/strings.xml15
-rw-r--r--res/values-da/strings.xml15
-rw-r--r--res/values-de/strings.xml15
-rw-r--r--res/values-el/strings.xml15
-rw-r--r--res/values-en-rGB/strings.xml15
-rw-r--r--res/values-en-rIN/strings.xml15
-rw-r--r--res/values-en/dimens.xml24
-rw-r--r--res/values-es-rUS/strings.xml15
-rw-r--r--res/values-es/strings.xml15
-rw-r--r--res/values-et-rEE/strings.xml15
-rw-r--r--res/values-et/strings.xml1
-rw-r--r--res/values-eu-rES/strings.xml121
-rw-r--r--res/values-fa/strings.xml15
-rw-r--r--res/values-fi/strings.xml15
-rw-r--r--res/values-fr-rCA/strings.xml15
-rw-r--r--res/values-fr/strings.xml15
-rw-r--r--res/values-gl-rES/strings.xml121
-rw-r--r--res/values-hi/strings.xml19
-rw-r--r--res/values-hr/strings.xml15
-rw-r--r--res/values-hu/strings.xml15
-rw-r--r--res/values-hy-rAM/strings.xml15
-rw-r--r--res/values-in/strings.xml15
-rw-r--r--res/values-is-rIS/strings.xml121
-rw-r--r--res/values-it/strings.xml15
-rw-r--r--res/values-iw/strings.xml15
-rw-r--r--res/values-ja/strings.xml15
-rw-r--r--res/values-ka-rGE/strings.xml15
-rw-r--r--res/values-kk-rKZ/strings.xml122
-rw-r--r--res/values-km-rKH/strings.xml15
-rw-r--r--res/values-kn-rIN/strings.xml121
-rw-r--r--res/values-ko/strings.xml15
-rw-r--r--res/values-ky-rKG/strings.xml122
-rw-r--r--res/values-land/dimens.xml2
-rw-r--r--res/values-lo-rLA/strings.xml15
-rw-r--r--res/values-lt/strings.xml15
-rw-r--r--res/values-lv/strings.xml15
-rw-r--r--res/values-mk-rMK/strings.xml121
-rw-r--r--res/values-ml-rIN/strings.xml121
-rw-r--r--res/values-mn-rMN/strings.xml15
-rw-r--r--res/values-mr-rIN/strings.xml121
-rw-r--r--res/values-ms-rMY/strings.xml15
-rw-r--r--res/values-ms/strings.xml1
-rw-r--r--res/values-my-rMM/strings.xml121
-rw-r--r--res/values-nb/strings.xml15
-rw-r--r--res/values-ne-rNP/strings.xml124
-rw-r--r--res/values-nl/strings.xml15
-rw-r--r--res/values-pl/strings.xml15
-rw-r--r--res/values-port/dimens.xml3
-rw-r--r--res/values-pt-rPT/strings.xml15
-rw-r--r--res/values-pt/strings.xml53
-rw-r--r--res/values-rm/strings.xml10
-rw-r--r--res/values-ro/strings.xml15
-rw-r--r--res/values-ru/strings.xml15
-rw-r--r--res/values-si-rLK/strings.xml122
-rw-r--r--res/values-sk/strings.xml15
-rw-r--r--res/values-sl/strings.xml15
-rw-r--r--res/values-sr/strings.xml15
-rw-r--r--res/values-sv/strings.xml15
-rw-r--r--res/values-sw/strings.xml15
-rw-r--r--res/values-sw600dp/config.xml3
-rw-r--r--res/values-sw600dp/dimens.xml9
-rw-r--r--res/values-sw720dp-land/dimens.xml7
-rw-r--r--res/values-sw720dp-port/dimens.xml8
-rw-r--r--res/values-sw720dp/dimens.xml5
-rw-r--r--res/values-ta-rIN/strings.xml121
-rw-r--r--res/values-te-rIN/strings.xml121
-rw-r--r--res/values-th/strings.xml21
-rw-r--r--res/values-tl/strings.xml15
-rw-r--r--res/values-tr/strings.xml17
-rw-r--r--res/values-uk/strings.xml23
-rw-r--r--res/values-ur-rPK/strings.xml122
-rw-r--r--res/values-uz-rUZ/strings.xml122
-rw-r--r--res/values-vi/strings.xml15
-rw-r--r--res/values-zh-rCN/strings.xml15
-rw-r--r--res/values-zh-rHK/strings.xml19
-rw-r--r--res/values-zh-rTW/strings.xml15
-rw-r--r--res/values-zu/strings.xml15
-rw-r--r--res/values/attrs.xml33
-rw-r--r--res/values/colors.xml11
-rw-r--r--res/values/config.xml23
-rw-r--r--res/values/dimens.xml35
-rw-r--r--res/values/strings.xml46
-rw-r--r--res/values/styles.xml75
-rw-r--r--res/xml/default_workspace_4x4.xml (renamed from res/xml/default_workspace.xml)49
-rw-r--r--res/xml/default_workspace_4x4_no_all_apps.xml (renamed from res/xml/default_workspace_no_all_apps.xml)0
-rw-r--r--res/xml/default_workspace_5x5.xml (renamed from res/xml-sw720dp/default_workspace.xml)55
-rw-r--r--res/xml/default_workspace_5x5_no_all_apps.xml56
-rw-r--r--res/xml/default_workspace_5x6.xml (renamed from res/xml-sw600dp/default_workspace.xml)48
-rw-r--r--res/xml/default_workspace_5x6_no_all_apps.xml56
-rw-r--r--src/com/android/launcher3/AddAdapter.java101
-rw-r--r--src/com/android/launcher3/AllAppsList.java92
-rw-r--r--src/com/android/launcher3/AppInfo.java65
-rw-r--r--src/com/android/launcher3/AppWidgetsRestoredReceiver.java94
-rw-r--r--src/com/android/launcher3/AppsCustomizeCellLayout.java10
-rw-r--r--src/com/android/launcher3/AppsCustomizePagedView.java393
-rw-r--r--src/com/android/launcher3/AppsCustomizeTabHost.java351
-rw-r--r--src/com/android/launcher3/AutoInstallsLayout.java564
-rw-r--r--src/com/android/launcher3/BorderCropDrawable.java90
-rw-r--r--src/com/android/launcher3/BubbleTextView.java329
-rw-r--r--src/com/android/launcher3/CellLayout.java218
-rw-r--r--src/com/android/launcher3/Cling.java571
-rw-r--r--src/com/android/launcher3/DeleteDropTarget.java52
-rw-r--r--src/com/android/launcher3/DeviceProfile.java184
-rw-r--r--src/com/android/launcher3/DragController.java4
-rw-r--r--src/com/android/launcher3/DragLayer.java163
-rw-r--r--src/com/android/launcher3/DynamicGrid.java39
-rw-r--r--src/com/android/launcher3/FastBitmapDrawable.java148
-rw-r--r--src/com/android/launcher3/FastBitmapView.java58
-rw-r--r--src/com/android/launcher3/FocusHelper.java189
-rw-r--r--src/com/android/launcher3/FocusIndicatorView.java146
-rw-r--r--src/com/android/launcher3/Folder.java200
-rw-r--r--src/com/android/launcher3/FolderIcon.java63
-rw-r--r--src/com/android/launcher3/FolderInfo.java13
-rw-r--r--src/com/android/launcher3/HideFromAccessibilityHelper.java114
-rw-r--r--src/com/android/launcher3/HolographicOutlineHelper.java188
-rw-r--r--src/com/android/launcher3/Hotseat.java18
-rw-r--r--src/com/android/launcher3/IconCache.java284
-rw-r--r--src/com/android/launcher3/InfoDropTarget.java18
-rw-r--r--src/com/android/launcher3/InstallShortcutReceiver.java27
-rw-r--r--src/com/android/launcher3/ItemInfo.java68
-rw-r--r--src/com/android/launcher3/Launcher.java1804
-rw-r--r--src/com/android/launcher3/LauncherAnimUtils.java11
-rw-r--r--src/com/android/launcher3/LauncherAppState.java40
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHost.java33
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHostView.java26
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetInfo.java53
-rw-r--r--src/com/android/launcher3/LauncherBackupAgentHelper.java28
-rw-r--r--src/com/android/launcher3/LauncherBackupHelper.java66
-rw-r--r--src/com/android/launcher3/LauncherClings.java514
-rw-r--r--src/com/android/launcher3/LauncherModel.java1160
-rw-r--r--src/com/android/launcher3/LauncherProvider.java886
-rw-r--r--src/com/android/launcher3/LauncherProviderChangeListener.java11
-rw-r--r--src/com/android/launcher3/LauncherSettings.java8
-rw-r--r--src/com/android/launcher3/LogAccelerateInterpolator.java25
-rw-r--r--src/com/android/launcher3/LogDecelerateInterpolator.java26
-rw-r--r--src/com/android/launcher3/MainThreadExecutor.java80
-rw-r--r--src/com/android/launcher3/PagedView.java130
-rw-r--r--src/com/android/launcher3/PagedViewGridLayout.java12
-rw-r--r--src/com/android/launcher3/PagedViewIcon.java134
-rw-r--r--src/com/android/launcher3/PagedViewIconCache.java133
-rw-r--r--src/com/android/launcher3/PagedViewWidget.java4
-rw-r--r--src/com/android/launcher3/Partner.java171
-rw-r--r--src/com/android/launcher3/PendingAppWidgetHostView.java241
-rw-r--r--src/com/android/launcher3/PreloadIconDrawable.java249
-rw-r--r--src/com/android/launcher3/PreloadReceiver.java51
-rw-r--r--src/com/android/launcher3/ScrimView.java42
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java155
-rw-r--r--src/com/android/launcher3/StartupReceiver.java15
-rw-r--r--src/com/android/launcher3/Stats.java10
-rw-r--r--src/com/android/launcher3/Utilities.java197
-rw-r--r--src/com/android/launcher3/WidgetAdder.java7
-rw-r--r--src/com/android/launcher3/WidgetPreviewLoader.java386
-rw-r--r--src/com/android/launcher3/Workspace.java1059
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompat.java82
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java90
-rw-r--r--src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java139
-rw-r--r--src/com/android/launcher3/compat/LauncherActivityInfoCompat.java35
-rw-r--r--src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java99
-rw-r--r--src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java60
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompat.java76
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompatV16.java210
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompatVL.java137
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompat.java75
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompatV16.java175
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompatVL.java201
-rw-r--r--src/com/android/launcher3/compat/UserHandleCompat.java95
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompat.java46
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatV16.java51
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatV17.java42
-rw-r--r--src/com/android/launcher3/compat/UserManagerCompatVL.java65
398 files changed, 13539 insertions, 6930 deletions
diff --git a/Android.mk b/Android.mk
index dbce33f8b..110117be4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -36,7 +36,7 @@ LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/
-LOCAL_SDK_VERSION := 19
+LOCAL_SDK_VERSION := 21
LOCAL_PACKAGE_NAME := Launcher3
#LOCAL_CERTIFICATE := shared
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index d4cd6a2be..591d9b612 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,13 +20,9 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.launcher3">
- <uses-sdk android:targetSdkVersion="19" android:minSdkVersion="16"/>
+ <uses-sdk android:targetSdkVersion="21" android:minSdkVersion="16"/>
<permission
- android:name="com.android.launcher3.permission.PRELOAD_WORKSPACE"
- android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
- android:protectionLevel="signatureOrSystem" />
- <permission
android:name="com.android.launcher.permission.INSTALL_SHORTCUT"
android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
android:protectionLevel="dangerous"
@@ -50,11 +46,13 @@
android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_write_settings"
android:description="@string/permdesc_write_settings"/>
-
<permission
android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"
android:protectionLevel="signature"
/>
+ <permission
+ android:name="com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST"
+ android:protectionLevel="signatureOrSystem" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
@@ -63,19 +61,26 @@
<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="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" />
<application
android:name="com.android.launcher3.LauncherApplication"
- android:label="@string/application_name"
- android:icon="@mipmap/ic_launcher_home"
+ 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:largeHeap="@bool/config_largeHeap"
- android:supportsRtl="true">
+ android:restoreAnyVersion="true"
+ android:supportsRtl="true" >
+
<activity
android:name="com.android.launcher3.Launcher"
android:launchMode="singleTask"
@@ -152,15 +157,6 @@
>
</service>
- <!-- Intent received used to prepopulate the default workspace. -->
- <receiver
- android:name="com.android.launcher3.PreloadReceiver"
- android:permission="com.android.launcher3.permission.PRELOAD_WORKSPACE">
- <intent-filter>
- <action android:name="com.android.launcher3.action.PRELOAD_WORKSPACE" />
- </intent-filter>
- </receiver>
-
<receiver
android:name="com.android.launcher3.WallpaperChangedReceiver">
<intent-filter>
@@ -171,18 +167,25 @@
<!-- Intent received used to install shortcuts from other applications -->
<receiver
android:name="com.android.launcher3.InstallShortcutReceiver"
- android:permission="com.android.launcher3.permission.INSTALL_SHORTCUT">
+ android:permission="com.android.launcher.permission.INSTALL_SHORTCUT">
<intent-filter>
- <action android:name="com.android.launcher3.action.INSTALL_SHORTCUT" />
+ <action android:name="com.android.launcher.action.INSTALL_SHORTCUT" />
</intent-filter>
</receiver>
<!-- Intent received used to uninstall shortcuts from other applications -->
<receiver
android:name="com.android.launcher3.UninstallShortcutReceiver"
- android:permission="com.android.launcher3.permission.UNINSTALL_SHORTCUT">
+ android:permission="com.android.launcher.permission.UNINSTALL_SHORTCUT">
+ <intent-filter>
+ <action android:name="com.android.launcher.action.UNINSTALL_SHORTCUT" />
+ </intent-filter>
+ </receiver>
+
+ <!-- Intent received used to initialize a restored widget -->
+ <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
<intent-filter>
- <action android:name="com.android.launcher3.action.UNINSTALL_SHORTCUT" />
+ <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
</intent-filter>
</receiver>
@@ -204,6 +207,12 @@
</intent-filter>
</receiver>
+ <receiver android:name="com.android.launcher3.StartupReceiver" >
+ <intent-filter>
+ <action android:name="android.intent.action.BOOT_COMPLETED" />
+ </intent-filter>
+ </receiver>
+
<!-- The settings provider contains Home's data, like the workspace favorites -->
<provider
android:name="com.android.launcher3.LauncherProvider"
diff --git a/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png b/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png
new file mode 100755
index 000000000..d9ad51c9b
--- /dev/null
+++ b/WallpaperPicker/res/drawable-xxhdpi/ic_actionbar_accept.png
Binary files differ
diff --git a/WallpaperPicker/res/values-am/strings.xml b/WallpaperPicker/res/values-am/strings.xml
index 394161634..59c3bf733 100644
--- a/WallpaperPicker/res/values-am/strings.xml
+++ b/WallpaperPicker/res/values-am/strings.xml
@@ -19,7 +19,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="wallpaper_instructions" msgid="3524143401182707094">"የግድግዳ ወረቀት አዘጋጅ"</string>
+ <string name="wallpaper_instructions" msgid="3524143401182707094">"ልጣፍ አዘጋጅ"</string>
<string name="image_load_fail" msgid="7538534580694411837">"ምስሉን መጫን አልተቻለም"</string>
<string name="wallpaper_load_fail" msgid="4800700444605404650">"ምስሉን እንደ ግድግዳ ወረቀት መጫን አልተቻለም"</string>
<plurals name="number_of_items_selected">
@@ -27,10 +27,10 @@
<item quantity="one" msgid="8409622005831789373">"%1$d ተመርጧል"</item>
<item quantity="other" msgid="479468347731745357">"%1$d ተመርጧል"</item>
</plurals>
- <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"የግድግዳ ወረቀት %1$d የ%2$d"</string>
+ <string name="wallpaper_accessibility_name" msgid="4093221025304876354">"ልጣፍ %1$d የ%2$d"</string>
<string name="announce_selection" msgid="123723511662250539">"<xliff:g id="LABEL">%1$s</xliff:g> ተመርጧል"</string>
<string name="wallpaper_delete" msgid="1459353972739215344">"ሰርዝ"</string>
<string name="pick_image" msgid="6704438906027442697">"ምስል ይምረጡ"</string>
<string name="pick_wallpaper" msgid="4628969645948454559">"የግድግዳ ወረቀቶች"</string>
- <string name="crop_wallpaper" msgid="4882870800623585836">"የግድግዳ ወረቀት ይከርክሙ"</string>
+ <string name="crop_wallpaper" msgid="4882870800623585836">"ልጣፍ ይከርክሙ"</string>
</resources>
diff --git a/WallpaperPicker/res/values-th/strings.xml b/WallpaperPicker/res/values-th/strings.xml
index c81720563..6b4c23536 100644
--- a/WallpaperPicker/res/values-th/strings.xml
+++ b/WallpaperPicker/res/values-th/strings.xml
@@ -29,7 +29,7 @@
</plurals>
<string name="wallpaper_accessibility_name" msgid="4093221025304876354">"วอลเปเปอร์ %1$d จาก %2$d"</string>
<string name="announce_selection" msgid="123723511662250539">"เลือก <xliff:g id="LABEL">%1$s</xliff:g> แล้ว"</string>
- <string name="wallpaper_delete" msgid="1459353972739215344">"นำออก"</string>
+ <string name="wallpaper_delete" msgid="1459353972739215344">"ลบ"</string>
<string name="pick_image" msgid="6704438906027442697">"เลือกรูปภาพ"</string>
<string name="pick_wallpaper" msgid="4628969645948454559">"วอลเปเปอร์"</string>
<string name="crop_wallpaper" msgid="4882870800623585836">"ครอบตัดวอลเปเปอร์"</string>
diff --git a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
index 44bfdf1f9..2bdf8f1cd 100644
--- a/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/WallpaperPicker/src/com/android/launcher3/SavedWallpaperImages.java
@@ -34,8 +34,6 @@ import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListAdapter;
-import com.android.photos.BitmapRegionTileSource;
-
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -49,39 +47,17 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
Context mContext;
LayoutInflater mLayoutInflater;
- public static class SavedWallpaperTile extends WallpaperPickerActivity.WallpaperTileInfo {
+ public static class SavedWallpaperTile extends WallpaperPickerActivity.FileWallpaperInfo {
private int mDbId;
- private Drawable mThumb;
- public SavedWallpaperTile(int dbId, Drawable thumb) {
+ public SavedWallpaperTile(int dbId, File target, Drawable thumb) {
+ super(target, thumb);
mDbId = dbId;
- mThumb = thumb;
- }
- @Override
- public void onClick(WallpaperPickerActivity a) {
- String imageFilename = a.getSavedImages().getImageFilename(mDbId);
- File file = new File(a.getFilesDir(), imageFilename);
- BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
- new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024);
- a.setCropViewTileSource(bitmapSource, false, true, null);
- }
- @Override
- public void onSave(WallpaperPickerActivity a) {
- boolean finishActivityWhenDone = true;
- String imageFilename = a.getSavedImages().getImageFilename(mDbId);
- a.setWallpaper(imageFilename, finishActivityWhenDone);
}
+
@Override
public void onDelete(WallpaperPickerActivity a) {
a.getSavedImages().deleteImage(mDbId);
}
- @Override
- public boolean isSelectable() {
- return true;
- }
- @Override
- public boolean isNamelessWallpaper() {
- return true;
- }
}
public SavedWallpaperImages(Activity context) {
@@ -98,7 +74,8 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
SQLiteDatabase db = mDb.getReadableDatabase();
Cursor result = db.query(ImageDb.TABLE_NAME,
new String[] { ImageDb.COLUMN_ID,
- ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME }, // cols to return
+ ImageDb.COLUMN_IMAGE_THUMBNAIL_FILENAME,
+ ImageDb.COLUMN_IMAGE_FILENAME}, // cols to return
null, // select query
null, // args to select query
null,
@@ -112,7 +89,9 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
Bitmap thumb = BitmapFactory.decodeFile(file.getAbsolutePath());
if (thumb != null) {
- mImages.add(new SavedWallpaperTile(result.getInt(0), new BitmapDrawable(thumb)));
+ mImages.add(new SavedWallpaperTile(result.getInt(0),
+ new File(mContext.getFilesDir(), result.getString(2)),
+ new BitmapDrawable(thumb)));
}
}
result.close();
@@ -136,15 +115,7 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
}
return WallpaperPickerActivity.createImageTileView(
- mLayoutInflater, position, convertView, parent, thumbDrawable);
- }
-
- public String getImageFilename(int id) {
- Pair<String, String> filenames = getImageFilenames(id);
- if (filenames != null) {
- return filenames.second;
- }
- return null;
+ mLayoutInflater, convertView, parent, thumbDrawable);
}
private Pair<String, String> getImageFilenames(int id) {
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
index 561c4bb6a..d5c7cd93d 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java
@@ -133,6 +133,14 @@ public class WallpaperCropActivity extends Activity {
setCropViewTileSource(bitmapSource, true, false, onLoad);
}
+ @Override
+ protected void onDestroy() {
+ if (mCropView != null) {
+ mCropView.destroy();
+ }
+ super.onDestroy();
+ }
+
public void setCropViewTileSource(
final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
final boolean moveToLeft, final Runnable postExecute) {
@@ -141,7 +149,21 @@ public class WallpaperCropActivity extends Activity {
final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
protected Void doInBackground(Void...args) {
if (!isCancelled()) {
- bitmapSource.loadInBackground();
+ try {
+ bitmapSource.loadInBackground();
+ } catch (SecurityException securityException) {
+ if (isDestroyed()) {
+ // Temporarily granted permissions are revoked when the activity
+ // finishes, potentially resulting in a SecurityException here.
+ // Even though {@link #isDestroyed} might also return true in different
+ // situations where the configuration changes, we are fine with
+ // catching these cases here as well.
+ cancel(false);
+ } else {
+ // otherwise it had a different cause and we throw it further
+ throw securityException;
+ }
+ }
}
return null;
}
@@ -286,10 +308,10 @@ public class WallpaperCropActivity extends Activity {
return 0;
}
- protected void setWallpaper(String filePath, final boolean finishActivityWhenDone) {
- int rotation = getRotationFromExif(filePath);
+ protected void setWallpaper(Uri uri, final boolean finishActivityWhenDone) {
+ int rotation = getRotationFromExif(this, uri);
BitmapCropTask cropTask = new BitmapCropTask(
- this, filePath, null, rotation, 0, 0, true, false, null);
+ this, uri, null, rotation, 0, 0, true, false, null);
final Point bounds = cropTask.getImageBounds();
Runnable onEndCrop = new Runnable() {
public void run() {
@@ -352,10 +374,13 @@ public class WallpaperCropActivity extends Activity {
getWindowManager());
// Get the crop
RectF cropRect = mCropView.getCrop();
+
+ Point inSize = mCropView.getSourceDimensions();
+
int cropRotation = mCropView.getImageRotation();
float cropScale = mCropView.getWidth() / (float) cropRect.width();
- Point inSize = mCropView.getSourceDimensions();
+
Matrix rotateMatrix = new Matrix();
rotateMatrix.setRotate(cropRotation);
float[] rotatedInSize = new float[] { inSize.x, inSize.y };
@@ -363,6 +388,14 @@ public class WallpaperCropActivity extends Activity {
rotatedInSize[0] = Math.abs(rotatedInSize[0]);
rotatedInSize[1] = Math.abs(rotatedInSize[1]);
+
+ // due to rounding errors in the cropview renderer the edges can be slightly offset
+ // therefore we ensure that the boundaries are sanely defined
+ cropRect.left = Math.max(0, cropRect.left);
+ cropRect.right = Math.min(rotatedInSize[0], cropRect.right);
+ cropRect.top = Math.max(0, cropRect.top);
+ cropRect.bottom = Math.min(rotatedInSize[1], cropRect.bottom);
+
// ADJUST CROP WIDTH
// Extend the crop all the way to the right, for parallax
// (or all the way to the left, in RTL)
@@ -799,17 +832,28 @@ public class WallpaperCropActivity extends Activity {
editor.commit();
suggestWallpaperDimension(getResources(),
- sp, getWindowManager(), WallpaperManager.getInstance(this));
+ sp, getWindowManager(), WallpaperManager.getInstance(this), true);
}
static public void suggestWallpaperDimension(Resources res,
final SharedPreferences sharedPrefs,
WindowManager windowManager,
- final WallpaperManager wallpaperManager) {
+ final WallpaperManager wallpaperManager, boolean fallBackToDefaults) {
final Point defaultWallpaperSize = getDefaultWallpaperSize(res, windowManager);
// If we have saved a wallpaper width/height, use that instead
- int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, defaultWallpaperSize.x);
- int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, defaultWallpaperSize.y);
+
+ int savedWidth = sharedPrefs.getInt(WALLPAPER_WIDTH_KEY, -1);
+ int savedHeight = sharedPrefs.getInt(WALLPAPER_HEIGHT_KEY, -1);
+
+ if (savedWidth == -1 || savedHeight == -1) {
+ if (!fallBackToDefaults) {
+ return;
+ } else {
+ savedWidth = defaultWallpaperSize.x;
+ savedHeight = defaultWallpaperSize.y;
+ }
+ }
+
if (savedWidth != wallpaperManager.getDesiredMinimumWidth() ||
savedHeight != wallpaperManager.getDesiredMinimumHeight()) {
wallpaperManager.suggestDesiredDimensions(savedWidth, savedHeight);
diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
index 013606a50..07285372e 100644
--- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import android.animation.LayoutTransition;
+import android.annotation.TargetApi;
import android.app.ActionBar;
import android.app.Activity;
import android.app.WallpaperInfo;
@@ -61,12 +62,12 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
+import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
-import android.widget.ListAdapter;
import android.widget.Toast;
import com.android.photos.BitmapRegionTileSource;
@@ -109,6 +110,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
public static abstract class WallpaperTileInfo {
protected View mView;
+ public Drawable mThumb;
+
public void setView(View v) {
mView = v;
}
@@ -194,10 +197,36 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
+ public static class FileWallpaperInfo extends WallpaperTileInfo {
+ private File mFile;
+
+ public FileWallpaperInfo(File target, Drawable thumb) {
+ mFile = target;
+ mThumb = thumb;
+ }
+ @Override
+ public void onClick(WallpaperPickerActivity a) {
+ BitmapRegionTileSource.UriBitmapSource bitmapSource =
+ new BitmapRegionTileSource.UriBitmapSource(a, Uri.fromFile(mFile), 1024);
+ a.setCropViewTileSource(bitmapSource, false, true, null);
+ }
+ @Override
+ public void onSave(WallpaperPickerActivity a) {
+ a.setWallpaper(Uri.fromFile(mFile), true);
+ }
+ @Override
+ public boolean isSelectable() {
+ return true;
+ }
+ @Override
+ public boolean isNamelessWallpaper() {
+ return true;
+ }
+ }
+
public static class ResourceWallpaperInfo extends WallpaperTileInfo {
private Resources mResources;
private int mResId;
- private Drawable mThumb;
public ResourceWallpaperInfo(Resources res, int resId, Drawable thumb) {
mResources = res;
@@ -237,8 +266,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
+ @TargetApi(Build.VERSION_CODES.KITKAT)
public static class DefaultWallpaperInfo extends WallpaperTileInfo {
- public Drawable mThumb;
public DefaultWallpaperInfo(Drawable thumb) {
mThumb = thumb;
}
@@ -431,9 +460,9 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
};
// Populate the built-in wallpapers
- ArrayList<ResourceWallpaperInfo> wallpapers = findBundledWallpapers();
+ ArrayList<WallpaperTileInfo> wallpapers = findBundledWallpapers();
mWallpapersView = (LinearLayout) findViewById(R.id.wallpaper_list);
- BuiltInWallpapersAdapter ia = new BuiltInWallpapersAdapter(this, wallpapers);
+ SimpleWallpapersAdapter ia = new SimpleWallpapersAdapter(this, wallpapers);
populateWallpapersFromAdapter(mWallpapersView, ia, false);
// Populate the saved wallpapers
@@ -484,20 +513,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
pickImageInfo.setView(pickImageTile);
pickImageTile.setOnClickListener(mThumbnailOnClickListener);
- // Add a tile for the default wallpaper
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- DefaultWallpaperInfo defaultWallpaperInfo = getDefaultWallpaper();
- if (defaultWallpaperInfo != null) {
- FrameLayout defaultWallpaperTile = (FrameLayout) createImageTileView(
- getLayoutInflater(), 0, null, mWallpapersView, defaultWallpaperInfo.mThumb);
- setWallpaperItemPaddingToZero(defaultWallpaperTile);
- defaultWallpaperTile.setTag(defaultWallpaperInfo);
- mWallpapersView.addView(defaultWallpaperTile, 0);
- defaultWallpaperTile.setOnClickListener(mThumbnailOnClickListener);
- defaultWallpaperInfo.setView(defaultWallpaperTile);
- }
- }
-
// Select the first item; wait for a layout pass so that we initialize the dimensions of
// cropView or the defaultWallpaperView first
mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@@ -674,13 +689,16 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
new String[] { MediaStore.Images.ImageColumns._ID,
MediaStore.Images.ImageColumns.DATE_TAKEN},
null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC LIMIT 1");
+
Bitmap thumb = null;
- if (cursor.moveToNext()) {
- int id = cursor.getInt(0);
- thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),
- id, MediaStore.Images.Thumbnails.MINI_KIND, null);
+ if (cursor != null) {
+ if (cursor.moveToNext()) {
+ int id = cursor.getInt(0);
+ thumb = MediaStore.Images.Thumbnails.getThumbnail(getContentResolver(),
+ id, MediaStore.Images.Thumbnails.MINI_KIND, null);
+ }
+ cursor.close();
}
- cursor.close();
return thumb;
}
@@ -822,12 +840,26 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
final Context context = this;
new AsyncTask<Void, Bitmap, Bitmap>() {
protected Bitmap doInBackground(Void...args) {
- int rotation = WallpaperCropActivity.getRotationFromExif(context, uri);
- return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
-
+ try {
+ int rotation = WallpaperCropActivity.getRotationFromExif(context, uri);
+ return createThumbnail(defaultSize, context, uri, null, null, 0, rotation, false);
+ } catch (SecurityException securityException) {
+ if (isDestroyed()) {
+ // Temporarily granted permissions are revoked when the activity
+ // finishes, potentially resulting in a SecurityException here.
+ // Even though {@link #isDestroyed} might also return true in different
+ // situations where the configuration changes, we are fine with
+ // catching these cases here as well.
+ cancel(false);
+ } else {
+ // otherwise it had a different cause and we throw it further
+ throw securityException;
+ }
+ return null;
+ }
}
protected void onPostExecute(Bitmap thumb) {
- if (thumb != null) {
+ if (!isCancelled() && thumb != null) {
image.setImageBitmap(thumb);
Drawable thumbDrawable = image.getDrawable();
thumbDrawable.setDither(true);
@@ -886,27 +918,68 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
v.setOnLongClickListener(mLongClickListener);
}
- private ArrayList<ResourceWallpaperInfo> findBundledWallpapers() {
- ArrayList<ResourceWallpaperInfo> bundledWallpapers =
- new ArrayList<ResourceWallpaperInfo>(24);
+ private ArrayList<WallpaperTileInfo> findBundledWallpapers() {
+ final PackageManager pm = getPackageManager();
+ final ArrayList<WallpaperTileInfo> bundled = new ArrayList<WallpaperTileInfo>(24);
+
+ Partner partner = Partner.get(pm);
+ if (partner != null) {
+ final Resources partnerRes = partner.getResources();
+ final int resId = partnerRes.getIdentifier(Partner.RES_WALLPAPERS, "array",
+ partner.getPackageName());
+ if (resId != 0) {
+ addWallpapers(bundled, partnerRes, partner.getPackageName(), resId);
+ }
+
+ // Add system wallpapers
+ File systemDir = partner.getWallpaperDirectory();
+ if (systemDir != null && systemDir.isDirectory()) {
+ for (File file : systemDir.listFiles()) {
+ if (!file.isFile()) {
+ continue;
+ }
+ String name = file.getName();
+ int dotPos = name.lastIndexOf('.');
+ String extension = "";
+ if (dotPos >= -1) {
+ extension = name.substring(dotPos);
+ name = name.substring(0, dotPos);
+ }
+
+ if (name.endsWith("_small")) {
+ // it is a thumbnail
+ continue;
+ }
+
+ File thumbnail = new File(systemDir, name + "_small" + extension);
+ Bitmap thumb = BitmapFactory.decodeFile(thumbnail.getAbsolutePath());
+ if (thumb != null) {
+ bundled.add(new FileWallpaperInfo(file, new BitmapDrawable(thumb)));
+ }
+ }
+ }
+ }
Pair<ApplicationInfo, Integer> r = getWallpaperArrayResourceId();
if (r != null) {
try {
Resources wallpaperRes = getPackageManager().getResourcesForApplication(r.first);
- bundledWallpapers = addWallpapers(wallpaperRes, r.first.packageName, r.second);
+ addWallpapers(bundled, wallpaperRes, r.first.packageName, r.second);
} catch (PackageManager.NameNotFoundException e) {
}
}
- // Add an entry for the default wallpaper (stored in system resources)
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- ResourceWallpaperInfo defaultWallpaperInfo = getPreKKDefaultWallpaperInfo();
+ if (partner == null || !partner.hideDefaultWallpaper()) {
+ // Add an entry for the default wallpaper (stored in system resources)
+ WallpaperTileInfo defaultWallpaperInfo =
+ (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+ ? getPreKKDefaultWallpaperInfo()
+ : getDefaultWallpaper();
if (defaultWallpaperInfo != null) {
- bundledWallpapers.add(0, defaultWallpaperInfo);
+ bundled.add(0, defaultWallpaperInfo);
}
}
- return bundledWallpapers;
+ return bundled;
}
private boolean writeImageToFileAsJpeg(File f, Bitmap b) {
@@ -924,11 +997,27 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
return false;
}
+ private File getDefaultThumbFile() {
+ return new File(getFilesDir(), Build.VERSION.SDK_INT
+ + "_" + DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+ }
+
+ private boolean saveDefaultWallpaperThumb(Bitmap b) {
+ // Delete old thumbnails.
+ new File(getFilesDir(), OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
+ new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
+
+ for (int i = Build.VERSION_CODES.JELLY_BEAN; i < Build.VERSION.SDK_INT; i++) {
+ new File(getFilesDir(), i + "_" + DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
+ }
+ return writeImageToFileAsJpeg(getDefaultThumbFile(), b);
+ }
+
private ResourceWallpaperInfo getPreKKDefaultWallpaperInfo() {
Resources sysRes = Resources.getSystem();
int resId = sysRes.getIdentifier("default_wallpaper", "drawable", "android");
- File defaultThumbFile = new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+ File defaultThumbFile = getDefaultThumbFile();
Bitmap thumb = null;
boolean defaultWallpaperExists = false;
if (defaultThumbFile.exists()) {
@@ -941,7 +1030,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
thumb = createThumbnail(
defaultThumbSize, this, null, null, sysRes, resId, rotation, false);
if (thumb != null) {
- defaultWallpaperExists = writeImageToFileAsJpeg(defaultThumbFile, thumb);
+ defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
}
}
if (defaultWallpaperExists) {
@@ -950,18 +1039,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
return null;
}
+ @TargetApi(Build.VERSION_CODES.KITKAT)
private DefaultWallpaperInfo getDefaultWallpaper() {
- File defaultThumbFile = new File(getFilesDir(), DEFAULT_WALLPAPER_THUMBNAIL_FILENAME);
+ File defaultThumbFile = getDefaultThumbFile();
Bitmap thumb = null;
boolean defaultWallpaperExists = false;
if (defaultThumbFile.exists()) {
thumb = BitmapFactory.decodeFile(defaultThumbFile.getAbsolutePath());
defaultWallpaperExists = true;
} else {
- // Delete old thumbnail file, since we had a bug where the thumbnail wasn't being drawn
- // before
- new File(getFilesDir(), OLD_DEFAULT_WALLPAPER_THUMBNAIL_FILENAME).delete();
-
Resources res = getResources();
Point defaultThumbSize = getDefaultThumbnailSize(res);
Drawable wallpaperDrawable = WallpaperManager.getInstance(this).getBuiltInDrawable(
@@ -975,7 +1061,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
c.setBitmap(null);
}
if (thumb != null) {
- defaultWallpaperExists = writeImageToFileAsJpeg(defaultThumbFile, thumb);
+ defaultWallpaperExists = saveDefaultWallpaperThumb(thumb);
}
}
if (defaultWallpaperExists) {
@@ -998,10 +1084,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- private ArrayList<ResourceWallpaperInfo> addWallpapers(
- Resources res, String packageName, int listResId) {
- ArrayList<ResourceWallpaperInfo> bundledWallpapers =
- new ArrayList<ResourceWallpaperInfo>(24);
+ private void addWallpapers(ArrayList<WallpaperTileInfo> known, Resources res,
+ String packageName, int listResId) {
final String[] extras = res.getStringArray(listResId);
for (String extra : extras) {
int resId = res.getIdentifier(extra, "drawable", packageName);
@@ -1011,14 +1095,13 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
if (thumbRes != 0) {
ResourceWallpaperInfo wallpaperInfo =
new ResourceWallpaperInfo(res, resId, res.getDrawable(thumbRes));
- bundledWallpapers.add(wallpaperInfo);
+ known.add(wallpaperInfo);
// Log.d(TAG, "add: [" + packageName + "]: " + extra + " (" + res + ")");
}
} else {
Log.e(TAG, "Couldn't find wallpaper " + extra);
}
}
- return bundledWallpapers;
}
public CropView getCropView() {
@@ -1048,37 +1131,24 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- private static class BuiltInWallpapersAdapter extends BaseAdapter implements ListAdapter {
- private LayoutInflater mLayoutInflater;
- private ArrayList<ResourceWallpaperInfo> mWallpapers;
+ private static class SimpleWallpapersAdapter extends ArrayAdapter<WallpaperTileInfo> {
+ private final LayoutInflater mLayoutInflater;
- BuiltInWallpapersAdapter(Activity activity, ArrayList<ResourceWallpaperInfo> wallpapers) {
+ SimpleWallpapersAdapter(Activity activity, ArrayList<WallpaperTileInfo> wallpapers) {
+ super(activity, R.layout.wallpaper_picker_item, wallpapers);
mLayoutInflater = activity.getLayoutInflater();
- mWallpapers = wallpapers;
- }
-
- public int getCount() {
- return mWallpapers.size();
- }
-
- public ResourceWallpaperInfo getItem(int position) {
- return mWallpapers.get(position);
- }
-
- public long getItemId(int position) {
- return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
- Drawable thumb = mWallpapers.get(position).mThumb;
+ Drawable thumb = getItem(position).mThumb;
if (thumb == null) {
Log.e(TAG, "Error decoding thumbnail for wallpaper #" + position);
}
- return createImageTileView(mLayoutInflater, position, convertView, parent, thumb);
+ return createImageTileView(mLayoutInflater, convertView, parent, thumb);
}
}
- public static View createImageTileView(LayoutInflater layoutInflater, int position,
+ public static View createImageTileView(LayoutInflater layoutInflater,
View convertView, ViewGroup parent, Drawable thumb) {
View view;
diff --git a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
index 764156de0..66ece4ff6 100644
--- a/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
+++ b/WallpaperPicker/src/com/android/photos/BitmapRegionTileSource.java
@@ -283,9 +283,6 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
} catch (FileNotFoundException e) {
Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
return null;
- } catch (IOException e) {
- Log.e("BitmapRegionTileSource", "Failure while reading URI " + mUri, e);
- return null;
}
}
@Override
diff --git a/proguard.flags b/proguard.flags
index a922e919d..0b28c0ef4 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -52,3 +52,8 @@
-keep class com.android.launcher3.MemoryDumpActivity {
*;
}
+
+-keep class com.android.launcher3.PreloadIconDrawable {
+ public float getAnimationProgress();
+ public void setAnimationProgress(float);
+}
diff --git a/res/values-sw340dp-port/dimens.xml b/res/anim/no_anim.xml
index e36056570..02b162519 100644
--- a/res/values-sw340dp-port/dimens.xml
+++ b/res/anim/no_anim.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -14,7 +14,5 @@
limitations under the License.
-->
-<resources>
-<!-- Clings -->
- <dimen name="folderClingMarginTop">70dp</dimen>
-</resources>
+<alpha xmlns:android="http://schemas.android.com/apk/res/android"
+ android:duration="417" />
diff --git a/res/anim/task_open_enter.xml b/res/anim/task_open_enter.xml
new file mode 100644
index 000000000..3eb191537
--- /dev/null
+++ b/res/anim/task_open_enter.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+
+ <alpha android:fromAlpha="0" android:toAlpha="1.0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quart"
+ android:startOffset="0"
+ android:duration="167"/>
+
+ <translate android:fromYDelta="110%" android:toYDelta="0"
+ android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
+ android:interpolator="@interpolator/decelerate_quint"
+ android:startOffset="0"
+ android:duration="417" />
+</set>
diff --git a/res/drawable-hdpi/bg_cling1.png b/res/drawable-hdpi/bg_cling1.png
deleted file mode 100644
index 0e1553299..000000000
--- a/res/drawable-hdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/bg_cling2.png b/res/drawable-hdpi/bg_cling2.png
deleted file mode 100644
index e65d9a21c..000000000
--- a/res/drawable-hdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/bg_cling3.png b/res/drawable-hdpi/bg_cling3.png
deleted file mode 100644
index ea71fbdb3..000000000
--- a/res/drawable-hdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/bg_cling4.png b/res/drawable-hdpi/bg_cling4.png
deleted file mode 100644
index 94036677e..000000000
--- a/res/drawable-hdpi/bg_cling4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling.9.png b/res/drawable-hdpi/cling.9.png
deleted file mode 100644
index 36fbfc8b6..000000000
--- a/res/drawable-hdpi/cling.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling_arrow_down.png b/res/drawable-hdpi/cling_arrow_down.png
deleted file mode 100644
index 4f521eadc..000000000
--- a/res/drawable-hdpi/cling_arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling_arrow_left.png b/res/drawable-hdpi/cling_arrow_left.png
deleted file mode 100644
index 13764c9e2..000000000
--- a/res/drawable-hdpi/cling_arrow_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling_arrow_right.png b/res/drawable-hdpi/cling_arrow_right.png
deleted file mode 100644
index be522441d..000000000
--- a/res/drawable-hdpi/cling_arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling_arrow_up.png b/res/drawable-hdpi/cling_arrow_up.png
deleted file mode 100644
index 83b5b3783..000000000
--- a/res/drawable-hdpi/cling_arrow_up.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling_bg.9.png b/res/drawable-hdpi/cling_bg.9.png
new file mode 100644
index 000000000..e173ba595
--- /dev/null
+++ b/res/drawable-hdpi/cling_bg.9.png
Binary files differ
diff --git a/res/drawable-hdpi/cling_button.9.png b/res/drawable-hdpi/cling_button.9.png
deleted file mode 100644
index e30838241..000000000
--- a/res/drawable-hdpi/cling_button.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/cling_button_pressed.9.png b/res/drawable-hdpi/cling_button_pressed.9.png
deleted file mode 100644
index 4f9ca6f7a..000000000
--- a/res/drawable-hdpi/cling_button_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/custom_content_page.png b/res/drawable-hdpi/custom_content_page.png
deleted file mode 100644
index 9eef50c92..000000000
--- a/res/drawable-hdpi/custom_content_page.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/focused_bg.9.png b/res/drawable-hdpi/focused_bg.9.png
deleted file mode 100644
index 2925ae858..000000000
--- a/res/drawable-hdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/ic_allapps.png b/res/drawable-hdpi/ic_allapps.png
index e7677d5d2..b98e65f99 100644
--- a/res/drawable-hdpi/ic_allapps.png
+++ b/res/drawable-hdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_allapps_pressed.png b/res/drawable-hdpi/ic_allapps_pressed.png
index 863eebaa5..b7eaa675f 100644
--- a/res/drawable-hdpi/ic_allapps_pressed.png
+++ b/res/drawable-hdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_add.png b/res/drawable-hdpi/ic_pageindicator_add.png
index c37d622e5..ab0e5dbd5 100644
--- a/res/drawable-hdpi/ic_pageindicator_add.png
+++ b/res/drawable-hdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_current.png b/res/drawable-hdpi/ic_pageindicator_current.png
index aac8d406d..2e841f5f5 100644
--- a/res/drawable-hdpi/ic_pageindicator_current.png
+++ b/res/drawable-hdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_pageindicator_default.png b/res/drawable-hdpi/ic_pageindicator_default.png
index bafd94b87..07ab94865 100644
--- a/res/drawable-hdpi/ic_pageindicator_default.png
+++ b/res/drawable-hdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting.png b/res/drawable-hdpi/ic_setting.png
index 3f5bc43fd..1c12a5b80 100644
--- a/res/drawable-hdpi/ic_setting.png
+++ b/res/drawable-hdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_setting_pressed.png b/res/drawable-hdpi/ic_setting_pressed.png
index 9201064cf..d5b5ca204 100644
--- a/res/drawable-hdpi/ic_setting_pressed.png
+++ b/res/drawable-hdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_wallpaper.png b/res/drawable-hdpi/ic_wallpaper.png
index 5e5d1186b..34d594336 100644
--- a/res/drawable-hdpi/ic_wallpaper.png
+++ b/res/drawable-hdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_wallpaper_pressed.png b/res/drawable-hdpi/ic_wallpaper_pressed.png
index d104e5770..1588ce717 100644
--- a/res/drawable-hdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-hdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget.png b/res/drawable-hdpi/ic_widget.png
index 8c57af0de..ed7e1ca0d 100644
--- a/res/drawable-hdpi/ic_widget.png
+++ b/res/drawable-hdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_widget_pressed.png b/res/drawable-hdpi/ic_widget_pressed.png
index 081f9f9ce..19d6feded 100644
--- a/res/drawable-hdpi/ic_widget_pressed.png
+++ b/res/drawable-hdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left.9.png b/res/drawable-hdpi/page_hover_left.9.png
new file mode 100644
index 000000000..cc029d816
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_active.9.png b/res/drawable-hdpi/page_hover_left_active.9.png
new file mode 100644
index 000000000..20c91a0fd
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_left_holo.9.png b/res/drawable-hdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 8a1aa5fe8..000000000
--- a/res/drawable-hdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right.9.png b/res/drawable-hdpi/page_hover_right.9.png
new file mode 100644
index 000000000..a42822ad8
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_active.9.png b/res/drawable-hdpi/page_hover_right_active.9.png
new file mode 100644
index 000000000..523fafd6d
--- /dev/null
+++ b/res/drawable-hdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-hdpi/page_hover_right_holo.9.png b/res/drawable-hdpi/page_hover_right_holo.9.png
deleted file mode 100644
index abf8f5173..000000000
--- a/res/drawable-hdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-hdpi/quantum_panel.9.png b/res/drawable-hdpi/quantum_panel.9.png
new file mode 100644
index 000000000..b4ac9c0a0
--- /dev/null
+++ b/res/drawable-hdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-hdpi/quantum_panel_dark.9.png b/res/drawable-hdpi/quantum_panel_dark.9.png
new file mode 100644
index 000000000..abaf23026
--- /dev/null
+++ b/res/drawable-hdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel.9.png b/res/drawable-hdpi/screenpanel.9.png
index 36e7dfd9c..f7ae01162 100644
--- a/res/drawable-hdpi/screenpanel.9.png
+++ b/res/drawable-hdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-hdpi/screenpanel_hover.9.png b/res/drawable-hdpi/screenpanel_hover.9.png
index 0fed7c9d8..ac8e83dbd 100644
--- a/res/drawable-hdpi/screenpanel_hover.9.png
+++ b/res/drawable-hdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-hdpi/virtual_preload.9.png b/res/drawable-hdpi/virtual_preload.9.png
new file mode 100644
index 000000000..71e53263e
--- /dev/null
+++ b/res/drawable-hdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-hdpi/virtual_preload_folder.9.png b/res/drawable-hdpi/virtual_preload_folder.9.png
new file mode 100644
index 000000000..ece322669
--- /dev/null
+++ b/res/drawable-hdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-hdpi/widget_container_holo.9.png b/res/drawable-hdpi/widget_container_holo.9.png
deleted file mode 100644
index 8c15a7c86..000000000
--- a/res/drawable-hdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-hdpi/bg_cling1.png b/res/drawable-land-hdpi/bg_cling1.png
deleted file mode 100644
index 7123c5ca7..000000000
--- a/res/drawable-land-hdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-hdpi/bg_cling2.png b/res/drawable-land-hdpi/bg_cling2.png
deleted file mode 100644
index 889b6274e..000000000
--- a/res/drawable-land-hdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-hdpi/bg_cling3.png b/res/drawable-land-hdpi/bg_cling3.png
deleted file mode 100644
index 4ff338c13..000000000
--- a/res/drawable-land-hdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-mdpi/bg_cling1.png b/res/drawable-land-mdpi/bg_cling1.png
deleted file mode 100644
index f5faeb439..000000000
--- a/res/drawable-land-mdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-mdpi/bg_cling2.png b/res/drawable-land-mdpi/bg_cling2.png
deleted file mode 100644
index 963967d30..000000000
--- a/res/drawable-land-mdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-mdpi/bg_cling3.png b/res/drawable-land-mdpi/bg_cling3.png
deleted file mode 100644
index 921831af9..000000000
--- a/res/drawable-land-mdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xhdpi/bg_cling1.png b/res/drawable-land-xhdpi/bg_cling1.png
deleted file mode 100644
index 2282117ca..000000000
--- a/res/drawable-land-xhdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xhdpi/bg_cling2.png b/res/drawable-land-xhdpi/bg_cling2.png
deleted file mode 100644
index 5243889be..000000000
--- a/res/drawable-land-xhdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xhdpi/bg_cling3.png b/res/drawable-land-xhdpi/bg_cling3.png
deleted file mode 100644
index 08475f732..000000000
--- a/res/drawable-land-xhdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xxhdpi/bg_cling1.png b/res/drawable-land-xxhdpi/bg_cling1.png
deleted file mode 100644
index 78943f01c..000000000
--- a/res/drawable-land-xxhdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xxhdpi/bg_cling2.png b/res/drawable-land-xxhdpi/bg_cling2.png
deleted file mode 100644
index 98b65682b..000000000
--- a/res/drawable-land-xxhdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-land-xxhdpi/bg_cling3.png b/res/drawable-land-xxhdpi/bg_cling3.png
deleted file mode 100644
index e249fe598..000000000
--- a/res/drawable-land-xxhdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bg_cling1.png b/res/drawable-mdpi/bg_cling1.png
deleted file mode 100644
index f284412a7..000000000
--- a/res/drawable-mdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bg_cling2.png b/res/drawable-mdpi/bg_cling2.png
deleted file mode 100644
index 0052dc29d..000000000
--- a/res/drawable-mdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bg_cling3.png b/res/drawable-mdpi/bg_cling3.png
deleted file mode 100644
index fabdf7a0d..000000000
--- a/res/drawable-mdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bg_cling4.png b/res/drawable-mdpi/bg_cling4.png
deleted file mode 100644
index 2f152f4db..000000000
--- a/res/drawable-mdpi/bg_cling4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/bg_cling5.png b/res/drawable-mdpi/bg_cling5.png
deleted file mode 100644
index e094809b4..000000000
--- a/res/drawable-mdpi/bg_cling5.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling.9.png b/res/drawable-mdpi/cling.9.png
deleted file mode 100644
index 4c0f139ee..000000000
--- a/res/drawable-mdpi/cling.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling_arrow_down.png b/res/drawable-mdpi/cling_arrow_down.png
deleted file mode 100644
index 58e66fbb2..000000000
--- a/res/drawable-mdpi/cling_arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling_arrow_left.png b/res/drawable-mdpi/cling_arrow_left.png
deleted file mode 100644
index 023c71705..000000000
--- a/res/drawable-mdpi/cling_arrow_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling_arrow_right.png b/res/drawable-mdpi/cling_arrow_right.png
deleted file mode 100644
index cf0eb1047..000000000
--- a/res/drawable-mdpi/cling_arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling_arrow_up.png b/res/drawable-mdpi/cling_arrow_up.png
deleted file mode 100644
index 9b0e6b7ae..000000000
--- a/res/drawable-mdpi/cling_arrow_up.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling_bg.9.png b/res/drawable-mdpi/cling_bg.9.png
new file mode 100644
index 000000000..fc49c891c
--- /dev/null
+++ b/res/drawable-mdpi/cling_bg.9.png
Binary files differ
diff --git a/res/drawable-mdpi/cling_button.9.png b/res/drawable-mdpi/cling_button.9.png
deleted file mode 100644
index a0b6f9735..000000000
--- a/res/drawable-mdpi/cling_button.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/cling_button_pressed.9.png b/res/drawable-mdpi/cling_button_pressed.9.png
deleted file mode 100644
index 986e66944..000000000
--- a/res/drawable-mdpi/cling_button_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/custom_content_page.png b/res/drawable-mdpi/custom_content_page.png
deleted file mode 100644
index cc4005ded..000000000
--- a/res/drawable-mdpi/custom_content_page.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/focused_bg.9.png b/res/drawable-mdpi/focused_bg.9.png
deleted file mode 100644
index 89c29ac51..000000000
--- a/res/drawable-mdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps.png b/res/drawable-mdpi/ic_allapps.png
index e0fd9c07a..f4106734a 100644
--- a/res/drawable-mdpi/ic_allapps.png
+++ b/res/drawable-mdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_allapps_pressed.png b/res/drawable-mdpi/ic_allapps_pressed.png
index 3bd87b187..aa4f913b9 100644
--- a/res/drawable-mdpi/ic_allapps_pressed.png
+++ b/res/drawable-mdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_add.png b/res/drawable-mdpi/ic_pageindicator_add.png
index 8e05e64f0..11659a3b2 100644
--- a/res/drawable-mdpi/ic_pageindicator_add.png
+++ b/res/drawable-mdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_current.png b/res/drawable-mdpi/ic_pageindicator_current.png
index ab5f4c82f..08f43b4c1 100644
--- a/res/drawable-mdpi/ic_pageindicator_current.png
+++ b/res/drawable-mdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_pageindicator_default.png b/res/drawable-mdpi/ic_pageindicator_default.png
index c919ee835..635be4a37 100644
--- a/res/drawable-mdpi/ic_pageindicator_default.png
+++ b/res/drawable-mdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting.png b/res/drawable-mdpi/ic_setting.png
index 1e7645953..c614e9183 100644
--- a/res/drawable-mdpi/ic_setting.png
+++ b/res/drawable-mdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_setting_pressed.png b/res/drawable-mdpi/ic_setting_pressed.png
index d7aca18e1..61e574ad1 100644
--- a/res/drawable-mdpi/ic_setting_pressed.png
+++ b/res/drawable-mdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_wallpaper.png b/res/drawable-mdpi/ic_wallpaper.png
index 333a206e2..8f2a00a3f 100644
--- a/res/drawable-mdpi/ic_wallpaper.png
+++ b/res/drawable-mdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_wallpaper_pressed.png b/res/drawable-mdpi/ic_wallpaper_pressed.png
index 273c48b3a..aa598c3e2 100644
--- a/res/drawable-mdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-mdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget.png b/res/drawable-mdpi/ic_widget.png
index 5f974c28c..1bd393503 100644
--- a/res/drawable-mdpi/ic_widget.png
+++ b/res/drawable-mdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_widget_pressed.png b/res/drawable-mdpi/ic_widget_pressed.png
index 0a3e8838f..9b690d99b 100644
--- a/res/drawable-mdpi/ic_widget_pressed.png
+++ b/res/drawable-mdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left.9.png b/res/drawable-mdpi/page_hover_left.9.png
new file mode 100644
index 000000000..2bbf42807
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_active.9.png b/res/drawable-mdpi/page_hover_left_active.9.png
new file mode 100644
index 000000000..bf70f3601
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_left_holo.9.png b/res/drawable-mdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 561d3cd31..000000000
--- a/res/drawable-mdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right.9.png b/res/drawable-mdpi/page_hover_right.9.png
new file mode 100644
index 000000000..4bafd0f71
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_active.9.png b/res/drawable-mdpi/page_hover_right_active.9.png
new file mode 100644
index 000000000..4aaa0148e
--- /dev/null
+++ b/res/drawable-mdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-mdpi/page_hover_right_holo.9.png b/res/drawable-mdpi/page_hover_right_holo.9.png
deleted file mode 100644
index 2681f23f0..000000000
--- a/res/drawable-mdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel.9.png b/res/drawable-mdpi/quantum_panel.9.png
new file mode 100644
index 000000000..c5a6eb735
--- /dev/null
+++ b/res/drawable-mdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-mdpi/quantum_panel_dark.9.png b/res/drawable-mdpi/quantum_panel_dark.9.png
new file mode 100644
index 000000000..7728a7268
--- /dev/null
+++ b/res/drawable-mdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel.9.png b/res/drawable-mdpi/screenpanel.9.png
index 4de3017f5..c2779fcf3 100644
--- a/res/drawable-mdpi/screenpanel.9.png
+++ b/res/drawable-mdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-mdpi/screenpanel_hover.9.png b/res/drawable-mdpi/screenpanel_hover.9.png
index 7dd885860..70b30785f 100644
--- a/res/drawable-mdpi/screenpanel_hover.9.png
+++ b/res/drawable-mdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-mdpi/virtual_preload.9.png b/res/drawable-mdpi/virtual_preload.9.png
new file mode 100644
index 000000000..a3c75192d
--- /dev/null
+++ b/res/drawable-mdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-mdpi/virtual_preload_folder.9.png b/res/drawable-mdpi/virtual_preload_folder.9.png
new file mode 100644
index 000000000..fa2f1313d
--- /dev/null
+++ b/res/drawable-mdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-mdpi/widget_container_holo.9.png b/res/drawable-mdpi/widget_container_holo.9.png
deleted file mode 100644
index db24457d6..000000000
--- a/res/drawable-mdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-nodpi/ic_migration.png b/res/drawable-nodpi/ic_migration.png
new file mode 100644
index 000000000..c282cd283
--- /dev/null
+++ b/res/drawable-nodpi/ic_migration.png
Binary files differ
diff --git a/res/drawable-xhdpi/bg_cling1.png b/res/drawable-xhdpi/bg_cling1.png
deleted file mode 100644
index b71351a32..000000000
--- a/res/drawable-xhdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/bg_cling2.png b/res/drawable-xhdpi/bg_cling2.png
deleted file mode 100644
index ad78dfeb0..000000000
--- a/res/drawable-xhdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/bg_cling3.png b/res/drawable-xhdpi/bg_cling3.png
deleted file mode 100644
index ae04195c4..000000000
--- a/res/drawable-xhdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/bg_cling4.png b/res/drawable-xhdpi/bg_cling4.png
deleted file mode 100644
index f4bb83e39..000000000
--- a/res/drawable-xhdpi/bg_cling4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling.9.png b/res/drawable-xhdpi/cling.9.png
deleted file mode 100644
index 1cb468159..000000000
--- a/res/drawable-xhdpi/cling.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling_arrow_down.png b/res/drawable-xhdpi/cling_arrow_down.png
deleted file mode 100644
index ee1093340..000000000
--- a/res/drawable-xhdpi/cling_arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling_arrow_left.png b/res/drawable-xhdpi/cling_arrow_left.png
deleted file mode 100644
index cffbcf3ce..000000000
--- a/res/drawable-xhdpi/cling_arrow_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling_arrow_right.png b/res/drawable-xhdpi/cling_arrow_right.png
deleted file mode 100644
index d880d67f0..000000000
--- a/res/drawable-xhdpi/cling_arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling_arrow_up.png b/res/drawable-xhdpi/cling_arrow_up.png
deleted file mode 100644
index fd2c60c31..000000000
--- a/res/drawable-xhdpi/cling_arrow_up.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling_bg.9.png b/res/drawable-xhdpi/cling_bg.9.png
new file mode 100644
index 000000000..4db356f25
--- /dev/null
+++ b/res/drawable-xhdpi/cling_bg.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/cling_button.9.png b/res/drawable-xhdpi/cling_button.9.png
deleted file mode 100644
index 4192563b5..000000000
--- a/res/drawable-xhdpi/cling_button.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/cling_button_pressed.9.png b/res/drawable-xhdpi/cling_button_pressed.9.png
deleted file mode 100644
index d3ce46977..000000000
--- a/res/drawable-xhdpi/cling_button_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/custom_content_page.png b/res/drawable-xhdpi/custom_content_page.png
deleted file mode 100644
index e1da91c61..000000000
--- a/res/drawable-xhdpi/custom_content_page.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/focused_bg.9.png b/res/drawable-xhdpi/focused_bg.9.png
deleted file mode 100644
index 197a26988..000000000
--- a/res/drawable-xhdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps.png b/res/drawable-xhdpi/ic_allapps.png
index f71964c2d..ff3d82372 100644
--- a/res/drawable-xhdpi/ic_allapps.png
+++ b/res/drawable-xhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_allapps_pressed.png b/res/drawable-xhdpi/ic_allapps_pressed.png
index d678f027e..5f188f633 100644
--- a/res/drawable-xhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_add.png b/res/drawable-xhdpi/ic_pageindicator_add.png
index 28e164b23..af1da2d42 100644
--- a/res/drawable-xhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_current.png b/res/drawable-xhdpi/ic_pageindicator_current.png
index aed3d7172..0e9a52f6d 100644
--- a/res/drawable-xhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_pageindicator_default.png b/res/drawable-xhdpi/ic_pageindicator_default.png
index 0887416f0..d0f14cdfb 100644
--- a/res/drawable-xhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting.png b/res/drawable-xhdpi/ic_setting.png
index 6f06bcfd9..3a7310b7c 100644
--- a/res/drawable-xhdpi/ic_setting.png
+++ b/res/drawable-xhdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_setting_pressed.png b/res/drawable-xhdpi/ic_setting_pressed.png
index bca8ccdd0..005d49c8c 100644
--- a/res/drawable-xhdpi/ic_setting_pressed.png
+++ b/res/drawable-xhdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wallpaper.png b/res/drawable-xhdpi/ic_wallpaper.png
index 41dc000fd..d2bf246ee 100644
--- a/res/drawable-xhdpi/ic_wallpaper.png
+++ b/res/drawable-xhdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_wallpaper_pressed.png b/res/drawable-xhdpi/ic_wallpaper_pressed.png
index ffff0531d..5a9b84d7d 100644
--- a/res/drawable-xhdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-xhdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget.png b/res/drawable-xhdpi/ic_widget.png
index 47dcdd14f..cf6be8173 100644
--- a/res/drawable-xhdpi/ic_widget.png
+++ b/res/drawable-xhdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_widget_pressed.png b/res/drawable-xhdpi/ic_widget_pressed.png
index 8bb387b88..633c9c648 100644
--- a/res/drawable-xhdpi/ic_widget_pressed.png
+++ b/res/drawable-xhdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left.9.png b/res/drawable-xhdpi/page_hover_left.9.png
new file mode 100644
index 000000000..a2b9b65d7
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_active.9.png b/res/drawable-xhdpi/page_hover_left_active.9.png
new file mode 100644
index 000000000..ba9478ece
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_left_holo.9.png b/res/drawable-xhdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 4972a2eeb..000000000
--- a/res/drawable-xhdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right.9.png b/res/drawable-xhdpi/page_hover_right.9.png
new file mode 100644
index 000000000..1243ea9ec
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_active.9.png b/res/drawable-xhdpi/page_hover_right_active.9.png
new file mode 100644
index 000000000..582261c9b
--- /dev/null
+++ b/res/drawable-xhdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/page_hover_right_holo.9.png b/res/drawable-xhdpi/page_hover_right_holo.9.png
deleted file mode 100644
index b99461f69..000000000
--- a/res/drawable-xhdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel.9.png b/res/drawable-xhdpi/quantum_panel.9.png
new file mode 100644
index 000000000..1797ad500
--- /dev/null
+++ b/res/drawable-xhdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/quantum_panel_dark.9.png b/res/drawable-xhdpi/quantum_panel_dark.9.png
new file mode 100644
index 000000000..4c1868b55
--- /dev/null
+++ b/res/drawable-xhdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel.9.png b/res/drawable-xhdpi/screenpanel.9.png
index b4b828d3e..53a781206 100644
--- a/res/drawable-xhdpi/screenpanel.9.png
+++ b/res/drawable-xhdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/screenpanel_hover.9.png b/res/drawable-xhdpi/screenpanel_hover.9.png
index 251bf2085..a2e200f13 100644
--- a/res/drawable-xhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/virtual_preload.9.png b/res/drawable-xhdpi/virtual_preload.9.png
new file mode 100644
index 000000000..d2c3fea9b
--- /dev/null
+++ b/res/drawable-xhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/virtual_preload_folder.9.png b/res/drawable-xhdpi/virtual_preload_folder.9.png
new file mode 100644
index 000000000..1f9202b41
--- /dev/null
+++ b/res/drawable-xhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/widget_container_holo.9.png b/res/drawable-xhdpi/widget_container_holo.9.png
deleted file mode 100644
index 1313fe70d..000000000
--- a/res/drawable-xhdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/apps_customize_bg.png b/res/drawable-xxhdpi/apps_customize_bg.png
new file mode 100644
index 000000000..a51cc112b
--- /dev/null
+++ b/res/drawable-xxhdpi/apps_customize_bg.png
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling1.png b/res/drawable-xxhdpi/bg_cling1.png
deleted file mode 100644
index 077785600..000000000
--- a/res/drawable-xxhdpi/bg_cling1.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling2.png b/res/drawable-xxhdpi/bg_cling2.png
deleted file mode 100644
index 1797a1b6c..000000000
--- a/res/drawable-xxhdpi/bg_cling2.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling3.png b/res/drawable-xxhdpi/bg_cling3.png
deleted file mode 100644
index a87be630e..000000000
--- a/res/drawable-xxhdpi/bg_cling3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling4.png b/res/drawable-xxhdpi/bg_cling4.png
deleted file mode 100644
index cabe919d5..000000000
--- a/res/drawable-xxhdpi/bg_cling4.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling_home.png b/res/drawable-xxhdpi/bg_cling_home.png
deleted file mode 100644
index 1ae93e7fe..000000000
--- a/res/drawable-xxhdpi/bg_cling_home.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_cling_nakasi3.png b/res/drawable-xxhdpi/bg_cling_nakasi3.png
deleted file mode 100644
index f47236cac..000000000
--- a/res/drawable-xxhdpi/bg_cling_nakasi3.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling.9.png b/res/drawable-xxhdpi/cling.9.png
deleted file mode 100644
index 7beae03bf..000000000
--- a/res/drawable-xxhdpi/cling.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_arrow_down.png b/res/drawable-xxhdpi/cling_arrow_down.png
deleted file mode 100644
index 48c4f06fa..000000000
--- a/res/drawable-xxhdpi/cling_arrow_down.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_arrow_left.png b/res/drawable-xxhdpi/cling_arrow_left.png
deleted file mode 100644
index 8760d05da..000000000
--- a/res/drawable-xxhdpi/cling_arrow_left.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_arrow_right.png b/res/drawable-xxhdpi/cling_arrow_right.png
deleted file mode 100644
index 356ba178c..000000000
--- a/res/drawable-xxhdpi/cling_arrow_right.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_arrow_up.png b/res/drawable-xxhdpi/cling_arrow_up.png
deleted file mode 100644
index 4cb805f45..000000000
--- a/res/drawable-xxhdpi/cling_arrow_up.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_bg.9.png b/res/drawable-xxhdpi/cling_bg.9.png
new file mode 100644
index 000000000..dc9f69ae9
--- /dev/null
+++ b/res/drawable-xxhdpi/cling_bg.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_button.9.png b/res/drawable-xxhdpi/cling_button.9.png
deleted file mode 100644
index e41287613..000000000
--- a/res/drawable-xxhdpi/cling_button.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/cling_button_pressed.9.png b/res/drawable-xxhdpi/cling_button_pressed.9.png
deleted file mode 100644
index 55e89da08..000000000
--- a/res/drawable-xxhdpi/cling_button_pressed.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/focused_bg.9.png b/res/drawable-xxhdpi/focused_bg.9.png
deleted file mode 100644
index 84d3062f1..000000000
--- a/res/drawable-xxhdpi/focused_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps.png b/res/drawable-xxhdpi/ic_allapps.png
index 624e0ef44..5dbfe4c5c 100644
--- a/res/drawable-xxhdpi/ic_allapps.png
+++ b/res/drawable-xxhdpi/ic_allapps.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_allapps_pressed.png b/res/drawable-xxhdpi/ic_allapps_pressed.png
index 77b45aefb..e76172381 100644
--- a/res/drawable-xxhdpi/ic_allapps_pressed.png
+++ b/res/drawable-xxhdpi/ic_allapps_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_add.png b/res/drawable-xxhdpi/ic_pageindicator_add.png
index fd8a662e1..c28895229 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_add.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_add.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_current.png b/res/drawable-xxhdpi/ic_pageindicator_current.png
index 08615f371..b74e92ea7 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_current.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_current.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_pageindicator_default.png b/res/drawable-xxhdpi/ic_pageindicator_default.png
index 9d4fbf820..e362ece71 100644
--- a/res/drawable-xxhdpi/ic_pageindicator_default.png
+++ b/res/drawable-xxhdpi/ic_pageindicator_default.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting.png b/res/drawable-xxhdpi/ic_setting.png
index b3729d315..01bdcd544 100644
--- a/res/drawable-xxhdpi/ic_setting.png
+++ b/res/drawable-xxhdpi/ic_setting.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_setting_pressed.png b/res/drawable-xxhdpi/ic_setting_pressed.png
index 5c9c1be44..d0cad5e0e 100644
--- a/res/drawable-xxhdpi/ic_setting_pressed.png
+++ b/res/drawable-xxhdpi/ic_setting_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wallpaper.png b/res/drawable-xxhdpi/ic_wallpaper.png
index c71844410..490c45a87 100644
--- a/res/drawable-xxhdpi/ic_wallpaper.png
+++ b/res/drawable-xxhdpi/ic_wallpaper.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_wallpaper_pressed.png b/res/drawable-xxhdpi/ic_wallpaper_pressed.png
index 03324aa6c..e5d200be1 100644
--- a/res/drawable-xxhdpi/ic_wallpaper_pressed.png
+++ b/res/drawable-xxhdpi/ic_wallpaper_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget.png b/res/drawable-xxhdpi/ic_widget.png
index fddfecaa3..d4b8324b1 100644
--- a/res/drawable-xxhdpi/ic_widget.png
+++ b/res/drawable-xxhdpi/ic_widget.png
Binary files differ
diff --git a/res/drawable-xxhdpi/ic_widget_pressed.png b/res/drawable-xxhdpi/ic_widget_pressed.png
index 3d3670ed4..b8dd35dc6 100644
--- a/res/drawable-xxhdpi/ic_widget_pressed.png
+++ b/res/drawable-xxhdpi/ic_widget_pressed.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left.9.png b/res/drawable-xxhdpi/page_hover_left.9.png
new file mode 100644
index 000000000..63869dd3e
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_left.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_active.9.png b/res/drawable-xxhdpi/page_hover_left_active.9.png
new file mode 100644
index 000000000..9a418ce1e
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_left_active.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_left_holo.9.png b/res/drawable-xxhdpi/page_hover_left_holo.9.png
deleted file mode 100644
index 626aafb55..000000000
--- a/res/drawable-xxhdpi/page_hover_left_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right.9.png b/res/drawable-xxhdpi/page_hover_right.9.png
new file mode 100644
index 000000000..c6fd39885
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_right.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_active.9.png b/res/drawable-xxhdpi/page_hover_right_active.9.png
new file mode 100644
index 000000000..7aef3733c
--- /dev/null
+++ b/res/drawable-xxhdpi/page_hover_right_active.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/page_hover_right_holo.9.png b/res/drawable-xxhdpi/page_hover_right_holo.9.png
deleted file mode 100644
index 66257dc8e..000000000
--- a/res/drawable-xxhdpi/page_hover_right_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel.9.png b/res/drawable-xxhdpi/quantum_panel.9.png
new file mode 100644
index 000000000..d7ba87416
--- /dev/null
+++ b/res/drawable-xxhdpi/quantum_panel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/quantum_panel_dark.9.png b/res/drawable-xxhdpi/quantum_panel_dark.9.png
new file mode 100644
index 000000000..17ba0f116
--- /dev/null
+++ b/res/drawable-xxhdpi/quantum_panel_dark.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel.9.png b/res/drawable-xxhdpi/screenpanel.9.png
index c44f3b898..2d1395479 100644
--- a/res/drawable-xxhdpi/screenpanel.9.png
+++ b/res/drawable-xxhdpi/screenpanel.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/screenpanel_hover.9.png b/res/drawable-xxhdpi/screenpanel_hover.9.png
index e8b36d8f1..369fc44e1 100644
--- a/res/drawable-xxhdpi/screenpanel_hover.9.png
+++ b/res/drawable-xxhdpi/screenpanel_hover.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload.9.png b/res/drawable-xxhdpi/virtual_preload.9.png
new file mode 100644
index 000000000..93e3b3362
--- /dev/null
+++ b/res/drawable-xxhdpi/virtual_preload.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/virtual_preload_folder.9.png b/res/drawable-xxhdpi/virtual_preload_folder.9.png
new file mode 100644
index 000000000..fae19b3f9
--- /dev/null
+++ b/res/drawable-xxhdpi/virtual_preload_folder.9.png
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_container_holo.9.png b/res/drawable-xxhdpi/widget_container_holo.9.png
deleted file mode 100644
index 8f79920c4..000000000
--- a/res/drawable-xxhdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bg_migration_cling.xml b/res/drawable/bg_migration_cling.xml
new file mode 100644
index 000000000..bfff5a41a
--- /dev/null
+++ b/res/drawable/bg_migration_cling.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="oval" >
+
+ <gradient
+ android:endColor="#00ffeb3a"
+ android:gradientRadius="50%p"
+ android:startColor="#80ffeb3a"
+ android:type="radial" />
+
+</shape> \ No newline at end of file
diff --git a/res/drawable/cling_arrow_start.xml b/res/drawable/cling_arrow_start.xml
deleted file mode 100644
index ebe91830d..000000000
--- a/res/drawable/cling_arrow_start.xml
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/cling_arrow_left"
- android:autoMirrored="true">
-</bitmap>
diff --git a/res/drawable/cling_button_bg.xml b/res/drawable/cling_button_bg.xml
deleted file mode 100644
index 7bf6ce79f..000000000
--- a/res/drawable/cling_button_bg.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?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_pressed="true" android:drawable="@drawable/cling_button_pressed" />
- <item android:drawable="@drawable/cling_button" />
-</selector>
diff --git a/res/drawable/focusable_view_bg.xml b/res/drawable/focusable_view_bg.xml
index 66661e28b..e156513ef 100644
--- a/res/drawable/focusable_view_bg.xml
+++ b/res/drawable/focusable_view_bg.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+ 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.
@@ -15,5 +16,11 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_focused="true" android:drawable="@drawable/focused_bg" />
-</selector>
+
+ <item android:state_focused="true">
+ <shape android:shape="rectangle">
+ <solid android:color="@color/focused_background" />
+ </shape>
+ </item>
+
+</selector> \ No newline at end of file
diff --git a/res/interpolator/decelerate_quart.xml b/res/interpolator/decelerate_quart.xml
new file mode 100644
index 000000000..5dc5d3857
--- /dev/null
+++ b/res/interpolator/decelerate_quart.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:factor="2" />
diff --git a/res/interpolator/decelerate_quint.xml b/res/interpolator/decelerate_quint.xml
new file mode 100644
index 000000000..fa89a648e
--- /dev/null
+++ b/res/interpolator/decelerate_quint.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2014, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<decelerateInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:factor="2.5" />
diff --git a/res/layout-land/first_run_cling.xml b/res/layout-land/first_run_cling.xml
deleted file mode 100644
index 9baee64b7..000000000
--- a/res/layout-land/first_run_cling.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="first_run_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/bubble_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="100dp"
- android:layout_marginRight="100dp"
- android:orientation="vertical">
- <TextView
- style="@style/ClingAltTitleText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="10dp"
- android:text="@string/first_run_cling_title"
- android:textColor="#FFFFFFFF"
- android:textSize="30sp"
- android:gravity="center" />
- <TextView
- style="@style/ClingAltTitleText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/first_run_cling_description"
- android:textColor="#80000000"
- android:textSize="16sp"
- android:gravity="center" />
- </LinearLayout>
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/search_bar_hint"
- android:layout_width="160dp"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- android:layout_marginEnd="10dp"
- android:layout_marginTop="65dp"
- android:visibility="gone"
- android:drawableTop="@drawable/cling_arrow_up"
- android:drawablePadding="5dp"
- android:text="@string/first_run_cling_search_bar_hint" />
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/custom_content_hint"
- android:layout_width="160dp"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:layout_marginStart="10dp"
- android:layout_marginTop="100dp"
- android:visibility="gone"
- android:drawableStart="@drawable/cling_arrow_left"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_custom_content_hint" />
- <TextView
- style="@style/ClingHintText"
- android:layout_width="160dp"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="10dp"
- android:layout_marginBottom="85dp"
- android:drawableEnd="@drawable/cling_arrow_right"
- android:drawablePadding="5dp"
- android:text="@string/first_run_cling_create_screens_hint" />
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="15dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="bottom|end"
- android:onClick="dismissFirstRunCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-land/folder_cling.xml b/res/layout-land/folder_cling.xml
deleted file mode 100644
index 5dd372973..000000000
--- a/res/layout-land/folder_cling.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- launcher:drawIdentifier="folder_landscape">
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginStart="15dp"
- android:layout_marginEnd="15dp"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="10dp">
- <LinearLayout
- android:id="@+id/folder_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:id="@+id/folder_cling_title"
- android:text="@string/folder_cling_title" />
- <TextView
- style="@style/ClingText"
- android:id="@+id/folder_cling_create_folder"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/folder_cling_create_folder" />
- </LinearLayout>
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:id="@+id/cling_dismiss"
- android:layout_marginBottom="15dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="bottom|right"
- android:onClick="dismissFolderCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-land/launcher.xml b/res/layout-land/launcher.xml
index 779160925..8cd867366 100644
--- a/res/layout-land/launcher.xml
+++ b/res/layout-land/launcher.xml
@@ -30,6 +30,11 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
+ <com.android.launcher3.FocusIndicatorView
+ android:id="@+id/focus_indicator"
+ android:layout_width="52dp"
+ android:layout_height="52dp" />
+
<!-- The workspace contains 5 screens of cells -->
<com.android.launcher3.Workspace
android:id="@+id/workspace"
@@ -52,40 +57,6 @@
android:id="@+id/overview_panel"
android:visibility="gone" />
- <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
- that it is still visible during the transition to AllApps and doesn't overlay on
- top of that view. -->
- <com.android.launcher3.ScrimView
- android:id="@+id/cling_scrim"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/first_run_cling"
- android:id="@+id/first_run_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/migration_cling"
- android:id="@+id/migration_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/migration_workspace_cling"
- android:id="@+id/migration_workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/workspace_cling"
- android:id="@+id/workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/folder_cling"
- android:id="@+id/folder_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
-
<include layout="@layout/apps_customize_pane"
android:id="@+id/apps_customize_pane"
android:layout_width="match_parent"
diff --git a/res/layout-land/longpress_cling.xml b/res/layout-land/longpress_cling.xml
new file mode 100644
index 000000000..93bbc077a
--- /dev/null
+++ b/res/layout-land/longpress_cling.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+ android:id="@+id/longpress_cling"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/cling_scrim_background"
+ android:orientation="vertical" >
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <FrameLayout
+ android:id="@+id/cling_content"
+ android:layout_width="360dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="@drawable/cling_bg" />
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="2" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout-land/migration_cling.xml b/res/layout-land/migration_cling.xml
index 343f43f1f..307cba8a8 100644
--- a/res/layout-land/migration_cling.xml
+++ b/res/layout-land/migration_cling.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+ 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.
@@ -13,88 +14,90 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/migration_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
- launcher:drawIdentifier="migration_landscape">
+ android:background="#FF009688"
+ android:baselineAligned="false"
+ android:gravity="center_vertical" >
+
<FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
+ android:layout_width="0dp"
android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_weight="1" >
- <LinearLayout
- android:layout_width="match_parent"
+ <ImageView
+ android:layout_width="@dimen/cling_migration_bg_size"
+ android:layout_height="@dimen/cling_migration_bg_size"
+ android:layout_gravity="center"
+ android:background="@drawable/bg_migration_cling" />
+
+ <ImageView
+ android:layout_width="@dimen/cling_migration_logo_width"
+ android:layout_height="@dimen/cling_migration_logo_height"
+ android:layout_gravity="center"
+ android:src="@drawable/ic_migration" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="@dimen/cling_migration_content_width"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/cling_migration_content_margin"
+ android:orientation="vertical"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp" >
+
+ <TextView
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:gravity="center"
- android:text="@string/first_run_cling_title"
- android:textSize="42dp"
- android:textColor="#FFffffff" />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="0dp"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/on_boarding_welcome" />
+ android:paddingBottom="8dp"
+ android:text="@string/first_run_cling_title"
+ android:textColor="#E1000000"
+ android:textSize="34sp" />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_up" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="25dp"
- android:layout_marginRight="25dp"
- android:paddingLeft="25dp"
- android:paddingRight="25dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_description" />
- </LinearLayout>
- </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/migration_cling_title"
+ android:textColor="#E1000000"
+ android:textSize="20sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingBottom="24dp"
+ android:text="@string/migration_cling_description"
+ android:textColor="#99000000"
+ android:textSize="16sp" />
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginLeft="25dp"
- android:layout_marginRight="25dp"
- android:layout_marginBottom="25dp"
- android:orientation="vertical">
+ android:layout_height="wrap_content" >
+
<Button
- style="@style/ClingButton"
- android:layout_width="match_parent"
+ android:id="@+id/cling_dismiss_migration_copy_apps"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_copy_apps"
- android:onClick="dismissMigrationClingCopyApps" />
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp" />
+
<Button
- style="@style/ClingButton"
- android:layout_width="match_parent"
+ android:id="@+id/cling_dismiss_migration_use_default"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:fontFamily="sans-serif-medium"
android:text="@string/migration_cling_use_default"
- android:onClick="dismissMigrationClingUseDefault" />
+ android:textColor="#deFFFFFF"
+ android:textSize="14sp" />
</LinearLayout>
- </FrameLayout>
-</com.android.launcher3.Cling>
+ </LinearLayout>
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout-land/migration_workspace_cling.xml b/res/layout-land/migration_workspace_cling.xml
deleted file mode 100644
index 1148be45f..000000000
--- a/res/layout-land/migration_workspace_cling.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="migration_workspace_landscape">
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="400dp"
- android:layout_height="wrap_content"
- android:layout_gravity="end|center_vertical"
- android:paddingEnd="60dp"
- android:paddingRight="60dp"
- android:orientation="vertical">
- <LinearLayout
- android:id="@+id/migration_workspace_cling_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginRight="4dp"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:src="@drawable/cling_arrow_end" />
- </LinearLayout>
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="5dp"
- android:layout_gravity="right"
- android:onClick="dismissMigrationWorkspaceCling" />
- </LinearLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-land/workspace_cling.xml b/res/layout-land/workspace_cling.xml
deleted file mode 100644
index d3b07d74d..000000000
--- a/res/layout-land/workspace_cling.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="workspace_landscape">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/workspace_cling_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="25dp"
- android:layout_marginTop="30dp"
- android:orientation="vertical">
- <LinearLayout
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/focused_hotseat_app_bubble"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|left"
- android:layout_marginLeft="25dp"
- android:layout_marginBottom="90dp"
- android:orientation="vertical"
- android:visibility="gone">
- <LinearLayout
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:layout_width="240dp"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- android:id="@+id/focused_hotseat_app_title"
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/focused_hotseat_app_description"
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:layout_marginLeft="78dp"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
- </FrameLayout>
-
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="15dp"
- android:layout_marginRight="20dp"
- android:layout_gravity="bottom|right"
- android:onClick="dismissWorkspaceCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-port/first_run_cling.xml b/res/layout-port/first_run_cling.xml
deleted file mode 100644
index ac3939cfd..000000000
--- a/res/layout-port/first_run_cling.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="first_run_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/bubble_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="100dp"
- android:layout_marginRight="100dp"
- android:orientation="vertical">
- <TextView
- style="@style/ClingAltTitleText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="10dp"
- android:text="@string/first_run_cling_title"
- android:textColor="#FFFFFFFF"
- android:gravity="center" />
- <TextView
- style="@style/ClingAltText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/first_run_cling_description"
- android:textColor="#80000000"
- android:gravity="center" />
- </LinearLayout>
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/search_bar_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- android:layout_marginEnd="10dp"
- android:layout_marginTop="65dp"
- android:gravity="center_horizontal"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableTop="@drawable/cling_arrow_up"
- android:drawablePadding="5dp"
- android:text="@string/first_run_cling_search_bar_hint" />
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/custom_content_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|start"
- android:layout_marginStart="10dp"
- android:layout_marginEnd="10dp"
- android:layout_marginTop="100dp"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableStart="@drawable/cling_arrow_start"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_custom_content_hint" />
- <TextView
- style="@style/ClingHintText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="10dp"
- android:layout_marginBottom="85dp"
- android:maxWidth="180dp"
- android:drawableEnd="@drawable/cling_arrow_end"
- android:drawablePadding="5dp"
- android:text="@string/first_run_cling_create_screens_hint" />
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="15dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="bottom|end"
- android:onClick="dismissFirstRunCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-port/folder_cling.xml b/res/layout-port/folder_cling.xml
deleted file mode 100644
index 1a1b11fce..000000000
--- a/res/layout-port/folder_cling.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- launcher:drawIdentifier="folder_portrait">
- <FrameLayout
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_marginStart="15dp"
- android:layout_marginEnd="15dp"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="10dp">
- <LinearLayout
- android:id="@+id/folder_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:id="@+id/folder_cling_title"
- android:text="@string/folder_cling_title" />
- <TextView
- style="@style/ClingText"
- android:id="@+id/folder_cling_create_folder"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/folder_cling_create_folder" />
- </LinearLayout>
- <ImageView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:id="@+id/cling_dismiss"
- android:layout_marginBottom="15dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="bottom|right"
- android:onClick="dismissFolderCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-port/launcher.xml b/res/layout-port/launcher.xml
index 574b73e46..9e98d4257 100644
--- a/res/layout-port/launcher.xml
+++ b/res/layout-port/launcher.xml
@@ -29,6 +29,11 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
+ <com.android.launcher3.FocusIndicatorView
+ android:id="@+id/focus_indicator"
+ android:layout_width="52dp"
+ android:layout_height="52dp" />
+
<!-- The workspace contains 5 screens of cells -->
<com.android.launcher3.Workspace
android:id="@+id/workspace"
@@ -60,40 +65,6 @@
android:id="@+id/search_drop_target_bar"
layout="@layout/search_drop_target_bar" />
- <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
- that it is still visible during the transition to AllApps and doesn't overlay on
- top of that view. -->
- <com.android.launcher3.ScrimView
- android:id="@+id/cling_scrim"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/first_run_cling"
- android:id="@+id/first_run_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/migration_cling"
- android:id="@+id/migration_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/migration_workspace_cling"
- android:id="@+id/migration_workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/workspace_cling"
- android:id="@+id/workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/folder_cling"
- android:id="@+id/folder_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
-
<!-- This is the search bar voice button proxy view. It allows us to have a larger
touch target than the microphone constrained by the search bar bounds. -->
<com.android.launcher3.DrawableStateProxyView
diff --git a/res/layout-port/longpress_cling.xml b/res/layout-port/longpress_cling.xml
new file mode 100644
index 000000000..8e35f5c1a
--- /dev/null
+++ b/res/layout-port/longpress_cling.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+ android:id="@+id/longpress_cling"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/cling_scrim_background" >
+
+ <FrameLayout
+ android:id="@+id/cling_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="top"
+ android:background="@drawable/cling_bg"
+ android:tag="crop_bg_top_and_sides" />
+
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout-port/migration_cling.xml b/res/layout-port/migration_cling.xml
index 1bffe6c82..dde8dbcdb 100644
--- a/res/layout-port/migration_cling.xml
+++ b/res/layout-port/migration_cling.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
+<!--
+ 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.
@@ -13,88 +14,93 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/migration_cling"
android:layout_width="match_parent"
android:layout_height="match_parent"
- launcher:drawIdentifier="migration_portrait">
- <FrameLayout
- android:id="@+id/content"
+ android:background="#FF009688" >
+
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" >
+
+ <ImageView
+ android:layout_width="@dimen/cling_migration_bg_size"
+ android:layout_height="@dimen/cling_migration_bg_size"
+ android:layout_below="@+id/ic_cling_migration"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="@dimen/cling_migration_bg_shift"
+ android:src="@drawable/bg_migration_cling" />
+
+ <ImageView
+ android:id="@+id/ic_cling_migration"
+ android:layout_width="@dimen/cling_migration_logo_width"
+ android:layout_height="@dimen/cling_migration_logo_height"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ android:src="@drawable/ic_migration" />
<LinearLayout
- android:layout_width="match_parent"
+ android:layout_width="@dimen/cling_migration_content_width"
android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:orientation="vertical">
+ android:layout_below="@+id/ic_cling_migration"
+ android:layout_marginStart="@dimen/cling_migration_content_margin"
+ android:orientation="vertical"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp" >
+
<TextView
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:gravity="center"
+ android:paddingBottom="8dp"
android:text="@string/first_run_cling_title"
- android:textSize="42dp"
- android:textColor="#FFffffff" />
- <ImageView
+ android:textColor="#E1000000"
+ android:textSize="34sp" />
+
+ <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="0dp"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/on_boarding_welcome" />
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/migration_cling_title"
+ android:textColor="#E1000000"
+ android:textSize="20sp" />
- <ImageView
+ <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_up" />
+ android:paddingBottom="24dp"
+ android:text="@string/migration_cling_description"
+ android:textColor="#99000000"
+ android:textSize="16sp" />
+
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="25dp"
- android:layout_marginRight="25dp"
- android:paddingLeft="25dp"
- android:paddingRight="25dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
+ android:layout_height="wrap_content" >
+
+ <Button
+ android:id="@+id/cling_dismiss_migration_copy_apps"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/migration_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
+ android:layout_weight="1"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/migration_cling_copy_apps"
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp" />
+
+ <Button
+ android:id="@+id/cling_dismiss_migration_use_default"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/migration_cling_description" />
+ android:layout_weight="1"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/migration_cling_use_default"
+ android:textColor="#deFFFFFF"
+ android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
+ </RelativeLayout>
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginLeft="25dp"
- android:layout_marginRight="25dp"
- android:layout_marginBottom="25dp"
- android:orientation="vertical">
- <Button
- style="@style/ClingButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_copy_apps"
- android:onClick="dismissMigrationClingCopyApps" />
- <Button
- style="@style/ClingButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_use_default"
- android:onClick="dismissMigrationClingUseDefault" />
- </LinearLayout>
- </FrameLayout>
-</com.android.launcher3.Cling>
+</FrameLayout> \ No newline at end of file
diff --git a/res/layout-port/migration_workspace_cling.xml b/res/layout-port/migration_workspace_cling.xml
deleted file mode 100644
index 576bb41f2..000000000
--- a/res/layout-port/migration_workspace_cling.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="migration_workspace_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/migration_workspace_cling_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="25dp"
- android:orientation="vertical">
- <LinearLayout
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
-
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="15dp"
- android:layout_marginEnd="20dp"
- android:layout_gravity="bottom|right"
- android:onClick="dismissMigrationWorkspaceCling" />
- </FrameLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-port/workspace_cling.xml b/res/layout-port/workspace_cling.xml
deleted file mode 100644
index 624568668..000000000
--- a/res/layout-port/workspace_cling.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="workspace_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/workspace_cling_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="25dp"
- android:layout_marginTop="30dp"
- android:orientation="vertical">
- <LinearLayout
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
-
- <LinearLayout
- android:id="@+id/focused_hotseat_app_bubble"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|left"
- android:layout_marginLeft="25dp"
- android:layout_marginBottom="90dp"
- android:orientation="vertical"
- android:visibility="gone">
- <LinearLayout
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:layout_width="240dp"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- android:id="@+id/focused_hotseat_app_title"
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/focused_hotseat_app_description"
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="left"
- android:layout_marginLeft="78dp"
- android:src="@drawable/cling_arrow_down" />
- </LinearLayout>
- </FrameLayout>
-
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="15dp"
- android:layout_marginRight="20dp"
- android:layout_gravity="bottom|right"
- android:onClick="dismissWorkspaceCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw600dp-port/first_run_cling.xml b/res/layout-sw600dp-port/first_run_cling.xml
deleted file mode 100644
index d80c084ab..000000000
--- a/res/layout-sw600dp-port/first_run_cling.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="first_run_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/bubble_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="100dp"
- android:layout_marginRight="100dp"
- android:orientation="vertical">
- <TextView
- style="@style/ClingAltTitleText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="10dp"
- android:text="@string/first_run_cling_title"
- android:textColor="#FFFFFFFF"
- android:gravity="center" />
- <TextView
- style="@style/ClingAltText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/first_run_cling_description"
- android:textColor="#80000000"
- android:gravity="center" />
- </LinearLayout>
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/search_bar_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- android:layout_marginEnd="30dp"
- android:layout_marginTop="80dp"
- android:gravity="center_horizontal"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableTop="@drawable/cling_arrow_up"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_search_bar_hint" />
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/custom_content_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|start"
- android:layout_marginStart="30dp"
- android:layout_marginTop="120dp"
- android:gravity="start"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableStart="@drawable/cling_arrow_start"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_custom_content_hint" />
- <TextView
- style="@style/ClingHintText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="30dp"
- android:layout_marginBottom="120dp"
- android:maxWidth="180dp"
- android:drawableEnd="@drawable/cling_arrow_end"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_create_screens_hint" />
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="30dp"
- android:layout_marginEnd="30dp"
- android:layout_gravity="bottom|end"
- android:onClick="dismissFirstRunCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw600dp-port/longpress_cling.xml b/res/layout-sw600dp-port/longpress_cling.xml
new file mode 100644
index 000000000..b42d697a5
--- /dev/null
+++ b/res/layout-sw600dp-port/longpress_cling.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
+ android:id="@+id/longpress_cling"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/cling_scrim_background"
+ android:orientation="vertical" >
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1" />
+
+ <FrameLayout
+ android:id="@+id/cling_content"
+ android:layout_width="360dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:background="@drawable/cling_bg" />
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="3" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout-sw600dp-port/migration_workspace_cling.xml b/res/layout-sw600dp-port/migration_workspace_cling.xml
deleted file mode 100644
index eb13137a1..000000000
--- a/res/layout-sw600dp-port/migration_workspace_cling.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="migration_workspace_large_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="480dp"
- android:layout_height="match_parent"
- android:layout_gravity="bottom|center_horizontal">
- <LinearLayout
- android:id="@+id/migration_workspace_cling_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="25dp"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginRight="4dp"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_down" />
- <Button
- style="@style/ClingButton"
- android:id="@+id/dismiss_migration_workspace_cling_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:onClick="dismissMigrationWorkspaceCling" />
- </LinearLayout>
- </FrameLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw600dp/first_run_cling.xml b/res/layout-sw600dp/first_run_cling.xml
deleted file mode 100644
index 295765b6e..000000000
--- a/res/layout-sw600dp/first_run_cling.xml
+++ /dev/null
@@ -1,100 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="first_run_landscape">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/bubble_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="100dp"
- android:layout_marginRight="100dp"
- android:orientation="vertical">
- <TextView
- style="@style/ClingAltTitleText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="10dp"
- android:text="@string/first_run_cling_title"
- android:textColor="#FFFFFFFF"
- android:gravity="center" />
- <TextView
- style="@style/ClingAltText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/first_run_cling_description"
- android:textColor="#80000000"
- android:gravity="center" />
- </LinearLayout>
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/search_bar_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|start"
- android:layout_marginStart="60dp"
- android:layout_marginTop="105dp"
- android:gravity="start"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableStart="@drawable/cling_arrow_start"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_search_bar_hint" />
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/custom_content_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|start"
- android:layout_marginStart="60dp"
- android:layout_marginTop="200dp"
- android:gravity="start"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableStart="@drawable/cling_arrow_start"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_custom_content_hint" />
- <TextView
- style="@style/ClingHintText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom|end"
- android:layout_marginEnd="30dp"
- android:layout_marginBottom="120dp"
- android:maxWidth="180dp"
- android:drawableEnd="@drawable/cling_arrow_end"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_create_screens_hint" />
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="30dp"
- android:layout_marginEnd="30dp"
- android:layout_gravity="bottom|end"
- android:onClick="dismissFirstRunCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw600dp/folder_cling.xml b/res/layout-sw600dp/folder_cling.xml
deleted file mode 100644
index f21aef4e4..000000000
--- a/res/layout-sw600dp/folder_cling.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- launcher:drawIdentifier="folder_large">
- <LinearLayout
- android:id="@+id/folder_bubble"
- android:layout_width="300dp"
- android:layout_height="match_parent"
- android:layout_gravity="left|top"
- android:paddingTop="28dp"
- android:paddingRight="10dp"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:src="@drawable/cling_arrow_start" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginRight="4dp"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:id="@+id/folder_cling_title"
- android:text="@string/folder_cling_title" />
- <TextView
- style="@style/ClingText"
- android:id="@+id/folder_cling_create_folder"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/folder_cling_create_folder" />
- </LinearLayout>
- </LinearLayout>
- <Button
- style="@style/ClingButton"
- android:id="@+id/cling_dismiss"
- android:layout_marginTop="5dp"
- android:layout_gravity="right"
- android:onClick="dismissFolderCling" />
- </LinearLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw600dp/migration_cling.xml b/res/layout-sw600dp/migration_cling.xml
deleted file mode 100644
index 19def6a56..000000000
--- a/res/layout-sw600dp/migration_cling.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="migration_portrait">
- <LinearLayout
- android:id="@+id/content"
- android:layout_width="360dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:orientation="vertical">
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="top"
- android:layout_marginBottom="15dp"
- android:orientation="vertical">
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center"
- android:text="@string/first_run_cling_title"
- android:textSize="42dp"
- android:textColor="#FFffffff" />
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="10dp"
- android:layout_marginBottom="0dp"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/on_boarding_welcome" />
-
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_up" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="4dp"
- android:layout_marginRight="4dp"
- android:paddingLeft="25dp"
- android:paddingRight="25dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_description" />
- </LinearLayout>
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:orientation="vertical">
- <Button
- style="@style/ClingButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_copy_apps"
- android:onClick="dismissMigrationClingCopyApps" />
- <Button
- style="@style/ClingButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/migration_cling_use_default"
- android:onClick="dismissMigrationClingUseDefault" />
- </LinearLayout>
- </LinearLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw600dp/workspace_cling.xml b/res/layout-sw600dp/workspace_cling.xml
deleted file mode 100644
index 63b5522ca..000000000
--- a/res/layout-sw600dp/workspace_cling.xml
+++ /dev/null
@@ -1,65 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="workspace_large">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/workspace_cling_bubble"
- android:layout_width="400dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_down" />
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:onClick="dismissWorkspaceCling" />
- </LinearLayout>
- </FrameLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw720dp/first_run_cling.xml b/res/layout-sw720dp/first_run_cling.xml
deleted file mode 100644
index c43d8d32e..000000000
--- a/res/layout-sw720dp/first_run_cling.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="first_run_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <LinearLayout
- android:id="@+id/bubble_content"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_marginLeft="100dp"
- android:layout_marginRight="100dp"
- android:orientation="vertical">
- <TextView
- style="@style/ClingAltTitleText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:layout_marginBottom="10dp"
- android:text="@string/first_run_cling_title"
- android:textColor="#FFFFFFFF"
- android:gravity="center" />
- <TextView
- style="@style/ClingAltText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:text="@string/first_run_cling_description"
- android:textColor="#80000000"
- android:gravity="center" />
- </LinearLayout>
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/search_bar_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="top|end"
- android:layout_marginEnd="120dp"
- android:layout_marginTop="80dp"
- android:gravity="center_horizontal"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableTop="@drawable/cling_arrow_up"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_search_bar_hint" />
- <TextView
- style="@style/ClingHintText"
- android:id="@+id/custom_content_hint"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|start"
- android:layout_marginStart="30dp"
- android:gravity="start"
- android:maxWidth="160dp"
- android:visibility="gone"
- android:drawableStart="@drawable/cling_arrow_start"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_custom_content_hint" />
- <TextView
- style="@style/ClingHintText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical|end"
- android:layout_marginEnd="30dp"
- android:maxWidth="180dp"
- android:drawableEnd="@drawable/cling_arrow_end"
- android:drawablePadding="10dp"
- android:text="@string/first_run_cling_create_screens_hint" />
- </FrameLayout>
- <Button
- style="@style/ClingButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="30dp"
- android:layout_marginEnd="40dp"
- android:layout_gravity="bottom|end"
- android:onClick="dismissFirstRunCling" />
-</com.android.launcher3.Cling>
diff --git a/res/layout-sw720dp/launcher.xml b/res/layout-sw720dp/launcher.xml
index 685d03c6f..62615411f 100644
--- a/res/layout-sw720dp/launcher.xml
+++ b/res/layout-sw720dp/launcher.xml
@@ -30,6 +30,11 @@
android:layout_height="match_parent"
android:fitsSystemWindows="true">
+ <com.android.launcher3.FocusIndicatorView
+ android:id="@+id/focus_indicator"
+ android:layout_width="52dp"
+ android:layout_height="52dp" />
+
<!-- The workspace contains 5 screens of cells -->
<com.android.launcher3.Workspace
android:id="@+id/workspace"
@@ -61,40 +66,6 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" />
- <!-- The Workspace cling must appear under the AppsCustomizePagedView below to ensure
- that it is still visible during the transition to AllApps and doesn't overlay on
- top of that view. -->
- <com.android.launcher3.ScrimView
- android:id="@+id/cling_scrim"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/first_run_cling"
- android:id="@+id/first_run_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/migration_cling"
- android:id="@+id/migration_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/migration_workspace_cling"
- android:id="@+id/migration_workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/workspace_cling"
- android:id="@+id/workspace_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
- <include layout="@layout/folder_cling"
- android:id="@+id/folder_cling"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="gone" />
-
<com.android.launcher3.DrawableStateProxyView
android:id="@+id/voice_button_proxy"
android:layout_width="0dp"
diff --git a/res/layout-sw720dp/migration_workspace_cling.xml b/res/layout-sw720dp/migration_workspace_cling.xml
deleted file mode 100644
index eb13137a1..000000000
--- a/res/layout-sw720dp/migration_workspace_cling.xml
+++ /dev/null
@@ -1,70 +0,0 @@
-<?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.
--->
-<com.android.launcher3.Cling
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- launcher:drawIdentifier="migration_workspace_large_portrait">
- <FrameLayout
- android:id="@+id/content"
- android:layout_width="480dp"
- android:layout_height="match_parent"
- android:layout_gravity="bottom|center_horizontal">
- <LinearLayout
- android:id="@+id/migration_workspace_cling_bubble"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="bottom"
- android:layout_marginStart="25dp"
- android:layout_marginEnd="25dp"
- android:orientation="vertical">
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginRight="4dp"
- android:paddingLeft="20dp"
- android:paddingRight="20dp"
- android:paddingTop="20dp"
- android:paddingBottom="20dp"
- android:orientation="vertical"
- android:background="@drawable/cling">
- <TextView
- style="@style/ClingTitleText"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_title" />
- <TextView
- style="@style/ClingText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/workspace_cling_move_item" />
- </LinearLayout>
- <ImageView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:src="@drawable/cling_arrow_down" />
- <Button
- style="@style/ClingButton"
- android:id="@+id/dismiss_migration_workspace_cling_button"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="right"
- android:onClick="dismissMigrationWorkspaceCling" />
- </LinearLayout>
- </FrameLayout>
-</com.android.launcher3.Cling>
diff --git a/res/layout/all_apps_button.xml b/res/layout/all_apps_button.xml
index 1b9ea082f..9d6d82bb2 100644
--- a/res/layout/all_apps_button.xml
+++ b/res/layout/all_apps_button.xml
@@ -16,5 +16,4 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/WorkspaceIcon"
- android:focusable="true"
- android:background="@drawable/focusable_view_bg" />
+ android:focusable="true" />
diff --git a/res/layout/application.xml b/res/layout/application.xml
index e4909ddad..c21dea070 100644
--- a/res/layout/application.xml
+++ b/res/layout/application.xml
@@ -16,5 +16,4 @@
<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/WorkspaceIcon"
- android:focusable="true"
- android:background="@drawable/focusable_view_bg" />
+ android:focusable="true" />
diff --git a/res/layout/apps_customize_application.xml b/res/layout/apps_customize_application.xml
index 3b0fa6f4e..c56cdf3d2 100644
--- a/res/layout/apps_customize_application.xml
+++ b/res/layout/apps_customize_application.xml
@@ -14,15 +14,8 @@
limitations under the License.
-->
-<com.android.launcher3.PagedViewIcon
+<com.android.launcher3.BubbleTextView
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
-
style="@style/WorkspaceIcon.AppsCustomize"
-
android:id="@+id/application_icon"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
-
- android:focusable="true"
- android:background="@drawable/focusable_view_bg" />
+ android:focusable="true" />
diff --git a/res/layout/apps_customize_pane.xml b/res/layout/apps_customize_pane.xml
index eae216e93..bf5f71b90 100644
--- a/res/layout/apps_customize_pane.xml
+++ b/res/layout/apps_customize_pane.xml
@@ -16,64 +16,47 @@
<com.android.launcher3.AppsCustomizeTabHost
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:background="#80FFFFFF">
+ android:clipChildren="false">
+
<LinearLayout
- android:id="@+id/apps_customize_content"
- android:orientation="vertical"
+ android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:visibility="gone">
- <!-- The layout_width of the tab bar gets overriden to align the content
- with the text in the tabs in AppsCustomizeTabHost. -->
+ android:clipChildren="false"
+ android:orientation="vertical">
+
<FrameLayout
- android:id="@+id/tabs_container"
- android:layout_width="wrap_content"
- android:layout_height="@dimen/apps_customize_tab_bar_height"
- android:layout_marginTop="@dimen/apps_customize_tab_bar_margin_top"
- android:layout_gravity="center_horizontal"
- android:visibility="gone">
- <com.android.launcher3.FocusOnlyTabWidget
- android:id="@android:id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:clipChildren="false">
+ <FrameLayout
+ android:id="@+id/fake_page_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:layout_gravity="center"
- android:gravity="start"
- android:background="@drawable/tab_unselected_holo"
- android:tabStripEnabled="false"
- android:divider="@null" />
- <include
- android:id="@+id/market_button"
- layout="@layout/market_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_gravity="end" />
- </FrameLayout>
- <FrameLayout
- android:id="@android:id/tabcontent"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:clipChildren="false"
+ android:clipToPadding="false">
+ <FrameLayout
+ android:id="@+id/fake_page"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:visibility="invisible"
+ android:clipToPadding="false" />
+ </FrameLayout>
<com.android.launcher3.AppsCustomizePagedView
android:id="@+id/apps_customize_pane_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
launcher:widgetCountX="@integer/apps_customize_widget_cell_count_x"
launcher:widgetCountY="@integer/apps_customize_widget_cell_count_y"
- launcher:clingFocusedX="@integer/apps_customize_cling_focused_x"
- launcher:clingFocusedY="@integer/apps_customize_cling_focused_y"
launcher:maxGap="@dimen/workspace_max_gap"
launcher:pageIndicator="@+id/apps_customize_page_indicator" />
- <FrameLayout
- android:id="@+id/animation_buffer"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:background="#FF000000"
- android:visibility="gone" />
- <include
- android:id="@+id/apps_customize_page_indicator"
- layout="@layout/page_indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal|bottom" />
</FrameLayout>
+ <include
+ android:id="@+id/apps_customize_page_indicator"
+ layout="@layout/page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center" />
</LinearLayout>
</com.android.launcher3.AppsCustomizeTabHost>
diff --git a/res/layout/apps_customize_widget.xml b/res/layout/apps_customize_widget.xml
index 7c98b4a9b..e299b32b0 100644
--- a/res/layout/apps_customize_widget.xml
+++ b/res/layout/apps_customize_widget.xml
@@ -25,24 +25,45 @@
android:background="@drawable/focusable_view_bg"
android:focusable="true">
- <!-- The preview of the widget or shortcut. -->
- <com.android.launcher3.PagedViewWidgetImageView
- android:id="@+id/widget_preview"
- style="@style/PagedViewWidgetImageView"
+ <LinearLayout
+ android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:paddingTop="@dimen/app_widget_preview_padding_top"
- android:paddingEnd="@dimen/app_widget_preview_padding_right"
- android:paddingRight="@dimen/app_widget_preview_padding_right"
- android:scaleType="matrix"
- android:background="@drawable/screenpanel" />
+ android:layout_weight="1">
+ <FrameLayout
+ android:id="@+id/left_border"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="@color/widget_text_panel"
+ android:visibility="gone" />
+
+ <!-- The preview of the widget or shortcut. -->
+ <com.android.launcher3.PagedViewWidgetImageView
+ android:id="@+id/widget_preview"
+ style="@style/PagedViewWidgetImageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:paddingTop="@dimen/app_widget_preview_padding_top"
+ android:paddingEnd="@dimen/app_widget_preview_padding_right"
+ android:paddingRight="@dimen/app_widget_preview_padding_right"
+ android:scaleType="matrix" />
+ <FrameLayout
+ android:id="@+id/right_border"
+ android:layout_width="1dp"
+ android:layout_height="match_parent"
+ android:background="@color/widget_text_panel"
+ android:visibility="gone" />
+ </LinearLayout>
+
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/app_widget_preview_label_margin_top"
- android:layout_marginStart="@dimen/app_widget_preview_label_margin_left"
- android:layout_marginEnd="@dimen/app_widget_preview_label_margin_right"
+ android:paddingTop="@dimen/app_widget_preview_label_vertical_padding"
+ android:paddingBottom="@dimen/app_widget_preview_label_vertical_padding"
+ android:paddingLeft="@dimen/app_widget_preview_label_horizontal_padding"
+ android:paddingRight="@dimen/app_widget_preview_label_horizontal_padding"
+ android:background="@color/widget_text_panel"
android:orientation="horizontal">
<!-- The name of the widget. -->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
@@ -56,7 +77,7 @@
android:fadingEdge="horizontal"
android:textColor="#FFFFFFFF"
- android:textSize="13sp"
+ android:textSize="12sp"
android:textAlignment="viewStart"
android:fontFamily="sans-serif-condensed"
android:shadowRadius="2.0"
@@ -73,7 +94,7 @@
android:layout_weight="0"
android:gravity="start"
- android:textColor="#FFAAAAAA"
+ android:textColor="#FFFFFFFF"
android:textSize="12sp"
android:fontFamily="sans-serif-condensed"
android:shadowRadius="2.0"
diff --git a/res/values-sw340dp-land/dimens.xml b/res/layout/appwidget_not_ready.xml
index 7901dc47d..be7c33b36 100644
--- a/res/values-sw340dp-land/dimens.xml
+++ b/res/layout/appwidget_not_ready.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 201 The Android Open Source Project
+<!--
+ 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,7 +15,6 @@
limitations under the License.
-->
-<resources>
-<!-- Clings -->
- <dimen name="folderClingMarginTop">50dp</dimen>
-</resources>
+<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/res/layout/custom_content_page_indicator_marker.xml b/res/layout/custom_content_page_indicator_marker.xml
deleted file mode 100644
index 8fe3f8fdf..000000000
--- a/res/layout/custom_content_page_indicator_marker.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<com.android.launcher3.PageIndicatorMarker
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
- android:layout_width="16dp"
- android:layout_height="16dp"
- android:layout_gravity="center_vertical">
- <ImageView
- android:id="@+id/inactive"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:src="@drawable/custom_content_page"
- />
- <ImageView
- android:id="@+id/active"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:src="@drawable/custom_content_page"
- android:alpha="0"
- android:scaleX="0.5"
- android:scaleY="0.5"
- />
-</com.android.launcher3.PageIndicatorMarker>
diff --git a/res/drawable/cling_arrow_end.xml b/res/layout/folder_application.xml
index 3f63c7d24..b48b61331 100644
--- a/res/drawable/cling_arrow_end.xml
+++ b/res/layout/folder_application.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2013 The Android Open Source Project
+<!-- Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
- android:src="@drawable/cling_arrow_right"
- android:autoMirrored="true">
-</bitmap>
+
+<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/WorkspaceIcon.Folder"
+ android:focusable="true" />
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 5147f9960..fd45d7685 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -19,8 +19,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:focusable="true"
- android:background="@drawable/focusable_view_bg">
+ android:focusable="true" >
<ImageView
android:id="@+id/preview_background"
android:layout_gravity="center_horizontal"
diff --git a/res/layout/longpress_cling_content.xml b/res/layout/longpress_cling_content.xml
new file mode 100644
index 000000000..47a8e9797
--- /dev/null
+++ b/res/layout/longpress_cling_content.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="24dp"
+ android:paddingTop="36dp" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="36dp"
+ android:paddingRight="36dp"
+ android:text="@string/workspace_cling_longpress_title"
+ android:textColor="#E1000000"
+ android:textSize="24sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:paddingLeft="36dp"
+ android:paddingRight="36dp"
+ android:text="@string/workspace_cling_longpress_description"
+ android:textColor="#99000000"
+ android:textSize="16sp" />
+
+ <Button
+ android:id="@+id/cling_dismiss_longpress_info"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:layout_marginRight="12dp"
+ android:layout_marginTop="27dp"
+ android:fontFamily="sans-serif-medium"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:text="@string/workspace_cling_longpress_dismiss"
+ android:textColor="#FFFFFFFF"
+ 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
new file mode 100644
index 000000000..dd4f8d767
--- /dev/null
+++ b/res/layout/longpress_cling_welcome_content.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:paddingBottom="24dp"
+ android:paddingTop="36dp" >
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:paddingLeft="36dp"
+ android:paddingRight="36dp"
+ android:text="@string/first_run_cling_title"
+ android:textColor="#E1000000"
+ android:textSize="34sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="5.3dp"
+ android:fontFamily="sans-serif-medium"
+ android:paddingLeft="36dp"
+ android:paddingRight="36dp"
+ android:text="@string/workspace_cling_longpress_title"
+ android:textColor="#E1000000"
+ android:textSize="20sp" />
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:paddingLeft="36dp"
+ android:paddingRight="36dp"
+ android:text="@string/workspace_cling_longpress_description"
+ android:textColor="#99000000"
+ android:textSize="16sp" />
+
+ <Button
+ android:id="@+id/cling_dismiss_longpress_info"
+ style="?android:attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ android:layout_marginRight="12dp"
+ android:layout_marginTop="27dp"
+ android:fontFamily="sans-serif-medium"
+ android:paddingLeft="24dp"
+ android:paddingRight="24dp"
+ android:text="@string/workspace_cling_longpress_dismiss"
+ android:textColor="#FFFFFFFF"
+ android:textSize="14sp" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/res/layout/user_folder.xml b/res/layout/user_folder.xml
index 5d5f33b1f..4e5303ac0 100644
--- a/res/layout/user_folder.xml
+++ b/res/layout/user_folder.xml
@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:background="@drawable/portal_container_holo">
+ android:background="@drawable/quantum_panel">
<ScrollView
android:id="@+id/scroll_view"
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index b5c17c5d1..4a4bb78a2 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android-kernprogramme"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Program is nie geïnstalleer nie."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Afgelaaide program in veiligmodus gedeaktiveer"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Legstukke"</string>
<string name="widget_adder" msgid="3201040140710381657">"Legstukke"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Wys Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"skryf Tuis-instellings en -kortpaaie"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Laat die program toe om die instellings en kortpaaie in Tuis te verander."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Kon nie legstuk laai nie"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Stel op"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is \'n stelselprogram en kan nie gedeïnstalleer word nie."</string>
<string name="dream_name" msgid="1530253749244328964">"Vuurpyllanseerder"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Naamlose vouer"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"BEGIN VAN NUUTS AF"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organiseer jou spasie"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Raak en hou agtergrond om muurpapier, legstukke en instellings te bestuur."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Muurpapiere, legstukke en instellings"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Raak en hou agtergrond om te pasmaak"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"HET DIT"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Hier\'s \'n vouer"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Om een soos dié te skep, raak en hou \'n program en skuif dit dan oor \'n ander een."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Legstukke"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Muurpapiere"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Instellings"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Wag tans…"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Laai tans af…"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installeer tans…"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nie teruggestel nie"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Verwyder almal"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwyder"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Soek"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Hierdie program is nie geïnstalleer nie"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die program vir hierdie ikoon is nie geïnstalleer nie. Jy kan dit verwyder of die program soek en dit self installeer."</string>
</resources>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index ce9fb9b39..25931db7d 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android ዋና መተግበሪያዎች"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"መተግበሪያ አልተጫነም።"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"የወረደው መተግበሪያ ደህንነቱ በተጠበቀ ሁኔታ ውስጥ ተሰናክሏል"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"ፍርግሞች"</string>
<string name="widget_adder" msgid="3201040140710381657">"ፍርግሞች"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"ማህደረ ማስታወሻ አሳይ"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"የመነሻ ቅንብሮችን እና አቋራጮችን ይጽፋል"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"መተግብሪያው ቅንብሮችን እና አቋራጮችን በመነሻ ውስጥ እንዲቀይራቸው ያስችለዋል።"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"ፍርግም የመጫን ችግር"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ማዋቀሪያ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ይህ የስርዓት መተግበሪያ ነው እና ማራገፍ አይቻልም።"</string>
<string name="dream_name" msgid="1530253749244328964">"የሮኬት ማስጀመሪያ"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"ስም-አልባ አቃፊ"</string>
@@ -94,7 +96,10 @@
<string name="migration_cling_copy_apps" msgid="946331230090919440">"አዶዎችን ይቅዱ"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"እንደ አዲስ ይጀምሩ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"ቦታዎን ያደራጁ"</string>
- <string name="workspace_cling_move_item" msgid="528201129978005352">"የግድግዳ ወረቀት፣ ምግብሮችን እና ቅንብሮችን ለማቀናበር ጀርባውን ይንኩ እና ይያዙት።"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"ልጣፍ ፣ ምግብሮችን እና ቅንብሮችን ለማቀናበር ጀርባውን ይንኩ እና ይያዙት።"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"የግድግዳ ወረቀቶች፣ ንዑስ ፕሮግራሞች እና ቅንብሮች"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ለማበጀት ጀርባውን ነክተው ይያዙት"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ገባኝ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"አንድ አቃፊ እነሆ"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"አንድ እንደዚህ አይነት ለመፍጠር መተግበሪያውን ነክተው ይያዙት እና ወደ ሌላ ያንቀሳቅሱት።"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"እሺ"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"ፍርግሞች"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"የግድግዳ ወረቀቶች"</string>
<string name="settings_button_text" msgid="8119458837558863227">"ቅንብሮች"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"በመጠበቅ ላይ"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"በማውረድ ላይ"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"በመጫን ላይ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"የማይታወቅ"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"ወደነበረበት አልተመለሰም"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"ሁሉንም አስወግድ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"አስወግድ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ፈልግ"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ይህ መተግበሪያ አልተጫነም"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"የዚህ አዶ መተግበሪያ አልተጫነም። ማስወገድ ወይም መተግበሪያውን መፈለግና ራስዎ መጫን ይችላሉ።"</string>
</resources>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 7eb37d3e9..8b6aa025c 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"‏تطبيقات Android الأساسية"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"لم يتم تثبيت التطبيق."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"تم تعطيل التطبيق الذي تم تنزيله في الوضع الآمن"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"الأدوات"</string>
<string name="widget_adder" msgid="3201040140710381657">"الأدوات"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"عرض الذاكرة"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"كتابة إعدادات واختصارات الشاشة الرئيسية"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"للسماح للتطبيق بتغيير الإعدادات والاختصارات في الشاشة الرئيسية."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"حدثت مشكلة أثناء تحميل الأداة"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"الإعداد"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"هذا تطبيق نظام وتتعذر إزالته."</string>
<string name="dream_name" msgid="1530253749244328964">"قاذفة صواريخ"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"مجلد بدون اسم"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"بداية جديدة"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"تنظيم مساحتك"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"المس مع الاستمرار الجزء الخلفي من صورة الشاشة لإدارة الخلفية والأدوات والإعدادات."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"الخلفيات والأدوات والإعدادات"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"المس مع الاستمرار الخلفية لتخصيصها"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"حسنًا"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"إليك المجلد"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"لإنشاء مجلد مثل هذا، المس أحد التطبيقات مع استمرار اللمس، ثم حركه فوق آخر."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"موافق"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"الأدوات"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"الخلفيات"</string>
<string name="settings_button_text" msgid="8119458837558863227">"الإعدادات"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"انتظار"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"جارٍ التنزيل"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"جارٍ التثبيت"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"غير معروفة"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"استعادة مخفقة"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"إزالة الكل"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"إزالة"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"بحث"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"لم يتم تثبيت هذا التطبيق"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"لم يتم تثبيت تطبيق لهذا الرمز. يمكنك إزالته أو البحث عن التطبيق وتثبيته يدويًا."</string>
</resources>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index 13c011859..dcd193050 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Основни приложения на Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Приложението не е инсталирано."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Изтегленото приложение е деактивирано в безопасния режим"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Приспособления"</string>
<string name="widget_adder" msgid="3201040140710381657">"Приспособления"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Показване на паметта"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"запис на настройките и преките пътища в Начало"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Разрешава на приложението да променя настройките и преките пътища в Начало."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Проблем при зареждане на приспособлението"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Настройване"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Това е системно приложение и не може да се деинсталира."</string>
<string name="dream_name" msgid="1530253749244328964">"Ракетна площадка"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Папка без име"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"СТАРТИРАНЕ ОТНАЧАЛО"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Организиране на мястото ви"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Докоснете и задръжте фона, за да управлявате тапета, приспособленията и настройките."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Тапети, приспособления и настройки"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Докоснете и задръжте фона за персонализиране"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"РАЗБРАХ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ето една папка"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"За да създадете подобна, докоснете и задръжте приложение, след което го преместете върху друго."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"ОK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Приспособления"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Изчаква"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Изтегля се"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Инсталира се"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Няма информация"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Не е възстановено"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Премахване на всички"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Премахване"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Търсене"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Това приложение не е инсталирано"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Приложението за тази икона не е инсталирано. Можете да я премахнете или да потърсите приложението и да го инсталирате ръчно."</string>
</resources>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
new file mode 100644
index 000000000..f60f1c507
--- /dev/null
+++ b/res/values-bn-rBD/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"লঞ্চার৩"</string>
+ <string name="home" msgid="7658288663002113681">"হোম"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android প্রাথমিক অ্যাপ্লিকেশানগুলি"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"অ্যাপ্লিকেশান ইনস্টল করা নেই৷"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ডাউনলোড করা অ্যাপ্লিকেশান নিরাপদ মোডে অক্ষম রয়েছে"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"উইজেটগুলি"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"উইজেটগুলি"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"মেম দেখান"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"একটি উইজেট তুলতে তা স্পর্শ করে ধরে রাখুন৷"</string>
+ <string name="market" msgid="2619650989819296998">"দোকান"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"এই হোম স্ক্রীনে আইটেম রাখা যায়নি৷"</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"তৈরি করেতে উইজেট চয়ন করুন"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"ফোল্ডারের নাম"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"ফোল্ডার পুনঃনামকরণ করুন"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ঠিক আছে"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"বাতিল করুন"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"হোম স্ক্রীনে যোগ করুন"</string>
+ <string name="group_applications" msgid="3797214114206693605">"অ্যাপ্লিকেশানগুলি"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"শর্টকাটগুলি"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"উইজেটগুলি"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"আপনার হোম স্ক্রীনগুলিতে আর কোনো জায়গা নেই৷"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"পছন্দসই ট্রে-তে আর কোনো জায়গা নেই"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"পছন্দসই ট্রে\'র জন্য এই উইজেটটি খুবই বড়"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"শর্টকাট \"<xliff:g id="NAME">%s</xliff:g>\" তৈরি করা হয়েছে৷"</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"শর্টকাট \"<xliff:g id="NAME">%s</xliff:g>\" সরানো হয়েছে৷"</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"শর্টকাট <xliff:g id="NAME">%s</xliff:g> আগে থেকেই আছে৷"</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"শর্টকাট চয়ন করুন"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"অ্যাপ্লিকেশান চয়ন করুন"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"অ্যাপ্লিকেশানগুলি"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"হোম"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"সরান"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"আনইনস্টল করুন"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"সরান"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"আনইনস্টল করুন"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"অ্যাপ্লিকেশানের তথ্য"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"অনুসন্ধান করুন"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"ভয়েস অনুসন্ধান"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"অ্যাপ্লিকেশানগুলি"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"সরান"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"আপডেট আনইনস্টল করুন"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"অ্যাপ্লিকেশান আনইনস্টল করুন"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"অ্যাপ্লিকেশানের বিশদ বিবরণ"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"১টি অ্যাপ্লিকেশান নির্বাচন করা হয়েছে"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"১টি উইজেট নির্বাচন করা হয়েছে"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"১টি ফোল্ডার নির্বাচন করা হয়েছে"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"১টি শর্টকাট নির্বাচন করা হয়েছে"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"শর্টকাটগুলি ইনস্টল করে"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"একটি অ্যাপ্লিকেশানকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি যোগ করার অনুমতি দেয়৷"</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"শর্টকাটগুলি আনইনস্টল করে"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"অ্যাপ্লিকেশানটিকে ব্যবহারকারীর হস্তক্ষেপ ছাড়াই শর্টকাটগুলি সরানোর অনুমতি দেয়৷"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"হোম সেটিংস এবং শর্টকাটগুলি পড়ে"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"হোমে অ্যাপ্লিকেশানটিকে সেটিংস এবং শর্টকাটগুলি পড়তে দেয়৷"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"হোম সেটিংস এবং শর্টকাটগুলি লেখে"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"হোমে অ্যাপ্লিকেশানটিকে সেটিংস এবং শর্টকাটগুলি পরিবর্তন করতে দেয়৷"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"উইজেট লোড হতে সমস্যা হয়েছে"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"এটি একটি সিস্টেম অ্যাপ্লিকেশান এবং আনইনস্টল করা যাবে না৷"</string>
+ <string name="dream_name" msgid="1530253749244328964">"রকেট লঞ্চার"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"নামবিহীন ফোল্ডার"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"%1$d নম্বর হোম স্ক্রীন"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$dটির মধ্যে %1$dটি পৃষ্ঠা"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dটির %1$d নম্বর হোম স্ক্রীন"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%2$dটির মধ্যে %1$dটি অ্যাপ্লিকেশান পৃষ্ঠা"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"%2$dটির মধ্যে %1$dটি উইজেট পৃষ্ঠা"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"স্বাগতম"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"নিজের বাড়ির মতো স্বাচ্ছন্দ্য বোধ করুন৷"</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"অ্যাপ্লিকেশান এবং ফোল্ডারগুলির জন্য আরো স্ক্রীn তৈরি করুন"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"আপনার অ্যাপ্লিকেশান আইকনগুলি অনুলিপি করুন"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"আপনার পুরানো হোম স্ক্রীন থেকে আইকন এবং ফোল্ডারগুলি আমদানি করবেন?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"আইকনগুলি অনুলিপি করুন"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"নতুন করে শুরু করুন"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"আপনার স্থান সংগঠিত করুন"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"ওয়ালপেপার, উইজেট এবং সেটিংস পরিচালনা করতে পটভূমি স্পর্শ করে ধরে রাখুন৷"</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"এখানে একটি ফোল্ডার আছে"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"এটির মতো একটি তৈরি করতে, একটি অ্যাপ্লিকেশান স্পর্শ করে ধরে রাখুন, এবং তারপরে এটিকে অন্য একটির উপরে সরিয়ে নিয়ে যান৷"</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ঠিক আছে"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ফোল্ডার খোলা হয়েছে, <xliff:g id="WIDTH">%1$d</xliff:g> বাই <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"ফোল্ডার বন্ধ করতে স্পর্শ করুন"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"পুনঃনামকরণ সংরক্ষণ করতে স্পর্শ করুন"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ফোল্ডার বন্ধ করা হয়েছে"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ফোল্ডারের নাম পাল্টে <xliff:g id="NAME">%1$s</xliff:g> করা হয়েছে"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ফোল্ডার: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"উইজেটগুলি"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ওয়ালপেপারগুলি"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"সেটিংস"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"প্রতীক্ষা"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ডাউনলোড হচ্ছে"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ইনস্টল করা হচ্ছে"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"অজানা"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"পুনঃস্থাপন করা যায়নি"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"সবগুলি সরান"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"সরান"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"অনুসন্ধান"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"এই অ্যাপ্লিকেশানটি ইন্সটল করা নাই"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"এই আইকনের অ্যাপ্লিকেশানটি ইন্সটল করা নাই। আপনি এটি সরাতে পারেন বা অ্যাপ্লিকেশানটি অনুসন্ধান করে এটি নিজে ইন্সটল করতে পারেন।"</string>
+</resources>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 3e6bedba9..6d1023545 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Aplicacions principals d\'Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'aplicació no s\'ha instal·lat."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'aplicació que has baixat està desactivada al mode segur."</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostra la memòria"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"escriu la configuració i les dreceres de la pantalla d\'inici"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permet que l\'aplicació canviï la configuració i les dreceres de la pantalla d\'inici."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"S\'ha produït un problema en carregar el widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuració"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Aquesta aplicació és una aplicació del sistema i no es pot desinstal·lar."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sense nom"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"NOU COMENÇAMENT"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organitza el teu espai"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Toca i mantén premut el fons per gestionar el fons de pantalla, els widgets i la configuració."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fons de pantalla, widgets i configuració"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén premut el fons per fer personalitzacions."</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"D\'ACORD"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Aquí hi ha una carpeta"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Per crear-ne una com aquesta, mantén premuda una aplicació i, a continuació, mou-la sobre una altra."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"D\'acord"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fons de pantalla"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Configuració"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"En espera"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"S\'està baixant"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instal·lant"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Desconegut"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"No restaurat"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Suprimeix-ho tot"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Suprimeix"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Aquesta aplicació no està instal·lada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'aplicació d\'aquesta icona no està instal·lada. Pots suprimir-la o cercar l\'aplicació i instal·lar-la manualment."</string>
</resources>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 3a0e6721c..f729a460e 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikace není nainstalována."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Stažená aplikace je v nouzovém režimu zakázána"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgety"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgety"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Zobrazit Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"zápis nastavení a odkazů plochy"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Umožňuje aplikaci změnit nastavení a odkazy na ploše."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problém s načtením widgetu"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavení"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikace a nelze ji odinstalovat."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Složka bez názvu"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ZAČÍT S VÝCHOZÍM ROZVRŽENÍM"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organizace prostoru"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Chcete-li spravovat tapetu, widgety a nastavení, dotkněte se pozadí a přidržte je."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Tapety, widgety a nastavení"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Pozadí můžete přizpůsobit klepnutím a podržením"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ROZUMÍM"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Toto je složka"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Chcete-li vytvořit složku, přetáhněte aplikaci na jinou aplikaci."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgety"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Nastavení"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Čekání"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Stahování"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalace"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Neznámé"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nebylo obnoveno"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Odstranit vše"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstranit"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Hledat"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Tato aplikace není nainstalována"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikace pro tuto ikonu není nainstalována. Můžete ikonu odstranit nebo zkusit aplikaci vyhledat a nainstalovat ručně."</string>
</resources>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index eabde3e2b..85159b2e1 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Kerneapps i Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installeret."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloadet app er deaktiveret i sikker tilstand"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Vis Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"skrive indstillinger og genveje for startskærmen"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Tillader, at appen ændrer indstillingerne og genvejene på startskærmen."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Der er problemer med indlæsning af widgetten"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurer"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp, som ikke kan afinstalleres."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unavngiven mappe"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"START PÅ EN FRISK"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organiser din arbejdsplads"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Tryk på en baggrund, og hold fingeren nede for at administrere baggrunde, widgets og indstillinger."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Baggrunde, widgets og indstillinger"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Tryk på baggrunden, og hold fingeren nede for at tilpasse den"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK, FORSTÅET"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Her kan du se en mappe"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Du kan oprette en mappe magen til denne ved at trykke på en app og holde fingeren nede, mens du flytter appen til en anden mappe."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Baggrunde"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Indstillinger"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Afventer"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Downloader"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installerer"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Ukendt"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ikke gendannet"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Slet alle"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Søg"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Denne app er ikke installeret"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen, der hører til dette ikon, er ikke installeret. Du kan fjerne den eller prøve at søge efter appen og installere den manuelt."</string>
</resources>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 27ad73ada..698a25413 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"App ist nicht installiert."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Heruntergeladene App im abgesicherten Modus deaktiviert"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Speicher anzeigen"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"Einstellungen und Verknüpfungen für den Startbildschirm schreiben"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ermöglicht der App, die Einstellungen und Verknüpfungen auf dem Startbildschirm zu ändern"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem beim Laden des Widgets"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Einrichten"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dies ist eine Systemanwendung, die nicht deinstalliert werden kann."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unbenannter Ordner"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"Standardübersicht verwenden"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Arbeitsbereich organisieren"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Hintergrund berühren und halten, um Hintergrund, Widgets und Einstellungen zu verwalten"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Hintergründe, Widgets &amp; Einstellungen"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Berühren und halten Sie den Hintergrund, um ihn anzupassen."</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Hier ist ein Ordner"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Um einen Ordner zu erstellen, berühren und halten Sie eine App und verschieben Sie sie auf eine andere."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hintergründe"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Einstellungen"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Warten"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Download läuft"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installation"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Unbekannt"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nicht wiederhergestellt"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Alle entfernen"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Entfernen"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Suchen"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Diese App ist nicht installiert"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Die App für dieses Symbol ist nicht installiert. Sie können das Symbol entfernen oder nach der App suchen und sie manuell installieren."</string>
</resources>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ec05ae57c..3eec27d4f 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Βασικές εφαρμογές Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Η εφαρμογή δεν έχει εγκατασταθεί."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Η λήψη εφαρμογών απενεργοποήθηκε στην Ασφαλή λειτουργία"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Γραφικά στοιχεία"</string>
<string name="widget_adder" msgid="3201040140710381657">"Γραφικά στοιχεία"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Εμφάνιση Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"εγγραφή ρυθμίσεων και συντομεύσεων αρχικής οθόνης"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Επιτρέπει στην εφαρμογή την αλλαγή των ρυθμίσεων και των συντομεύσεων στην Αρχική οθόνη."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Παρουσιάστηκε πρόβλημα στη φόρτωση του γραφικού στοιχείου"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Ρύθμιση"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Αυτή είναι μια εφαρμογή συστήματος και δεν είναι δυνατή η κατάργηση της εγκατάστασής της."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Φάκελος χωρίς όνομα"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ΝΕΑ ΕΝΑΡΞΗ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Οργανώστε το χώρο σας"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Αγγίξτε παρατεταμένα το φόντο για να διαχειριστείτε την ταπετσαρία, τα γραφικά στοιχεία και τις ρυθμίσεις."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Ταπετσαρίες, γραφικά στοιχεία και ρυθμίσεις"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Αγγίξτε παρατεταμένα το παρασκήνιο για προσαρμογή"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ΕΓΙΝΕ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ορίστε ένας φάκελος"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Για να δημιουργήσετε έναν φάκελο σαν κι αυτόν, πατήστε παρατεταμένα μια εφαρμογή και στη συνέχεια, μετακινήστε τη πάνω σε μια άλλη."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Γραφικά στοιχεία"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ταπετσαρίες"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Ρυθμίσεις"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Αναμονή"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Λήψη "</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Εγκατάσταση"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Άγνωστο"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Δεν ανακτήθηκε"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Κατάργηση όλων"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Κατάργηση"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Αναζήτηση"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Αυτή η εφαρμογή δεν είναι εγκατεστημένη"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Η εφαρμογή γι\' αυτό το εικονίδιο δεν είναι εγκατεστημένη. Μπορείτε να το καταργήσετε ή να αναζητήσετε την εφαρμογή και να την εγκαταστήσετε με μη αυτόματο τρόπο."</string>
</resources>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index cab17077a..615e5c90d 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Show Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organise your space"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Touch &amp; hold background to manage wallpaper, widgets and settings."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, &amp; settings"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Touch &amp; hold background to customise"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Here\'s a folder"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"To create one like this, touch &amp; hold an app, then move it over another."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Waiting"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Downloading"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installing"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Not restored"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Remove All"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
</resources>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index cab17077a..615e5c90d 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"App isn\'t installed."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Downloaded app disabled in Safe mode"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Show Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"write Home settings and shortcuts"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Allows the app to change the settings and shortcuts in Home."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem loading widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"This is a system app and can\'t be uninstalled."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Unnamed Folder"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"START AFRESH"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organise your space"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Touch &amp; hold background to manage wallpaper, widgets and settings."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpapers, widgets, &amp; settings"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Touch &amp; hold background to customise"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"GOT IT"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Here\'s a folder"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"To create one like this, touch &amp; hold an app, then move it over another."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpapers"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Settings"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Waiting"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Downloading"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installing"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Unknown"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Not restored"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Remove All"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Remove"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Search"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"This app is not installed"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"The app for this icon isn\'t installed. You can remove it, or search for the app and install it manually."</string>
</resources>
diff --git a/res/values-en/dimens.xml b/res/values-en/dimens.xml
deleted file mode 100644
index 01d4693c7..000000000
--- a/res/values-en/dimens.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?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.
--->
-
-<resources>
-<!-- Cling -->
- <dimen name="cling_title_text_size">22sp</dimen>
- <dimen name="cling_text_size">16sp</dimen>
- <dimen name="cling_alt_title_text_size">30sp</dimen>
- <dimen name="cling_alt_text_size">16sp</dimen>
- <dimen name="cling_hint_text_size">18sp</dimen>
-</resources>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index 278f0d82f..31afa3e52 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Aplicaciones básicas de Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"No se instaló la aplicación."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"escribir configuración y accesos directos de la pantalla principal"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que la aplicación cambie la configuración y los accesos directos de la pantalla principal."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema al cargar el widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta es una aplicación del sistema y no se puede desinstalar."</string>
<string name="dream_name" msgid="1530253749244328964">"Lanzacohetes"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"EMPEZAR DE CERO"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organiza tu espacio"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Mantén presionado el fondo para administrar el fondo de pantalla, los widgets y la configuración."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fondos, widgets y configuración"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén presionado el fondo para personalizarlo"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ENTENDIDO"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Aquí tienes una carpeta"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Para crear una carpeta como esta, mantén presionada una aplicación y luego muévela sobre otra."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Aceptar"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Pendiente"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Descargando"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"No restaurado"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Eliminar todo"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación para este ícono no está instalada. Puedes eliminar el ícono o buscar la aplicación e instarla manualmente."</string>
</resources>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index f9cf809b2..4e9141208 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Aplicaciones básicas de Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"La aplicación no está instalada."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicación descargada inhabilitada en modo seguro"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"escribir información de accesos directos y de ajustes de la pantalla de inicio"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que las aplicaciones cambien los ajustes y los accesos directos de la pantalla de inicio."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema al cargar el widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuración"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación es del sistema y no se puede desinstalar."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Carpeta sin nombre"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"AJUSTES PREDETERMINADOS"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organiza tu espacio"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Mantén pulsado el fondo para gestionar el fondo de pantalla, los widgets y los ajustes."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fondos de pantalla, widgets y ajustes"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Mantén pulsado el fondo para personalizarlo"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ENTENDIDO"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Esto es una carpeta"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Para crear una carpeta como esta, mantén pulsada una aplicación y muévela sobre otra."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Aceptar"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Ajustes"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Esperando"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Descargando"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Desconocido"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"No restaurado"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Eliminar todo"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación no está instalada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"La aplicación de este icono no está instalada. Puedes eliminar el icono o buscar la aplicación e instalarla manualmente."</string>
</resources>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 92985d229..fa352a1a3 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Androidi tuumrakendused"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Rakendus pole installitud."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Allalaetud rakendus on turvarežiimis keelatud"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Vidinad"</string>
<string name="widget_adder" msgid="3201040140710381657">"Vidinad"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mälu kuvamine"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"kirjuta avaekraani seaded ja otseteed"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Võimaldab rakendusel muuta avaekraanil seadeid ja otseteid."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Probleem vidina laadimisel"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Seadistamine"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"See on süsteemirakendus ja seda ei saa desinstallida."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Nimetu kaust"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ALUSTA ALGUSEST"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Korraldage oma ruumi"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Taustapildi, vidinate ja seadete haldamiseks puudutage tausta ning hoidke seda all."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Taustapildid, vidinad ja seaded"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Kohandamiseks puudutage ja hoidke tausta all"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SELGE"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Siin on kaust"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Sarnase loomiseks vajutage ja hoidke rakendust all, seejärel viige see teise peale."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Vidinad"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustapildid"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Seaded"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Ootamine"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Allalaadimine"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installimine"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Teadmata"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ei taastatud"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Eemalda kõik"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eemalda"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Otsing"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"See rakendus ei ole installitud"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Selle ikooni rakendust pole installitud. Saate selle eemaldada või rakendust otsida ja käsitsi installida."</string>
</resources>
diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml
index acd072725..3b0deb85b 100644
--- a/res/values-et/strings.xml
+++ b/res/values-et/strings.xml
@@ -41,7 +41,6 @@
<string name="group_applications" msgid="2103752818818161976">"Rakendused"</string>
<string name="group_shortcuts" msgid="9133529424900391877">"Otseteed"</string>
<string name="group_widgets" msgid="6704978494073105844">"Vidinad"</string>
- <string name="group_wallpapers" msgid="1568191644272224858">"Taustapildid"</string>
<string name="completely_out_of_space" msgid="1759078539443491182">"Teie avakuvadel ei ole enam ruumi."</string>
<string name="out_of_space" msgid="8365249326091984698">"Sellel avalehel pole enam ruumi."</string>
<string name="hotseat_out_of_space" msgid="6304886797358479361">"Kohandataval dokialal pole rohkem ruumi."</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
new file mode 100644
index 000000000..4a4c959f4
--- /dev/null
+++ b/res/values-eu-rES/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Abiarazlea3"</string>
+ <string name="home" msgid="7658288663002113681">"Hasiera"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android-en nukleoko aplikazioak"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Aplikazioa instalatu gabe dago."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Deskargatutako aplikazioa modu seguruan desgaitu da"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Widgetak"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Widgetak"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Erakutsi memoria"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Eduki ukituta widgeta aukeratzeko."</string>
+ <string name="market" msgid="2619650989819296998">"Denda"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Ezin izan da elementua hasierako pantailan jaregin."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Aukeratu sortu beharreko widgeta"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Karpetaren izena"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Aldatu karpetaren izena"</string>
+ <string name="rename_action" msgid="5559600076028658757">"Ados"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Utzi"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Gehitu hasierako pantailan"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Aplikazioak"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Lasterbideak"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Widgetak"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Hasierako pantailetan ez dago toki gehiago."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Hasierako pantaila honetan ez dago toki gehiago."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ez dago toki gehiago Gogokoak erretiluan"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Widgeta handiegia da Gogokoak erretiluan ezartzeko"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" lasterbidea sortu da."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" lasterbidea kendu da."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" lasterbidea lehendik dago."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Aukeratu lasterbidea"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Aukeratu aplikazioa"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Aplikazioak"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Hasiera"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"Kendu"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Desinstalatu"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Kendu"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalatu"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Aplikazioaren informazioa"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Bilaketa"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Ahots bidezko bilaketa"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Aplikazioak"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"Kendu"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Desinstalatu eguneratzea"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Desinstalatu aplikazioa"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Aplikazioaren xehetasunak"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"Aplikazio bat hautatu da"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"Widget bat hautatu da"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"Karpeta bat hautatu da"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"Lasterbide bat hautatu da"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"Instalatu lasterbideak"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Erabiltzaileak ezer egin gabe lasterbideak gehitzea baimentzen die aplikazioei."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"Desinstalatu lasterbideak"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Erabiltzaileak ezer egin gabe lasterbideak kentzea baimentzen die aplikazioei."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Irakurri hasierako ezarpenak eta lasterbideak"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Hasierako pantailako ezarpenak eta lasterbideak irakurtzea baimentzen die aplikazioei."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Idatzi hasierako ezarpenak eta lasterbideak"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Hasierako pantailako ezarpenak eta lasterbideak aldatzea baimentzen die aplikazioei."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Arazo bat izan da widgeta kargatzean"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Sistema-aplikazioa da hau eta ezin da desinstalatu."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Izenik gabeko karpeta"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"%1$d hasierako pantaila"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%1$d/%2$d orria"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d/%2$d hasierako pantaila"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%1$d/%2$d aplikazio-orria"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"%1$d/%2$d widget-orria"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Ongi etorri"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"Senti zaitez etxean bezala."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Sortu pantaila gehiago aplikazioak eta karpetak ezartzeko"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Kopiatu aplikazioen ikonoak"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Ikonoak eta karpetak aurreko hasierako pantailatik inportatu?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"KOPIATU IKONOAK"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"HASI HUTSETIK"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Antolatu zure txokoa"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Eduki ukituta atzeko planoa horma-paperak, widgetak eta ezarpenak kudeatzeko."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Hortxe duzu karpeta"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Horrelako bat sortzeko, eduki ukituta aplikazio bat eta eraman beste baten gainera."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"Ados"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Karpeta ireki da: <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Karpeta ixteko, uki ezazu"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"Karpetaren izen berria gordetzeko, uki ezazu"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Karpeta itxi da"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Karpetari <xliff:g id="NAME">%1$s</xliff:g> izena eman zaio"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Karpeta: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Widgetak"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Horma-paperak"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Ezarpenak"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Zain"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Deskargatzen"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalatzen"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Ezezaguna"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ez da leheneratu"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Kendu guztiak"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Kendu"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Bilatu"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikazio hau ez dago instalatuta"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ikono honen aplikazioa ez dago instalatuta. Ikonoa ken dezakezu, edo aplikazioa bilatu eta eskuz instalatu."</string>
+</resources>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index a01ea7932..13f40e223 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"‏برنامه‌های Android Core"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"برنامه نصب نشده است."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"برنامه دانلود شده در حالت ایمن غیرفعال شد"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"ابزارک‌ها"</string>
<string name="widget_adder" msgid="3201040140710381657">"ابزارک‌ها"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"‏نمایش Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"نوشتن تنظیمات و میان‌برهای صفحه اصلی"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"به برنامه اجازه می‌دهد تنظیمات و میان‌برها را در صفحه اصلی تغییر دهد."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"مشکل در بارگیری ابزارک"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"تنظیم"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"این برنامه سیستمی است و حذف نصب نمی‌شود."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"پوشه بی‌نام"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"شروع تازه"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"فضای خود را سازماندهی کنید"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"برای مدیریت کاغذدیواری، ابزارک‌ها و تنظیمات، پس‌زمینه را لمس کرده و نگه‌دارید."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"کاغذدیواری‌ها، ابزارک‌ها و تنظیمات"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"برای سفارشی کردن، پس‌زمینه را لمس کنید و نگه‌دارید"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"متوجه شدم"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"اینجا یک پوشه است"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"برای ایجاد پوشه‌ای مثل این، یک برنامه را لمس کرده و نگه‌دارید، سپس آن را روی برنامه دیگر بیاندازید."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"تأیید"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"ابزارک‌ها"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"کاغذدیواری‌ها"</string>
<string name="settings_button_text" msgid="8119458837558863227">"تنظیمات"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"در حال انتظار"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"در حال دانلود"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"در حال نصب"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"نامشخص"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"بازیابی نشد"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"حذف همه"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"حذف"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"جستجو"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"این برنامه نصب نشده است."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"برنامه برای این نماد نصب نشده است. می‌توانید آن را حذف کنید یا سعی کنید برنامه را جستجو کنید و آن را به صورت دستی نصب کنید."</string>
</resources>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 9a48ab359..3990a163c 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Androidin ydinsovellukset"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Sovellusta ei ole asennettu."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ladattu sovellus poistettiin käytöstä suojatussa tilassa"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgetit"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgetit"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Näytä muisti"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"kirjoita aloitusruudun asetuksia ja pikakuvakkeita"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Antaa sovelluksen muuttaa aloitusruudun asetuksia ja pikakuvakkeita."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Ongelma ladattaessa widgetiä"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Asetus"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tämä on järjestelmäsovellus, eikä sitä voi poistaa."</string>
<string name="dream_name" msgid="1530253749244328964">"Sinko"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Nimetön kansio"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ALOITA ALUSTA"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Järjestä tilasi"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Hallitse taustakuvaa, widgetejä ja asetuksia koskettamalla taustaa pitkään."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Taustakuvat, widgetit ja asetukset"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Muokkaa taustaa koskettamalla ja painamalla pitkään"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SELVÄ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Tässä on kansio"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Luo se seuraavasti: kosketa sovellusta pitkään ja siirrä se sitten toisen päälle."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgetit"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Taustakuvat"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Asetukset"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Odottaa"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Ladataan"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Asennetaan"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Tuntematon"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ei palautettu"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Poista kaikki"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Poista"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Haku"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Sovellusta ei ole asennettu"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Kuvakkeen sovellusta ei ole asennettu. Voit poistaa kuvakkeen tai etsiä sovelluksen ja asentaa sen manuaalisesti."</string>
</resources>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index 92c5cc489..f42835014 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Applications de base Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Afficher la mémoire"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"enregistrer les paramètres de la page d\'accueil et des raccourcis"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permet à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problème lors du chargement du widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
<string name="dream_name" msgid="1530253749244328964">"Lance-missile"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"DISPOSITION PAR DÉFAUT"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organiser son espace personnel"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Maintenez votre doigt sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fonds d\'écran, widgets et paramètres"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Maintenez le doigt sur le fond d\'écran pour personnaliser"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"J\'ai compris"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Voici un dossier"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Pour créer un dossier comme ça, maintenez votre doigt sur une application, puis déplacez-la sur une autre."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"En attente"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Téléchargement..."</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installation…"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Non restauré"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Tout supprimer"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Cette application n\'est pas installée"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'application liée à cette icône n\'est pas installée. Vous pouvez la supprimer ou rechercher l\'application et l\'installer manuellement."</string>
</resources>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index 18732f849..31448f18b 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Applications de base Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"L\'application n\'est pas installée."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'application téléchargée est désactivée en mode sécurisé."</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Afficher la mémoire"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"modifier les paramètres et les raccourcis de l\'écran d\'accueil"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permettre à l\'application de modifier les paramètres et les raccourcis de l\'écran d\'accueil"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problème lors du chargement du widget."</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Impossible de désinstaller cette application, car il s\'agit d\'une application système."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dossier sans nom"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"DISPOSITION PAR DÉFAUT"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organisez votre espace"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Appuyez de manière prolongée sur l\'arrière-plan pour gérer les fonds d\'écran, les widgets et les paramètres."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fonds d\'écran, widgets et paramètres"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Appuyez de manière prolongée sur l\'arrière-plan pour le personnaliser."</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Voici un dossier"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Pour en créer un, appuyez de manière prolongée sur une application, puis déplacez-la vers une autre."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fonds d\'écran"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Paramètres"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"En attente"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Téléchargement…"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installation…"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Inconnu"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Non restauré"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Tout supprimer"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Supprimer"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Rechercher"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Cette application n\'est pas installée"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'application correspondant à cette icône n\'est pas installée. Vous pouvez supprimer cette dernière, ou essayer de rechercher l\'application et de l\'installer manuellement."</string>
</resources>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
new file mode 100644
index 000000000..c053b07fe
--- /dev/null
+++ b/res/values-gl-rES/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"Inicio"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Aplicacións básicas de Android"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"A aplicación non está instalada"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Desactivouse a aplicación descargada no modo seguro"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memoria"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Mantén premido un widget para seleccionalo."</string>
+ <string name="market" msgid="2619650989819296998">"Tenda"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Non se puido engadir á pantalla de inicio."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Escolle o widget que queiras crear"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Nome do cartafol"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Cambiar o nome do cartafol"</string>
+ <string name="rename_action" msgid="5559600076028658757">"Aceptar"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Cancelar"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Engadir á pantalla de inicio"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Aplicacións"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Atallos"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Widgets"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Non hai máis espazo nas pantallas de inicio."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Non hai máis espazo nesta pantalla de inicio."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Non hai máis espazo na bandexa de favoritos"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Este widget é demasiado grande para a bandexa de favoritos"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"Creouse o atallo \"<xliff:g id="NAME">%s</xliff:g>\"."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"Eliminouse o atallo \"<xliff:g id="NAME">%s</xliff:g>\"."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"O atallo \"<xliff:g id="NAME">%s</xliff:g>\" xa existe."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Escoller un atallo"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Escoller unha aplicación"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicacións"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Inicio"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"Eliminar"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Desinstalar"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Eliminar"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Información da aplicación"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Buscar"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Busca de voz"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Aplicacións"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"Eliminar"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Desinstalar actualización"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Desinstalar aplicación"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Detalles da aplicación"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 aplicación seleccionada"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 widget seleccionado"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 cartafol seleccionado"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 atallo seleccionado"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atallos"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite a unha aplicación engadir atallos sen intervención do usuario."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"desinstalar atallos"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Permite a unha aplicación eliminar atallos sen intervención do usuario."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ler a configuración e os atallos da pantalla de inicio"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite a unha aplicación ler a configuración e os atallos da páxina de inicio."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"modificar a configuración e os atallos da pantalla de inicio"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite a unha aplicación cambiar a configuración e os atallos da pantalla de inicio."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Produciuse un problema ao cargar o widget"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Esta aplicación é do sistema e non se pode desinstalar."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Lanzacohetes"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Cartafol sen nome"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"Pantalla de inicio %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"Páxina %1$d de %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Pantalla de inicio %1$d de %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Páxina de aplicacións %1$d de %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Páxina de widgets %1$d de %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Dámosche a benvida"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"Síntete como na túa casa."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Crea máis pantallas para aplicacións e cartafoles"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Copiar iconas das aplicacións"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Queres importar as iconas e os cartafoles doutras pantallas de inicio?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ICONAS"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"COMEZAR DE CERO"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Organiza o espazo"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Mantén premido o fondo para xestionar o fondo de pantalla e máis."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Isto é un cartafol"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Para crear un igual, mantén premida a aplicación e móvea sobre outra."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"Aceptar"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Abriuse o cartafol, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Toca para pechar o cartafol"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"Toca para gardar o cambio de nome"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Pechouse o cartafol"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"O cartafol cambiou o nome a <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Cartafol: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Fondos de pantalla"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Configuración"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"En espera"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Descargando"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Descoñecido"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Non restaurado"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Eliminar todas"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminar"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Buscar"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicación non está instalada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicación para esta icona non está instalada. Podes eliminala ou buscar a aplicación e instalala manualmente."</string>
+</resources>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index b889625bf..0ae90d469 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -24,9 +24,10 @@
<string name="uid_name" msgid="7820867637514617527">"Android के मुख्य ऐप्लिकेशन"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"एप्‍लिकेशन इंस्‍टॉल नहीं है."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड किए गए ऐप्स सुरक्षित मोड में अक्षम है"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"विजेट"</string>
<string name="widget_adder" msgid="3201040140710381657">"विजेट"</string>
- <string name="toggle_weight_watcher" msgid="5645299835184636119">"स्मृति दिखाएं"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"मेमोरी दिखाएं"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट को चुनने के लिए स्‍पर्श करके रखें."</string>
<string name="market" msgid="2619650989819296998">"खरीदारी करें"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
@@ -35,7 +36,7 @@
<string name="rename_folder_label" msgid="3727762225964550653">"फ़ोल्‍डर का नाम"</string>
<string name="rename_folder_title" msgid="3771389277707820891">"फ़ोल्‍डर का नाम बदलें"</string>
<string name="rename_action" msgid="5559600076028658757">"ठीक"</string>
- <string name="cancel_action" msgid="7009134900002915310">"रद्द करें"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"रहने दें"</string>
<string name="menu_item_add_item" msgid="1264911265836810421">"होम स्‍क्रीन में जोड़ें"</string>
<string name="group_applications" msgid="3797214114206693605">"ऐप्लिकेशन"</string>
<string name="group_shortcuts" msgid="6012256992764410535">"शॉर्टकट"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"होम सेटिंग और शॉर्टकट लिखें"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"ऐप्लिकेशन को होम में सेटिंग और शॉर्टकट बदलने देती है."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"विजेट लोड करने में समस्‍या"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"सेटअप"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"यह एक सिस्टम ऐप्लिकेशन है और इसे अनइंस्टॉल नहीं किया जा सकता."</string>
<string name="dream_name" msgid="1530253749244328964">"रॉकेट लॉन्‍चर"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"अनामित फ़ोल्डर"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"फिर से शुरू करें"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"अपने स्थान को व्यवस्थित करें"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"वॉलपेपर, विजेट और सेटिंग प्रबंधित करने के लिए पृष्ठभूमि को स्पर्श करके रखें."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"वॉलपेपर, विजेट और सेटिंग"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"पृष्ठभूमि कस्टमाइज़ करने के लिए स्पर्श करके रखें"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"समझ लिया"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"यहां एक फ़ोल्डर है"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"इसके जैसा कोई एक बनाने के लिए, किसी ऐप्लिकेशन को स्पर्श करके रखें, फिर इसे किसी दूसरे पर ले जाएं."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"ठीक"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
<string name="settings_button_text" msgid="8119458837558863227">"सेटिंग"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"प्रतीक्षा में"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"डाउनलोड हो रहा है"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"इंस्टॉल हो रहा है"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"पुन:स्थापित नहीं हुआ"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"सभी निकालें"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"निकालें"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"खोजें"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"यह ऐप्स इंस्टॉल नहीं है"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"इस आइकन का ऐप्स इंस्टॉल नहीं है. आप उसे निकाल सकते हैं या ऐप्स की खोज करके उसे मैन्युअल रूप से इंस्टॉल कर सकते हैं."</string>
</resources>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index 32f3feb2f..c881077b1 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Matične aplikacije za Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija nije instalirana."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Preuzeta aplikacija onemogućena je u Sigurnom načinu rada"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgeti"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgeti"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Prikaži mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"pisanje postavki početnog zaslona i prečaca"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Aplikaciji omogućuje promjenu postavki i prečaca na početnom zaslonu."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem pri učitavanju widgeta"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Postavljanje"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ovo je aplikacija sustava i ne može se ukloniti."</string>
<string name="dream_name" msgid="1530253749244328964">"Lansirna rampa"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"POKRENI NOVO"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organizirajte svoj prostor"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Dodirnite i držite pozadinu da biste upravljali pozadinskom slikom, widgetima i postavkama."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Pozadinske slike, widgeti i postavke"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Dodirnite i zadržite pozadinu radi prilagodbe"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SHVAĆAM"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Evo mape"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Da biste izradili ovakvu mapu, dodirnite i držite aplikaciju pa je pomaknite preko druge aplikacije."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"U redu"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Pozadinske slike"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Postavke"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Čekanje"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Preuzimanje"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instaliranje"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Nepoznato"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nije vraćeno"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Ukloni sve"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Ukloni"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Traži"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacija nije instalirana"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija ove ikone nije instalirana. Možete je ukloniti ili potražiti aplikaciju i instalirati je ručno."</string>
</resources>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 37d505945..08d309558 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Alap Android-alkalmazások"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Az alkalmazás nincs telepítve."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"A letöltött alkalmazás Csökkentett módban ki van kapcsolva"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Modulok"</string>
<string name="widget_adder" msgid="3201040140710381657">"Modulok"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem. megjelenítése"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"Főoldal beállításainak és parancsikonjainak írása"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a kezdőképernyő beállításait és parancsikonjait."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Probléma történt a modul betöltésekor"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Beállítás"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ez egy rendszeralkalmazás, és nem lehet eltávolítani."</string>
<string name="dream_name" msgid="1530253749244328964">"Aknavető"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Névtelen mappa"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"TELJESEN ÚJ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Munkaterület rendezése"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Érintse meg és tartsa lenyomva a hátteret a háttérkép, modulok és beállítások kezeléséhez."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Háttérképek, modulok és beállítások"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Érintse meg és tartsa lenyomva a személyre szabáshoz"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"MEGÉRTETTEM"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Itt egy mappa"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Mappa létrehozásához érintse meg és tartsa lenyomva az alkalmazást, majd húzza egy másik fölé."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Modulok"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Háttérképek"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Beállítások"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Várakozik"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Letöltés alatt"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Települ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Ismeretlen"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nincs visszaállítva"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Az összes eltávolítása"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eltávolítás"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Keresés"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Az alkalmazás nincs telepítve"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Az ikonhoz tartozó alkalmazás nincs telepítve. Törölheti az ikont, vagy az alkalmazás megkeresése után manuálisan telepítheti azt."</string>
</resources>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 6e9491bca..4ec39c88c 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Ծրագիրը տեղադրված չէ:"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ներբեռնված ծրագիրն անջատված է Անվտանգ ռեժիմում"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Վիջեթներ"</string>
<string name="widget_adder" msgid="3201040140710381657">"Վիջեթներ"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Ցուցադրել մեմը"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"ստեղծել հիմնաէջի կարգավորումներ ու դյուրանցումներ"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ծրագրին թույլ է տալիս փոփոխել հիմնաէջի կարգավորումներն ու դյուրանցումները:"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Վիջեթի բեռնման խնդիր կա"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Կարգավորում"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Սա համակարգային ծրագիր է և չի կարող ապատեղադրվել:"</string>
<string name="dream_name" msgid="1530253749244328964">"Հրթիռային թողարկիչ"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Անանուն թղթապանակ"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ՄԵԿՆԱՐԿԵԼ ԸՍՏ ԿԱՆԽԱԴՐՎԱԾԻ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Կառավարեք ձեր տարածությունը"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Հպեք և պահեք հետնաշերտի վրա՝ պաստառները, վիջեթներն ու կարգավորումները կառավարելու համար:"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Պաստառներ, վիջեթներ և կարգավորումներ"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ՀԱՍԿԱՆԱԼԻ Է"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ահա մի թղթապանակ"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Նման թղթապանակ ստեղծելու համար հպեք և պահեք որևէ ծրագրի վրա, ապա տեղաշարժեք այն մեկ ուրիշ ծրագրի վրա:"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Լավ"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Վիջեթներ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Պաստառներ"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Կարգավորումներ"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Առկախ է"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Ներբեռնվում է"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Տեղադրվում է"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Անհայտ է"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Չի վերականգնվել"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Հեռացնել բոլորը"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Հեռացնել"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Գտնել"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Այս ծրագիրը տեղադրված չէ:"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Այս պատկերակի ծրագիրը տեղադրված չէ: Դուք կարող եք հեռացնել այն կամ գտնել ծրագիրը և տեղադրել այն ձեռքով:"</string>
</resources>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index d5df63bfa..137e3cc7d 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Aplikasi Inti Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikasi tidak dipasang."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplikasi yang diunduh dinonaktifkan dalam mode Aman"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widget"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widget"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Tampilkan Memori"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"menulis setelan dan pintasan layar Utama"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Mengizinkan aplikasi mengubah setelan dan pintasan di layar Utama."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuat widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Siapkan"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini adalah aplikasi sistem dan tidak dapat dicopot pemasangannya."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"MULAI DARI AWAL"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Kelola ruang Anda"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Sentuh lama latar belakang untuk mengelola wallpaper, widget, dan setelan."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Wallpaper, widget, &amp; setelan"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Sentuh &amp; tahan latar belakang untuk menyesuaikan"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"MENGERTI"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ini adalah folder"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Untuk membuat seperti yang ini, sentuh lama aplikasi, lalu pindahkan ke atas aplikasi lain."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Oke"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Wallpaper"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Setelan"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Menunggu"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Mengunduh"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Memasang"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Tidak dikenal"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Tak dipulihkan"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Buang Semua"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Buang"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Telusuri"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplikasi ini belum terpasang"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikasi untuk ikon ini belum dipasang. Anda dapat membuangnya, atau menelusuri aplikasi dan memasangnya secara manual."</string>
</resources>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
new file mode 100644
index 000000000..71f0edea9
--- /dev/null
+++ b/res/values-is-rIS/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"Heim"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Kjarnaforrit Android"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Forritið er ekki uppsett."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Sótt forrit er óvirkt í öryggisstillingu"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Græjur"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Græjur"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Sýna minni"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Haltu fingri á græju til að grípa hana."</string>
+ <string name="market" msgid="2619650989819296998">"Verslun"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Ekki er hægt að sleppa atriði á þennan heimaskjá."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Veldu græju til að búa til"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Möppuheiti"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Endurnefna möppu"</string>
+ <string name="rename_action" msgid="5559600076028658757">"Í lagi"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Hætta við"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Bæta á heimaskjá"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Forrit"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Flýtileiðir"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Græjur"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Heimaskjáirnir þínir eru fullskipaðir."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Ekki meira pláss á þessum heimaskjá."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ekki meira pláss í bakka fyrir uppáhald"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Þessi græja er of stór fyrir bakkann fyrir uppáhald"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"Flýtileiðin „<xliff:g id="NAME">%s</xliff:g>“ var búin til."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"Flýtileiðin „<xliff:g id="NAME">%s</xliff:g>“ var fjarlægð."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"Flýtileiðin „<xliff:g id="NAME">%s</xliff:g>“ er þegar til."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Veldu flýtileið"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Veldu forrit"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Forrit"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Heim"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"Fjarlægja"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Eyða"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Fjarlægja"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Eyða"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Upplýsingar um forrit"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Leita"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Raddleit"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Forrit"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"Fjarlægja"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Fjarlægja uppfærslu"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Fjarlægja forrit"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Upplýsingar um forrit"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 forrit valið"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 græja valin"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 mappa valin"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 flýtileið valin"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"setja upp flýtileiðir"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Leyfir forriti að bæta við flýtileiðum án íhlutunar notanda."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"fjarlægja flýtileiðir"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Leyfir forriti að fjarlægja flýtileiðir án íhlutunar notanda."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"lesa stillingar og flýtileiðir heimaskjás"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Leyfir forriti að lesa stillingar og flýtileiðir heimaskjás."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"skrifa stillingar og flýtileiðir heimaskjás"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Leyfir forriti að breyta stillingum og flýtileiðum heimaskjás."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Vandamál við að hlaða græju"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Þetta er kerfisforrit sem ekki er hægt að fjarlægja."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Eldflaugapallur"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Ónefnd mappa"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"Heimaskjár %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"Síða %1$d af %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Heimaskjár %1$d af %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Forritasíða %1$d af %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Græjusíða %1$d af %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Komdu fagnandi"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"Komdu þér vel fyrir."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Búðu til fleiri skjái fyrir forrit og möppur"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Afritaðu forritatáknin þín"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Flytja inn tákn og möppur af eldri heimaskjáum?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"AFRITA TÁKN"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"BYRJA UPP Á NÝTT"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Settu hlutina á sína staði"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Haltu inni á bakgrunni til að stjórna veggfóðri, græjum og stillingum."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Hér er mappa"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Til að búa til svona skaltu draga forrit yfir á annað forrit."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"Í lagi"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Mappa opnuð, <xliff:g id="WIDTH">%1$d</xliff:g> sinnum <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Snertu til að loka möppunni"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"Snertu til að staðfesta nýtt heiti"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Möppu lokað"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Heiti möppu breytt í <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Mappa: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Græjur"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Veggfóður"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Stillingar"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Bíður"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Sækir"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Setur upp"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Óþekkt"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ekki endurheimt"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Fjarlægja öll"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjarlægja"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Leita"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Þetta forrit er ekki uppsett"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Forritið fyrir þetta tákn er ekki uppsett. Þú getur fjarlægt það eða leitað að forritinu og sett það upp handvirkt."</string>
+</resources>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index 20451d135..b01b25174 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Applicazioni di base Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"App non installata."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"L\'app scaricata è stata disattivata in modalità provvisoria"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widget"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widget"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostra Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"creazione di impostazioni e scorciatoie in Home"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Consente all\'app di modificare le impostazioni e le scorciatoie in Home."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Errore durante il caricamento del widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configurazione"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Questa è un\'app di sistema e non può essere disinstallata."</string>
<string name="dream_name" msgid="1530253749244328964">"Lanciamissili"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Cartella senza nome"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"RICOMINCIA"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organizza il tuo spazio"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Tocca e tieni premuto lo sfondo per gestire sfondi, widget e impostazioni."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Sfondi, widget e impostazioni"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Tocca lo sfondo e tieni premuto per personalizzare"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ecco una cartella"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Per crearne una simile, tocca un\'app e tieni premuto, dopodiché spostala sopra un\'altra."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Sfondi"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Impostazioni"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"In attesa"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Download..."</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installazione..."</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Sconosciuto"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Non ripristinato"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Rimuovi tutto"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Rimuovi"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Cerca"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"L\'app non è installata"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"L\'app per questa icona non è installata. Puoi rimuoverla o cercare l\'app e installarla manualmente."</string>
</resources>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 08d965399..6318207ff 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"‏אפליקציות הליבה של Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"האפליקציה לא מותקנת."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"אפליקציה שהורדת הושבתה במצב בטוח"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"רכיבי ווידג\'ט"</string>
<string name="widget_adder" msgid="3201040140710381657">"רכיבי ווידג\'ט"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"הצג זכרון"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"כתוב הגדרות וקיצורי דרך של דף הבית"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"מאפשר לאפליקציה לשנות את ההגדרות וקיצורי הדרך בדף הבית."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"בעיה בטעינת ווידג\'ט"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"הגדר"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"זוהי אפליקציית מערכת ולא ניתן להסיר את התקנתה."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"תיקיה ללא שם"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"התחל דף חדש"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"ארגן את אזור העבודה שלך"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"גע נגיעה רציפה ברקע כדי לנהל את הטפט, רכיבי הווידג\'ט וההגדרות."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"טפטים, ווידג\'טים והגדרות"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"גע והחזק ברקע לביצוע התאמה אישית"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"הבנתי"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"הנה תיקייה"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"כדי ליצור תיקייה כזו, גע נגיעה רציפה באפליקציה, ולאחר מכן גרור ושחרר אותו על-גבי אפליקציה אחרת."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"אישור"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"רכיבי ווידג\'ט"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"טפטים"</string>
<string name="settings_button_text" msgid="8119458837558863227">"הגדרות"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"ממתין"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"מוריד"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"מתקין"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"לא ידוע"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"לא שוחזרה"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"הסר את הכל"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"הסר"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"חפש"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"אפליקציה זו אינה מותקנת"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"האפליקציה של סמל זה אינה מותקנת. ניתן להסיר אותו, או לחפש את האפליקציה ולהתקין אותה ידנית."</string>
</resources>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index ad5e8ff70..232845af0 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"このアプリはインストールされていません。"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ダウンロードしたアプリは、セーフモードでは無効です"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"ウィジェット"</string>
<string name="widget_adder" msgid="3201040140710381657">"ウィジェット"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"メモリーを表示"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"ホームの設定とショートカットの書き込み"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"ホームの設定とショートカットの変更をアプリに許可します。"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"ウィジェットを表示できません"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"セットアップ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"このシステムアプリはアンインストールできません。"</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"名前のないフォルダ"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"初期状態にリセットする"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"スペースを整理"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"壁紙、ウィジェット、設定を管理するには、背景を押し続けます。"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"壁紙、ウィジェット、設定"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"カスタマイズするにはバックグラウンドを押し続けます"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"これがフォルダです"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"これと同じフォルダを作成するには、アプリを押し続けてから別のアプリの上に移動します。"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"ウィジェット"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁紙"</string>
<string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"待機中"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ダウンロード中"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"インストール中"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"復元失敗"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"すべて削除"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"削除"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"検索"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"このアプリはインストールされていません"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"このアイコンのアプリはインストールされていません。このアイコンは削除できます。または、手動でアプリを検索してインストールしください。"</string>
</resources>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index 086df7222..2fb51f541 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android-ის ბირთვის აპები"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"აპი არ არის დაყენებული."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"უსაფრთხო რეჟიმში ჩამოტვირთული აპი გაუქმებულია"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"ვიჯეტები"</string>
<string name="widget_adder" msgid="3201040140710381657">"ვიჯეტები"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem-ის ჩვენება"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"მთავარი ეკრანის პარამეტრებისა და მალსახმობების ჩაწერა"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"აპისთვის მთავარი ეკრანის პარამეტრებისა და მალსახმობების შეცვლის უფლების მიცემა."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"პრობლემა ვიჯეტის ჩატვირთვისას"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"დაყენება"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ეს სისტემური აპია და მისი წაშლა შეუძლებელია."</string>
<string name="dream_name" msgid="1530253749244328964">"ფეიერვერკი"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"უსახელო საქაღალდე"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"სტანდარტული განლაგება"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"თქვენი სივრცის ორგანიზება"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"თუ გსურთ ფონების, ვიჯეტების და პარამეტრების მართვა, შეეხეთ და არ აუშვათ ფონს."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ფონები, ვიჯეტები, &amp; პარამეტრები"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"მოსარგებად შეეხეთ &amp; დააყოვნეთ ფონზე"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"გასაგებია"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"აი, საქაღალდე"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"ასეთის შესაქმნელად, შეეხეთ და დააყოვნეთ აპზე, ხოლო შემდეგ გადააჩოჩეთ შემდეგზე."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"კარგი"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"ვიჯეტები"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ფონები"</string>
<string name="settings_button_text" msgid="8119458837558863227">"პარამეტრები"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"მოცდა..."</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ჩამოტვირთვა..."</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ინსტალაცია..."</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"უცნობი"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"არ აღდგა"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"ყველას ამოშლა"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ამოშლა"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ძიება"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ეს აპი დაყენებული არ არის"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ამ ხატულის აპი დაყენებული არ არის. შეგიძლიათ ამოშალოთ, ან მოიძიოთ აპი და ხელით მოახდინოთ მისი ინსტალაცია."</string>
</resources>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
new file mode 100644
index 000000000..288129ace
--- /dev/null
+++ b/res/values-kk-rKZ/strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"Негізгі"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android Core қолданбалары"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Қолданба орнатылмаған."</string>
+ <!-- no translation found for safemode_shortcut_error (9160126848219158407) -->
+ <skip />
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Виджеттер"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Виджеттер"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Жадты көрсету"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетті таңдау үшін түртіп, мықтап ұстаңыз."</string>
+ <string name="market" msgid="2619650989819296998">"Дүкен"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Элементті осы Негізгі Экранға тастау орындалмады."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Жасақтау үшін виджет таңдау"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Қалта атауы"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Қалтаның атауын өзгерту"</string>
+ <string name="rename_action" msgid="5559600076028658757">"Жарайды"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Өшіру"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Негізгі экранға қосу"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Қолданбалар"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Төте пернелер"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Виджеттер"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Негізгі экранда орын жоқ."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Бұл Негізгі экранда орын қалмады."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Қалаулылар науасында орын қалмады"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Бұл виджет Қалаулылар науасы үшін тым үлкен"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" төте пернесі жасақталды."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" төте пернесі алынды."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" төте пернесі бұрыннан бар."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Төте перне таңдау"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Қолданба таңдау"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Қолданбалар"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Негізгі"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"Алып тастау"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Алмау"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Алып тастау"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Алмау"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Қолданба ақпары"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Іздеу"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Дауыс арқылы іздеу"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Қолданбалар"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"Алып тастау"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Жаңартуды алмау"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Қолданбаны алып тастау"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Қолданба туралы толығырақ"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 қолданба таңдалған"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 виджет таңдалған"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 қалта таңдалған"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 төте перне таңдалған"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"төте пернелерді орнату"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді қосу мүмкіндігін береді."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"төте пернелерді алып тастау"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Қолданбаға пайдаланушының қатысуынсыз төте пернелерді алу мүмкіндігін береді."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Негізгі экрандағы параметрлер мен төте пернелерді оқу"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Қолданбаға Негізгі экрандағы параметрлер мен төте пернелерді оқу мүмкіндігін береді."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Негізгі экран параметрлері мен төте пернелерін жазу"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Қолданбаға Негізгі экрандағы параметрлер мен төте пернелерді өзгерту мүмкіндігін береді."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Виджетті жүктеу барысында мәселе орын алды"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бұл жүйе қолданбасы, сондықтан оны алу мүмкін емес."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Атауы жоқ қалта"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"%1$d негізгі экран"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%1$d бет, барлығы %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%1$d негізгі экран, барлығы %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%1$d қолданба беті, барлығы %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"%1$d виджет беті, барлығы %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Қош келдіңіз"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"Өзіңізді ыңғайлы сезініңіз."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Қолданбалар мен қалталар үшін көбірек экрандар жасау"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Қолданба таңбаларын көшіру"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Бұрынғы негізгі экрандарыңыздағы таңбалар мен қалталар импортталсын ба?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"ТАҢБАЛАРДЫ КӨШІРУ"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"ЖАҢАДАН БАСТАУ"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Кеңістікті реттеу"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Артқы фонды, виджеттерді және параметрлерді басқару үшін артқы шебін түртіп, мықтап ұстаңыз."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Міне, қалта."</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Осы сияқты қалта жасау үшін, қолданбаны түртіп, мықтап ұстаңыз, одан кейін екіншісінің үстінен жылжытыңыз."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"Жарайды"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Қалта ашылды, <xliff:g id="WIDTH">%1$d</xliff:g> және <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Қалтаны жабу үшін түртіңіз"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"Өзгертілген атауын сақтау үшін түртіңіз"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Қалта жабылды"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Қалта атауы <xliff:g id="NAME">%1$s</xliff:g> болып өзгертілді"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Қалта: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Артқы фондар"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Параметрлер"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Күтілуде"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Жүктелуде"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Орнатылуда"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Белгісіз"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Қалп. кел-меді"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Барлығын алып тастау"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып тастау"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Іздеу"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Бұл қолданба орнатылмаған"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Осы белгіше үшін қолданба орнатылмаған. Оны жоюға болады немесе қолданбаны іздеп, қолмен орнатуға болады."</string>
+</resources>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 8bf26c221..bcd606000 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"កម្មវិធី​​សំខាន់​ៗ​របស់ Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"មិន​បាន​ដំឡើង​កម្មវិធី។"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"បាន​បិទ​កម្មវិធី​ដែល​បាន​ទាញ​យក​ក្នុង​របៀប​សុវត្ថិភាព"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"ធាតុ​ក្រាហ្វិក"</string>
<string name="widget_adder" msgid="3201040140710381657">"ធាតុ​ក្រាហ្វិក"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"បង្ហាញ​ Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"សរសេរ​ការ​កំណត់ ​និង​ផ្លូវកាត់​​លើ​អេក្រង់​ដើម"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"អនុញ្ញាត​ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​កំណត់ និង​ផ្លូវ​កាត់​ក្នុង​អេក្រង់​ដើម។"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"បញ្ហា​ក្នុង​ការ​ផ្ទុក​ធាតុ​​ក្រាហ្វិក"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"រៀបចំ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"នេះ​​​ជា​កម្មវិធី​ប្រព័ន្ធ មិន​អាច​លុប​បាន​ទេ។"</string>
<string name="dream_name" msgid="1530253749244328964">"កម្មវិធី​ចាប់ផ្ដើម​រ៉ូកែត"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"ថត​គ្មាន​ឈ្មោះ"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ចាប់ផ្ដើម​ធ្វើ​ឲ្យ​ស្រស់"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"រៀបចំ​ចន្លោះ​របស់​អ្នក"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"ប៉ះ &amp; សង្កត់​លើ​ផ្ទៃ​ខាង​ក្រោម ដើម្បី​គ្រប់គ្រង​ផ្ទាំង​រូបភាព, ធាតុ​ក្រាហ្វិក និង​ការ​កំណត់។"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"ផ្ទាំងរូបភាព,ធាតុក្រាហ្វិក &amp; ការកំណត់"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ប៉ះ &amp; សង្កត់​ផ្ទៃ​ខាង​ក្រោយ​ដើម្បី​ប្ដូរ​តាម​​តម្រូវ​ការ"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"យល់​ហើយ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"នេះ​ជា​ថត"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"ដើម្បី​បង្កើត​មួយ​ដូច​នេះ ប៉ះ &amp; សង្កត់​​លើ​កម្មវិធី បន្ទាប់​មក​ផ្លាស់ទី​វា​ទៅ​លើ​ធាតុ​មួយ​ផ្សេង​ទៀត។"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"យល់ព្រម"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"ធាតុ​ក្រាហ្វិក"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ផ្ទាំង​រូបភាព"</string>
<string name="settings_button_text" msgid="8119458837558863227">"ការកំណត់"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"រង់ចាំ"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"​ទាញ​យក"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ដំឡើង"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"មិន​ស្គាល់"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"មិនបាន​​ស្តា​រ"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"លុបចេញ​​​ទាំងអស់"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"លុបចេញ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ស្វែងរក"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"មិន​បាន​ដំឡើង​កម្មវិធី​នេះ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"មិន​បាន​ដំឡើង​កម្មវិធី​សម្រាប់​រូបតំណាង​នេះ។ អ្នក​អាច​លុប​វា ឬ​ស្វែងរក​កម្មវិធី និង​ដំឡើង​វា​ដោយ​ដៃ។"</string>
</resources>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
new file mode 100644
index 000000000..e84ece03c
--- /dev/null
+++ b/res/values-kn-rIN/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"ಲಾಂಚರ್3"</string>
+ <string name="home" msgid="7658288663002113681">"ಮುಖಪುಟ"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android Core ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾದ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"ವಿಜೆಟ್‌ಗಳು"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"ವಿಜೆಟ್‌ಗಳು"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"ಸ್ಮರಣೆ ತೋರಿಸು"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"ವಿಜೆಟ್ ಅನ್ನು ಆರಿಸಿಕೊಳ್ಳಲು ಸ್ಪರ್ಶಿಸಿ &amp; ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+ <string name="market" msgid="2619650989819296998">"ಶಾಪ್‌"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಐಟಂ ಅನ್ನು ಬಿಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"ರಚಿಸಲು ವಿಜೆಟ್‌ ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"ಫೋಲ್ಡರ್ ಹೆಸರು"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"ಫೋಲ್ಡರ್ ಅನ್ನು ಮರುಹೆಸರಿಸಿ"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ಸರಿ"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"ರದ್ದುಮಾಡು"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"ಮುಖಪುಟಕ್ಕೆ ಸೇರಿಸು"</string>
+ <string name="group_applications" msgid="3797214114206693605">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳು"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"ವಿಜೆಟ್‌ಗಳು"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"ನಿಮ್ಮ ಮುಖಪುಟದ ಪರದೆಗಳಲ್ಲಿ ಯಾವುದೇ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇಗೆ ಈ ವಿಜೆಟ್‌ ತುಂಬಾ ದೊಡ್ಡದಾಗಿದೆ"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"ಶಾರ್ಟ್‌ಕಟ್‌ \"<xliff:g id="NAME">%s</xliff:g>\" ಅನ್ನು ರಚಿಸಲಾಗಿದೆ."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"ಶಾರ್ಟ್‌ಕಟ್‌ \"<xliff:g id="NAME">%s</xliff:g>\" ಅನ್ನು ತೆಗೆದುಹಾಕಲಾಗಿದೆ."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"ಶಾರ್ಟ್‌ಕಟ್‌ \"<xliff:g id="NAME">%s</xliff:g>\" ಈಗಾಗಲೇ ಅಸ್ತಿತ್ವದಲ್ಲಿದೆ."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"ಶಾರ್ಟ್‌ಕಟ್‌ ಆರಿಸಿ"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"ಅಪ್ಲಿಕೇಶನ್ ಆಯ್ಕೆಮಾಡಿ"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ಮುಖಪುಟ"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"ತೆಗೆದುಹಾಕು"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"ಅಸ್ಥಾಪಿಸು"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"ತೆಗೆದುಹಾಕು"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ಅಸ್ಥಾಪಿಸು"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿ"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"ಹುಡುಕು"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"ಧ್ವನಿ ಹುಡುಕಾಟ"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"ತೆಗೆದುಹಾಕು"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"ನವೀಕರಣವನ್ನು ಅಸ್ಥಾಪಿಸು"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸು"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"ಅಪ್ಲಿಕೇಶನ್ ವಿವರಗಳು"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 ಅಪ್ಲಿಕೇಶನ್‌ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 ವಿಜೆಟ್‌ ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 ಫೋಲ್ಡರ್‌ ಅನ್ನು ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 ಶಾರ್ಟ್‌ಕಟ್‌ ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸ್ಥಾಪಿಸಿ"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಸೇರಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿಸುತ್ತದೆ."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಅಸ್ಥಾಪಿಸಿ"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"ಬಳಕೆದಾರರ ಹಸ್ತಕ್ಷೇಪವಿಲ್ಲದೆ ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ತೆಗೆದುಹಾಕಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಿ"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ಮುಖಪುಟದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಓದಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ಮುಖಪುಟದ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಬರೆಯಿರಿ"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ಮುಖಪುಟದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್‌ಗಳು ಮತ್ತು ಶಾರ್ಟ್‌ಕಟ್‌ಗಳನ್ನು ಬದಲಾಯಿಸಲು ಅಪ್ಲಿಕೇಶನ್‌ಗೆ ಅನುಮತಿ ನೀಡುತ್ತದೆ."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ವಿಜೆಟ್ ಲೋಡ್‌ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ಇದೊಂದು ಅಪ್ಲಿಕೇಶನ್ ಆಗಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."</string>
+ <string name="dream_name" msgid="1530253749244328964">"ರಾಕೆಟ್ ಲಾಂಚರ್"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"ಹೆಸರಿಲ್ಲದ ಫೋಲ್ಡರ್"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"ಮುಖಪುಟದ ಪರದೆ %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ರಲ್ಲಿ %1$d ಪುಟ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d ರಲ್ಲಿ %1$d ಮುಖಪುಟದ ಪರದೆ"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%2$d ರಲ್ಲಿ %1$d ಅಪ್ಲಿಕೇಶನ್ ಪುಟ"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"%2$d ರಲ್ಲಿ %1$d ವಿಜೆಟ್‌ಗಳ ಪುಟ"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"ಸುಸ್ವಾಗತ"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"ನಿಮ್ಮ ಮನೆಯಂತೆ ಭಾವಿಸಿ."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಫೋಲ್ಡರ್‌ಗಳಿಗಾಗಿ ಇನ್ನಷ್ಟು ಪರದೆಗಳನ್ನು ರಚಿಸಿ"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"ಅಪ್ಲಿಕೇಶನ್‌ ಐಕಾನ್‌ ನಕಲಿಸು"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"ನಿಮ್ಮ ಹಳೆಯ ಮುಖಪುಟದ ಪರದೆಗಳಿಂದ ಐಕಾನ್‌ಗಳು ಮತ್ತು ಫೋಲ್ಡರ್‌ಗಳನ್ನು ಆಮದು ಮಾಡಿಕೊಳ್ಳುವುದೇ?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"ಐಕಾನ್‌ಗಳನ್ನು ನಕಲಿಸು"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"ಹೊಸದಾಗಿ ಪ್ರಾರಂಭಿಸು"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"ನಿಮ್ಮ ಸ್ಥಳವನ್ನು ವ್ಯವಸ್ಥಿತಗೊಳಿಸಿ"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"ವಾಲ್‌ಪೇಪರ್‌, ವಿಜೆಟ್‌ಗಳು ಮತ್ತು ಸೆಟ್ಟಿಂಗ್‌ಗಳನ್ನು ನಿರ್ವಹಿಸಲು ಹಿನ್ನೆಲೆಯನ್ನು ಸ್ಪರ್ಶಿಸಿ &amp; ಹಿಡಿದುಕೊಳ್ಳಿ."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"ಇಲ್ಲೊಂದು ಫೋಲ್ಡರ್ ಇದೆ"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"ಈ ರೀತಿ ರಚಿಸಲು, ಸ್ಪರ್ಶಿಸಿ &amp; ಆಪ್‌ ಹಿಡಿದುಕೊಂಡು ಮತ್ತೊಂದರ ಮೇಲೆ ಸರಿಸಿ."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ಸರಿ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ಫೋಲ್ಡರ್ ತೆರೆಯಲಾಗಿದೆ, <xliff:g id="WIDTH">%1$d</xliff:g> ಬೈ <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"ಫೋಲ್ಡರ್‌ ಮುಚ್ಚಲು ಸ್ಪರ್ಶಿಸಿ"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"ಮರುಹೆಸರನ್ನು ಉಳಿಸಲು ಸ್ಪರ್ಶಿಸಿ"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ಫೋಲ್ಡರ್ ಮುಚ್ಚಿದೆ"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ಫೋಲ್ಡರ್‌ ಅನ್ನು <xliff:g id="NAME">%1$s</xliff:g> ಗೆ ಮರುಹೆಸರಿಸಲಾಗಿದೆ"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ಫೋಲ್ಡರ್: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ವಿಜೆಟ್‌ಗಳು"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"ವಾಲ್‌ಪೇಪರ್‌ಗಳು"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ಸೆಟ್ಟಿಂಗ್‌ಗಳು"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"ನಿರೀಕ್ಷಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ಡೌನ್‌ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"ಅಜ್ಞಾತ"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"ಇನ್ನೂ ಪುನಃಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"ಎಲ್ಲವನ್ನೂ ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ತೆಗೆದುಹಾಕಿ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ಹುಡುಕು"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ಈ ಐಕಾನ್ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪನೆಗೊಂಡಿಲ್ಲ. ನೀವು ಅದನ್ನು ತೆಗೆದುಹಾಕಬಹುದು ಅಥವಾ ಅಪ್ಲಿಕೇಶನ್ ಹುಡುಕಬಹುದು ಮತ್ತು ಹಸ್ತಚಾಲಿತವಾಗಿ ಅದನ್ನು ಸ್ಥಾಪಿಸಬಹುದು."</string>
+</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index dde4eb781..41c854ec3 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android 핵심 앱"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"앱이 설치되지 않았습니다."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"다운로드한 앱은 안전 모드에서 사용할 수 없습니다."</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"위젯"</string>
<string name="widget_adder" msgid="3201040140710381657">"위젯"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"메모리 표시"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"홈 설정 및 바로가기 쓰기"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"앱이 홈에 있는 설정 및 바로가기를 변경할 수 있도록 합니다."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"위젯을 로드하는 중 문제가 발생했습니다."</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"설정"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"시스템 앱은 제거할 수 없습니다."</string>
<string name="dream_name" msgid="1530253749244328964">"로켓 실행기"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"이름이 없는 폴더"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"새로 시작"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"공간 관리하기"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"배경화면, 위젯, 설정을 관리하려면 백그라운드를 길게 터치합니다."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"배경화면, 위젯, 설정"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"백그라운드를 길게 터치하여 맞춤설정합니다."</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"확인"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"폴더"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"폴더를 만들려면 앱을 길게 터치한 다음 다른 앱 위에 올려 놓으세요."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"확인"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"위젯"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"배경화면"</string>
<string name="settings_button_text" msgid="8119458837558863227">"설정"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"대기 중"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"다운로드 중"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"설치 중"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"알 수 없음"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"복원되지 않음"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"모두 삭제"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"삭제"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"검색"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"이 앱이 설치되어 있지 않음"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"이 아이콘의 앱이 설치되어 있지 않습니다. 아이콘을 삭제하거나 앱을 검색하여 수동으로 설치하세요."</string>
</resources>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
new file mode 100644
index 000000000..5b50555ae
--- /dev/null
+++ b/res/values-ky-rKG/strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"Үйгө"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android Core колдонмолору"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Колдонмо орнотулган эмес."</string>
+ <!-- no translation found for safemode_shortcut_error (9160126848219158407) -->
+ <skip />
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Виджеттер"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Виджеттер"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Мемди көргөзүү"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Виджетти тандаш үчүн, басып туруңуз"</string>
+ <string name="market" msgid="2619650989819296998">"Дүкөн"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Муну бул Үй экранына ыргытуу мүмкүн эмес."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Түзүлүүчү виджетти тандаңыз"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Фолдердин аты"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Фолдердин атын өзгөртүү"</string>
+ <string name="rename_action" msgid="5559600076028658757">"OK"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Жокко чыгаруу"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Үй экранына кошуу"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Колдонмолор"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Тез чакырмалар"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Виджеттер"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Үй экрандарыңызда бош орун калган жок."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Бул Үй экранында бош орун жок."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Тандамалдар тайпасында орун калган жок"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Бул виджет Тандамалдар үчүн өтө чоң"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" тез чакырмасы түзүлдү."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" тез чакырмасы алынып салынды."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" тез чакырмасы бар."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Тез чакырма тандоо"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Колдонмо тандоо"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Колдонмолор"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Үйгө"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"Алып салуу"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Чечип салуу"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Алып салуу"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Чечип салуу"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Колдонмо тууралуу"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Издөө"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Үн менен издөө"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Колдонмолор"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"Алып салуу"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Жаңыртууну чечип салуу"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Колдонмону чечип салуу"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Колдонмонун кеңири маалыматтары"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 колдонмо тандалды"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 виджет тандалды"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 фолдер тандалды"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 тез чакырма тандалды"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"тез чакырмаларды орнотуу"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Колдонмого колдонуучуга кайрылбастан тез чакырма кошууга уруксат берет."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"тез чакырмаларды жок кылуу"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Колдонмого колдонуучуга кайрылбастан тез чакырмаларды жок кылууга уруксат берет."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Үйдүн тууралоолорун жана тез чакырмаларын окуу"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Колдонмого Үйдүн тууралоолорун жана тез чакырмаларын окууга уруксат берет."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Үйдүн тууралоолорун жана тез чакырмаларын жазуу"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Колдонмого Үйдүн тууралоолорун жана тез чакырмаларын өзгөртүүгө уруксат берет."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Виджетти жүктөөдө маселе бар"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Бул системдик колдонмо жана аны чечкенге болбойт."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Аты жок фолдер"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"Үй экраны %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d ичинен %1$d барак"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Үй экраны %2$d ичинен %1$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Колдонмолор барагы %2$d ичинен %1$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Виджеттер барагы %2$d ичинен %1$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Кош келиңиз"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"Өзүңүздү үйүңүздөгүдөй эркин сезиңиз."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Колдонмолор жана фолдерлер үчүн кошумча экрандарды түзүңүз"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Колдонмоңуздун сүрөтчөлөрүн көчүрүү"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Эски үй экрандарыңыздан сүрөтчөлөр жана фолдерлер импорттолсунбу?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"СҮРӨТЧӨЛӨРДҮ КӨЧҮРҮҮ"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"ТАЗАСЫН БАШТОО"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Өз мейкиндигиңизди уюштуруңуз"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Тушкагаздарды, виджеттерди жана тууралоолорду башкаруу үчүн фонду басып туруңуз."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Мынакей фолдер"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Башкасын түзүш үчүн колдонмону басып туруп, башканын жанына жылдырыңыз."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Фолдер ачылды, туурасы <xliff:g id="WIDTH">%1$d</xliff:g>, бийиктиги <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Фолдерди жабыш үчүн тийиңиз"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"Тийип, аттын өзгөртүлүшүн сактаңыз"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Фолдер жабык"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Фолдердин аты <xliff:g id="NAME">%1$s</xliff:g> деп өзгөртүлдү"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Фолдер: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Виджеттер"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Тушкагаздар"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Тууралоолор"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Күтүүдө"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Жүктөлп алнууда"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Орнотулууда"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Белгисиз"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Калыбн келт. жок"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Баарын алып салуу"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Алып салуу"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Издөө"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Бул колдонмо орнотулган эмес"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Бул сүрөтчөнүн колдонмосу орнотулган эмес. Аны алып салсаңыз же колдонмону издеп, кол менен орнотсоңуз болот."</string>
+</resources>
diff --git a/res/values-land/dimens.xml b/res/values-land/dimens.xml
index 07d92798d..1b3418154 100644
--- a/res/values-land/dimens.xml
+++ b/res/values-land/dimens.xml
@@ -23,6 +23,4 @@
<dimen name="apps_customize_tab_bar_height">42dp</dimen>
<integer name="apps_customize_widget_cell_count_x">3</integer>
<integer name="apps_customize_widget_cell_count_y">2</integer>
- <integer name="apps_customize_cling_focused_x">2</integer>
- <integer name="apps_customize_cling_focused_y">1</integer>
</resources>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 50b5c08f4..1d953ffde 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"ແອັບພລິເຄຊັນຫຼັກຂອງ Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"ແອັບຯບໍ່ໄດ້ຖືກຕິດຕັ້ງ."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ແອັບຯ​ທີ່​ດາວ​ໂຫລດ​ແລ້ວ​ຖືກ​ປິດ​ການ​ນຳ​ໃຊ້​ໃນ Safe mode"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"ວິດເຈັດ"</string>
<string name="widget_adder" msgid="3201040140710381657">"ວິດເຈັດ"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"ສະແດງຄວາມຈຳ"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"ຂຽນການຕັ້ງຄ່າໜ້າຫຼັກ ແລະທາງລັດ"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"ອະນຸຍາດໃຫ້ແອັບຯດັ່ງກ່າວ ປ່ຽນການຕັ້ງຄ່າ ແລະທາງລັດໃນໜ້າຫຼັກ."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"ມີບັນຫາໃນການໂຫລດວິດເຈັດ"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ຕິດຕັ້ງ"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"ນີ້ແມ່ນແອັບຯຂອງລະບົບ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງອອກໄດ້."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"ໂຟນເດີຍັງບໍ່ຖືກຕັ້ງຊື່"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ເລີ່ມຕົ້ນໃໝ່"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"ຈັດການພື້ນທີ່ຂອງທ່ານ"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"ແຕະຄ້າງໄວ້ທີ່ພາບພື້ນຫຼັງເພື່ອຈັດການພາບພື້ນຫຼັງ, ວິດເຈັດແລະການຕັ້ງຄ່າ."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"​ຮູບ​ພື້ນຫຼັງ, ວິດເຈັດ, &amp; ​ການ​ຕັ້ງ​ຄ່າ"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"ແຕະທີ່​ພາບ​ພື້ນ​ຫລັງ​ຄ້າງ​ໄວ້​ເພື່ອ​ປັບ​ແຕ່ງ"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ເຂົ້າໃຈແລ້ວ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"ນີ້ແມ່ນໂຟນເດີ"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"ເພື່ອ​ສ້າງ​ອັນໃໝ່​ແບບນີ້ ໃຫ້​ແຕະ​ຄ້າງ​ໄວ້​ທີ່​ແອັບຯ​ທີ່​ຕ້ອງການ​ຍ້າຍ​ແລ້ວ​ລາກ​ມັນ​ໄປ​ຫາ​ໂຕ​ອື່ນ."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"ຕົກລົງ"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"ວິດເຈັດ"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"ພາບພື້ນຫຼັງ"</string>
<string name="settings_button_text" msgid="8119458837558863227">"ການຕັ້ງຄ່າ"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"ກຳ​ລັງ​ລໍ​ຖ້າ"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ກຳລັງດາວໂຫລດ"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"​ກຳ​ລັງ​ຕິດ​ຕັ້ງ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"​ບໍ່​ຮູ້​ຈັກ"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"ບໍ່​ໄດ້​ກູ້​ຂໍ້ມູນ​ມາ​ເທື່ອ"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"ລຶບ​ທັງ​ໝົດ"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ລຶບ​"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ຊອກຫາ"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ແອັບຯ​ນີ້​ຍັງ​ບໍ່​ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"​ແອັບຯ​ສຳ​ລັບ​ໄອ​ຄອນ​ນີ້​ຍັງ​ບໍ່ໄດ້​ຕິດ​ຕັ້ງ​ເທື່ອ. ທ່ານ​ສາ​ມາດ​ລຶບ​ມັນ​ອອກ ຫຼື​ຊອກ​ຫາ​ແອັບຯ ແລ້ວ​ຕິດ​ຕັ້ງ​ມັນ​ໄດ້​ດ້ວຍ​ຕົນ​ເອງ."</string>
</resources>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index aba231d85..f7db79291 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Pagrindinės „Android“ programos"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Programa neįdiegta."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Atsisiųsta programa išjungta Saugos režimu"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Valdikliai"</string>
<string name="widget_adder" msgid="3201040140710381657">"Valdikliai"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Rodyti atmintinę"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"rašyti pagrindinio puslapio nustatymus ir sparčiuosius klavišus"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Programai leidžiama keisti pagrindinio puslapio nustatymus ir sparčiuosius klavišus."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema įkeliant valdiklį"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Sąranka"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Tai sistemos programa ir jos negalima pašalinti."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Aplankas be pavadinimo"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"PRADĖTI IŠ NAUJO"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Tvarkykite savo vietą"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Palieskite ir laikykite foną, jei norite tvarkyti ekrano foną, valdiklius ir nustatymus."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Ekrano fonai, valdikliai ir nustatymai"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Jei norite tinkinti, palieskite ir palaikykite foną"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SUPRATAU"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Štai aplankas"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Kad sukurtumėte tokį patį, palieskite ir laikykite programą, tada perkelkite ją virš kitos programos."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Gerai"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Valdikliai"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ekrano fonai"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Nustatymai"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Laukiama"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Atsisiunčiama"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Diegiama"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Nežinoma"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Neatkurta"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Pašalinti viską"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Pašalinti"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Ieškoti"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ši programa neįdiegta"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Šios piktogramos programa neįdiegta. Galite ją pašalinti arba bandyti ieškoti programos ir ją įdiegti patys."</string>
</resources>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 6d4888d8c..75eb0548b 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android pamatlietotnes"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Lietotne nav instalēta."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Lejupielādētā lietotne ir atspējota drošajā režīmā."</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Logrīki"</string>
<string name="widget_adder" msgid="3201040140710381657">"Logrīki"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Rādīt atmiņu"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"rakstīt sākuma ekrāna iestatījumus un saīsnes"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ļauj lietotnei mainīt iestatījumus un saīsnes sākuma ekrānā."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Ielādējot logrīku, radās problēma."</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Notiek iestatīšana"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Šī ir sistēmas lietotne, un to nevar atinstalēt."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Mape bez nosaukuma"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"SĀKT NO SĀKUMA"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Kārtojiet savu darbvietu"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Pieskarieties fonam un turiet to, lai pārvaldītu fona tapeti, logrīkus un iestatījumus."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Fona tapetes, logrīki un iestatījumi"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Lai pielāgotu, pieskarieties fonam un turiet to nospiestu."</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SAPRATU!"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Lūk, mape!"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Lai izveidotu tādu pašu, pieskarieties lietotnei un turiet to, pēc tam pārvietojiet to virs citas lietotnes."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Labi"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Logrīki"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Fona tapetes"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Iestatījumi"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Gaida"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Lejupielādē"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalē"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Nezināma"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nav atjaunota"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Noņemt visas"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Noņemt"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Meklēt"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Šī lietotne nav instalēta"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Šai ikonai paredzētā lietotne nav instalēta. Varat noņemt ikonu vai meklēt lietotni un instalēt to manuāli."</string>
</resources>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
new file mode 100644
index 000000000..0d775712f
--- /dev/null
+++ b/res/values-mk-rMK/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"Почетна страница"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Основни апликации на Android"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Апликацијата не е инсталирана."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Преземената апликација е оневозможена во безбеден режим"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Виџети"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Виџети"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Прикажи „Мени“"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Допри и задржи за да се избере виџетот."</string>
+ <string name="market" msgid="2619650989819296998">"Продавница"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Не можеше да се спушти елемент на овој екран на почетната страница."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Избери виџет за да се создаде"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Име на папка"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Преименувај папка"</string>
+ <string name="rename_action" msgid="5559600076028658757">"Во ред"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Откажи"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Додај во екран на почетна страница"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Апликации"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Кратенки"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Виџети"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Нема повеќе простор на вашите екрани на почетна страница."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Нема повеќе простор на овој екран на почетната страница."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Нема повеќе простор на лентата „Омилени“"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Овој виџет е премногу голем за лентата „Омилени“"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"Кратенката „<xliff:g id="NAME">%s</xliff:g>“ е создадена."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"Кратенката „<xliff:g id="NAME">%s</xliff:g>“ е отстранета."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"Кратенката „<xliff:g id="NAME">%s</xliff:g>“ веќе постои."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Избери кратенка"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Избери апликација"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Апликации"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Почетна страница"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"Отстрани"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Деинсталирај"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"Отстрани"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"Деинсталирај"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Информации за апликацијата"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Пребарај"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Гласовно пребарување"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Апликации"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"Отстрани"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Деинсталирај ажурирање"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Деинсталирај апликација"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Детали за апликација"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 апликација е избрана"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 виџет е избран"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 папка е избрана"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 кратенка е избрана"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"инсталирај кратенки"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Овозможува апликацијата да додава кратенки без интервенција на корисникот."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"деинсталирај кратенки"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Овозможува апликацијата да ги отстрани кратенките без интервенција на корисникот."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"прочитај подесувања и кратенки на почетна страница"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"напиши подесувања и кратенки на почетна страница"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Овозможува апликацијата да ги менува подесувањата и кратенките на почетната страница."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Проблем при вчитувањето на виџетот"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Ова е системска апликација и не може да се деинсталира."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Неименувана папка"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"Екран на почетна страница %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"Страница %1$d од %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Екран на почетна страница %1$d од %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Страница на апликации %1$d од %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Страница на виџети %1$d од %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Добредојдовте"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"Чувствувајте се како дома."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Создади повеќе екрани за апликации и папки"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Копирај икони за апликација"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Увези икони и папки од старите екрани на почетната страница?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"КОПИРАЈ ИКОНИ"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"СТАРТУВАЈ ОД ПОЧЕТОК"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Организирајте го вашиот простор"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Допри и задржи ја заднината за управување со тапети, виџети и подесувања."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Еве папка"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"За да создадете ваква, допрете и држете ја апликацијата, а потоа поместете ја врз другата."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"Во ред"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Отворена е папка, <xliff:g id="WIDTH">%1$d</xliff:g> на <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Допри за да се затвори папката"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"Допри за да се зачува преименувањето"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Папката е затворена"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Папката е преименувана во <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Папка: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Тапети"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Подесувања"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"На чекање"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Се презема"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Се инсталира"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Не е обновено"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Отстрани ги сите"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Отстрани"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Барај"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Апликацијата не е инсталирана"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Апликацијата за оваа икона не е инсталирана. Може да ја отстраните или да се обидете да ја најдете апликацијата и да ја инсталирате рачно."</string>
+</resources>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
new file mode 100644
index 000000000..32329f1c5
--- /dev/null
+++ b/res/values-ml-rIN/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"ഹോം"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android Core അപ്ലിക്കേഷനുകൾ"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"അപ്ലിക്കേഷൻ ഇൻസ്‌റ്റാളുചെ‌യ്‌തിട്ടില്ല."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ഡൗൺലോഡുചെയ്‌ത അപ്ലിക്കേഷൻ സുരക്ഷാ മോഡിൽ പ്രവർത്തനരഹിതമാക്കി"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"വിജറ്റുകൾ"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"വിജറ്റുകൾ"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"മെമ്മറി കാണിക്കുക"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"ഒരു വിജറ്റ് ചേർക്കുന്നതിന് അത് സ്‌പർശിച്ച് പിടിക്കുക."</string>
+ <string name="market" msgid="2619650989819296998">"ഷോപ്പുചെയ്യുക"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"ഹോം സ്‌ക്രീനിൽ ഇനം വലിച്ചിടാനായില്ല."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"സൃഷ്‌ടിക്കുന്നതിന് വിജറ്റ് തിരഞ്ഞെടുക്കുക"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"ഫോൾഡറിന്റെ പേര്"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"ഫോൾഡറിന്റെ പേരുമാറ്റുക"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ശരി"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"റദ്ദാക്കുക"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"ഹോം സ്ക്രീനിൽ ചേർക്കുക"</string>
+ <string name="group_applications" msgid="3797214114206693605">"അപ്ലിക്കേഷനുകൾ"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"കുറുക്കുവഴികൾ"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"വിജറ്റുകൾ"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"നിങ്ങളുടെ ഹോം സ്‌ക്രീനുകളിൽ സ്ഥലമില്ല."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"ഈ വിജറ്റ് പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഉൾക്കൊള്ളിക്കാവുന്നതിലും വളരെ വലുതാണ്"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"കുറുക്കുവഴി \"<xliff:g id="NAME">%s</xliff:g>\" സൃഷ്‌ടിച്ചു."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"കുറുക്കുവഴി \"<xliff:g id="NAME">%s</xliff:g>\" നീക്കംചെയ്‌തു."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"കുറുക്കുവഴി \"<xliff:g id="NAME">%s</xliff:g>\" ഇതിനകം നിലവിലുണ്ട്."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"കുറുക്കുവഴി തിരഞ്ഞെടുക്കുക"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"അപ്ലിക്കേഷൻ തിരഞ്ഞെടുക്കുക"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"അപ്ലിക്കേഷനുകൾ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ഹോം"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"നീക്കംചെയ്യുക"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"അണ്‍ഇസ്റ്റാളുചെയ്യുക"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"നീക്കംചെയ്യുക"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"അണ്‍ഇസ്റ്റാളുചെയ്യുക"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"അപ്ലിക്കേഷൻ വിവരം"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"തിരയുക"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"വോയ്‌സ് തിരയൽ"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"അപ്ലിക്കേഷനുകൾ"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"നീക്കംചെയ്യുക"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"അപ്‌ഡേറ്റ് അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"അപ്ലിക്കേഷൻ വിശദാംശങ്ങൾ"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"ഒരു അപ്ലിക്കേഷൻ തിരഞ്ഞെടുത്തു"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"ഒരു വിജറ്റ് തിരഞ്ഞെടുത്തു"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"ഒരു ഫോൾഡർ തിരഞ്ഞെടുത്തു"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"ഒരു കുറുക്കുവഴി തിരഞ്ഞെടുത്തു"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"കുറുക്കുവഴികൾ ഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ ചേർക്കാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"കുറുക്കുവഴികൾ അൺഇൻസ്റ്റാളുചെയ്യുക"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"ഉപയോക്തൃ ഇടപെടൽ ഇല്ലാതെ കുറുക്കുവഴികൾ നീക്കംചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യുക"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ഹോമിലെ ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റീഡുചെയ്യാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ഹോം ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും റൈറ്റുചെയ്യുക"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ഹോമിലെ ക്രമീകരണങ്ങളും കുറുക്കുവഴികളും മാറ്റാൻ അപ്ലിക്കേഷനെ അനുവദിക്കുന്നു."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"വിജറ്റ് ലോഡുചെയ്യുന്നതിൽ പ്രശ്നമുണ്ട്"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ഇതൊരു സിസ്‌റ്റം അപ്ലിക്കേഷനായതിനാൽ അൺഇൻസ്‌റ്റാളുചെയ്യാനാവില്ല."</string>
+ <string name="dream_name" msgid="1530253749244328964">"റോക്കറ്റ് ലോഞ്ചർ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"പേരുനൽകാത്ത ഫോൾഡർ"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"ഹോം സ്‌ക്രീൻ %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"പേജ് %1$d / %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ഹോം സ്‌ക്രീൻ %1$d / %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"അപ്ലിക്കേഷനുകളുടെ പേജ് %1$d / %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"വിജറ്റുകളുടെ പേജ് %1$d / %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"സ്വാഗതം"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"ഹോം നിങ്ങളുടേതാക്കി മാറ്റുക."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"അപ്ലിക്കേഷനുകൾക്കും ഫോൾഡറുകൾക്കും വേണ്ടി കൂടുതൽ സ്‌ക്രീനുകൾ സൃഷ്‌ടിക്കുക"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"നിങ്ങളുടെ അപ്ലിക്കേഷൻ ഐക്കണുകൾ പകർത്തുക"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"നിങ്ങളുടെ പഴയ ഹോം സ്ക്രീനുകളിൽ നിന്ന് ഐക്കണുകളും ഫോൾഡറുകളും ഇമ്പോർട്ടുചെയ്യണോ?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"ഐക്കണുകൾ പകർത്തുക"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"പുതുതായി ആരംഭിക്കുക"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"നിങ്ങളുടെ ഇടം ഓർഗനൈസുചെയ്യുക"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"വാൾപേപ്പർ, വിജറ്റുകൾ, ക്രമീകരണങ്ങൾ എന്നിവ നിയന്ത്രിക്കുന്നതിന് പശ്ചാത്തലം സ്‌പർശിച്ച് പിടിക്കുക."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"ഇവിടെയൊരു ഫോൾഡർ ഉണ്ട്"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"ഇതുപോലൊന്ന് സൃഷ്‌ടിക്കുന്നതിന്, ഒരു അപ്ലിക്കേഷൻ സ്‌പർശിച്ച് പിടിച്ചുകൊണ്ട് അത് മറ്റൊന്നിലേക്ക് നീക്കുക."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ശരി"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ഫോൾഡർ തുറന്നു, <xliff:g id="WIDTH">%1$d</xliff:g> / <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"ഫോൾഡർ അടയ്ക്കാൻ സ്‌പർശിക്കുക"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"പേരുമാറ്റം സംരക്ഷിക്കുന്നതിന് സ്‌പർശിക്കുക"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ഫോൾഡർ അടച്ചു"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ഫോൾഡറിന്റെ പേര് <xliff:g id="NAME">%1$s</xliff:g> എന്നായി മാറ്റി"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ഫോൾഡർ: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"വിജറ്റുകൾ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"വാൾപേപ്പറുകൾ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ക്രമീകരണങ്ങൾ"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"കാത്തിരിക്കുന്നു"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ഡൗൺലോഡുചെയ്യുന്നു"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ഇൻസ്‌റ്റാൾ ചെയ്യുന്നു"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"അജ്ഞാതം"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"പുനഃസ്ഥാപിച്ചിട്ടില്ല"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"എല്ലാം നീക്കം ചെയ്യുക"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"നീക്കംചെയ്യുക"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"തിരയുക"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ഈ ഐക്കണുവേണ്ടി അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്‌തിട്ടില്ല. നിങ്ങൾക്കത് നീക്കംചെയ്യാനാകും അല്ലെങ്കിൽ അപ്ലിക്കേഷനുവേണ്ടി തിരഞ്ഞുകൊണ്ട് അത് സ്വമേധയാ ഇൻസ്റ്റാളുചെയ്യുക."</string>
+</resources>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index db92a5c81..34fb79423 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Андройд үндсэн апп"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Апп суугаагүй байна."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Татаж авсан апп-г Аюулгүй горим дотроос идэвхгүйжүүлсэн"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Виджет"</string>
<string name="widget_adder" msgid="3201040140710381657">"Виджет"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Мем харуулах"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"Нүүрний тохиргоо болон товчлолыг бичих"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Апп нь Нүүрэндэх товчлол болон тохиргоог өөрчилж чадна."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Виджет ачаалахад асуудал гарав"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Тохируулга"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Энэ апп нь системийн апп ба устгах боломжгүй."</string>
<string name="dream_name" msgid="1530253749244328964">"Пуужин хөөргөгч"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Нэргүй фолдер"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ШИНЭЭР ЭХЛЭХ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Өөрийнхөө зайг тохируулаарай"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Арын дэвсгэр дээр хүрээд &amp; дарснаар ханын зураг, виджет болон тохиргоог өөрчилж болно."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Дэвсгэр зураг, виджет, &amp; тохиргоо"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Тааруулахын тулд арын дэлгэцэнд хүрээд &amp; барина уу"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"Ойлголоо"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Фолдер энд байна"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Үүнтэй адилханыг үүсгэхийн тулд апп дээр хүрч &amp; бариад нөгөөхийн дээр зөөнө үү."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Тийм"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Виджет"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ханын зураг"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Тохиргоо"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Хүлээж байна"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Татаж авч байна"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Суулгаж байна"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Тодорхойгүй"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Сэргээгээгүй"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Бүгдийг устгах"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Устгах"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Хайх"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Энэ апп-г суулгаагүй байна"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Энэ дүрсний апп-г суулгаагүй байна. Та үүнийг устгах буюу апп-г хайж суулгах боломжтой."</string>
</resources>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
new file mode 100644
index 000000000..2e51e7b26
--- /dev/null
+++ b/res/values-mr-rIN/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"मुख्‍यपृष्‍ठ"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"अॅप स्थापित केलेला नाही."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"डाउनलोड केलेला अ‍ॅप सुरक्षित मोड मध्‍ये अक्षम केला"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"विजेट"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"विजेट"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem दर्शवा"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"विजेट निवडण्यासाठी स्पर्श करा आणि धरून ठेवा."</string>
+ <string name="market" msgid="2619650989819296998">"खरेदी करा"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"या मुख्य स्क्रीनवर आयटम ड्रॉप करू शकलो नाही."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"तयार करण्यासाठी विजेट निवडा"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"फोल्डर नाव"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"फोल्डरचे नाव बदला"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ठीक"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"रद्द करा"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"मुख्य स्क्रीनवर जोडा"</string>
+ <string name="group_applications" msgid="3797214114206693605">"अॅप्स"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"शॉर्टकट"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"विजेट"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"आपल्या मुख्य स्क्रीनवर अधिक जागा नाही."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"या मुख्य स्क्रीनवर आणखी जागा नाही."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"आवडीच्या ट्रे मध्ये आणखी जागा नाही"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"हे विजेट आवडत्या ट्रे साठी खूप मोठे आहे"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" शॉर्टकट तयार केला."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" शॉर्टकट काढला."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" शॉर्टकट आधीपासून अस्तित्वात आहे."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"शॉर्टकट निवडा"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"अॅप निवडा"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"अॅप्स"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"मुख्‍यपृष्‍ठ"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"काढा"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"विस्थापित करा"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"काढा"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"विस्थापित करा"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"अॅप माहिती"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"शोधा"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"व्हॉइस शोध"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"अॅप्स"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"काढा"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"अद्यतन विस्थापित करा"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"अॅप विस्थापित करा"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"अॅप तपशील"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 अॅप निवडला"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 विजेट निवडले"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 फोल्डर निवडले"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 शॉर्टकट निवडला"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"शॉर्टकट स्‍थापित करा"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट जोडण्यास अॅप ला अनुमती देते."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"शॉर्टकट विस्‍थापित करा"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"वापरकर्ता हस्तक्षेपाशिवाय शॉर्टकट काढण्यास अॅप ला अनुमती देते."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट वाचा"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट वाचण्यास अॅप ला अनुमती देते."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"मुख्यपृष्ठ सेटिंग्ज आणि शॉर्टकट लिहा"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"मुख्यपृष्ठातील सेटिंग्ज आणि शॉर्टकट बदलण्यास अॅप ला अनुमती देते."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"विजेट लोड करण्यात समस्या"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"हा सिस्टम अॅप आहे आणि विस्थापित केला जाऊ शकत नाही."</string>
+ <string name="dream_name" msgid="1530253749244328964">"रॉकेट लाँचर"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"अनामित फोल्डर"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"मुख्य स्क्रीन %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d पैकी %1$d पृष्ठ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$d पैकी %1$d मुख्य स्क्रीन"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%2$d पैकी %1$d Apps पृष्ठ"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"%2$d पैकी %1$d विजेट पृष्ठ"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"सुस्वागतम"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"जसे पाहिजे तसे वापरा."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"अॅप्स आणि फोल्डरसाठी आणखी स्क्रीन तयार करा"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"आपली अॅप चिन्हे कॉपी करा"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"आपल्या जुन्या मुख्य स्क्रीनवरून चिन्हे आणि फोल्डर आयात करायची?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"चिन्हे कॉपी करा"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"नव्याने प्रारंभ करा"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"आपले स्थान व्यवस्थापित करा"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"वॉलपेपर, विजेट आणि सेटिंग्ज व्यवस्थापित करण्यासाठी पार्श्वभूमीस स्पर्श करा आणि धरून ठेवा."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"येथे एक फोल्डर आहे"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"यासारखे एखादे तयार करण्यासाठी अॅप ला स्पर्श करा आणि धरून ठेवा, नंतर तो दुसर्‍यावर हलवा."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ठीक"</string>
+ <string name="folder_opened" msgid="94695026776264709">"फोल्डर उघडले, <xliff:g id="WIDTH">%1$d</xliff:g> बाय <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"फोल्डर बंद करण्यासाठी स्पर्श करा"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"नवे नाव जतन करण्यासाठी स्पर्श करा"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बंद"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"फोल्डरचे नाव बदलून <xliff:g id="NAME">%1$s</xliff:g> असे ठेवले"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"विजेट"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"वॉलपेपर"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"सेटिंग्ज"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"प्रतीक्षारत"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"डाउनलोड करत आहे"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"स्थापित करत आहे"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"पुनर्स्थापित झाले नाही"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"सर्व काढा"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"काढा"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"शोधा"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"हा अॅप स्थापित केलेला नाही"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"या चिन्हासाठी अॅप स्थापित केलेला नाही. आपण ते काढू शकता किंवा अॅपचा शोध घेऊ शकता आणि त्यास व्यक्तिचलितपणे स्थापित करू शकता."</string>
+</resources>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index 678eeb46a..3c5876268 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Apl Teras Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Apl tidak dipasang."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Apl yang dimuat turun dilumpuhkan dalam mod Selamat"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widget"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widget"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Papar Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"tulis tetapan dan pintasan Laman Utama"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Membenarkan apl menukar tetapan dan pintasan di Laman Utama."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Masalah memuatkan widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Persediaan"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ini ialah apl sistem dan tidak boleh dinyahpasang."</string>
<string name="dream_name" msgid="1530253749244328964">"Pelancar Roket"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Folder Tanpa Nama"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"MULAKAN YANG BAHARU"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Susun ruang anda"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Sentuh &amp; tahan latar belakang untuk mengurus kertas dinding, widget dan tetapan."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Kertas dinding, widget &amp; tetapan"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Sentuh &amp; tahan latar belakang untuk memperibadikan"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"FAHAM"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ini ada folder"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Untuk membuat satu folder seperti ini, sentuh &amp; tahan apl, kemudian alihkan ke atas folder lain."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Kertas dinding"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Tetapan"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Menunggu"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Memuat turun"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Memasang"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Tidak diketahui"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Tak dipulihkan"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Buang Semua"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Alih keluar"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Carian"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Apl ini tidak dipasang"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Apl untuk ikon ini tidak dipasang. Anda boleh mengalih keluar atau mencari dan memasang apl itu secara manual."</string>
</resources>
diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml
index d2f6bb142..f50a5db5b 100644
--- a/res/values-ms/strings.xml
+++ b/res/values-ms/strings.xml
@@ -41,7 +41,6 @@
<string name="group_applications" msgid="2103752818818161976">"Aplikasi"</string>
<string name="group_shortcuts" msgid="9133529424900391877">"Pintasan"</string>
<string name="group_widgets" msgid="6704978494073105844">"Widget"</string>
- <string name="group_wallpapers" msgid="1568191644272224858">"Kertas dinding"</string>
<string name="completely_out_of_space" msgid="1759078539443491182">"Tiada lagi ruang pada skrin Utama anda."</string>
<string name="out_of_space" msgid="8365249326091984698">"Tiada lagi ruang pada skrin Utama ini"</string>
<string name="hotseat_out_of_space" msgid="6304886797358479361">"Tiada lagi ruang pada kerusi panas."</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
new file mode 100644
index 000000000..8048b02a3
--- /dev/null
+++ b/res/values-my-rMM/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher၃"</string>
+ <string name="home" msgid="7658288663002113681">"ပင်မစာမျက်နှာ"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Androidပင်မ အပ်ပလီကေးရှင်းများ"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"အပ်ပလီကေးရှင်း မထည့်သွင်းထားပါ"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"ဒေါင်းလုဒ် appကို လုံခြုံရေး မုဒ်ထဲမှာ ပိတ်ထား"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"ဝဒ်ဂျက်များ"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"ဝဒ်ဂျက်များ"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem ကိုပြရန်"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"ဝဒ်ဂျက်တစ်ခုကို ကောက်ယူရန် ဖိနှိပ်ထားပါ"</string>
+ <string name="market" msgid="2619650989819296998">"စျေးဝယ်ရန်"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"ပင်မမျက်နှာစာတွင် အရာများ ချ လို့ မရတော့ပါ"</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"ဝဒ်ဂျက်တစ်ခုအား ပြုဖန်တီးရန် ရွေးပါ"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"အကန့်အမည်"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"အကန့်အမည်ပြောင်းရန်"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ကောင်းပြီ"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"ပယ်ဖျက်သည်"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"ပင်မမျက်နှာစာသို့ ထည့်ပါ"</string>
+ <string name="group_applications" msgid="3797214114206693605">"အပ်ပလီကေးရှင်းများ"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"အတိုကောက်မှတ်သားမှုများ"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"ဝဒ်ဂျက်များ"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"ပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"ဤဝဒ်ဂျက်မှာ အနှစ်သက်ဆုံးအရာများထားရာနေရာ အတွက် ကြီးလွန်းနေပါသည်"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"အတိုကောက်မှတ်သားမှု \"<xliff:g id="NAME">%s</xliff:g>\" ကို ပြုလုပ်ပြီးပါပြီ"</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"အတိုကောက်မှတ်သားမှု \"<xliff:g id="NAME">%s</xliff:g>\" ကို ဖယ်ရှားပြီးပါပြီ"</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"အတိုကောက်မှတ်သားမှု \"<xliff:g id="NAME">%s</xliff:g>\" ရှိပြီးသား ဖြစ်နေပါသည်"</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"အတိုကောက်မှတ်သားမှုကို ရွေးရန်"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"အပလီကေးရှင်း တစ်ခုခုကို ရွေးချယ်ပါ"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"အပ်ပလီကေးရှင်းများ"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ပင်မစာမျက်နှာ"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"ဖယ်ရှာခြင်း"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"ဖယ်ရှားခြင်း"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"ဖယ်ရှာခြင်း"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"ဖယ်ရှားခြင်း"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"အပ်ပလီကေးရှင်း အချက်အလက်များ"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"ရှာဖွေခြင်း"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"အသံဖြင့် ရှာဖွေခြင်း"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"အပ်ပလီကေးရှင်းများ"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"ဖယ်ရှာခြင်း"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"အဆင့်မြှင့်ခြင်းကို ဖယ်ရှားပါ"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"အပ်ပလီကေးရှင်းကို ဖယ်ရှားပါ"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"အပ်ပလီကေးရှင်း အသေးစိတ် အချက်အလက်"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"အပ်ပလီကေးရှင်းတစ်ခု ရွေးချယ်ထားပြီး"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"ဝဒ်ဂျက်တစ်ခု ရွေးချယ်ထားပြီး"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"အကန့် တစ်ခု ရွေးချယ်ထားပြီး"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"အတိုကောက်မှတ်သားမှုတစ်ခု ရွေးချယ်ထားပြီး"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"အတိုကောက်မှတ်သားမှုများအား ထည့်သွင်းခြင်း"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"အသုံးပြုသူ လုပ်ဆောင်မှုမရှိပဲ အပ်ပလီကေးရှင်းကို အတိုကောက်မှတ်သားမှုများ ပြုလုပ်ခွင့် ပေးခြင်း"</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"အတိုကောက်မှတ်သားမှုများ ဖယ်ထုတ်ခြင်း"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"အပ်ပလီကေးရှင်းအား အသုံးပြုသူ မပါဝင်ပဲ အတိုကောက်မှတ်သားမှုများ ဖယ်ရှားခွင့် ပြုခြင်း"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ဖတ်ခြင်း"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ပင်မမျက်နှာစာတွင်ရှိသော အပြင်အဆင်နှင့် အတိုကောက်မှတ်သားမှုများကို အပ်ပလီကေးရှင်းအား ဖတ်ခွင့်ပြုခြင်း"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ပင်မမျက်နှာစာ အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများအား ရေးသားခြင်း"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ပင်မမျက်နှာစာတွင် ရှိသော အပြင်အဆင် နှင့် အတိုကောက်မှတ်သားမှုများ ကို အပ်ပလီကေးရှင်းအား ပြောင်းခွင့်ပြုခြင်း"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ဝဒ်ဂျက် တင်ရာတွင် ပြသနာ ရှိပါသည်"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ဤအပ်ပလီကေးရှင်းမှာ စစ်စတန်ပိုင်းဆိုင်ရာ အပ်ပလီကေးရှင်းဖြစ်ပါသည်။ ထုတ်ပစ်၍ မရပါ"</string>
+ <string name="dream_name" msgid="1530253749244328964">"ဒုံပျံ ပစ်လွှတ်သောအရာ"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"အမည်မရှိအကန့်"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"ပင်မစာမျက်နှာ %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"စာမျက်နှာ %1$d မှ %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"ပင်မစာမျက်နှာ %1$d မှ %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"အပ်ပလီကေးရှင်းပြ စာမျက်နှာ %1$d မှ %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"ဝဒ်ဂျက်ပြ စာမျက်နှာ %1$d မှ %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"ကြိုဆိုပါသည်"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"ကိုယ့်အိမ်ကိုယ့်ယာလို သဘောထားပါ"</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"အပ်ပလီကေးရှင်း နှင့် အကန့်များအတွက် ဖန်သားပြင်မှာ ထပ်ထည့်ပါ"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"အပ်ပလီကေးရှင်းပုံညွှန်းများကို ကူးယူပါ"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"ပင်မစာမျက်နှာအဟောင်းမှ ပုံညွှန်းများ နှင့် အကန့်များကို ယူလာပါမလား"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"COPY ICONS"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"START FRESH"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"စနစ်တကျဖြစ်အောင် ပြုလုပ်ပါ"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"နောက်ခံကို ဖိကိုင်၍ နောက်ခံပုံ၊ဝဒ်ဂျက်များ၊အပြင်အဆင်များကို ထိန်းချုပ်ပါ"</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"ဒီမှာ အကန့်တစ်ခုဖြစ်ပါသည်"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"ဤကဲ့သို့လုပ်ရန်အတွက်၊ အပ်ပလီကေးရှင်းတစ်ခုကို ဖိကိုင်ပြီး နောက်တစ်ခုပေါ်သို့ ရွှေ့လိုက်ပါ"</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ကောင်းပြီ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ဖွင့်ထားသောအကန့်, <xliff:g id="WIDTH">%1$d</xliff:g> နှင့် <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"အကန့်ကို ပိတ်ရန် ဖိကိုင်ပါ"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"အမည်ပြောင်းခြင်း အတည်ပြုရန် ဖိကိုင်ပါ"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ပိတ်ထားသောအကန့်"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ပြောင်းလဲလိုက်သော အကန့်အမည် <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"အကန့်အမည်: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ဝဒ်ဂျက်များ"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"နောက်ခံများ"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"အပြင်အဆင်များ"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"စောင့်နေ"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ဒေါင်းလုဒ် လုပ်နေ"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"တပ်ဆင်နေ"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"မသိရ"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"ပြန်မဖေါ်ခဲ့"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"အားလုံး ဖယ်ရှားရန်"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ဖယ်ရှားရန်"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ရှာဖွေရန်"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"App မတပ်ဆင်ရသေးပါ"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ဤအိုင်ကွန်အတွက် app အားမထည့်သွင်းထားပါ။ You can remove it, or search for the app and install it manually."</string>
+</resources>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index dd89a4f60..ff8280e73 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Kjerneapper for Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen er ikke installert."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"En nedlastet app er deaktivert i sikker modus"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Moduler"</string>
<string name="widget_adder" msgid="3201040140710381657">"Moduler"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Vis minne"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"angi startsideinnstillinger og -snarveier"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Lar appen endre innstillingene og snarveiene på startsiden."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem ved innlasting av modul"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Konfigurering"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dette er en systemapp som ikke kan avinstalleres."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Mappe uten navn"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"START PÅ NYTT"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organiser plassen din"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Trykk og hold på bakgrunnen for å administrere bakgrunnen, moduler og innstillinger."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Bakgrunner, moduler og innstillinger"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Trykk og hold på bakgrunnen for å tilpasse den"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"SKJØNNER"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Dette er en mappe"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"For å opprette en som denne, trykker og holder du på en app og flytter den over en annen."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Moduler"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunner"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Innstillinger"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Venter …"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Laster ned …"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installerer …"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Ukjent"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ikke gjenoppr."</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Fjern alle"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Fjern"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Søk"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Denne appen er ikke installert"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen for dette ikonet er ikke installert. Du kan fjerne det, eller prøve å søke etter appen og installere den manuelt."</string>
</resources>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
new file mode 100644
index 000000000..fe75ea70d
--- /dev/null
+++ b/res/values-ne-rNP/strings.xml
@@ -0,0 +1,124 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"गृह"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android मूल अनुप्रयोगहरू"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"अनुप्रयोग स्थापित छैन।"</string>
+ <!-- no translation found for safemode_shortcut_error (9160126848219158407) -->
+ <skip />
+ <string name="widgets_tab_label" msgid="2921133187116603919">"विजेटहरू"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"विजेटहरू"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem देखाउनुहोस्"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"एउटा विजेटलाई टिप्नको लागि टच गरेर होल्ड गर्नुहोस्।"</string>
+ <string name="market" msgid="2619650989819296998">"पसल"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"यो गृह स्क्रिनमा वस्तु खसाउन सकिँदैन।"</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"सृजना गर्नको लागि विजेट छान्नुहोस्"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"फोल्डरको नाम"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"फोल्डरलाई पुनःनामाकरण गर्नुहोस्"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ठिक छ"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"रद्द गर्नुहोस्"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"गृह स्क्रिनमा थप्नुहोस्"</string>
+ <string name="group_applications" msgid="3797214114206693605">"अनुप्रयोगहरू"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"सर्टकटहरू"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"विजेटहरू"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"यो गृह स्क्रिनहरूमा कुनै थप ठाउँ छैन"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"मनपर्ने ट्रे अब कुनै ठाँउ छैन"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"यो विजेट मनपर्ने ट्रे को लागि निकै ठूलो छ"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"सर्टकट \"<xliff:g id="NAME">%s</xliff:g>\" सिर्जित गरियो।"</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"सर्टकट \"<xliff:g id="NAME">%s</xliff:g>\" हटाइयो।"</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"सर्टकट \"<xliff:g id="NAME">%s</xliff:g>\" पहिल्यै अवस्थित छ।"</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"सर्टकट छान्नुहोस्"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"अनुप्रयोग छनौट गर्नुहोस्"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"अनुप्रयोगहरू"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"गृह"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"हटाउनुहोस्"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"हटाउनुहोस्"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"हटाउनुहोस्"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"हटाउनुहोस्"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"अनुप्रयोग जानकारी"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"खोज्नुहोस्"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"ध्वनि खोज"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"अनुप्रयोगहरू"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"हटाउनुहोस्"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"अद्यावधिक अस्थापित गर्नुहोस्"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"अनुप्रयोग अस्थापना गर्नुहोस्"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"अनुप्रयोग विवरणहरु"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"१ अनुप्रयोग चयन गरियो"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"१ विजेट चयन गरियो"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"१ फोल्डर चयन गरियो"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"१ सर्टकट चयन गरियो"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"सर्टकट स्थापना गर्नेहोस्"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"प्रयोगकर्ताको हस्तक्षेप बिना एउटा अनुप्रयोगलाई सर्टकटमा थप्नको लागि अनुमति दिनुहोस्।"</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"सर्टकटहरूको स्थापन रद्द गर्नुहोस्"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"अनुप्रयोगलाई उपयोगकर्ताको हस्तक्षेप बिना सर्टकटहरूलाई हटाउन अनुमति दिनुहोस्।"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"गृह सेटिङहरू र सर्टकटहरू पढ्नुहोस्"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"गृहमा एउटा अनुप्रयोगलाई सेटिङहरू र सर्टकटहरू पढ्न अनुमति दिनुहोस्।"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"गृह सेटिङहरू र सर्टकटहरू लेख्नुहोस्"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"गृहमा एउटा अनुप्रयोगलाई सेटिङ र सर्टकट बदल्न अनुमति दिनुहोस्।"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"समस्या लोडिङ गर्ने विजेट"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"यो प्रणाली अनुप्रयोग हो र यसलाई स्थापना रद्द गर्न सकिँदैन।"</string>
+ <string name="dream_name" msgid="1530253749244328964">"रकेट लन्चर"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"बेनाम फोल्डर"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"गृह स्क्रिन %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"पृष्ठ %2$d को %1$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"गृह स्क्रिन %2$d को %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"अनुप्रयोग पृष्ठ %2$d को %1$d"</string>
+ <!-- String.format failed for translation -->
+ <!-- no translation found for apps_customize_widgets_scroll_format (3106209519974971521) -->
+ <skip />
+ <string name="first_run_cling_title" msgid="2459738000155917941">"स्वागतम"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"गृह आरामसँग बस्नुहोस्"</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"अनुप्रयोगहरु र फोल्डरहरुलाई थप स्क्रीनहरु सिर्जना गर्नुहोस्"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"तपाईँको अनुप्रयोग आईकनको प्रतिलिप गर्नुहोस्"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"आफ्नो पुरानो गृह स्क्रीनबाट अाईकन र फोल्डरहरू आयात गर्नुहोस्?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"ICONS प्रतिलिप गर्नुहोस्"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"START FRESH"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"आफ्नो ठाउँ व्यवस्थापन गर्नुहोस्"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"वालपेपर, विजेट र सेटिङ्स प्रबन्ध गर्न पृष्ठभूमिलाई टच गरेर होल्ड गर्नुहोस्।"</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"यहाँ एउटा फोल्डर छ"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"यस्तै एक किसिमका सिर्जना गर्न, अनुप्रयोगलाई टच गरेर होल्ड गर्नुहोस्, त्यसपछि यसलाई अर्को माथि सार्नुहोस्।"</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ठिक छ"</string>
+ <string name="folder_opened" msgid="94695026776264709">"फोल्डर खुल्यो <xliff:g id="WIDTH">%1$d</xliff:g> बाट <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"फोल्डर बन्द गर्नको लागि टच गर्नुहोस्"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"पुन: नामाकरण बचत गर्न टच गर्नुहोस्।"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"फोल्डर बन्द भयो"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"फोल्डर <xliff:g id="NAME">%1$s</xliff:g> मा पुनःनामाकरण गरियो।"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"फोल्डर: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"विजेटहरू"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"वालपेपरहरु"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"सेटिंङहरू"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"पर्खँदै"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"डाउनलोड हुँदै"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"स्थापना गर्दै"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"अज्ञात"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"पुनर्स्थापित भएन"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"सबै हटाउनुहोस्"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"हटाउनुहोस्"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"खोजी गर्नुहोस्"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"यो अनुप्रयोग स्थापित छैन"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"यो प्रतिमाका लागि अनुप्रयोगलाई स्थापना गरिएको छैन। तपाईँ यसलाई हटाउन, वा अनुप्रयोग खोजी र स्वयं यो स्थापित गर्न सक्नुहुन्छ।"</string>
+</resources>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index d39c1ea11..470eb8722 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android-kernapps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"App is niet geïnstalleerd."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Gedownloade app uitgeschakeld in veilige modus"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Geheugen weergeven"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"instellingen en snelkoppelingen op de startpagina schrijven"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"De app toestaan de instellingen en snelkoppelingen op de startpagina te wijzigen."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Probleem bij het laden van widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuratie"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Dit is een systeemapp die niet kan worden verwijderd."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Naamloze map"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"OPNIEUW BEGINNEN"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Uw ruimte indelen"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Blijf de achtergrond aanraken om de achtergrond, widgets en instellingen te beheren."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Achtergronden, widgets en instellingen"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Blijf de achtergrond aanraken om deze aan te passen"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Dit is een map"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Als u een map zoals deze wilt maken, blijft u een app aanraken en schuift u deze boven op een andere app."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Achtergronden"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Instellingen"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Wachten"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Downloaden"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installeren"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Onbekend"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Niet hersteld"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Alles verwijderen"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Verwijderen"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Zoeken"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Deze app is niet geïnstalleerd"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"De app voor dit pictogram is niet geïnstalleerd. U kunt het pictogram verwijderen of de app zoeken en handmatig installeren."</string>
</resources>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index b428a95ca..61a51e0a5 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Główne aplikacje Androida"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacja nie jest zainstalowana."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Pobrana aplikacja została wyłączona w trybie awaryjnym"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widżety"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widżety"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Pokaż pamięć"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"zapisywanie ustawień i skrótów na ekranie głównym"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Umożliwia aplikacji zmianę ustawień i skrótów na ekranie głównym."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problem podczas ładowania widżetu"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguracja"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To aplikacja systemowa i nie można jej odinstalować."</string>
<string name="dream_name" msgid="1530253749244328964">"Wyrzutnia rakiet"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Folder bez nazwy"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ZACZNIJ OD NOWA"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Uporządkuj obszar roboczy"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Kliknij i przytrzymaj tło, by zmienić tapetę, widżety lub ustawienia."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Tapety, widżety i ustawienia"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Kliknij i przytrzymaj tło, by dostosować"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Tu jest folder"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Aby utworzyć taki sam, kliknij i przytrzymaj aplikację, a następnie przenieś ją na następną."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widżety"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Ustawienia"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Oczekiwanie"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Pobieranie"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalowanie"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Brak informacji"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nie przywrócono"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Usuń wszystkie"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Usuń"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Szukaj"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacja nie jest zainstalowana"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacja, której odpowiada ta ikona, nie jest zainstalowana. Możesz usunąć ikonę lub wyszukać aplikację i zainstalować ją ręcznie."</string>
</resources>
diff --git a/res/values-port/dimens.xml b/res/values-port/dimens.xml
index 7753ab318..c20f57b2b 100644
--- a/res/values-port/dimens.xml
+++ b/res/values-port/dimens.xml
@@ -16,9 +16,6 @@
<resources>
<!-- AppsCustomize -->
- <integer name="apps_customize_cling_focused_x">1</integer>
- <integer name="apps_customize_cling_focused_y">1</integer>
-
<integer name="apps_customize_widget_cell_count_x">2</integer>
<integer name="apps_customize_widget_cell_count_y">3</integer>
</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index b8e51d6d2..2fe062a4a 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Aplicações principais do Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"A aplicação não está instalada."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicação transferida desativada no Modo de segurança"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"escrever definições e atalhos do Ecrã principal"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite à aplicação alterar as definições e os atalhos no Ecrã Principal."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"É uma aplicação de sistema e não pode ser desinstalada."</string>
<string name="dream_name" msgid="1530253749244328964">"Lança-mísseis"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"COMEÇAR DO INÍCIO"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organizar o seu espaço"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Toque sem soltar no fundo para gerir a imagem de fundo, os widgets e as definições."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Imagens de fundo, widgets e definições"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Toque sem soltar no fundo para personalizar"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"COMPREENDI"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Eis uma pasta"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Para criar uma pasta, toque sem soltar numa aplicação e arraste-a para cima de outra aplicação."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagens de fundo"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Definições"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"A aguardar"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"A transferir "</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"A instalar"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Não restaurado"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Remover todos"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Esta aplicação não está instalada"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"A aplicação deste ícone não está instalada. Pode removê-lo ou pesquisar a aplicação e instalá-la manualmente."</string>
</resources>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 03c621af2..ddbb25310 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -21,9 +21,10 @@
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
<string name="home" msgid="7658288663002113681">"Início"</string>
- <string name="uid_name" msgid="7820867637514617527">"Principais aplicativos do Android"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Principais apps do Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
- <string name="activity_not_found" msgid="8071924732094499514">"O aplicativo não está instalado."</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"O app não está instalado."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"App transferido por download desativado no modo de segurança"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgets"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgets"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Mostrar memória"</string>
@@ -37,7 +38,7 @@
<string name="rename_action" msgid="5559600076028658757">"Ok"</string>
<string name="cancel_action" msgid="7009134900002915310">"Cancelar"</string>
<string name="menu_item_add_item" msgid="1264911265836810421">"Adicionar à tela inicial"</string>
- <string name="group_applications" msgid="3797214114206693605">"Aplicativos"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Apps"</string>
<string name="group_shortcuts" msgid="6012256992764410535">"Atalhos"</string>
<string name="group_widgets" msgid="1569030723286851002">"Widgets"</string>
<string name="completely_out_of_space" msgid="6106288382070760318">"Não há mais espaço nas telas iniciais."</string>
@@ -48,55 +49,59 @@
<string name="shortcut_uninstalled" msgid="8176767991305701821">"O atalho \"<xliff:g id="NAME">%s</xliff:g>\" foi removido."</string>
<string name="shortcut_duplicate" msgid="9167217446062498127">"O atalho \"<xliff:g id="NAME">%s</xliff:g>\" já existe."</string>
<string name="title_select_shortcut" msgid="6680642571148153868">"Selecione um atalho"</string>
- <string name="title_select_application" msgid="3280812711670683644">"Selecione um aplicativo"</string>
- <string name="all_apps_button_label" msgid="9110807029020582876">"Aplicativos"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Selecione um app"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Apps"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Início"</string>
<string name="delete_zone_label_workspace" msgid="4009607676751398685">"Remover"</string>
<string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Desinstalar"</string>
<string name="delete_target_label" msgid="1822697352535677073">"Remover"</string>
<string name="delete_target_uninstall_label" msgid="5100785476250872595">"Desinstalar"</string>
- <string name="info_target_label" msgid="8053346143994679532">"Informações do aplicativo"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Informações do app"</string>
<string name="accessibility_search_button" msgid="1628520399424565142">"Pesquisar"</string>
<string name="accessibility_voice_search_button" msgid="4637324840434406584">"Pesquisa por voz"</string>
- <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Aplicativos"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Apps"</string>
<string name="accessibility_delete_button" msgid="6466114477993744621">"Remover"</string>
<string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Desinstalar atualização"</string>
- <string name="cab_menu_delete_app" msgid="7435191475867183689">"Desinstalar aplicativo"</string>
- <string name="cab_menu_app_info" msgid="8593722221450362342">"Detalhes do aplicativo"</string>
- <string name="cab_app_selection_text" msgid="374688303047985416">"Um aplicativo selecionado"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Desinstalar app"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Detalhes do app"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"Um app selecionado"</string>
<string name="cab_widget_selection_text" msgid="1833458597831541241">"Um widget selecionado"</string>
<string name="cab_folder_selection_text" msgid="7999992513806132118">"Uma pasta selecionada"</string>
<string name="cab_shortcut_selection_text" msgid="2103811025667946450">"Um atalho selecionado"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"instalar atalhos"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um aplicativo adicione atalhos sem intervenção do usuário."</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Permite que um app adicione atalhos sem intervenção do usuário."</string>
<string name="permlab_uninstall_shortcut" msgid="864595034498083837">"desinstalar atalhos"</string>
- <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Permite que o aplicativo remova atalhos sem a intervenção do usuário."</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Permite que o app remova atalhos sem a intervenção do usuário."</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"ler configurações e atalhos da tela inicial"</string>
- <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite que o aplicativo leia as configurações e os atalhos na tela inicial."</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Permite que o app leia as configurações e os atalhos na tela inicial."</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"gravar configurações e atalhos da tela inicial"</string>
- <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que o aplicativo altere as configurações e os atalhos na tela inicial."</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Permite que o app altere as configurações e os atalhos na tela inicial."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema ao carregar o widget"</string>
- <string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um aplicativo do sistema e não pode ser desinstalado."</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configuração"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Este é um app do sistema e não pode ser desinstalado."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Pasta sem nome"</string>
<string name="workspace_description_format" msgid="2950174241104043327">"Tela inicial %1$d"</string>
<string name="default_scroll_format" msgid="7475544710230993317">"Página %1$d de %2$d"</string>
<string name="workspace_scroll_format" msgid="8458889198184077399">"Tela inicial %1$d de %2$d"</string>
- <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Página de aplicativos, %1$d de %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Página de apps, %1$d de %2$d"</string>
<string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Página de widgets, %1$d de %2$d"</string>
<string name="first_run_cling_title" msgid="2459738000155917941">"Bem-vindo"</string>
<string name="first_run_cling_description" msgid="6447072552696253358">"Fique à vontade."</string>
<string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
<string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
- <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Crie mais telas para aplicativos e pastas"</string>
- <string name="migration_cling_title" msgid="9181776667882933767">"Copiar ícones de aplicativos"</string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Crie mais telas para apps e pastas"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Copiar ícones de apps"</string>
<string name="migration_cling_description" msgid="2752413805582227644">"Importar ícones e pastas de suas telas iniciais antigas?"</string>
<string name="migration_cling_copy_apps" msgid="946331230090919440">"COPIAR ÍCONES"</string>
<string name="migration_cling_use_default" msgid="2626475813981258626">"COMEÇAR DO ZERO"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organize seu espaço"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Toque e mantenha pressionada a tela de fundo para gerenciar o plano de fundo, os widgets e as configurações."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Plano de fundo, widgets e configurações"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Toque e mantenha pressionado o segundo plano para personalizar"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ENTENDI"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Aqui está uma pasta"</string>
- <string name="folder_cling_create_folder" msgid="6158215559475836131">"Para criar uma pasta como esta, mantenha pressionado um aplicativo e mova-o para cima de outro."</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Para criar uma pasta como esta, mantenha pressionado um app e mova-o para cima de outro."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Ok"</string>
<string name="folder_opened" msgid="94695026776264709">"Pasta aberta, <xliff:g id="WIDTH">%1$d</xliff:g> por <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
<string name="folder_tap_to_close" msgid="1884479294466410023">"Toque para fechar a pasta"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgets"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Planos de fundo"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Configurações"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Aguardando"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Transferindo"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Instalando"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Desconhecido"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Não restaurado"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Remover tudo"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Remover"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Pesquisar"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Este app não está instalado"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"O app deste ícone não está instalado. Você pode remover o ícone, ou procurar o app e instalá-lo manualmente."</string>
</resources>
diff --git a/res/values-rm/strings.xml b/res/values-rm/strings.xml
index 36000956f..be35c6b09 100644
--- a/res/values-rm/strings.xml
+++ b/res/values-rm/strings.xml
@@ -192,4 +192,14 @@
<skip />
<!-- no translation found for settings_button_text (8119458837558863227) -->
<skip />
+ <!-- no translation found for package_state_enqueued (6227252464303085641) -->
+ <skip />
+ <!-- no translation found for package_state_downloading (4088770468458724721) -->
+ <skip />
+ <!-- no translation found for package_state_installing (7588193972189849870) -->
+ <skip />
+ <!-- no translation found for package_state_unknown (7592128424511031410) -->
+ <skip />
+ <!-- no translation found for package_state_error (7672093962724223588) -->
+ <skip />
</resources>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index a667ffdc5..04aebc8b9 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplicația nu este instalată."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Aplicația descărcată este dezactivată în modul de siguranță"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgeturi"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgeturi"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Afișați memoria"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"scrie setări și comenzi rapide pentru ecranul de pornire"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Permite aplicației să modifice setările și comenzile rapide din ecranul de pornire."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problemă la încărcarea widgetului"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Configurați"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Aceasta este o aplicație de sistem și nu poate fi dezinstalată."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Dosar fără nume"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"REÎNCEPEȚI"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organizați-vă spațiul"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Atingeți lung fundalul pentru a gestiona imaginea de fundal, widgeturile și setările."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Imagini de fundal, widgeturi și setări"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Atingeți lung fundalul pentru a-l personaliza"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"AM ÎNȚELES"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Iată un dosar"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Pentru a crea un dosar similar, atingeți și țineți degetul pe o aplicație, apoi mutați-o deasupra alteia."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgeturi"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Imagini de fundal"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Setări"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"În așteptare"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Se descarcă"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Se instalează"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Necunoscut"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nerestabilit"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Eliminați-le pe toate"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Eliminați"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Căutați"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Aplicația nu este instalată"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplicația pentru această pictogramă nu este instalată. Puteți să ștergeți pictograma sau să căutați aplicația și s-o instalați manual."</string>
</resources>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index c2e8bc718..b1d7713a0 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Основные приложения Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Приложение удалено"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Скачанное приложение отключено в безопасном режиме"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Виджеты"</string>
<string name="widget_adder" msgid="3201040140710381657">"Виджеты"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Сведения о памяти"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"Изменение настроек и ярлыков главного экрана"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Приложение сможет изменять настройки и ярлыки на главном экране."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Не удалось загрузить виджет"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Настройка"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Это системное приложение, его нельзя удалить."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Папка без названия"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ИСПОЛЬЗОВАТЬ СТАНДАРТНЫЙ МАКЕТ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Организация рабочего пространства"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Чтобы перейти к управлению обоями, виджетами и настройками, нажмите на фоновое изображение и удерживайте его."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Обои, виджеты и настройки"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Чтобы выполнить настройку, коснитесь фона и удерживайте его"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ОК"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Это папка"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Чтобы создать папку, нажмите и удерживайте значок приложения, а затем перетащите его на другой значок."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"ОК"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Виджеты"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Обои"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Настройки"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Ожидается"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Скачивается"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Устанавливается"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Неизвестно"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Не восстановлен"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Удалить все"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Удалить"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Найти"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Приложение не установлено"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Приложение не установлено. Вы можете удалить значок или найти приложение и установить его вручную."</string>
</resources>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
new file mode 100644
index 000000000..d334da92b
--- /dev/null
+++ b/res/values-si-rLK/strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"මුල් පිටුව"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android මධ්‍ය යෙදුම්"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"යෙදුම ස්ථාපනය කර නැත."</string>
+ <!-- no translation found for safemode_shortcut_error (9160126848219158407) -->
+ <skip />
+ <string name="widgets_tab_label" msgid="2921133187116603919">"විජට්"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"විජට්"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Mem පෙන්වන්න"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"විජට් එක ස්පර්ශ කර අහුලා ගැනීමට අල්ලාගෙන සිටින්න."</string>
+ <string name="market" msgid="2619650989819296998">"සාප්පුයාම"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"මෙම මුල් පිටු තිරය වෙත අයිතමය ඇද හෙළිය නොහැකි විය."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"සැදීමට විජට් එක තෝරන්න"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"ෆෝල්ඩරයේ නම"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"ෆෝල්ඩරය නැවත නම් කරන්න"</string>
+ <string name="rename_action" msgid="5559600076028658757">"හරි"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"අවලංගු කරන්න"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"මුල් පිටු තිරය වෙත එක් කරන්න"</string>
+ <string name="group_applications" msgid="3797214114206693605">"යෙදුම්"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"කෙටිමං"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"විජට්"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"මෙම මුල් පිටු තිර මත තවත් ඉඩ නැත."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"මෙම මුල් පිටු තිරය මත තවත් අවසර නැත."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"ප්‍රියතම දෑ ඇති තැටිය සඳහා මෙම විජටය ඉතා විශාලය"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" කෙටිමග සාදන ලදි."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" කෙටිමග ඉවත් කෙරිණි."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" කෙටිමග දැනටමත් පවතී."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"කෙටිමග තේරීම"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"යෙදුම තේරීම"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"යෙදුම්"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"මුල් පිටුව"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"ඉවත් කරන්න"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"අස්ථාපනය කරන්න"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"ඉවත් කරන්න"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"අස්ථාපනය කරන්න"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"යෙදුම් තොරතුරු"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"සොයන්න"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"හඬ සෙවීම"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"යෙදුම්"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"ඉවත් කරන්න"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"යාවත්කාලිනය අස්ථාපනය කරන්න"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"යෙදුම අස්ථාපනය කරන්න"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"යෙදුම් විස්තර"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 යෙදුමක් තෝරා ඇත"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 විජටයක් තෝරා ඇත"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 ෆෝල්ඩරයක් තෝරා ඇත"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 කෙටිමඟක් තෝරා ඇත"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"කෙටිමං ස්ථාපනය කරන්න"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"පරිශීලක මැදිහත්වීමෙන් තොරව කෙටිමං එක් කිරීමට යෙදුමකට අවසර දෙයි."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"කෙටිමං අස්ථාපනය කරන්න"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"පරිශීලකයාගේ මැදිහත්වීමෙන් තොරව කෙටිමං ඉවත් කිරීමට යෙදුමකට අවසර දෙයි."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"මුල් පිටු සැකසීම් සහ කෙටිමං කියවන්න"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"මුල් පිටුවේ ඇති සැකසීම් සහ කෙටිමං කියවීමට යෙදුමකට අවසර දෙයි."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"මුල් පිටු සැකසීම් සහ කෙටිමං ලියන්න"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"මුල් පිටුවේ සැකසීම් සහ කෙටිමං ඉවත් කිරීමට යෙදුමට අවසර දෙයි."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ගැටලු පූරණ විජට් එක"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"මෙය පද්ධති යෙදුමක් වන අතර අස්ථාපනය කළ නොහැක."</string>
+ <string name="dream_name" msgid="1530253749244328964">"රොකට් ආරම්භකය"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"නම් නොකළ ෆෝල්ඩරය"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"මුල් පිටු තිරය %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$d හි %1$d පිටුව"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"මුල් පිටු තිරය %2$d හි %1$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%2$d හි %1$d යෙදුම් පිටුව"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"විජට් පිටුව %2$d හි %1$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"සාදරයෙන් පිළිගනිමු"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"ගෙදර ඉන්නවා වගේ ඉන්න."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"යෙදුම් සහ ෆෝල්ඩර සඳහා තවත් තිර සාදන්න"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"ඔබේ යෙදුම් නිරූපක පිටපත් කිරීම"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"ඔබගේ පැරණි මුල් තිර වල නිරූපක සහ ෆෝල්ඩර ආයාත කරන්නද?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"නිරූපක පිටපත් කරන්න"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"අලුතින් පටන්ගන්න"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"ඔබගේ ඉඩ සංවිධානය කරගන්න"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"බිතුපත, විජට් සහ සැකසීම් කළමනාකරණය කිරීමට පසුබිම ස්පර්ශ කර අල්ලාගෙන සිටින්න."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"මෙන්න ෆෝල්ඩරයක්"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"මෙවැනි එකක් තැනීමට, යෙදුමක් තට්ටු කර අල්ලාගෙන සිටින්න, අනතුරුව එය තවත් එකක් උඩින් ගෙන යන්න."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"හරි"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ෆෝල්ඩරය විවෘත විය, <xliff:g id="WIDTH">%1$d</xliff:g> හි <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"ෆෝල්ඩරය වැසීමට ස්පර්ශ කරන්න"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"නැවත නම් කිරීම සුරැකීමට ස්පර්ශ කරන්න"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ෆෝල්ඩරය වසා ඇත"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"<xliff:g id="NAME">%1$s</xliff:g> වෙත ෆෝල්ඩරය නැවත නම් කෙරිණි"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ෆෝල්ඩරය: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"විජට්"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"වෝල්පේපර"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"සැකසීම්"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"රැඳී සිටිමින්"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"බාගනිමින්"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ස්ථාපනය කරමින්"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"නොදනී"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"ප්‍රතිස්ථාපනය කළේ නැත"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"සියල්ල ඉවත් කරන්න"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ඉවත් කරන්න"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"සොයන්න"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"මෙම යෙදුම ස්ථාපනය කර නොමැත"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"මෙම නිරුපකයට යෙදුම ස්ථාපනය කර නොමැත. ඔබට එය ඉවත් කළ හැක, හෝ යෙදුම් සඳහා සොයන්න සහ අතින් ස්ථාපනය කරන්න."</string>
+</resources>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index acd8e051c..a00367949 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikácia nie je nainštalovaná."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Stiahnutá aplikácia je v núdzovom režime zakázaná"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Miniaplikácie"</string>
<string name="widget_adder" msgid="3201040140710381657">"Miniaplikácie"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Zobraziť pamäť"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"zápis nastavení a odkazov plochy"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Povoľuje aplikácii zmeniť nastavenia a odkazy na ploche."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problém s načítaním miniaplikácií"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavenie"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Toto je systémová aplikácia a nedá sa odinštalovať."</string>
<string name="dream_name" msgid="1530253749244328964">"Raketomet"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Nepomenovaný priečinok"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ZAČAŤ S PREDVOLENÝM ROZLOŽENÍM"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Usporiadajte svoj priestor"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Ak chcete spravovať tapetu, miniaplikácie a nastavenia, dotknite sa pozadia a podržte."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Pozadia, miniaplikácie a nastavenia"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Ak si chcete pozadie prispôsobiť, klepnite naň a podržte ho"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ROZUMIEM"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Tu je priečinok"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Ak chcete vytvoriť takýto priečinok, dotknite sa príslušnej aplikácie a podržte ju. Potom ju presuňte na druhú aplikáciu."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Miniaplikácie"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Tapety"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Nastavenia"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Čaká sa"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Sťahovanie"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Inštalácia"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Neznáme"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Nebolo obnovené"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Odstrániť všetky"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrániť"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Vyhľadať"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Táto aplikácia nie je nainštalovaná"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikácia, ktorú zastupuje táto ikona, nie je nainštalovaná. Ikonu môžete odstrániť alebo vyhľadajte aplikáciu a ručne ju nainštalujte."</string>
</resources>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index afbd6202e..9c5bebd15 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Osnovne aplikacije sistema Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Aplikacija ni nameščena."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Prenesena aplikacija je onemogočena v Varnem načinu"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Pripomočki"</string>
<string name="widget_adder" msgid="3201040140710381657">"Pripomočki"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Pokaži pomnilnik"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"zapis nastavitev in bližnjic na začetnem zaslonu"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Aplikaciji dovoli spreminjanje nastavitev in bližnjic na začetnem zaslonu."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Težava pri nalaganju pripomočka"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Nastavitev"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"To je sistemska aplikacija in je ni mogoče odstraniti."</string>
<string name="dream_name" msgid="1530253749244328964">"Raketno izstrelišče"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Neimenovana mapa"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"SVEŽ ZAČETEK"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organizirajte svoj prostor"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Če želite upravljati ozadje, pripomočke in nastavitve, se dotaknite ozadja in ga pridržite."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Ozadja, pripomočki in nastavitve"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Za prilagajanje se dotaknite ozadja in ga pridržite"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"V REDU"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"To je mapa"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Če želite ustvariti mapo, podobno tej, se dotaknite aplikacije in jo pridržite, nato pa jo premaknite nad drugo."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"V redu"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Pripomočki"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Ozadja"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Nastavitve"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Čakanje"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Prenašanje"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Nameščanje"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Neznano"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ni obnovljen"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Odstrani vse"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Odstrani"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Iskanje"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ta aplikacija ni nameščena."</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Aplikacija za to ikono ni nameščena. Lahko jo odstranite ali poiščete aplikacijo in to namestite ročno."</string>
</resources>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index bf71b2821..421f8d3ac 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Основне Android апликације"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Апликација није инсталирана."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Преузета апликација је онемогућена у Безбедном режиму"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Виџети"</string>
<string name="widget_adder" msgid="3201040140710381657">"Виџети"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Прикажи меморију"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"уписивање подешавања и пречица на почетном екрану"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Дозвољава апликацији да мења подешавања и пречице на почетном екрану."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Проблем при учитавању виџета"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Подешавање"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Ово је системска апликација и не може да се деинсталира."</string>
<string name="dream_name" msgid="1530253749244328964">"Лансер ракета"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Неименовани директоријум"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ПОЧНИТЕ ИСПОЧЕТКА"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Организујте простор"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Додирните позадину и задржите да бисте управљали позадином, виџетима и подешавањима."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Позадине, виџети и подешавања"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Додирните и задржите позадину да бисте прилагодили"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ВАЖИ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Ево једног директоријума"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Да бисте направили директоријум попут овога, додирните и задржите апликацију, па је превуците преко друге."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Потврди"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Виџети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Позадине"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Подешавања"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Чека се"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Преузима се"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Инсталира се"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Непознато"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Није враћено"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Уклони све"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Уклони"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Претражи"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ова апликација није инсталирана"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Апликација за ову икону није инсталирана. Можете да је уклоните или да потражите апликацију и инсталирате је ручно."</string>
</resources>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index 5825eecbd..e149c9efa 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Appen är inte installerad."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Den hämtade appen inaktiverades i säkert läge"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widgetar"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widgetar"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Visa Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"skriva inställningar och genvägar för startsidan"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Tillåter att appen ändrar inställningar och genvägar på startsidan."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Det gick inte att läsa in widgeten"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Konfiguration"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Det här är en systemapp som inte kan avinstalleras."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Namnlös mapp"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"BÖRJA OM"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Organisera ditt utrymme"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Tryck länge på bakgrunden om du vill hantera bakgrundsbilder, widgetar och inställningar."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Bakgrunder, widgetar och inställningar"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Tryck länge på bakgrunden om du vill anpassa den"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Det här är en mapp"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Skapa en till mapp av det här slaget genom att trycka och hålla ned en app och sedan dra den ovanpå en annan."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widgetar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Bakgrunder"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Inställningar"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Väntar"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Hämtas"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Installerar"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Okänt"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Inte återställt"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Ta bort alla"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Ta bort"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Sök"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Appen är inte installerad"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Appen för den här ikonen har inte installerats. Du kan ta bort den eller söka efter appen och installera den manuellt."</string>
</resources>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index d2a282a47..07d091393 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Programu Msingi za Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Programu haijasakinishwa."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Programu iliyopakuliwa imezimwa katika Hali Salama"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Wijeti"</string>
<string name="widget_adder" msgid="3201040140710381657">"Wijeti"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Onyesha Kumbukumbu"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"andika mipangilio ya skrini ya Mwanzo na njia za mkato"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Huruhusu programu kubadilisha mipangilio na njia za mkato katika skrini ya Mwanzo."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Tatizo la kupakia wijeti"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Sanidi"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Hii ni programu ya mfumo na haiwezi kuondolewa."</string>
<string name="dream_name" msgid="1530253749244328964">"Kizinduzi cha Roketi"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Folda isiyo na jina"</string>
@@ -97,6 +99,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ANZA UPYA"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Panga nafasi yako"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Gusa na ushikilie mandharinyuma ili udhibiti mandhari, wijeti, na mipangilio."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Mandhari, wijeti, na mipangilio"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Gusa na ushikilie mandhari ili uweke mapendeleo"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NIMEELEWA"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Hii ni folda"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Ili kuunda kama hii, gusa na ushikilie programu, kisha ipitishe juu ya nyingine."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"SAWA"</string>
@@ -109,4 +114,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Wijeti"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mandhari"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Mipangilio"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Inasubiri"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Inapakua"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Inasakinisha"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Yasiyojulikana"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Haijarejeshwa"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Ondoa Zote"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Ondoa"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Tafuta"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Programu hii haijasakinishwa"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Programu ya ikoni hii haijasakinishwa. Unaweza kuiondoa, au utafute programu na uisakinishe wewe mwenyewe."</string>
</resources>
diff --git a/res/values-sw600dp/config.xml b/res/values-sw600dp/config.xml
index 2ec2f146c..15d5725d1 100644
--- a/res/values-sw600dp/config.xml
+++ b/res/values-sw600dp/config.xml
@@ -2,9 +2,6 @@
<bool name="is_tablet">true</bool>
<bool name="allow_rotation">true</bool>
- <!-- Whether or not to use custom clings if a custom workspace layout is passed in -->
- <bool name="config_useCustomClings">true</bool>
-
<!-- DragController -->
<integer name="config_flingToDeleteMinVelocity">-1000</integer>
diff --git a/res/values-sw600dp/dimens.xml b/res/values-sw600dp/dimens.xml
index 8d6c7f4e3..28679be2e 100644
--- a/res/values-sw600dp/dimens.xml
+++ b/res/values-sw600dp/dimens.xml
@@ -23,4 +23,13 @@
<dimen name="app_widget_preview_label_margin_top">8dp</dimen>
<dimen name="app_widget_preview_label_margin_left">@dimen/app_widget_preview_padding_left</dimen>
<dimen name="app_widget_preview_label_margin_right">@dimen/app_widget_preview_padding_right</dimen>
+
+<!-- Cling -->
+ <dimen name="cling_migration_logo_height">400dp</dimen>
+ <dimen name="cling_migration_logo_width">274dp</dimen>
+ <dimen name="cling_migration_bg_size">600dp</dimen>
+ <dimen name="cling_migration_bg_shift">-300dp</dimen>
+ <dimen name="cling_migration_content_margin">64dp</dimen>
+ <dimen name="cling_migration_content_width">280dp</dimen>
+
</resources>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index 433a5d40d..ca13db05b 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -18,15 +18,8 @@
<!-- AppsCustomize -->
<integer name="apps_customize_widget_cell_count_x">4</integer>
<integer name="apps_customize_widget_cell_count_y">2</integer>
- <integer name="apps_customize_cling_focused_x">4</integer>
- <integer name="apps_customize_cling_focused_y">2</integer>
<!-- the area at the edge of the screen that makes the workspace go left
or right while you're dragging. -->
<dimen name="scroll_zone">100dip</dimen>
-
-<!-- Cling -->
- <!-- The offset for the text in the cling -->
- <dimen name="cling_text_block_offset_x">140dp</dimen>
- <dimen name="cling_text_block_offset_y">80dp</dimen>
</resources>
diff --git a/res/values-sw720dp-port/dimens.xml b/res/values-sw720dp-port/dimens.xml
index 9fe312bfe..6f594d518 100644
--- a/res/values-sw720dp-port/dimens.xml
+++ b/res/values-sw720dp-port/dimens.xml
@@ -19,12 +19,4 @@
<!-- the area at the edge of the screen that makes the workspace go left
or right while you're dragging. -->
<dimen name="scroll_zone">40dp</dimen>
-
- <integer name="apps_customize_cling_focused_x">2</integer>
- <integer name="apps_customize_cling_focused_y">2</integer>
-
-<!-- Cling -->
- <!-- The offset for the text in the cling -->
- <dimen name="cling_text_block_offset_x">80dp</dimen>
- <dimen name="cling_text_block_offset_y">160dp</dimen>
</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 9ae155b3c..8be996474 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -25,4 +25,9 @@
the drag view should be offset from the position of the original view. -->
<dimen name="dragViewOffsetX">0dp</dimen>
<dimen name="dragViewOffsetY">0dp</dimen>
+
+<!-- Cling -->
+ <dimen name="cling_migration_content_margin">96dp</dimen>
+ <dimen name="cling_migration_content_width">320dp</dimen>
+
</resources>
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
new file mode 100644
index 000000000..f3aef1cd7
--- /dev/null
+++ b/res/values-ta-rIN/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"லாஞ்சர்3"</string>
+ <string name="home" msgid="7658288663002113681">"முகப்பு"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android முக்கியப் பயன்பாடுகள்"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"பயன்பாடு நிறுவப்படவில்லை."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"இறக்கிய பயன்பாடு பாதுகாப்பு முறையில் முடக்கப்பட்டது"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"விட்ஜெட்கள்"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"விட்ஜெட்கள்"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"நினைவகத்தைக் காட்டு"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"விட்ஜெட்டைத் தேர்வுசெய்ய தொட்டுப் &amp; பிடிக்கவும்."</string>
+ <string name="market" msgid="2619650989819296998">"ஷாப்"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"உருப்படியை இந்த முகப்புத் திரையில் விட முடியவில்லை."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"உருவாக்குவதற்கு விட்ஜெட்டைத் தேர்வுசெய்யவும்"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"கோப்புறையின் பெயர்"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"கோப்புறைக்கு மறுபெயரிடு"</string>
+ <string name="rename_action" msgid="5559600076028658757">"சரி"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"ரத்துசெய்"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"முகப்புத் திரையில் சேர்"</string>
+ <string name="group_applications" msgid="3797214114206693605">"பயன்பாடுகள்"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"குறுக்குவழிகள்"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"விட்ஜெட்கள்"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"உங்கள் முகப்புத் திரைகளில் வேறு இடம் இல்லை."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"முகப்புத் திரையில் இடமில்லை."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"பிடித்தவை ட்ரேயில் இடமில்லை"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"பிடித்தவை ட்ரேவிற்கு விட்ஜெட் மிகவும் பெரியது"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" குறுக்குவழி உருவாக்கப்பட்டது."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" குறுக்குவழி அகற்றப்பட்டது."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" குறுக்குவழி ஏற்கனவே உள்ளது."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"குறுக்குவழியைத் தேர்வுசெய்யவும்"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"பயன்பாட்டைத் தேர்வுசெய்யவும்"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"பயன்பாடுகள்"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"முகப்பு"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"அகற்று"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"நிறுவல் நீக்கு"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"அகற்று"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"நிறுவல் நீக்கு"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"பயன்பாட்டுத் தகவல்"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"தேடு"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"குரல் தேடல்"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"பயன்பாடுகள்"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"அகற்று"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"புதுப்பிப்பை நிறுவல் நீக்கு"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"பயன்பாட்டை நிறுவல் நீக்கு"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"பயன்பாட்டின் விவரங்கள்"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 பயன்பாடு தேர்ந்தெடுக்கப்பட்டது"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 விட்ஜெட் தேர்ந்தெடுக்கப்பட்டது"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 கோப்புறை தேர்ந்தெடுக்கப்பட்டது"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 குறுக்குவழி தேர்ந்தெடுக்கப்பட்டது"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"குறுக்குவழிகளை நிறுவுதல்"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளைச் சேர்க்கப் பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"குறுக்குவழிகளை நிறுவல் நீக்குதல்"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"பயனரின் அனுமதி இல்லாமல் குறுக்குவழிகளை அகற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"முகப்பின் அமைப்புகள் மற்றும் குறுக்குவழிகளைப் படித்தல்"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"முகப்பில் உள்ள அமைப்புகள் மற்றும் குறுக்குவழிகளைப் படிக்க பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"முகப்பின் அமைப்புகள் மற்றும் குறுக்குவழிகளை எழுதுதல்"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"முகப்பில் உள்ள அமைப்புகள் மற்றும் குறுக்குவழிகளை மாற்ற பயன்பாட்டை அனுமதிக்கிறது."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"விட்ஜெட்டை ஏற்றுவதில் சிக்கல்"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"இது அமைப்பு பயன்பாடு என்பதால் நிறுவல் நீக்கம் செய்ய முடியாது."</string>
+ <string name="dream_name" msgid="1530253749244328964">"ராக்கெட் லாஞ்சர்"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"பெயரிடப்படாத கோப்புறை"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"முகப்புத் திரை %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"பக்கம் %1$d / %2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"முகப்புத் திரை %1$d of %2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"பயன்பாடுகளின் பக்கம் %1$d / %2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"விட்ஜெட்களின் பக்கம் %1$d / %2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"வரவேற்கிறோம்"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"உங்களுக்கேற்ற முறையில் உருவாக்கவும்."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"பயன்பாடுகள் மற்றும் கோப்புறைகளுக்காகக் கூடுதல் திரைகளை உருவாக்கவும்"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"பயன்பாட்டின் ஐகான்களை நகலெடுக்கவும்"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"பழைய முகப்புத் திரைகளிலிருந்து ஐகான்களையும் கோப்புறைகளையும் இறக்குமதி செய்யவா?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"ஐகான்களை நகலெடு"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"புதிதாகத் தொடங்கு"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"இடத்தை ஒழுங்கமைக்கவும்"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"வால்பேப்பர், விட்ஜெட்கள் மற்றும் அமைப்புகளை நிர்வகிப்பதற்கு பின்புலத்தைத் தொட்டுப் &amp; பிடிக்கவும்."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"இதோ கோப்புறை"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"இதுபோன்ற ஒன்றை உருவாக்கப் பயன்பாட்டைத் தொட்டுப் &amp; பிடிக்கவும், பிறகு அதை வேறொன்றிற்கு நகர்த்தவும்."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"சரி"</string>
+ <string name="folder_opened" msgid="94695026776264709">"திறக்கப்பட்டக் கோப்புறை, <xliff:g id="WIDTH">%1$d</xliff:g> x <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"கோப்புறையை மூட, தொடவும்"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"மறுபெயரிட்டதைச் சேமிக்க, தொடவும்"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"கோப்புறை மூடப்பட்டது"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"கோப்புறை <xliff:g id="NAME">%1$s</xliff:g> என மறுபெயரிடப்பட்டது"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"கோப்புறை: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"விட்ஜெட்கள்"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"வால்பேப்பர்கள்"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"அமைப்புகள்"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"காத்திருக்கிறது"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"பதிவிறக்குகிறது"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"நிறுவுகிறது"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"தெரியாதது"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"மீட்டெடுக்க முடியாது"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"அனைத்தையும் அகற்று"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"அகற்று"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"தேடு"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"பயன்பாடு நிறுவப்படவில்லை"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ஐகானுக்கான பயன்பாடு நிறுவப்படவில்லை. இதை அகற்றலாம் அல்லது பயன்பாட்டைத் தேடி கைமுறையாக நிறுவலாம்."</string>
+</resources>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
new file mode 100644
index 000000000..b48d6b80d
--- /dev/null
+++ b/res/values-te-rIN/strings.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"లాంచర్3"</string>
+ <string name="home" msgid="7658288663002113681">"హోమ్"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Android ప్రధాన అనువర్తనాలు"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"సురక్షిత మోడ్‌లో డౌన్‌లోడ్ చేసిన అనువర్తనం నిలిపివేయబడింది"</string>
+ <string name="widgets_tab_label" msgid="2921133187116603919">"విడ్జెట్‌లు"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"విడ్జెట్‌లు"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"మెమరీ చూపు"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"విడ్జెట్‌ను ఎంచుకోవడానికి తాకి &amp; నొక్కి పెట్టండి."</string>
+ <string name="market" msgid="2619650989819296998">"షాపింగ్ చేయి"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"ఈ హోమ్ స్క్రీన్‌లో అంశాన్ని వదలడం సాధ్యపడలేదు."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"సృష్టించాల్సిన విడ్జెట్ ఎంచుకోండి"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"ఫోల్డర్ పేరు"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"ఫోల్డర్‌ పేరు మార్చండి"</string>
+ <string name="rename_action" msgid="5559600076028658757">"సరే"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"రద్దు చేయి"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"హోమ్ స్క్రీన్‌కు జోడించు"</string>
+ <string name="group_applications" msgid="3797214114206693605">"అనువర్తనాలు"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"సత్వరమార్గాలు"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"విడ్జెట్‌లు"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"మీ హోమ్ స్క్రీన్‌ల్లో ఖాళీ లేదు."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"ఇష్టమైనవి ట్రేలో ఖాళీ లేదు"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"ఇష్టమైనవి ట్రే కోసం ఈ విడ్జెట్ చాలా పెద్దదిగా ఉంది"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"సత్వరమార్గం \"<xliff:g id="NAME">%s</xliff:g>\" సృష్టించబడింది."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"సత్వరమార్గం \"<xliff:g id="NAME">%s</xliff:g>\" తీసివేయబడింది."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"సత్వరమార్గం \"<xliff:g id="NAME">%s</xliff:g>\" ఇప్పటికే ఉంది."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"సత్వరమార్గాన్ని ఎంచుకోండి"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"అనువర్తనాన్ని ఎంచుకోండి"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"అనువర్తనాలు"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"హోమ్"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"తీసివేయి"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"అన్ఇన్‌స్టాల్ చేయి"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"తీసివేయి"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"అన్ఇన్‌స్టాల్ చేయి"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"అనువర్తన సమాచారం"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"శోధించు"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"వాయిస్ శోధన"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"అనువర్తనాలు"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"తీసివేయి"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"నవీకరణను అన్‌ఇన్‌స్టాల్ చేయి"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"అనువర్తనాన్ని అన్‌ఇన్‌స్టాల్ చేయి"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"అనువర్తన వివరాలు"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 అనువర్తనం ఎంచుకోబడింది"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 విడ్జెట్ ఎంచుకోబడింది"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 ఫోల్డర్ ఎంచుకోబడింది"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 సత్వరమార్గం ఎంచుకోబడింది"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"సత్వరమార్గాలను ఇన్‌స్టాల్ చేయడం"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను జోడించడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"సత్వరమార్గాలను అన్ఇన్‌స్టాల్ చేయడం"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"వినియోగదారు ప్రమేయం లేకుండా సత్వరమార్గాలను తీసివేయడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడం"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను చదవడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"హోమ్ సెట్టింగ్‌లు మరియు సత్వరమార్గాలను వ్రాయడం"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"హోమ్‌లో సెట్టింగ్‌లు మరియు సత్వరమార్గాలను మార్చడానికి అనువర్తనాన్ని అనుమతిస్తుంది."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"విడ్జెట్‌ను లోడ్ చేయడంలో సమస్య"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"ఇది సిస్టమ్ అనువర్తనం మరియు దీన్ని అన్‌ఇన్‌స్టాల్ చేయడం సాధ్యపడదు."</string>
+ <string name="dream_name" msgid="1530253749244328964">"రాకెట్ లాంచర్"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"పేరు లేని ఫోల్డర్"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"హోమ్ స్క్రీన్ %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$dలో %1$dవ పేజీ"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"%2$dలో %1$dవ హోమ్ స్క్రీన్"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"%2$dలో %1$dవ అనువర్తనాల పేజీ"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"%2$dలో %1$dవ విడ్జెట్‌‌ల పేజీ"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"స్వాగతం"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"మీ స్వంత స్థలంగా భావించండి."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"అనువర్తనాలు మరియు ఫోల్డర్‌ల కోసం మరిన్ని స్క్రీన్‌లు సృష్టి."</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"మీ అనువర్తన చిహ్నాలను కాపీ చేయండి"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"మీ పాత హోమ్ స్క్రీన్‌ల నుండి చిహ్నాలు మరియు ఫోల్డర్‌లను దిగుమతి చేయాలా?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"చిహ్నాలను కాపీ చేయి"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"తాజాగా ప్రారంభించు"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"మీ స్థలాన్ని నిర్వహించండి"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"వాల్‌., విడ్జె., సెట్టి. నిర్వ. నేపథ్యం తాకి &amp; నొక్కి పెట్టండి."</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"ఇక్కడ ఫోల్డర్ ఉంది"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"ఇలాంటిది సృష్టించడానికి అనువర్తనాన్ని తాకి &amp; నొక్కి పెట్టండి, ఆపై మరోదాని పైన ఉంచండి."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"సరే"</string>
+ <string name="folder_opened" msgid="94695026776264709">"ఫోల్డర్ తెరవబడింది, <xliff:g id="WIDTH">%1$d</xliff:g> X <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"ఫోల్డర్‌ను మూసివేయడానికి తాకండి"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"పేరు మార్పును సేవ్ చేయడానికి తాకండి"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"ఫోల్డర్ మూసివేయబడింది"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"ఫోల్డర్ పేరు <xliff:g id="NAME">%1$s</xliff:g>గా మార్చబడింది"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"ఫోల్డర్: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"విడ్జెట్‌లు"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"వాల్‌పేపర్‌లు"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"సెట్టింగ్‌లు"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"వేచి ఉంది"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"డౌన్‌లోడ్ చేస్తోంది"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"ఇన్‌స్టాల్ చేస్తోంది"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"తెలియదు"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"పునరుద్ధరించబడలేదు"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"అన్నీ తీసివేయి"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"తీసివేయి"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"శోధించు"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ఈ అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ఈ చిహ్నం యొక్క అనువర్తనం ఇన్‌స్టాల్ చేయబడలేదు. మీరు దీన్ని తీసివేయవచ్చు లేదా ఆ అనువర్తనం కోసం శోధించి దాన్ని మాన్యువల్‌గా ఇన్‌స్టాల్ చేయవచ్చు."</string>
+</resources>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index beaa0ae81..beed89844 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"แอปหลักของ Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"ไม่ได้ติดตั้งแอป"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"แอปที่ดาวน์โหลดถูกปิดในโหมดปลอดภัย"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"วิดเจ็ต"</string>
<string name="widget_adder" msgid="3201040140710381657">"วิดเจ็ต"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"แสดง Mem"</string>
@@ -51,15 +52,15 @@
<string name="title_select_application" msgid="3280812711670683644">"เลือกแอป"</string>
<string name="all_apps_button_label" msgid="9110807029020582876">"แอป"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"หน้าแรก"</string>
- <string name="delete_zone_label_workspace" msgid="4009607676751398685">"นำออก"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"ลบ"</string>
<string name="delete_zone_label_all_apps" msgid="8083826390278958980">"ถอนการติดตั้ง"</string>
- <string name="delete_target_label" msgid="1822697352535677073">"นำออก"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"ลบ"</string>
<string name="delete_target_uninstall_label" msgid="5100785476250872595">"ถอนการติดตั้ง"</string>
<string name="info_target_label" msgid="8053346143994679532">"ข้อมูลแอป"</string>
<string name="accessibility_search_button" msgid="1628520399424565142">"ค้นหา"</string>
<string name="accessibility_voice_search_button" msgid="4637324840434406584">"ค้นหาด้วยเสียง"</string>
<string name="accessibility_all_apps_button" msgid="2603132375383800483">"แอป"</string>
- <string name="accessibility_delete_button" msgid="6466114477993744621">"นำออก"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"ลบ"</string>
<string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"ถอนการติดตั้งการอัปเดต"</string>
<string name="cab_menu_delete_app" msgid="7435191475867183689">"ถอนการติดตั้งแอป"</string>
<string name="cab_menu_app_info" msgid="8593722221450362342">"รายละเอียดแอป"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"เขียนการตั้งค่าและทางลัดหน้าแรกแล้ว"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"อนุญาตให้แอปเปลี่ยนการตั้งค่าและทางลัดในหน้าแรก"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"มีปัญหาขณะโหลดวิดเจ็ต"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"ตั้งค่า"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"นี่เป็นแอประบบและไม่สามารถถอนการติดตั้งได้"</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"โฟลเดอร์ที่ไม่มีชื่อ"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"เริ่มต้นใหม่"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"จัดระเบียบพื้นที่ของคุณ"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"แตะพื้นหลังค้างไว้เพื่อจัดการวอลเปเปอร์ วิดเจ็ต และการตั้งค่า"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"วอลเปเปอร์ วิดเจ็ต และการตั้งค่า"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"แตะพื้นหลังค้างไว้เพื่อกำหนดค่า"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"รับทราบ"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"นี่คือโฟลเดอร์"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"หากต้องการสร้างโฟลเดอร์ลักษณะนี้ แตะแอปค้างไว้ แล้วย้ายไปทับอีกแอปหนึ่ง"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"ตกลง"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"วิดเจ็ต"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"วอลเปเปอร์"</string>
<string name="settings_button_text" msgid="8119458837558863227">"การตั้งค่า"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"กำลังรอ"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"กำลังดาวน์โหลด"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"กำลังติดตั้ง"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"ไม่รู้จัก"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"ไม่ได้คืนค่า"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"ลบทั้งหมด"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ลบ"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"ค้นหา"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"ไม่ได้ติดตั้งแอปนี้"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"ยังไม่ได้ติดตั้งแอปสำหรับไอคอนนี้ คุณสามารถนำไอคอนออก หรือค้นหาแอปดังกล่าวแล้วติดตั้งด้วยตนเอง"</string>
</resources>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 5be9d85a8..7c6acd2ab 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android Core Apps"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Hindi naka-install ang app."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Naka-disable ang na-download na app sa Safe mode"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Mga Widget"</string>
<string name="widget_adder" msgid="3201040140710381657">"Mga Widget"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Ipakita ang Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"magsulat ng mga setting at shortcut ng Home"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Pinapayagan ang app na baguhin ang mga setting at shortcut sa Home."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Problema sa pag-load ng widget"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"I-setup"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Isa itong app ng system at hindi maaaring i-uninstall."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Walang Pangalang Folder"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"MAGSIMULA NANG BAGO"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Ayusin ang iyong espasyo"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Pindutin nang matagal ang background upang pamahalaan ang wallpaper, mga widget at setting"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Mga wallpaper, widget at setting"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Pindutin nang matagal ang background upang i-customize"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NAKUHA KO"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Narito ang isang folder"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Upang gumawa ng katulad nito, pindutin nang matagal ang isang app, pagkatapos ay ilipat ito sa isa pang folder."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Mga Widget"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Mga Wallpaper"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Mga Setting"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Naghihintay"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Nagda-download"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Nag-i-install"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Hindi kilala"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Hindi naibalik"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Alisin Lahat"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Alisin"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Maghanap"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Hindi naka-install ang app na ito"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Hindi naka-install ang app para sa icon na ito. Maaari mo itong alisin, o maaari mong hanapin ang app at i-install ito nang manu-mano."</string>
</resources>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 01f79a2e5..6c0bb69a7 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -24,11 +24,12 @@
<string name="uid_name" msgid="7820867637514617527">"Android Çekirdek Uygulamaları"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Uygulama yüklü değil."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"İndirilen uygulama Güvenli modda devre dışı bırakıldı"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Widget\'lar"</string>
<string name="widget_adder" msgid="3201040140710381657">"Widget\'lar"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Belleği Göster"</string>
<string name="long_press_widget_to_add" msgid="7699152356777458215">"Widget seçmek için dokunun ve basılı tutun."</string>
- <string name="market" msgid="2619650989819296998">"Alışveriş yap"</string>
+ <string name="market" msgid="2619650989819296998">"Mağaza"</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="external_drop_widget_error" msgid="3165821058322217155">"Öğe bu Ana ekrana bırakılamadı."</string>
<string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Oluşturmak için widget seçin"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"Ana ekran ayarlarını ve kısayollarını yaz"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Uygulamaya, Ana ekrandaki ayarları ve kısayolları değiştirme izni verir."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Widget yüklenirken sorun oluştu"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Kurulum"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu bir sistem uygulamasıdır ve yüklemesi kaldırılamaz."</string>
<string name="dream_name" msgid="1530253749244328964">"Roket Fırlatıcı"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Adsız Klasör"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"VARSAYILANI KULLAN"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Alanınızı düzenleyin"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Duvar kağıdını, widget\'ları ve ayarları yönetmek için arka plana uzun basın."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Duvar kağıtları, widget\'lar ve ayarlar"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Özelleştirmek için arka plana dokunun ve basılı tutun"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"TAMAM"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"İşte bir klasör"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Buna benzer bir klasör oluşturmak için uygulamaya uzun basın ve sonra uygulamayı başka bir uygulamanın üzerine taşıyın."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"Tamam"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Widget\'lar"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Duvar Kağıtları"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Ayarlar"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Bekliyor"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"İndiriliyor"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Yükleniyor"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Bilinmiyor"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Geri yüklenmedi"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Tümünü Kaldır"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Kaldır"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Ara"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Bu uygulama yüklü değil"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Bu simgenin uygulaması yüklü değil. Uygulamayı kaldırabilir veya arayıp manuel olarak yükleyebilirsiniz."</string>
</resources>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index f266eeab5..e5b5cd8d2 100644
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -23,7 +23,8 @@
<string name="home" msgid="7658288663002113681">"Головний екран"</string>
<string name="uid_name" msgid="7820867637514617527">"Базові програми Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
- <string name="activity_not_found" msgid="8071924732094499514">"Програму не встановлено."</string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Додаток видалено."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Завантажений додаток вимкнено в безпечному режимі"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Віджети"</string>
<string name="widget_adder" msgid="3201040140710381657">"Віджети"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Показати пам’ять"</string>
@@ -37,7 +38,7 @@
<string name="rename_action" msgid="5559600076028658757">"OК"</string>
<string name="cancel_action" msgid="7009134900002915310">"Скасувати"</string>
<string name="menu_item_add_item" msgid="1264911265836810421">"Додати на головний екран"</string>
- <string name="group_applications" msgid="3797214114206693605">"Програми"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Додатки"</string>
<string name="group_shortcuts" msgid="6012256992764410535">"Ярлики"</string>
<string name="group_widgets" msgid="1569030723286851002">"Віджети"</string>
<string name="completely_out_of_space" msgid="6106288382070760318">"На головних екранах більше немає місця."</string>
@@ -49,7 +50,7 @@
<string name="shortcut_duplicate" msgid="9167217446062498127">"Ярлик \"<xliff:g id="NAME">%s</xliff:g>\" уже існує."</string>
<string name="title_select_shortcut" msgid="6680642571148153868">"Вибрати ярлик"</string>
<string name="title_select_application" msgid="3280812711670683644">"Вибрати програму"</string>
- <string name="all_apps_button_label" msgid="9110807029020582876">"Програми"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Додатки"</string>
<string name="all_apps_home_button_label" msgid="252062713717058851">"Головний екран"</string>
<string name="delete_zone_label_workspace" msgid="4009607676751398685">"Вилучити"</string>
<string name="delete_zone_label_all_apps" msgid="8083826390278958980">"Видалити"</string>
@@ -58,7 +59,7 @@
<string name="info_target_label" msgid="8053346143994679532">"Про програму"</string>
<string name="accessibility_search_button" msgid="1628520399424565142">"Пошук"</string>
<string name="accessibility_voice_search_button" msgid="4637324840434406584">"Голосовий пошук"</string>
- <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Програми"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Додатки"</string>
<string name="accessibility_delete_button" msgid="6466114477993744621">"Вилучити"</string>
<string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Видалити оновлення"</string>
<string name="cab_menu_delete_app" msgid="7435191475867183689">"Видалити програму"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"записувати налаштування та ярлики головного екрана"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Дозволяє програмі змінювати налаштування та ярлики на головному екрані."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Проблема із завантаженням віджета"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Налаштування"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Це системна програма, її неможливо видалити."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Папка без назви"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"ПАНЕЛЬ ЗАПУСКУ ЗА УМОВЧАННЯМ"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Організуйте робочий простір"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Натисніть і утримуйте фон, щоб керувати фоновим малюнком, віджетами та налаштуваннями."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Фонові малюнки, віджети й налаштування"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Натисніть і втримуйте фон, щоб налаштувати робочу область"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"ЗРОЗУМІЛО"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Це папка"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Щоб створити папку, натисніть і утримуйте програму, а потім перетягніть її на іншу."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OК"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Віджети"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Фонові малюнки"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Налаштування"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Очікування"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Завантаження"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Встановлення"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Невідомо"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Не відновлено"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Видалити всі"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Видалити"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Шукати"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Цей додаток не встановлено"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Додаток для цього значка не встановлено. Можна видалити значок або знайти додаток і встановити його вручну."</string>
</resources>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
new file mode 100644
index 000000000..33b502e9b
--- /dev/null
+++ b/res/values-ur-rPK/strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Launcher3"</string>
+ <string name="home" msgid="7658288663002113681">"ہوم"</string>
+ <string name="uid_name" msgid="7820867637514617527">"‏Android کور ایپس"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"ایپ انسٹال نہیں ہے۔"</string>
+ <!-- no translation found for safemode_shortcut_error (9160126848219158407) -->
+ <skip />
+ <string name="widgets_tab_label" msgid="2921133187116603919">"ویجیٹس"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"ویجیٹس"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"‏Mem دکھائیں"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"کوئی ویجیٹ منتخب کرنے کیلئے ٹچ کریں اور پکڑے رہیں۔"</string>
+ <string name="market" msgid="2619650989819296998">"خریداری کریں"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"آئٹم کو اس ہوم اسکرین پر ڈراپ نہیں کیا جا سکا۔"</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"بنانے کیلئے ویجیٹ منتخب کریں"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"فولڈر کا نام"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"فولڈر کا نام تبدیل کریں"</string>
+ <string name="rename_action" msgid="5559600076028658757">"ٹھیک ہے"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"منسوخ کریں"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"ہوم اسکرین میں شامل کریں"</string>
+ <string name="group_applications" msgid="3797214114206693605">"ایپس"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"شارٹ کٹس"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"ویجیٹس"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"آپ کی ہوم اسکرینوں پر مزید کوئی گنجائش نہیں ہے۔"</string>
+ <string name="out_of_space" msgid="4691004494942118364">"اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔"</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"یہ ویجیٹ پسندیدہ ٹرے کیلئے کافی بڑا ہے"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"شارٹ کٹ \"<xliff:g id="NAME">%s</xliff:g>\" بنایا گیا۔"</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"شارٹ کٹ \"<xliff:g id="NAME">%s</xliff:g>\" ہٹا دیا گیا۔"</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"شارٹ کٹ \"<xliff:g id="NAME">%s</xliff:g>\" پہلے سے موجود ہے۔"</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"شارٹ کٹ منتخب کریں"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"ایپ منتخب کریں"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"ایپس"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"ہوم"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"ہٹائیں"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"اَن انسٹال کریں"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"ہٹائیں"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"اَن انسٹال کریں"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"ایپ کی معلومات"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"تلاش کریں"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"صوتی تلاش"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"ایپس"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"ہٹائیں"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"اپ ڈیٹ اَن انسٹال کریں"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"ایپ کو اَن انسٹال کریں"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"ایپ کی تفصیلات"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 ایپ منتخب کی گئی"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 ویجیٹ منتخب کیا گیا"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 فولڈر منتخب کیا گیا"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 شارٹ کٹ منتخب کیا گیا"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"شارٹ کٹس انسٹال کریں"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"کسی ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس شامل کرنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"شارٹ کٹس کو اَن انسٹال کریں"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"ایپ کو صارف کی مداخلت کے بغیر شارٹ کٹس ہٹانے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"ہوم ترتیبات اور شارٹ کٹس کو پڑھیں"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"ایپ کو ہوم میں ترتیبات اور شارٹ کٹس کو پڑھنے کی اجازت دیتا ہے۔"</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"ہوم ترتیبات اور شارٹ کٹس کو لکھیں"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"ایپ کو ہوم میں ترتیبات اور شارٹ کٹس کو تبدیل کرنے کی اجازت دیتا ہے۔"</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"ویجیٹ کو لوڈ کرنے میں مسئلہ"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"یہ ایک سسٹم ایپ ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"</string>
+ <string name="dream_name" msgid="1530253749244328964">"راکٹ لانچر"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"بلا نام فولڈر"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"‏ہوم اسکرین ‎%1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"‏صفحہ ‎%1$d از ‎%2$d"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"‏ہوم اسکرین ‎%1$d از ‎%2$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"‏ایپس کا صفحہ ‎%1$d از ‎%2$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"‏ویجیٹس کا صفحہ ‎%1$d از ‎%2$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"خوش آمدید"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"ہوم سکرین مرضی کے مطابق بنائیں۔"</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"ایپس اور فولڈرز کیلئے مزید اسکرینیں بنائیں"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"اپنے ایپ آئیکنز کو کاپی کریں"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"اپنی پرانی ہوم اسکرینوں سے آئیکنز اور فولڈرز درآمد کریں؟"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"آئیکنز کاپی کریں"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"نئے سرے سے شروع کریں"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"اپنی جگہ کو منظم کریں"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"وال پیپر، ویجیٹس اور ترتیبات کا نظم کرنے کیلئے پس منظر کو ٹچ کریں اور پکڑ کر رکھیں۔"</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"یہ ہے ایک فولڈر"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"اس طرح کا ایک بنانے کیلئے، کسی ایپ کو ٹچ کریں اور پکڑ کر رکھیں، پھر اسے کسی دوسرے میں منتقل کریں۔"</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"ٹھیک ہے"</string>
+ <string name="folder_opened" msgid="94695026776264709">"فولڈر کھولا گیا، <xliff:g id="WIDTH">%1$d</xliff:g> × <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"فولڈر بند کرنے کیلئے ٹچ کریں"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"نام کی تبدیلی محفوظ کرنے کیلئے ٹچ کریں"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"فولڈر بند ہو گیا"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"فولڈر کا نام تبدیل کر کے <xliff:g id="NAME">%1$s</xliff:g> کر دیا گیا"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"فولڈر: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"ویجیٹس"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"وال پیپرز"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"ترتیبات"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"منتظر"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"ڈاؤن لوڈ کیا جا رہا ہے"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"انسٹال کیا جا رہا ہے"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"نامعلوم"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"بحال نہیں ہوا"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"سبھی کو ہٹا دیں"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"ہٹائیں"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"تلاش کریں"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"یہ ایپ انسٹال کردہ نہیں ہے"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"اس آئیکن کیلئے ایپ انسٹال کردہ نہیں ہے۔ آپ اسے ہٹا سکتے ہیں یا ایپ کو تلاش کر سکتے اور دستی طور پر اسے انسٹال کر سکتے ہیں۔"</string>
+</resources>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
new file mode 100644
index 000000000..791f6c888
--- /dev/null
+++ b/res/values-uz-rUZ/strings.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+* Copyright (C) 2008 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="application_name" msgid="5181331383435256801">"Ishga tushirgich3"</string>
+ <string name="home" msgid="7658288663002113681">"Uy"</string>
+ <string name="uid_name" msgid="7820867637514617527">"Androidga asoslangan dasturlar"</string>
+ <string name="folder_name" msgid="7371454440695724752"></string>
+ <string name="activity_not_found" msgid="8071924732094499514">"Ilova o‘rnatilmadi."</string>
+ <!-- no translation found for safemode_shortcut_error (9160126848219158407) -->
+ <skip />
+ <string name="widgets_tab_label" msgid="2921133187116603919">"Vidjetlar"</string>
+ <string name="widget_adder" msgid="3201040140710381657">"Vidjetlar"</string>
+ <string name="toggle_weight_watcher" msgid="5645299835184636119">"Xotirani ko‘rsatish"</string>
+ <string name="long_press_widget_to_add" msgid="7699152356777458215">"Vidjetni tanlash uchun bosib turing."</string>
+ <string name="market" msgid="2619650989819296998">"Do‘kon"</string>
+ <string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
+ <string name="external_drop_widget_error" msgid="3165821058322217155">"Elementni ushbu \"Uy\" ekraniga tashlab bo‘lmadi."</string>
+ <string name="external_drop_widget_pick_title" msgid="3486317258037690630">"Yaratish uchun vidjet tanlang"</string>
+ <string name="rename_folder_label" msgid="3727762225964550653">"Jild nomi"</string>
+ <string name="rename_folder_title" msgid="3771389277707820891">"Jild nomini o‘zgartirish"</string>
+ <string name="rename_action" msgid="5559600076028658757">"OK"</string>
+ <string name="cancel_action" msgid="7009134900002915310">"Bekor qilish"</string>
+ <string name="menu_item_add_item" msgid="1264911265836810421">"Uy ekraniga qo‘shish"</string>
+ <string name="group_applications" msgid="3797214114206693605">"Ilovalar"</string>
+ <string name="group_shortcuts" msgid="6012256992764410535">"Yorliqlar"</string>
+ <string name="group_widgets" msgid="1569030723286851002">"Vidjetlar"</string>
+ <string name="completely_out_of_space" msgid="6106288382070760318">"Uy ekraningizda birorta ham xona yo‘q."</string>
+ <string name="out_of_space" msgid="4691004494942118364">"Uy ekranida bitta ham xona yo‘q."</string>
+ <string name="hotseat_out_of_space" msgid="7448809638125333693">"Ajratilganlarda birorta ham xona yo‘q"</string>
+ <string name="invalid_hotseat_item" msgid="5779907847267573691">"Ajratilganlar uchun ushbu vidjet juda katta"</string>
+ <string name="shortcut_installed" msgid="1701742129426969556">"\"<xliff:g id="NAME">%s</xliff:g>\" yorlig‘i yaratildi."</string>
+ <string name="shortcut_uninstalled" msgid="8176767991305701821">"\"<xliff:g id="NAME">%s</xliff:g>\" yorlig‘i o‘chirildi."</string>
+ <string name="shortcut_duplicate" msgid="9167217446062498127">"\"<xliff:g id="NAME">%s</xliff:g>\" yorlig‘i allaqachon mavjud."</string>
+ <string name="title_select_shortcut" msgid="6680642571148153868">"Yorliqni tanlash"</string>
+ <string name="title_select_application" msgid="3280812711670683644">"Ilovani tanlash"</string>
+ <string name="all_apps_button_label" msgid="9110807029020582876">"Ilovalar"</string>
+ <string name="all_apps_home_button_label" msgid="252062713717058851">"Uy"</string>
+ <string name="delete_zone_label_workspace" msgid="4009607676751398685">"O‘chirish"</string>
+ <string name="delete_zone_label_all_apps" msgid="8083826390278958980">"O‘chirish"</string>
+ <string name="delete_target_label" msgid="1822697352535677073">"O‘chirish"</string>
+ <string name="delete_target_uninstall_label" msgid="5100785476250872595">"O‘chirish"</string>
+ <string name="info_target_label" msgid="8053346143994679532">"Ilova ma’lumoti"</string>
+ <string name="accessibility_search_button" msgid="1628520399424565142">"Izlash"</string>
+ <string name="accessibility_voice_search_button" msgid="4637324840434406584">"Ovozli qidiruv"</string>
+ <string name="accessibility_all_apps_button" msgid="2603132375383800483">"Ilovalar"</string>
+ <string name="accessibility_delete_button" msgid="6466114477993744621">"O‘chirish"</string>
+ <string name="delete_zone_label_all_apps_system_app" msgid="449755632749610895">"Yangilashni o‘chirish"</string>
+ <string name="cab_menu_delete_app" msgid="7435191475867183689">"Ilovani o‘chirish"</string>
+ <string name="cab_menu_app_info" msgid="8593722221450362342">"Ilova ma’lumotlari"</string>
+ <string name="cab_app_selection_text" msgid="374688303047985416">"1 ta ilova tanlandi"</string>
+ <string name="cab_widget_selection_text" msgid="1833458597831541241">"1 ta vidjet tanlandi"</string>
+ <string name="cab_folder_selection_text" msgid="7999992513806132118">"1 ta jild tanlandi"</string>
+ <string name="cab_shortcut_selection_text" msgid="2103811025667946450">"1 ta yorliq tanlandi"</string>
+ <string name="permlab_install_shortcut" msgid="5632423390354674437">"yorliqlar o‘rnatish"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"Ilovalarga foydalanuvchidan so‘ramasdan yorliqlar qo‘shishga ruxsat beradi."</string>
+ <string name="permlab_uninstall_shortcut" msgid="864595034498083837">"yorliqlarni o‘chirish"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"Ilovaga foydalanuvchiga bildirmasdan yorliqlarni o‘chirish uchun ruxsat beradi."</string>
+ <string name="permlab_read_settings" msgid="1941457408239617576">"Uy sozlamalari va yorliqlarini o‘qish"</string>
+ <string name="permdesc_read_settings" msgid="5833423719057558387">"Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalarni o‘qish uchun ruxsat beradi."</string>
+ <string name="permlab_write_settings" msgid="3574213698004620587">"Uy sozlamalari va yorliqlarini yozish"</string>
+ <string name="permdesc_write_settings" msgid="5440712911516509985">"Ilovaga \"Uy\" ekranidagi yorliqlar va sozlamalrni o‘zgartirish uchun ruxsat beradi."</string>
+ <string name="gadget_error_text" msgid="6081085226050792095">"Vidjetni yuklashda muammo"</string>
+ <string name="uninstall_system_app_text" msgid="4172046090762920660">"Bu tizim ilovasi, shuning uchun o‘chirib bo‘lmaydi."</string>
+ <string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
+ <string name="folder_hint_text" msgid="6617836969016293992">"Nomsiz jild"</string>
+ <string name="workspace_description_format" msgid="2950174241104043327">"Uy ekrani %1$d"</string>
+ <string name="default_scroll_format" msgid="7475544710230993317">"%2$ddan %1$d ta sahifa"</string>
+ <string name="workspace_scroll_format" msgid="8458889198184077399">"Uy ekrani %2$ddan %1$d"</string>
+ <string name="apps_customize_apps_scroll_format" msgid="370005296147130238">"Ilovalar sahifasi %2$ddan %1$d"</string>
+ <string name="apps_customize_widgets_scroll_format" msgid="3106209519974971521">"Vidjetlar sahifasi %2$ddan %1$d"</string>
+ <string name="first_run_cling_title" msgid="2459738000155917941">"Xush kelibsiz"</string>
+ <string name="first_run_cling_description" msgid="6447072552696253358">"O‘zingizni uyingizdagidek his qiling."</string>
+ <string name="first_run_cling_custom_content_hint" msgid="6090628589029352439"></string>
+ <string name="first_run_cling_search_bar_hint" msgid="5909062802402452582"></string>
+ <string name="first_run_cling_create_screens_hint" msgid="6950729526680114157">"Ilovalar va jildlar uchun ko‘proq ekranlar yaratish"</string>
+ <string name="migration_cling_title" msgid="9181776667882933767">"Ilovangiz nishonchalaridan nusxa olish"</string>
+ <string name="migration_cling_description" msgid="2752413805582227644">"Eski \"Uy\" ekranlaringizdan jildlar va nishonchalar import qilinsinmi?"</string>
+ <string name="migration_cling_copy_apps" msgid="946331230090919440">"NISHONCHALARNI NUSXALASH"</string>
+ <string name="migration_cling_use_default" msgid="2626475813981258626">"YANGIDAN BOSHLASH"</string>
+ <string name="workspace_cling_title" msgid="5626202359865825661">"Joylaringizni boshqaring"</string>
+ <string name="workspace_cling_move_item" msgid="528201129978005352">"Fon rasmi, vidjet va sozlamalarni boshqarish uchun orqa fonga bosib turing"</string>
+ <string name="folder_cling_title" msgid="3894908818693254164">"Mana sizga jild"</string>
+ <string name="folder_cling_create_folder" msgid="6158215559475836131">"Bunga o‘xshaganini yaratish uchun bosib turing, keyin boshqasiga o‘ting."</string>
+ <string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
+ <string name="folder_opened" msgid="94695026776264709">"Jild ochildi, <xliff:g id="WIDTH">%1$d</xliff:g> ga <xliff:g id="HEIGHT">%2$d</xliff:g>"</string>
+ <string name="folder_tap_to_close" msgid="1884479294466410023">"Jildni yopish uchun bosing"</string>
+ <string name="folder_tap_to_rename" msgid="9191075570492871147">"O‘zgartirilgan nomni saqlash uchun bosing"</string>
+ <string name="folder_closed" msgid="4100806530910930934">"Jild yopildi"</string>
+ <string name="folder_renamed" msgid="1794088362165669656">"Jild nomi <xliff:g id="NAME">%1$s</xliff:g>ga o‘zgartirildi"</string>
+ <string name="folder_name_format" msgid="6629239338071103179">"Jild: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="widget_button_text" msgid="2880537293434387943">"Vidjetlar"</string>
+ <string name="wallpaper_button_text" msgid="8404103075899945851">"Orqa fon rasmlari"</string>
+ <string name="settings_button_text" msgid="8119458837558863227">"Sozlamalar"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Kutilmoqda"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Yuklab olinmoqda"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"O‘rnatilmoqda"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Noma’lum"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Qayta tiklanmadi"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Barchasini o‘chirish"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"O‘chirish"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Qidirish"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ushbu ilova o‘rnatilmagan"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ilova o‘rnatilmagan. Belgini o‘chirib tashlashingiz yoki ilovani topib, uni qo‘lda o‘rnatishingiz mumkin."</string>
+</resources>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index 9d4ed413c..489123089 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Ứng dụng lõi Android"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Ứng dụng chưa được cài đặt."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Ứng dụng đã tải xuống bị tắt ở chế độ An toàn"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Tiện ích con"</string>
<string name="widget_adder" msgid="3201040140710381657">"Tiện ích con"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Hiển thị bộ nhớ"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"ghi cài đặt và lối tắt trên Màn hình chính"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Cho phép ứng dụng thay đổi cài đặt và lối tắt trên Màn hình chính."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Sự cố khi tải tiện ích con"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Thiết lập"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Đây là ứng dụng hệ thống và không thể gỡ cài đặt."</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Thư mục chưa đặt tên"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"BẮT ĐẦU LÀM MỚI"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Sắp xếp không gian của bạn"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Chạm và giữ nền để quản lý hình nền, tiện ích con và cài đặt."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Hình nền, tiện ích và cài đặt"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Chạm và giữ nền để tùy chỉnh"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"OK"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Đây là một thư mục"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Để tạo thư mục như thế này, hãy chạm và giữ một ứng dụng, sau đó di chuyển ứng dụng đó lên trên một ứng dụng khác."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"OK"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Tiện ích con"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Hình nền"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Cài đặt"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Đang đợi"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Đang tải xuống"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Đang cài đặt"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Không xác định"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Không được khôi phục"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Xóa tất cả"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Xóa"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Tìm kiếm"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Ứng dụng này chưa được cài đặt"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Ứng dụng cho biểu tượng này chưa được cài đặt. Bạn có thể xóa ứng dụng hoặc tìm kiếm và cài đặt ứng dụng theo cách thủ công."</string>
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 3a2638e34..3365581be 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android 核心应用"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"未安装该应用。"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"安全模式下不允许使用下载的此应用"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"小部件"</string>
<string name="widget_adder" msgid="3201040140710381657">"小部件"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"显示内存空间"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"写入主屏幕设置和快捷方式"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"允许应用更改主屏幕中的设置和快捷方式。"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"加载小部件时出现问题"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"设置"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"这是系统应用,无法卸载。"</string>
<string name="dream_name" msgid="1530253749244328964">"火箭发射器"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"未命名文件夹"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"使用全新配置"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"整理您的空间"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"触摸并按住背景,即可管理壁纸、小部件和设置。"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"壁纸、小部件和设置"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"触摸并按住背景,即可进行个性化设置"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"这是一个文件夹"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"要创建一个类似的文件夹,请触摸并按住某个应用,然后将其移至另一个应用上。"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"确定"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"小部件"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"壁纸"</string>
<string name="settings_button_text" msgid="8119458837558863227">"设置"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"正在等待"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"正在下载"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"正在安装"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"未知"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"无法还原"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"全部移除"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"搜索"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"未安装此应用"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"未安装此图标对应的应用。您可以移除此图标,也可以尝试搜索相应的应用并手动安装。"</string>
</resources>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index cad95f27c..457d5be9e 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android 核心應用程式"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"尚未安裝應用程式。"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"在安全模式中無法使用「已下載的應用程式」功能"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"小工具"</string>
<string name="widget_adder" msgid="3201040140710381657">"小工具"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"顯示記憶體"</string>
@@ -68,14 +69,15 @@
<string name="cab_folder_selection_text" msgid="7999992513806132118">"已選取 1 個資料夾"</string>
<string name="cab_shortcut_selection_text" msgid="2103811025667946450">"已選取 1 個捷徑"</string>
<string name="permlab_install_shortcut" msgid="5632423390354674437">"安裝捷徑"</string>
- <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需用戶許可也可新增捷徑。"</string>
+ <string name="permdesc_install_shortcut" msgid="923466509822011139">"允許應用程式無需使用者許可也可新增捷徑。"</string>
<string name="permlab_uninstall_shortcut" msgid="864595034498083837">"解除安裝捷徑"</string>
- <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"允許應用程式無需用戶許可也可移除捷徑。"</string>
+ <string name="permdesc_uninstall_shortcut" msgid="5134129545001836849">"允許應用程式無需使用者許可也可移除捷徑。"</string>
<string name="permlab_read_settings" msgid="1941457408239617576">"讀取主畫面的設定和捷徑"</string>
<string name="permdesc_read_settings" msgid="5833423719057558387">"允許應用程式讀取主畫面中的設定和捷徑。"</string>
<string name="permlab_write_settings" msgid="3574213698004620587">"寫入主畫面的設定和捷徑"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"允許應用程式更改主畫面中的設定和捷徑。"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"載入小工具時發生問題"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"設定"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,無法將其解除安裝。"</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"重新開始"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"管理您的空間"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"輕觸並按住背景,即可管理桌布、小工具和設定。"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"桌布、小工具和設定"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"輕觸並按住背景即可自訂"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"資料夾顯示如下"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"如要建立類似的資料夾,請輕觸並按住某個應用程式,然後疊到另一個應用程式之上。"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"確定"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"等候中"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"下載中"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"安裝中"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"無法還原"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"全部移除"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"尚未安裝這個應用程式"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string>
</resources>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index 5f482430e..2d400596f 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Android 核心應用程式"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"應用程式未安裝。"</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"在安全模式中無法使用「已下載的應用程式」功能"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"小工具"</string>
<string name="widget_adder" msgid="3201040140710381657">"小工具"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"顯示記憶體"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"寫入主螢幕設定和捷徑"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"允許應用程式變更主螢幕中的設定和捷徑。"</string>
<string name="gadget_error_text" msgid="6081085226050792095">"載入小工具時發生問題"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"設定"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"這是系統應用程式,不可解除安裝。"</string>
<string name="dream_name" msgid="1530253749244328964">"Rocket Launcher"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"未命名的資料夾"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"重新開始"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"管理您的空間"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"輕觸並按住背景,即可管理桌布、小工具和設定。"</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"桌布、小工具和設定"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"輕觸並按住背景即可自訂"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"知道了"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"資料夾顯示如下"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"如要建立類似的資料夾,請輕觸並按住應用程式,然後將應用程式疊放在另一個應用程式上。"</string>
<string name="cling_dismiss" msgid="8962359497601507581">"確定"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"小工具"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"桌布"</string>
<string name="settings_button_text" msgid="8119458837558863227">"設定"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"等待中"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"下載中…"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"安裝中"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"不明"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"無法還原"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"全部移除"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"移除"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"搜尋"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"尚未安裝這個應用程式"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"您尚未安裝這個圖示代表的應用程式。您可以移除這個圖示,也可以搜尋該應用程式並手動安裝。"</string>
</resources>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index e982eb419..89cd5cc12 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -24,6 +24,7 @@
<string name="uid_name" msgid="7820867637514617527">"Izinhlelo zokusebenza ze-Android Core"</string>
<string name="folder_name" msgid="7371454440695724752"></string>
<string name="activity_not_found" msgid="8071924732094499514">"Uhlelo lokusebenza alufakiwe."</string>
+ <string name="safemode_shortcut_error" msgid="9160126848219158407">"Uhlelo lokusebenza olulandiwe lukhutshaziwe kumodi ephephile"</string>
<string name="widgets_tab_label" msgid="2921133187116603919">"Amawijethi"</string>
<string name="widget_adder" msgid="3201040140710381657">"Amawijethi"</string>
<string name="toggle_weight_watcher" msgid="5645299835184636119">"Bonisa i-Mem"</string>
@@ -76,6 +77,7 @@
<string name="permlab_write_settings" msgid="3574213698004620587">"bhala izilungiselelo zokuthi Ikhaya nezinqamuleli"</string>
<string name="permdesc_write_settings" msgid="5440712911516509985">"Ivumela uhlelo lokusebenza ukuthi lushintshe izilungiselelo nezinqamuleli Ekhaya."</string>
<string name="gadget_error_text" msgid="6081085226050792095">"Inkinga yokulayisha iwijethi"</string>
+ <string name="gadget_setup_text" msgid="8274003207686040488">"Ukumisa"</string>
<string name="uninstall_system_app_text" msgid="4172046090762920660">"Lolu uhlelo lokusebenza lwesistimu futhi alikwazi ukukhishwa."</string>
<string name="dream_name" msgid="1530253749244328964">"Isiqalisi se-Rocket"</string>
<string name="folder_hint_text" msgid="6617836969016293992">"Ifolda engenagama"</string>
@@ -95,6 +97,9 @@
<string name="migration_cling_use_default" msgid="2626475813981258626">"QALISA KABUSHA"</string>
<string name="workspace_cling_title" msgid="5626202359865825661">"Hlela isikhala sakho"</string>
<string name="workspace_cling_move_item" msgid="528201129978005352">"Thinta uphinde ubambe okungemuva ukuze uphathe isithombe sangemuva, amawijethi nezilungiselelo."</string>
+ <string name="workspace_cling_longpress_title" msgid="9173998993909018310">"Izithombe zangemuva, amawijethi, nezilungiselelo"</string>
+ <string name="workspace_cling_longpress_description" msgid="4119994475505235248">"Thinta uphinde ubambe ingemuva ukuze wenze ngokwezifiso"</string>
+ <string name="workspace_cling_longpress_dismiss" msgid="368660286867640874">"NGIYITHOLILE"</string>
<string name="folder_cling_title" msgid="3894908818693254164">"Nayi ifolda"</string>
<string name="folder_cling_create_folder" msgid="6158215559475836131">"Ukuze udale eyodwa efana nale, thinta uphinde ubambe uhlelo lokusebenza, bese ulidlulisa ngaphezulu kwelinye."</string>
<string name="cling_dismiss" msgid="8962359497601507581">"KULUNGILE"</string>
@@ -107,4 +112,14 @@
<string name="widget_button_text" msgid="2880537293434387943">"Amawijethi"</string>
<string name="wallpaper_button_text" msgid="8404103075899945851">"Izithombe zangemuva"</string>
<string name="settings_button_text" msgid="8119458837558863227">"Izilungiselelo"</string>
+ <string name="package_state_enqueued" msgid="6227252464303085641">"Ilindile"</string>
+ <string name="package_state_downloading" msgid="4088770468458724721">"Iyalanda"</string>
+ <string name="package_state_installing" msgid="7588193972189849870">"Iyafaka"</string>
+ <string name="package_state_unknown" msgid="7592128424511031410">"Akwaziwa"</string>
+ <string name="package_state_error" msgid="7672093962724223588">"Ayibuyiselwe"</string>
+ <string name="abandoned_clean_all" msgid="5256770727689657618">"Susa konke"</string>
+ <string name="abandoned_clean_this" msgid="7610119707847920412">"Susa"</string>
+ <string name="abandoned_search" msgid="891119232568284442">"Sesha"</string>
+ <string name="abandoned_promises_title" msgid="7096178467971716750">"Lolu hlelo lokusebenza alifakiwe"</string>
+ <string name="abandoned_promise_explanation" msgid="3990027586878167529">"Uhlelo lokusebenza lalesi sithonjana alufakiwe. Ungalisusa, noma sesha uhlelo lokusebenza bese uzifakela lona ngokuzenzela."</string>
</resources>
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 0006a7455..65f8f22a6 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -25,17 +25,8 @@
<attr name="sourceViewId" format="integer" />
</declare-styleable>
- <!-- Cling specific attributes. These attributes are used to customize
- the cling in XML files. -->
- <declare-styleable name="Cling">
- <!-- Used to identify how to draw the cling bg -->
- <attr name="drawIdentifier" format="string" />
- </declare-styleable>
-
- <!-- Page Indicator specific attributes. These attributes are used to customize
- the cling in XML files. -->
+ <!-- Page Indicator specific attributes. -->
<declare-styleable name="PageIndicator">
- <!-- Used to identify how to draw the cling bg -->
<attr name="windowSize" format="integer" />
</declare-styleable>
@@ -98,16 +89,16 @@
<!-- A spacing override for the icons within a page -->
<attr name="pageLayoutWidthGap" format="dimension" />
<attr name="pageLayoutHeightGap" format="dimension" />
- <!-- The padding of the pages that are dynamically created per page -->
- <attr name="pageLayoutPaddingTop" format="dimension" />
- <attr name="pageLayoutPaddingBottom" format="dimension" />
- <attr name="pageLayoutPaddingLeft" format="dimension" />
- <attr name="pageLayoutPaddingRight" format="dimension" />
<!-- The page indicator for this workspace -->
<attr name="pageIndicator" format="reference" />
</declare-styleable>
+ <declare-styleable name="BubbleTextView">
+ <!-- A spacing override for the icons within a page -->
+ <attr name="customShadows" format="boolean" />
+ </declare-styleable>
+
<!-- AppsCustomizePagedView specific attributes. These attributes are used to
customize an AppsCustomizePagedView in xml files. -->
<declare-styleable name="AppsCustomizePagedView">
@@ -123,10 +114,6 @@
<attr name="widgetCountX" format="integer" />
<!-- Number of widgets vertically -->
<attr name="widgetCountY" format="integer" />
- <!-- The x index of the item to be focused in the cling -->
- <attr name="clingFocusedX" format="integer" />
- <!-- The y index of the item to be focused in the cling -->
- <attr name="clingFocusedY" format="integer" />
</declare-styleable>
<!-- XML attributes used by default_workspace.xml -->
@@ -151,9 +138,9 @@
<attr name="workspace" format="reference" />
</declare-styleable>
- <!-- Only used in the device overlays -->
- <declare-styleable name="CustomClingTitleText">
- </declare-styleable>
- <declare-styleable name="CustomClingText">
+ <declare-styleable name="PreloadIconDrawable">
+ <attr name="background" format="reference" />
+ <attr name="ringOutset" format="dimension" />
+ <attr name="indicatorSize" format="dimension" />
</declare-styleable>
</resources>
diff --git a/res/values/colors.xml b/res/values/colors.xml
index ffee05f18..29837eaf7 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -22,8 +22,10 @@
over the delete target or the info target -->
<color name="delete_target_hover_tint">#DAFF0000</color>
<color name="info_target_hover_tint">#DA0099CC</color>
+ <color name="cling_scrim_background">#80000000</color>
<color name="bubble_dark_background">#20000000</color>
+ <color name="focused_background">#80c6c5c5</color>
<color name="appwidget_error_color">#FCCC</color>
@@ -31,10 +33,9 @@
<color name="workspace_all_apps_and_delete_zone_text_shadow_color">#A0000000</color>
<color name="workspace_icon_text_color">#FFF</color>
- <color name="apps_customize_icon_text_color">#FFF</color>
- <color name="folder_items_text_color">#FF333333</color>
- <color name="folder_items_glow_color">#FFCCCCCC</color>
+ <color name="quantum_panel_text_color">#FF666666</color>
+ <color name="quantum_panel_text_shadow_color">#FFC4C4C4</color>
<color name="outline_color">#FFFFFFFF</color>
-
- <color name="first_run_cling_circle_background_color">#64b1ea</color>
+ <color name="widget_text_panel">#FF374248</color>
+
</resources>
diff --git a/res/values/config.xml b/res/values/config.xml
index b512ffe67..96bd13bbb 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -17,12 +17,16 @@
<!-- Max number of page indicators to show -->
<integer name="config_maxNumberOfPageIndicatorsToShow">21</integer>
+ <!-- App data backup and restore. To enble backup, register with an android backup service.
+ http://developer.android.com/guide/topics/data/backup.html#BackupKey -->
+ <bool name="enable_backup">false</bool>
+
<!-- DragController -->
<integer name="config_flingToDeleteMinVelocity">-1500</integer>
<!-- AllApps/Customize/AppsCustomize -->
<!-- The alpha of the AppsCustomize bg in spring loaded mode -->
- <integer name="config_appsCustomizeSpringLoadedBgAlpha">65</integer>
+ <integer name="config_workspaceScrimAlpha">55</integer>
<integer name="config_workspaceUnshrinkTime">300</integer>
<integer name="config_overviewTransitionTime">250</integer>
@@ -31,14 +35,20 @@
<!-- Fade/zoom in/out duration & scale in the AllApps transition.
Note: This should be less than the workspaceShrinkTime as they happen together. -->
+ <integer name="config_appsCustomizeRevealTime">220</integer>
<integer name="config_appsCustomizeZoomInTime">350</integer>
<integer name="config_appsCustomizeZoomOutTime">600</integer>
<integer name="config_appsCustomizeZoomScaleFactor">7</integer>
<integer name="config_appsCustomizeFadeInTime">250</integer>
<integer name="config_appsCustomizeFadeOutTime">200</integer>
<integer name="config_appsCustomizeWorkspaceShrinkTime">300</integer>
- <integer name="config_appsCustomizeWorkspaceAnimationStagger">40</integer>
- <integer name="config_workspaceAppsCustomizeAnimationStagger">100</integer>
+
+ <integer name="config_appsCustomizeConcealTime">250</integer>
+ <integer name="config_appsCustomizeItemsAlphaStagger">60</integer>
+
+ <!-- This constant stores the ratio of the all apps button drawable which
+ is used for internal (baked-in) padding -->
+ <integer name="config_allAppsButtonPaddingPercent">17</integer>
<integer name="config_workspaceDefaultScreen">0</integer>
@@ -69,7 +79,9 @@
<integer name="config_dropAnimMaxDuration">500</integer>
<!-- The duration of the UserFolder opening and closing animation -->
- <integer name="config_folderAnimDuration">120</integer>
+ <integer name="config_folderExpandDuration">120</integer>
+ <integer name="config_materialFolderExpandDuration">200</integer>
+ <integer name="config_materialFolderExpandStagger">60</integer>
<!-- The distance at which the animation should take the max duration -->
<integer name="config_dropAnimMaxDist">800</integer>
@@ -81,9 +93,6 @@
<!-- Camera distance for the overscroll effect -->
<integer name="config_cameraDistance">8000</integer>
- <!-- Whether or not to use custom clings if a custom workspace layout is passed in -->
- <bool name="config_useCustomClings">false</bool>
-
<!-- Hotseat -->
<bool name="hotseat_transpose_layout_with_orientation">true</bool>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 1eca5b382..2c9e689c6 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -18,8 +18,8 @@
<!-- Dynamic Grid -->
<dimen name="dynamic_grid_edge_margin">6dp</dimen>
<dimen name="dynamic_grid_search_bar_max_width">500dp</dimen>
- <dimen name="dynamic_grid_search_bar_height">48dp</dimen>
- <dimen name="dynamic_grid_page_indicator_height">24dp</dimen>
+ <dimen name="dynamic_grid_search_bar_height">56dp</dimen>
+ <dimen name="dynamic_grid_page_indicator_height">20dp</dimen>
<dimen name="dynamic_grid_icon_drawable_padding">4dp</dimen>
<dimen name="dynamic_grid_all_apps_cell_padding">18dp</dimen>
<dimen name="dynamic_grid_workspace_page_spacing">8dp</dimen>
@@ -29,21 +29,12 @@
<dimen name="dynamic_grid_overview_bar_spacer_width">68dp</dimen>
<!-- Cling -->
- <dimen name="clingPunchThroughGraphicCenterRadius">94dp</dimen>
- <dimen name="folderClingMarginTop">20dp</dimen>
- <!-- The offset for the text in the cling -->
- <dimen name="cling_text_block_offset_x">0dp</dimen>
- <dimen name="cling_text_block_offset_y">0dp</dimen>
- <!-- entries for custom clings, will be set in overlays -->
- <add-resource type="dimen" name="custom_cling_margin_top" />
- <add-resource type="dimen" name="custom_cling_margin_right" />
- <add-resource type="dimen" name="custom_cling_margin_left" />
-
- <dimen name="cling_title_text_size">20sp</dimen>
- <dimen name="cling_text_size">14sp</dimen>
- <dimen name="cling_alt_title_text_size">24sp</dimen>
- <dimen name="cling_alt_text_size">16sp</dimen>
- <dimen name="cling_hint_text_size">14sp</dimen>
+ <dimen name="cling_migration_logo_height">240dp</dimen>
+ <dimen name="cling_migration_logo_width">165dp</dimen>
+ <dimen name="cling_migration_bg_size">400dp</dimen>
+ <dimen name="cling_migration_bg_shift">-200dp</dimen>
+ <dimen name="cling_migration_content_margin">16dp</dimen>
+ <dimen name="cling_migration_content_width">280dp</dimen>
<!-- Workspace -->
<dimen name="workspace_max_gap">16dp</dimen>
@@ -62,6 +53,7 @@
<dimen name="apps_customize_tab_bar_height">52dp</dimen>
<dimen name="apps_customize_tab_bar_margin_top">0dp</dimen>
<dimen name="app_icon_size">48dp</dimen>
+ <dimen name="apps_customize_horizontal_padding">0dp</dimen>
<!-- The AppsCustomize page indicator -->
<dimen name="apps_customize_page_indicator_height">12dp</dimen>
@@ -88,9 +80,8 @@
<dimen name="app_widget_preview_padding_left">16dp</dimen>
<dimen name="app_widget_preview_padding_right">16dp</dimen>
<dimen name="app_widget_preview_padding_top">32dp</dimen>
- <dimen name="app_widget_preview_label_margin_top">4dp</dimen>
- <dimen name="app_widget_preview_label_margin_left">2dp</dimen>
- <dimen name="app_widget_preview_label_margin_right">2dp</dimen>
+ <dimen name="app_widget_preview_label_vertical_padding">8dp</dimen>
+ <dimen name="app_widget_preview_label_horizontal_padding">8dp</dimen>
<!-- Padding applied to shortcut previews -->
<dimen name="shortcut_preview_padding_left">0dp</dimen>
@@ -101,4 +92,8 @@
<!-- The amount that the preview contents are inset from the preview background -->
<dimen name="folder_preview_padding">4dp</dimen>
<dimen name="folder_name_padding">10dp</dimen>
+
+<!-- Sizes for managed profile badges -->
+ <dimen name="profile_badge_size">24dp</dimen>
+ <dimen name="profile_badge_margin">4dp</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9cb6c293b..ff3509bc5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -20,6 +20,16 @@
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<!-- General -->
<skip />
+
+ <!-- URI used to import old favorites. [DO NOT TRANSLATE] -->
+ <string name="old_launcher_provider_uri" translatable="false">content://com.android.launcher2.settings/favorites?notify=true</string>
+
+ <!-- Permission to receive the com.android.launcher3.action.LAUNCH intent -->
+ <string name="receive_launch_broadcasts_permission" translatable="false">com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS</string>
+
+ <!-- Permission to receive the com.android.launcher3.action.FIRST_LOAD_COMPLETE intent -->
+ <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>
<!-- Accessibility-facing application name -->
@@ -30,6 +40,8 @@
<string name="folder_name"></string>
<!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
<string name="activity_not_found">App isn\'t installed.</string>
+ <!-- SafeMode shortcut error string -->
+ <string name="safemode_shortcut_error">Downloaded app disabled in Safe mode</string>
<!-- Labels for the tabs in the customize drawer -->
<string name="widgets_tab_label">Widgets</string>
@@ -181,6 +193,9 @@ s -->
<!-- Text to show user in place of a gadget when we can't display it properly -->
<string name="gadget_error_text">Problem loading widget</string>
+ <!-- Text to show user in place of a gadget when it is not yet initialized. -->
+ <string name="gadget_setup_text">Setup</string>
+
<!-- Text to inform the user that they can't uninstall a system application -->
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
@@ -228,6 +243,12 @@ s -->
<string name="workspace_cling_title">Organize your space</string>
<!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
<string name="workspace_cling_move_item">Touch &amp; hold background to manage wallpaper, widgets and settings.</string>
+ <!-- The title text for workspace longpress action [CHAR_LIMIT=40] -->
+ <string name="workspace_cling_longpress_title">Wallpapers, widgets, &amp; settings</string>
+ <!-- The description of how to use the workspace [CHAR_LIMIT=70] -->
+ <string name="workspace_cling_longpress_description">Touch &amp; hold background to customize</string>
+ <!-- The description of the button to dismiss the cling [CHAR_LIMIT=30] -->
+ <string name="workspace_cling_longpress_dismiss">GOT IT</string>
<!-- The title text for the Folder cling [CHAR_LIMIT=30] -->
<string name="folder_cling_title">Here\'s a folder</string>
<!-- The description of how to create a folder [CHAR_LIMIT=70] -->
@@ -262,4 +283,29 @@ s -->
<string name="wallpaper_button_text">Wallpapers</string>
<!-- Text for settings button -->
<string name="settings_button_text">Settings</string>
+
+ <!-- Label on an icon that references an uninstalled package, that is going to be installed at some point. [CHAR_LIMIT=15] -->
+ <string name="package_state_enqueued">Waiting</string>
+ <!-- Label on an icon that references an uninstalled package, that is currently being downloaded. [CHAR_LIMIT=15] -->
+ <string name="package_state_downloading">Downloading</string>
+ <!-- Label on an icon that references an uninstalled package, that is currently being installed. [CHAR_LIMIT=15] -->
+ <string name="package_state_installing">Installing</string>
+ <!-- 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">Unknown</string>
+ <!-- Label on an icon that references an uninstalled package, for which restore from market has failed. [CHAR_LIMIT=15] -->
+ <string name="package_state_error">Not restored</string>
+
+ <!-- Button for abandoned promises dialog, that removes all abandoned promise icons. -->
+ <string name="abandoned_clean_all">Remove All</string>
+ <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. -->
+ <string name="abandoned_clean_this">Remove</string>
+ <!-- Button for abandoned promise dialog, to search in the market for the missing package. -->
+ <string name="abandoned_search">Search</string>
+ <!-- Title for abandoned promise dialog. -->
+ <string name="abandoned_promises_title">This app is not installed</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">The app for this icon isn\'t installed.
+ You can remove it, or search for the app and install it manually.
+ </string>
</resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index c18dccbab..56a205fd1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -18,53 +18,6 @@
-->
<resources>
- <style name="ClingButton">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:paddingTop">15dp</item>
- <item name="android:paddingBottom">15dp</item>
- <item name="android:paddingLeft">50dp</item>
- <item name="android:paddingRight">50dp</item>
- <item name="android:text">@string/cling_dismiss</item>
- <item name="android:textColor">#ffffff</item>
- <item name="android:textStyle">bold</item>
- <item name="android:textSize">16sp</item>
- <item name="android:background">@drawable/cling_button_bg</item>
- </style>
- <style name="ClingTitleText">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:layout_marginBottom">5dp</item>
- <item name="android:textSize">@dimen/cling_title_text_size</item>
- <item name="android:textColor">#ffffff</item>
- <item name="android:fontFamily">sans-serif-condensed</item>
- </style>
- <style name="ClingText">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textSize">@dimen/cling_text_size</item>
- <item name="android:textColor">#80000000</item>
- <item name="android:lineSpacingMultiplier">1.1</item>
- </style>
- <style name="ClingAltTitleText">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textSize">@dimen/cling_alt_title_text_size</item>
- <item name="android:textColor">#49C0EC</item>
- </style>
- <style name="ClingAltText">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textSize">@dimen/cling_alt_text_size</item>
- <item name="android:textColor">#49C0EC</item>
- </style>
- <style name="ClingHintText">
- <item name="android:layout_width">wrap_content</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textSize">@dimen/cling_hint_text_size</item>
- <item name="android:textColor">#80ffffff</item>
- <item name="android:fontFamily">sans-serif-condensed</item>
- </style>
<style name="WorkspaceIcon">
<item name="android:layout_width">match_parent</item>
@@ -86,11 +39,19 @@
<style name="WorkspaceIcon.AppsCustomize">
<item name="android:background">@null</item>
- <item name="android:textColor">@color/apps_customize_icon_text_color</item>
+ <item name="android:textColor">@color/quantum_panel_text_color</item>
<item name="android:drawablePadding">@dimen/dynamic_grid_icon_drawable_padding</item>
- <item name="android:shadowRadius">4.0</item>
- <item name="android:shadowColor">#FF000000</item>
+ <item name="android:shadowRadius">0</item>
+ <item name="customShadows">false</item>
</style>
+
+ <style name="WorkspaceIcon.Folder">
+ <item name="android:background">@null</item>
+ <item name="android:textColor">@color/quantum_panel_text_color</item>
+ <item name="android:shadowRadius">0</item>
+ <item name="customShadows">false</item>
+ </style>
+
<style name="SearchDropTargetBar">
</style>
<style name="SearchButton">
@@ -146,11 +107,19 @@
<item name="android:shadowRadius">2.0</item>
</style>
- <!-- Overridden in device overlays -->
- <style name="CustomClingTitleText">
+ <style name="PreloadIcon">
+ <item name="background">@drawable/virtual_preload</item>
+ <item name="indicatorSize">4dp</item>
+ <item name="ringOutset">4dp</item>
</style>
- <style name="CustomClingText">
+
+ <style name="PreloadIcon.Folder">
+ <item name="background">@drawable/virtual_preload_folder</item>
+ <item name="indicatorSize">4dp</item>
+ <item name="ringOutset">4dp</item>
</style>
+
+ <!-- Overridden in device overlays -->
<style name="PagedViewWidgetImageView">
<item name="android:paddingLeft">@dimen/app_widget_preview_padding_left</item>
</style>
diff --git a/res/xml/default_workspace.xml b/res/xml/default_workspace_4x4.xml
index 26fc504f0..9bec86aa8 100644
--- a/res/xml/default_workspace.xml
+++ b/res/xml/default_workspace_4x4.xml
@@ -60,13 +60,21 @@
<!-- Far-right screen [4] -->
<!-- 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"
+ <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+ <resolve
launcher:container="-101"
launcher:screen="0"
launcher:x="0"
- launcher:y="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" />
+ </resolve>
+
<favorite
launcher:packageName="com.android.contacts"
launcher:className="com.android.contacts.activities.PeopleActivity"
@@ -74,18 +82,35 @@
launcher:screen="1"
launcher:x="1"
launcher:y="0" />
- <favorite
- launcher:packageName="com.android.mms"
- launcher:className="com.android.mms.ui.ConversationList"
+
+ <resolve
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:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+ <favorite launcher:uri="sms:" />
+ <favorite launcher:uri="smsto:" />
+ <favorite launcher:uri="mms:" />
+ <favorite launcher:uri="mmsto:" />
+
+ <favorite
+ launcher:packageName="com.android.mms"
+ launcher:className="com.android.mms.ui.ConversationList" />
+ </resolve>
+ <resolve
launcher:container="-101"
launcher:screen="4"
launcher:x="4"
- launcher:y="0" />
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+ <favorite launcher:uri="http://www.example.com/" />
+
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity" />
+ </resolve>
+
</favorites>
diff --git a/res/xml/default_workspace_no_all_apps.xml b/res/xml/default_workspace_4x4_no_all_apps.xml
index 7e1301cef..7e1301cef 100644
--- a/res/xml/default_workspace_no_all_apps.xml
+++ b/res/xml/default_workspace_4x4_no_all_apps.xml
diff --git a/res/xml-sw720dp/default_workspace.xml b/res/xml/default_workspace_5x5.xml
index 1c1d70e92..9bec86aa8 100644
--- a/res/xml-sw720dp/default_workspace.xml
+++ b/res/xml/default_workspace_5x5.xml
@@ -58,4 +58,59 @@
launcher:y="3" />
<!-- Far-right screen [4] -->
+
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+ <resolve
+ launcher:container="-101"
+ launcher:screen="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" />
+ </resolve>
+
+ <favorite
+ launcher:packageName="com.android.contacts"
+ launcher:className="com.android.contacts.activities.PeopleActivity"
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" />
+
+ <resolve
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+ <favorite launcher:uri="sms:" />
+ <favorite launcher:uri="smsto:" />
+ <favorite launcher:uri="mms:" />
+ <favorite launcher:uri="mmsto:" />
+
+ <favorite
+ launcher:packageName="com.android.mms"
+ launcher:className="com.android.mms.ui.ConversationList" />
+ </resolve>
+ <resolve
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+ <favorite launcher:uri="http://www.example.com/" />
+
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity" />
+ </resolve>
+
</favorites>
diff --git a/res/xml/default_workspace_5x5_no_all_apps.xml b/res/xml/default_workspace_5x5_no_all_apps.xml
new file mode 100644
index 000000000..f54a20425
--- /dev/null
+++ b/res/xml/default_workspace_5x5_no_all_apps.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer Hangouts Maps Chrome Camera -->
+ <favorite
+ launcher:packageName="com.google.android.dialer"
+ launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.google.android.talk"
+ launcher:className="com.google.android.talk.SigningInActivity"
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.google.android.apps.maps"
+ launcher:className="com.google.android.maps.MapsActivity"
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0"/>
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main"
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.google.android.GoogleCamera"
+ launcher:className="com.android.camera.CameraLauncher"
+ launcher:container="-101"
+ launcher:screen="5"
+ launcher:x="5"
+ launcher:y="0" />
+</favorites>
+
diff --git a/res/xml-sw600dp/default_workspace.xml b/res/xml/default_workspace_5x6.xml
index 090c7a7af..d42a93a6a 100644
--- a/res/xml-sw600dp/default_workspace.xml
+++ b/res/xml/default_workspace_5x6.xml
@@ -60,13 +60,21 @@
<!-- Far-right screen [4] -->
<!-- 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"
+ <!-- Dialer, Contacts, [All Apps], Messaging, Browser -->
+ <resolve
launcher:container="-101"
launcher:screen="1"
launcher:x="1"
- launcher:y="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" />
+ </resolve>
+
<favorite
launcher:packageName="com.android.contacts"
launcher:className="com.android.contacts.activities.PeopleActivity"
@@ -74,18 +82,34 @@
launcher:screen="2"
launcher:x="2"
launcher:y="0" />
- <favorite
- launcher:packageName="com.android.mms"
- launcher:className="com.android.mms.ui.ConversationList"
+
+ <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"
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_MESSAGING;end" />
+ <favorite launcher:uri="sms:" />
+ <favorite launcher:uri="smsto:" />
+ <favorite launcher:uri="mms:" />
+ <favorite launcher:uri="mmsto:" />
+
+ <favorite
+ launcher:packageName="com.android.mms"
+ launcher:className="com.android.mms.ui.ConversationList" />
+ </resolve>
+ <resolve
launcher:container="-101"
launcher:screen="5"
launcher:x="5"
- launcher:y="0" />
+ launcher:y="0" >
+ <favorite
+ launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_BROWSER;end" />
+ <favorite launcher:uri="http://www.example.com/" />
+
+ <favorite
+ launcher:packageName="com.android.browser"
+ launcher:className="com.android.browser.BrowserActivity" />
+ </resolve>
</favorites>
diff --git a/res/xml/default_workspace_5x6_no_all_apps.xml b/res/xml/default_workspace_5x6_no_all_apps.xml
new file mode 100644
index 000000000..f54a20425
--- /dev/null
+++ b/res/xml/default_workspace_5x6_no_all_apps.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<favorites xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3">
+ <!-- Hotseat (We use the screen as the position of the item in the hotseat) -->
+ <!-- Dialer Hangouts Maps Chrome Camera -->
+ <favorite
+ launcher:packageName="com.google.android.dialer"
+ launcher:className="com.google.android.dialer.extensions.GoogleDialtactsActivity"
+ launcher:container="-101"
+ launcher:screen="1"
+ launcher:x="1"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.google.android.talk"
+ launcher:className="com.google.android.talk.SigningInActivity"
+ launcher:container="-101"
+ launcher:screen="2"
+ launcher:x="2"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.google.android.apps.maps"
+ launcher:className="com.google.android.maps.MapsActivity"
+ launcher:container="-101"
+ launcher:screen="3"
+ launcher:x="3"
+ launcher:y="0"/>
+ <favorite
+ launcher:packageName="com.android.chrome"
+ launcher:className="com.google.android.apps.chrome.Main"
+ launcher:container="-101"
+ launcher:screen="4"
+ launcher:x="4"
+ launcher:y="0" />
+ <favorite
+ launcher:packageName="com.google.android.GoogleCamera"
+ launcher:className="com.android.camera.CameraLauncher"
+ launcher:container="-101"
+ launcher:screen="5"
+ launcher:x="5"
+ launcher:y="0" />
+</favorites>
+
diff --git a/src/com/android/launcher3/AddAdapter.java b/src/com/android/launcher3/AddAdapter.java
deleted file mode 100644
index 5308a3de4..000000000
--- a/src/com/android/launcher3/AddAdapter.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.TextView;
-
-import java.util.ArrayList;
-
-/**
- * Adapter showing the types of items that can be added to a {@link Workspace}.
- */
-public class AddAdapter extends BaseAdapter {
-
- private final LayoutInflater mInflater;
-
- private final ArrayList<ListItem> mItems = new ArrayList<ListItem>();
-
- public static final int ITEM_SHORTCUT = 0;
- public static final int ITEM_APPWIDGET = 1;
- public static final int ITEM_APPLICATION = 2;
- public static final int ITEM_WALLPAPER = 3;
-
- /**
- * Specific item in our list.
- */
- public class ListItem {
- public final CharSequence text;
- public final Drawable image;
- public final int actionTag;
-
- public ListItem(Resources res, int textResourceId, int imageResourceId, int actionTag) {
- text = res.getString(textResourceId);
- if (imageResourceId != -1) {
- image = res.getDrawable(imageResourceId);
- } else {
- image = null;
- }
- this.actionTag = actionTag;
- }
- }
-
- public AddAdapter(Launcher launcher) {
- super();
-
- mInflater = (LayoutInflater) launcher.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- // Create default actions
- Resources res = launcher.getResources();
-
- mItems.add(new ListItem(res, R.string.group_wallpapers,
- R.mipmap.ic_launcher_wallpaper, ITEM_WALLPAPER));
- }
-
- public View getView(int position, View convertView, ViewGroup parent) {
- ListItem item = (ListItem) getItem(position);
-
- if (convertView == null) {
- convertView = mInflater.inflate(R.layout.add_list_item, parent, false);
- }
-
- TextView textView = (TextView) convertView;
- textView.setTag(item);
- textView.setText(item.text);
- textView.setCompoundDrawablesWithIntrinsicBounds(item.image, null, null, null);
-
- return convertView;
- }
-
- public int getCount() {
- return mItems.size();
- }
-
- public Object getItem(int position) {
- return mItems.get(position);
- }
-
- public long getItemId(int position) {
- return position;
- }
-}
diff --git a/src/com/android/launcher3/AllAppsList.java b/src/com/android/launcher3/AllAppsList.java
index 89b291f28..38d2fa541 100644
--- a/src/com/android/launcher3/AllAppsList.java
+++ b/src/com/android/launcher3/AllAppsList.java
@@ -23,6 +23,10 @@ import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
import java.util.ArrayList;
import java.util.List;
@@ -66,7 +70,7 @@ class AllAppsList {
if (mAppFilter != null && !mAppFilter.shouldShowApp(info.componentName)) {
return;
}
- if (findActivity(data, info.componentName)) {
+ if (findActivity(data, info.componentName, info.user)) {
return;
}
data.add(info);
@@ -92,12 +96,14 @@ class AllAppsList {
/**
* Add the icons for the supplied apk called packageName.
*/
- public void addPackage(Context context, String packageName) {
- final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
+ public void addPackage(Context context, String packageName, UserHandleCompat user) {
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
+ user);
if (matches.size() > 0) {
- for (ResolveInfo info : matches) {
- add(new AppInfo(context.getPackageManager(), info, mIconCache, null));
+ for (LauncherActivityInfoCompat info : matches) {
+ add(new AppInfo(context, info, user, mIconCache, null));
}
}
}
@@ -105,34 +111,37 @@ class AllAppsList {
/**
* Remove the apps for the given apk identified by packageName.
*/
- public void removePackage(String packageName) {
+ public void removePackage(String packageName, UserHandleCompat user) {
final List<AppInfo> data = this.data;
for (int i = data.size() - 1; i >= 0; i--) {
AppInfo info = data.get(i);
final ComponentName component = info.intent.getComponent();
- if (packageName.equals(component.getPackageName())) {
+ if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
removed.add(info);
data.remove(i);
}
}
- mIconCache.remove(packageName);
+ mIconCache.remove(packageName, user);
}
/**
* Add and remove icons for this package which has been updated.
*/
- public void updatePackage(Context context, String packageName) {
- final List<ResolveInfo> matches = findActivitiesForPackage(context, packageName);
+ public void updatePackage(Context context, String packageName, UserHandleCompat user) {
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ final List<LauncherActivityInfoCompat> matches = launcherApps.getActivityList(packageName,
+ user);
if (matches.size() > 0) {
// Find disabled/removed activities and remove them from data and add them
// to the removed list.
for (int i = data.size() - 1; i >= 0; i--) {
final AppInfo applicationInfo = data.get(i);
final ComponentName component = applicationInfo.intent.getComponent();
- if (packageName.equals(component.getPackageName())) {
+ if (user.equals(applicationInfo.user)
+ && packageName.equals(component.getPackageName())) {
if (!findActivity(matches, component)) {
removed.add(applicationInfo);
- mIconCache.remove(component);
+ mIconCache.remove(component, user);
data.remove(i);
}
}
@@ -142,14 +151,14 @@ class AllAppsList {
// Also updates existing activities with new labels/icons
int count = matches.size();
for (int i = 0; i < count; i++) {
- final ResolveInfo info = matches.get(i);
+ final LauncherActivityInfoCompat info = matches.get(i);
AppInfo applicationInfo = findApplicationInfoLocked(
- info.activityInfo.applicationInfo.packageName,
- info.activityInfo.name);
+ info.getComponentName().getPackageName(), user,
+ info.getComponentName().getClassName());
if (applicationInfo == null) {
- add(new AppInfo(context.getPackageManager(), info, mIconCache, null));
+ add(new AppInfo(context, info, user, mIconCache, null));
} else {
- mIconCache.remove(applicationInfo.componentName);
+ mIconCache.remove(applicationInfo.componentName, user);
mIconCache.getTitleAndIcon(applicationInfo, info, null);
modified.add(applicationInfo);
}
@@ -159,37 +168,24 @@ class AllAppsList {
for (int i = data.size() - 1; i >= 0; i--) {
final AppInfo applicationInfo = data.get(i);
final ComponentName component = applicationInfo.intent.getComponent();
- if (packageName.equals(component.getPackageName())) {
+ if (user.equals(applicationInfo.user)
+ && packageName.equals(component.getPackageName())) {
removed.add(applicationInfo);
- mIconCache.remove(component);
+ mIconCache.remove(component, user);
data.remove(i);
}
}
}
}
- /**
- * Query the package manager for MAIN/LAUNCHER activities in the supplied package.
- */
- static List<ResolveInfo> findActivitiesForPackage(Context context, String packageName) {
- final PackageManager packageManager = context.getPackageManager();
-
- final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- mainIntent.setPackage(packageName);
-
- final List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
- return apps != null ? apps : new ArrayList<ResolveInfo>();
- }
/**
* Returns whether <em>apps</em> contains <em>component</em>.
*/
- private static boolean findActivity(List<ResolveInfo> apps, ComponentName component) {
- final String className = component.getClassName();
- for (ResolveInfo info : apps) {
- final ActivityInfo activityInfo = info.activityInfo;
- if (activityInfo.name.equals(className)) {
+ private static boolean findActivity(List<LauncherActivityInfoCompat> apps,
+ ComponentName component) {
+ for (LauncherActivityInfoCompat info : apps) {
+ if (info.getComponentName().equals(component)) {
return true;
}
}
@@ -197,13 +193,24 @@ class AllAppsList {
}
/**
+ * Query the launcher apps service for whether the supplied package has
+ * MAIN/LAUNCHER activities in the supplied package.
+ */
+ static boolean packageHasActivities(Context context, String packageName,
+ UserHandleCompat user) {
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ return launcherApps.getActivityList(packageName, user).size() > 0;
+ }
+
+ /**
* Returns whether <em>apps</em> contains <em>component</em>.
*/
- private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component) {
+ private static boolean findActivity(ArrayList<AppInfo> apps, ComponentName component,
+ UserHandleCompat user) {
final int N = apps.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
final AppInfo info = apps.get(i);
- if (info.componentName.equals(component)) {
+ if (info.user.equals(user) && info.componentName.equals(component)) {
return true;
}
}
@@ -213,10 +220,11 @@ class AllAppsList {
/**
* Find an ApplicationInfo object for the given packageName and className.
*/
- private AppInfo findApplicationInfoLocked(String packageName, String className) {
+ private AppInfo findApplicationInfoLocked(String packageName, UserHandleCompat user,
+ String className) {
for (AppInfo info: data) {
final ComponentName component = info.intent.getComponent();
- if (packageName.equals(component.getPackageName())
+ if (user.equals(info.user) && packageName.equals(component.getPackageName())
&& className.equals(component.getClassName())) {
return info;
}
diff --git a/src/com/android/launcher3/AppInfo.java b/src/com/android/launcher3/AppInfo.java
index da222f11f..bfcad84b3 100644
--- a/src/com/android/launcher3/AppInfo.java
+++ b/src/com/android/launcher3/AppInfo.java
@@ -17,21 +17,26 @@
package com.android.launcher3;
import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.graphics.Bitmap;
import android.util.Log;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
/**
* Represents an app in AllAppsView.
*/
-class AppInfo extends ItemInfo {
+public class AppInfo extends ItemInfo {
private static final String TAG = "Launcher3.AppInfo";
/**
@@ -60,7 +65,7 @@ class AppInfo extends ItemInfo {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
- protected Intent getIntent() {
+ public Intent getIntent() {
return intent;
}
@@ -71,28 +76,25 @@ class AppInfo extends ItemInfo {
/**
* Must not hold the Context.
*/
- public AppInfo(PackageManager pm, ResolveInfo info, IconCache iconCache,
- HashMap<Object, CharSequence> labelCache) {
- final String packageName = info.activityInfo.applicationInfo.packageName;
-
- this.componentName = new ComponentName(packageName, info.activityInfo.name);
+ public AppInfo(Context context, LauncherActivityInfoCompat info, UserHandleCompat user,
+ IconCache iconCache, HashMap<Object, CharSequence> labelCache) {
+ this.componentName = info.getComponentName();
this.container = ItemInfo.NO_ID;
- this.setActivity(componentName,
- Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
-
- try {
- PackageInfo pi = pm.getPackageInfo(packageName, 0);
- flags = initFlags(pi);
- firstInstallTime = initFirstInstallTime(pi);
- } catch (NameNotFoundException e) {
- Log.d(TAG, "PackageManager.getApplicationInfo failed for " + packageName);
- }
+ flags = initFlags(info);
+ firstInstallTime = info.getFirstInstallTime();
iconCache.getTitleAndIcon(this, info, labelCache);
+ intent = new Intent(Intent.ACTION_MAIN);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ intent.setComponent(info.getComponentName());
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
+ intent.putExtra(EXTRA_PROFILE, serialNumber);
+ this.user = user;
}
- public static int initFlags(PackageInfo pi) {
- int appFlags = pi.applicationInfo.flags;
+ private static int initFlags(LauncherActivityInfoCompat info) {
+ int appFlags = info.getApplicationInfo().flags;
int flags = 0;
if ((appFlags & android.content.pm.ApplicationInfo.FLAG_SYSTEM) == 0) {
flags |= DOWNLOADED_FLAG;
@@ -104,10 +106,6 @@ class AppInfo extends ItemInfo {
return flags;
}
- public static long initFirstInstallTime(PackageInfo pi) {
- return pi.firstInstallTime;
- }
-
public AppInfo(AppInfo info) {
super(info);
componentName = info.componentName;
@@ -115,21 +113,7 @@ class AppInfo extends ItemInfo {
intent = new Intent(info.intent);
flags = info.flags;
firstInstallTime = info.firstInstallTime;
- }
-
- /**
- * Creates the application intent based on a component name and various launch flags.
- * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}.
- *
- * @param className the class name of the component representing the intent
- * @param launchFlags the launch flags
- */
- final void setActivity(ComponentName className, int launchFlags) {
- intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(className);
- intent.setFlags(launchFlags);
- itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
+ iconBitmap = info.iconBitmap;
}
@Override
@@ -137,7 +121,8 @@ class AppInfo extends ItemInfo {
return "ApplicationInfo(title=" + title.toString() + " id=" + this.id
+ " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY
- + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + dropPos + ")";
+ + " spanX=" + spanX + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ + " user=" + user + ")";
}
public static void dumpApplicationInfoList(String tag, String label, ArrayList<AppInfo> list) {
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
new file mode 100644
index 000000000..880aaf1ec
--- /dev/null
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -0,0 +1,94 @@
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "AppWidgetsRestoredReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
+ int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
+ int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ if (oldIds.length == newIds.length) {
+ restoreAppWidgetIds(context, oldIds, newIds);
+ } else {
+ Log.e(TAG, "Invalid host restored received");
+ }
+ }
+ }
+
+ /**
+ * Updates the app widgets whose id has changed during the restore process.
+ */
+ static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+ final ContentResolver cr = context.getContentResolver();
+ final List<Integer> idsToRemove = new ArrayList<Integer>();
+ final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+
+ for (int i = 0; i < oldWidgetIds.length; i++) {
+ Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
+
+ final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
+ final int state;
+ if (LauncherModel.isValidProvider(provider)) {
+ state = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ } else {
+ state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+ }
+
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]);
+ values.put(LauncherSettings.Favorites.RESTORED, state);
+
+ String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+
+ int result = cr.update(Favorites.CONTENT_URI, values,
+ "appWidgetId=? and (restored & 1) = 1", widgetIdParams);
+ if (result == 0) {
+ Cursor cursor = cr.query(Favorites.CONTENT_URI,
+ new String[] {Favorites.APPWIDGET_ID},
+ "appWidgetId=?", widgetIdParams, null);
+ try {
+ if (!cursor.moveToFirst()) {
+ // The widget no long exists.
+ idsToRemove.add(newWidgetIds[i]);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ // Unregister the widget IDs which are not present on the workspace. This could happen
+ // when a widget place holder is removed from workspace, before this method is called.
+ if (!idsToRemove.isEmpty()) {
+ final AppWidgetHost appWidgetHost =
+ new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
+ for (Integer id : idsToRemove) {
+ appWidgetHost.deleteAppWidgetId(id);
+ Log.e(TAG, "Widget no longer present, appWidgetId=" + id);
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/AppsCustomizeCellLayout.java b/src/com/android/launcher3/AppsCustomizeCellLayout.java
index 3c8bda9db..a50fb6821 100644
--- a/src/com/android/launcher3/AppsCustomizeCellLayout.java
+++ b/src/com/android/launcher3/AppsCustomizeCellLayout.java
@@ -20,8 +20,16 @@ import android.content.Context;
import android.view.View;
public class AppsCustomizeCellLayout extends CellLayout implements Page {
+
+ final FocusIndicatorView mFocusHandlerView;
+
public AppsCustomizeCellLayout(Context context) {
super(context);
+
+ mFocusHandlerView = new FocusIndicatorView(context);
+ addView(mFocusHandlerView, 0);
+ mFocusHandlerView.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
+ mFocusHandlerView.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
}
@Override
@@ -60,4 +68,4 @@ public class AppsCustomizeCellLayout extends CellLayout implements Page {
children.getChildAt(j).setOnKeyListener(null);
}
}
-} \ No newline at end of file
+}
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 251ae2108..1bd290777 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -28,7 +28,6 @@ import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
-import android.graphics.Canvas;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -44,12 +43,12 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
-import android.view.animation.DecelerateInterpolator;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
import java.util.ArrayList;
import java.util.Collections;
@@ -144,10 +143,11 @@ class AppsCustomizeAsyncTask extends AsyncTask<AsyncTaskPageData, Void, AsyncTas
*/
public class AppsCustomizePagedView extends PagedViewWithDraggableItems implements
View.OnClickListener, View.OnKeyListener, DragSource,
- PagedViewIcon.PressedCallback, PagedViewWidget.ShortPressListener,
- LauncherTransitionable {
+ PagedViewWidget.ShortPressListener, LauncherTransitionable {
static final String TAG = "AppsCustomizePagedView";
+ private static Rect sTmpRect = new Rect();
+
/**
* The different content types that this paged view can show.
*/
@@ -165,40 +165,22 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// Save and Restore
private int mSaveInstanceStateItemIndex = -1;
- private PagedViewIcon mPressedIcon;
// Content
private ArrayList<AppInfo> mApps;
private ArrayList<Object> mWidgets;
- // Cling
- private boolean mHasShownAllAppsCling;
- private int mClingFocusedX;
- private int mClingFocusedY;
-
// Caching
- private Canvas mCanvas;
private IconCache mIconCache;
// Dimens
private int mContentWidth, mContentHeight;
private int mWidgetCountX, mWidgetCountY;
- private int mWidgetWidthGap, mWidgetHeightGap;
private PagedViewCellLayout mWidgetSpacingLayout;
private int mNumAppsPages;
private int mNumWidgetPages;
private Rect mAllAppsPadding = new Rect();
- // Relating to the scroll and overscroll effects
- Workspace.ZInterpolator mZInterpolator = new Workspace.ZInterpolator(0.5f);
- private static float CAMERA_DISTANCE = 6500;
- private static float TRANSITION_SCALE_FACTOR = 0.74f;
- private static float TRANSITION_PIVOT = 0.65f;
- private static float TRANSITION_MAX_ROTATION = 22;
- private static final boolean PERFORM_OVERSCROLL_ROTATION = true;
- private AccelerateInterpolator mAlphaInterpolator = new AccelerateInterpolator(0.9f);
- private DecelerateInterpolator mLeftScreenAlphaInterpolator = new DecelerateInterpolator(4);
-
// Previews & outlines
ArrayList<AppsCustomizeAsyncTask> mRunningTasks;
private static final int sPageSleepDelay = 200;
@@ -213,6 +195,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
int mWidgetLoadingId = -1;
PendingAddWidgetInfo mCreateWidgetInfo = null;
private boolean mDraggingWidget = false;
+ boolean mPageBackgroundsVisible = true;
private Toast mWidgetInstructionToast;
@@ -223,19 +206,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
private ArrayList<Runnable> mDeferredPrepareLoadWidgetPreviewsTasks =
new ArrayList<Runnable>();
- private Rect mTmpRect = new Rect();
-
- // Used for drawing shortcut previews
- BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
- PaintCache mCachedShortcutPreviewPaint = new PaintCache();
- CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
-
- // Used for drawing widget previews
- CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
- RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
- RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
- PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
-
WidgetPreviewLoader mWidgetPreviewLoader;
private boolean mInBulkBind;
@@ -248,18 +218,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
mApps = new ArrayList<AppInfo>();
mWidgets = new ArrayList<Object>();
mIconCache = (LauncherAppState.getInstance()).getIconCache();
- mCanvas = new Canvas();
mRunningTasks = new ArrayList<AppsCustomizeAsyncTask>();
// Save the default widget preview background
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AppsCustomizePagedView, 0, 0);
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- mWidgetWidthGap = mWidgetHeightGap = grid.edgeMarginPx;
mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
- mClingFocusedX = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedX, 0);
- mClingFocusedY = a.getInt(R.styleable.AppsCustomizePagedView_clingFocusedY, 0);
a.recycle();
mWidgetSpacingLayout = new PagedViewCellLayout(getContext());
@@ -271,6 +235,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
+ setSinglePageInViewport();
}
@Override
@@ -295,8 +260,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
void setAllAppsPadding(Rect r) {
mAllAppsPadding.set(r);
}
+
void setWidgetsPageIndicatorPadding(int pageIndicatorHeight) {
- mPageLayoutPaddingBottom = pageIndicatorHeight;
+ setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), pageIndicatorHeight);
}
WidgetPreviewLoader getWidgetPreviewLoader() {
@@ -375,8 +341,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// use for each page
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- mWidgetSpacingLayout.setPadding(mPageLayoutPaddingLeft, mPageLayoutPaddingTop,
- mPageLayoutPaddingRight, mPageLayoutPaddingBottom);
mCellCountX = (int) grid.allAppsNumCols;
mCellCountY = (int) grid.allAppsNumRows;
updatePageCounts();
@@ -388,54 +352,32 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
mWidgetSpacingLayout.measure(widthSpec, heightSpec);
- AppsCustomizeTabHost host = (AppsCustomizeTabHost) getTabHost();
- final boolean hostIsTransitioning = host.isTransitioning();
-
- // Restore the page
+ final boolean hostIsTransitioning = getTabHost().isInTransition();
int page = getPageForComponent(mSaveInstanceStateItemIndex);
invalidatePageData(Math.max(0, page), hostIsTransitioning);
-
- // Show All Apps cling if we are finished transitioning, otherwise, we will try again when
- // the transition completes in AppsCustomizeTabHost (otherwise the wrong offsets will be
- // returned while animating)
- if (!hostIsTransitioning) {
- post(new Runnable() {
- @Override
- public void run() {
- showAllAppsCling();
- }
- });
- }
}
- void showAllAppsCling() {
- if (!mHasShownAllAppsCling && isDataReady()) {
- mHasShownAllAppsCling = true;
- // Calculate the position for the cling punch through
- int[] offset = new int[2];
- int[] pos = mWidgetSpacingLayout.estimateCellPosition(mClingFocusedX, mClingFocusedY);
- mLauncher.getDragLayer().getLocationInDragLayer(this, offset);
- // PagedViews are centered horizontally but top aligned
- // Note we have to shift the items up now that Launcher sits under the status bar
- pos[0] += (getMeasuredWidth() - mWidgetSpacingLayout.getMeasuredWidth()) / 2 +
- offset[0];
- pos[1] += offset[1] - mLauncher.getDragLayer().getPaddingTop();
- }
- }
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ super.onLayout(changed, l, t, r, b);
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- int width = MeasureSpec.getSize(widthMeasureSpec);
- int height = MeasureSpec.getSize(heightMeasureSpec);
if (!isDataReady()) {
if ((LauncherAppState.isDisableAllApps() || !mApps.isEmpty()) && !mWidgets.isEmpty()) {
- setDataIsReady();
- setMeasuredDimension(width, height);
- onDataReady(width, height);
+ post(new Runnable() {
+ // This code triggers requestLayout so must be posted outside of the
+ // layout pass.
+ public void run() {
+ boolean attached = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ attached = isAttachedToWindow();
+ }
+ if (attached) {
+ setDataIsReady();
+ onDataReady(getMeasuredWidth(), getMeasuredHeight());
+ }
+ }
+ });
}
}
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
public void onPackagesUpdated(ArrayList<Object> widgetsAndShortcuts) {
@@ -450,7 +392,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
if (!app.shouldShowAppOrWidgetProvider(widget.provider)) {
continue;
}
- widget.label = widget.label.trim();
if (widget.minWidth > 0 && widget.minHeight > 0) {
// Ensure that all widgets we show can be added on a workspace of this size
int[] spanXY = Launcher.getSpanForWidget(mLauncher, widget);
@@ -500,40 +441,29 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
@Override
public void onClick(View v) {
// When we have exited all apps or are in transition, disregard clicks
- if (!mLauncher.isAllAppsVisible() ||
- mLauncher.getWorkspace().isSwitchingState()) return;
-
- if (v instanceof PagedViewIcon) {
- // Animate some feedback to the click
- final AppInfo appInfo = (AppInfo) v.getTag();
+ if (!mLauncher.isAllAppsVisible()
+ || mLauncher.getWorkspace().isSwitchingState()
+ || !(v instanceof PagedViewWidget)) return;
- // Lock the drawable state to pressed until we return to Launcher
- if (mPressedIcon != null) {
- mPressedIcon.lockDrawableState();
- }
- mLauncher.startActivitySafely(v, appInfo.intent, appInfo);
- mLauncher.getStats().recordLaunch(appInfo.intent);
- } else if (v instanceof PagedViewWidget) {
- // Let the user know that they have to long press to add a widget
- if (mWidgetInstructionToast != null) {
- mWidgetInstructionToast.cancel();
- }
- mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
- Toast.LENGTH_SHORT);
- mWidgetInstructionToast.show();
-
- // Create a little animation to show that the widget can move
- float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
- final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
- AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
- ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
- tyuAnim.setDuration(125);
- ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
- tydAnim.setDuration(100);
- bounce.play(tyuAnim).before(tydAnim);
- bounce.setInterpolator(new AccelerateInterpolator());
- bounce.start();
+ // Let the user know that they have to long press to add a widget
+ if (mWidgetInstructionToast != null) {
+ mWidgetInstructionToast.cancel();
}
+ mWidgetInstructionToast = Toast.makeText(getContext(),R.string.long_press_widget_to_add,
+ Toast.LENGTH_SHORT);
+ mWidgetInstructionToast.show();
+
+ // Create a little animation to show that the widget can move
+ float offsetY = getResources().getDimensionPixelSize(R.dimen.dragViewOffsetY);
+ final ImageView p = (ImageView) v.findViewById(R.id.widget_preview);
+ AnimatorSet bounce = LauncherAnimUtils.createAnimatorSet();
+ ValueAnimator tyuAnim = LauncherAnimUtils.ofFloat(p, "translationY", offsetY);
+ tyuAnim.setDuration(125);
+ ValueAnimator tydAnim = LauncherAnimUtils.ofFloat(p, "translationY", 0f);
+ tydAnim.setDuration(100);
+ bounce.play(tyuAnim).before(tydAnim);
+ bounce.setInterpolator(new AccelerateInterpolator());
+ bounce.start();
}
public boolean onKey(View v, int keyCode, KeyEvent event) {
@@ -549,30 +479,29 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
}
private void beginDraggingApplication(View v) {
- mLauncher.getWorkspace().onDragStartedWithItem(v);
mLauncher.getWorkspace().beginDragShared(v, this);
}
- Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
+ static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
Bundle options = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
- AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, mTmpRect);
- Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(mLauncher,
+ AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, sTmpRect);
+ Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
info.componentName, null);
- float density = getResources().getDisplayMetrics().density;
+ float density = launcher.getResources().getDisplayMetrics().density;
int xPaddingDips = (int) ((padding.left + padding.right) / density);
int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
options = new Bundle();
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
- mTmpRect.left - xPaddingDips);
+ sTmpRect.left - xPaddingDips);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
- mTmpRect.top - yPaddingDips);
+ sTmpRect.top - yPaddingDips);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
- mTmpRect.right - xPaddingDips);
+ sTmpRect.right - xPaddingDips);
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
- mTmpRect.bottom - yPaddingDips);
+ sTmpRect.bottom - yPaddingDips);
}
return options;
}
@@ -591,18 +520,9 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
@Override
public void run() {
mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
- // Options will be null for platforms with JB or lower, so this serves as an
- // SDK level check.
- if (options == null) {
- if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
- mWidgetLoadingId, info.componentName)) {
- mWidgetCleanupState = WIDGET_BOUND;
- }
- } else {
- if (AppWidgetManager.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
- mWidgetLoadingId, info.componentName, options)) {
- mWidgetCleanupState = WIDGET_BOUND;
- }
+ if(AppWidgetManagerCompat.getInstance(mLauncher).bindAppWidgetIdIfAllowed(
+ mWidgetLoadingId, pInfo, options)) {
+ mWidgetCleanupState = WIDGET_BOUND;
}
}
};
@@ -730,9 +650,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
int[] previewSizeBeforeScale = new int[1];
- preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.componentName,
- createWidgetInfo.previewImage, createWidgetInfo.icon, spanX, spanY,
- maxWidth, maxHeight, null, previewSizeBeforeScale);
+ preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
+ spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale);
// Compare the size of the drag preview to the preview in the AppsCustomize tray
int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
@@ -749,15 +668,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
} else {
PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag();
Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.shortcutActivityInfo);
- preview = Bitmap.createBitmap(icon.getIntrinsicWidth(),
- icon.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
-
- mCanvas.setBitmap(preview);
- mCanvas.save();
- WidgetPreviewLoader.renderDrawableToBitmap(icon, preview, 0, 0,
- icon.getIntrinsicWidth(), icon.getIntrinsicHeight());
- mCanvas.restore();
- mCanvas.setBitmap(null);
+ preview = Utilities.createIconBitmap(icon, mLauncher);
createItemInfo.spanX = createItemInfo.spanY = 1;
}
@@ -783,7 +694,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
protected boolean beginDragging(final View v) {
if (!super.beginDragging(v)) return false;
- if (v instanceof PagedViewIcon) {
+ if (v instanceof BubbleTextView) {
beginDraggingApplication(v);
} else if (v instanceof PagedViewWidget) {
if (!beginDraggingWidget(v)) {
@@ -798,9 +709,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
public void run() {
// We don't enter spring-loaded mode if the drag has been cancelled
if (mLauncher.getDragController().isDragging()) {
- // Reset the alpha on the dragged icon before we drag
- resetDrawableState();
-
// Go into spring loaded mode (must happen before we startDrag())
mLauncher.enterSpringLoadedDragMode();
}
@@ -820,13 +728,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
!(target instanceof DeleteDropTarget) && !(target instanceof Folder))) {
// Exit spring loaded mode if we have not successfully dropped or have not handled the
// drop in Workspace
- mLauncher.getWorkspace().removeExtraEmptyScreen(true, new Runnable() {
- @Override
- public void run() {
- mLauncher.exitSpringLoadedDragMode();
- mLauncher.unlockScreenOrientation(false);
- }
- });
+ mLauncher.exitSpringLoadedDragMode();
+ mLauncher.unlockScreenOrientation(false);
} else {
mLauncher.unlockScreenOrientation(false);
}
@@ -1019,13 +922,28 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
setVisibilityOnChildren(layout, View.GONE);
int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
- layout.setMinimumWidth(getPageContentWidth());
layout.measure(widthSpec, heightSpec);
- layout.setPadding(mAllAppsPadding.left, mAllAppsPadding.top, mAllAppsPadding.right,
- mAllAppsPadding.bottom);
+
+ Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel);
+ if (bg != null) {
+ bg.setAlpha(mPageBackgroundsVisible ? 255: 0);
+ layout.setBackground(bg);
+ }
+
setVisibilityOnChildren(layout, View.VISIBLE);
}
+ public void setPageBackgroundsVisible(boolean visible) {
+ mPageBackgroundsVisible = visible;
+ int childCount = getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ Drawable bg = getChildAt(i).getBackground();
+ if (bg != null) {
+ bg.setAlpha(visible ? 255 : 0);
+ }
+ }
+ }
+
public void syncAppsPageItems(int page, boolean immediate) {
// ensure that we have the right number of items on the pages
final boolean isRtl = isLayoutRtl();
@@ -1039,13 +957,14 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
ArrayList<Bitmap> images = new ArrayList<Bitmap>();
for (int i = startIndex; i < endIndex; ++i) {
AppInfo info = mApps.get(i);
- PagedViewIcon icon = (PagedViewIcon) mLayoutInflater.inflate(
+ BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
R.layout.apps_customize_application, layout, false);
- icon.applyFromApplicationInfo(info, true, this);
- icon.setOnClickListener(this);
+ icon.applyFromApplicationInfo(info);
+ icon.setOnClickListener(mLauncher);
icon.setOnLongClickListener(this);
icon.setOnTouchListener(this);
icon.setOnKeyListener(this);
+ icon.setOnFocusChangeListener(layout.mFocusHandlerView);
int index = i - startIndex;
int x = index % mCellCountX;
@@ -1168,21 +1087,27 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// immediately after syncing, we don't have a proper width.
int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
- layout.setMinimumWidth(getPageContentWidth());
+
+ Drawable bg = getContext().getResources().getDrawable(R.drawable.quantum_panel_dark);
+ if (bg != null) {
+ bg.setAlpha(mPageBackgroundsVisible ? 255 : 0);
+ layout.setBackground(bg);
+ }
layout.measure(widthSpec, heightSpec);
}
public void syncWidgetPageItems(final int page, final boolean immediate) {
int numItemsPerPage = mWidgetCountX * mWidgetCountY;
+ final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
+
// Calculate the dimensions of each cell we are giving to each widget
final ArrayList<Object> items = new ArrayList<Object>();
- int contentWidth = mContentWidth;
- final int cellWidth = ((contentWidth - mPageLayoutPaddingLeft - mPageLayoutPaddingRight
- - ((mWidgetCountX - 1) * mWidgetWidthGap)) / mWidgetCountX);
- int contentHeight = mContentHeight;
- final int cellHeight = ((contentHeight - mPageLayoutPaddingTop - mPageLayoutPaddingBottom
- - ((mWidgetCountY - 1) * mWidgetHeightGap)) / mWidgetCountY);
+ int contentWidth = mContentWidth - layout.getPaddingLeft() - layout.getPaddingRight();
+ final int cellWidth = contentWidth / mWidgetCountX;
+ int contentHeight = mContentHeight - layout.getPaddingTop() - layout.getPaddingBottom();
+
+ final int cellHeight = contentHeight / mWidgetCountY;
// Prepare the set of widgets to load previews for in the background
int offset = page * numItemsPerPage;
@@ -1191,7 +1116,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
}
// Prepopulate the pages with the other widget info, and fill in the previews later
- final PagedViewGridLayout layout = (PagedViewGridLayout) getPageAt(page);
layout.setColumnCount(layout.getCellCountX());
for (int i = 0; i < items.size(); ++i) {
Object rawInfo = items.get(i);
@@ -1232,14 +1156,22 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// Layout each widget
int ix = i % mWidgetCountX;
int iy = i / mWidgetCountX;
+
+ if (ix > 0) {
+ View border = widget.findViewById(R.id.left_border);
+ border.setVisibility(View.VISIBLE);
+ }
+ if (ix < mWidgetCountX - 1) {
+ View border = widget.findViewById(R.id.right_border);
+ border.setVisibility(View.VISIBLE);
+ }
+
GridLayout.LayoutParams lp = new GridLayout.LayoutParams(
GridLayout.spec(iy, GridLayout.START),
GridLayout.spec(ix, GridLayout.TOP));
lp.width = cellWidth;
lp.height = cellHeight;
lp.setGravity(Gravity.TOP | Gravity.START);
- if (ix > 0) lp.leftMargin = mWidgetWidthGap;
- if (iy > 0) lp.topMargin = mWidgetHeightGap;
layout.addView(widget, lp);
}
@@ -1389,86 +1321,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// In apps customize, we have a scrolling effect which emulates pulling cards off of a stack.
@Override
protected void screenScrolled(int screenCenter) {
- final boolean isRtl = isLayoutRtl();
super.screenScrolled(screenCenter);
-
- for (int i = 0; i < getChildCount(); i++) {
- View v = getPageAt(i);
- if (v != null) {
- float scrollProgress = getScrollProgress(screenCenter, v, i);
-
- float interpolatedProgress;
- float translationX;
- float maxScrollProgress = Math.max(0, scrollProgress);
- float minScrollProgress = Math.min(0, scrollProgress);
-
- if (isRtl) {
- translationX = maxScrollProgress * v.getMeasuredWidth();
- interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(maxScrollProgress));
- } else {
- translationX = minScrollProgress * v.getMeasuredWidth();
- interpolatedProgress = mZInterpolator.getInterpolation(Math.abs(minScrollProgress));
- }
- float scale = (1 - interpolatedProgress) +
- interpolatedProgress * TRANSITION_SCALE_FACTOR;
-
- float alpha;
- if (isRtl && (scrollProgress > 0)) {
- alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(maxScrollProgress));
- } else if (!isRtl && (scrollProgress < 0)) {
- alpha = mAlphaInterpolator.getInterpolation(1 - Math.abs(scrollProgress));
- } else {
- // On large screens we need to fade the page as it nears its leftmost position
- alpha = mLeftScreenAlphaInterpolator.getInterpolation(1 - scrollProgress);
- }
-
- v.setCameraDistance(mDensity * CAMERA_DISTANCE);
- int pageWidth = v.getMeasuredWidth();
- int pageHeight = v.getMeasuredHeight();
-
- if (PERFORM_OVERSCROLL_ROTATION) {
- float xPivot = isRtl ? 1f - TRANSITION_PIVOT : TRANSITION_PIVOT;
- boolean isOverscrollingFirstPage = isRtl ? scrollProgress > 0 : scrollProgress < 0;
- boolean isOverscrollingLastPage = isRtl ? scrollProgress < 0 : scrollProgress > 0;
-
- if (i == 0 && isOverscrollingFirstPage) {
- // Overscroll to the left
- v.setPivotX(xPivot * pageWidth);
- v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
- scale = 1.0f;
- alpha = 1.0f;
- // On the first page, we don't want the page to have any lateral motion
- translationX = 0;
- } else if (i == getChildCount() - 1 && isOverscrollingLastPage) {
- // Overscroll to the right
- v.setPivotX((1 - xPivot) * pageWidth);
- v.setRotationY(-TRANSITION_MAX_ROTATION * scrollProgress);
- scale = 1.0f;
- alpha = 1.0f;
- // On the last page, we don't want the page to have any lateral motion.
- translationX = 0;
- } else {
- v.setPivotY(pageHeight / 2.0f);
- v.setPivotX(pageWidth / 2.0f);
- v.setRotationY(0f);
- }
- }
-
- v.setTranslationX(translationX);
- v.setScaleX(scale);
- v.setScaleY(scale);
- v.setAlpha(alpha);
-
- // If the view has 0 alpha, we set it to be invisible so as to prevent
- // it from accepting touches
- if (alpha == 0) {
- v.setVisibility(INVISIBLE);
- } else if (v.getVisibility() != VISIBLE) {
- v.setVisibility(VISIBLE);
- }
- }
- }
-
enableHwLayersOnVisiblePages();
}
@@ -1513,7 +1366,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
}
protected void overScroll(float amount) {
- acceleratedOverScroll(amount);
+ dampedOverScroll(amount);
}
/**
@@ -1587,7 +1440,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
int length = list.size();
for (int i = 0; i < length; ++i) {
AppInfo info = list.get(i);
- if (info.intent.getComponent().equals(removeComponent)) {
+ if (info.user.equals(item.user)
+ && info.intent.getComponent().equals(removeComponent)) {
return i;
}
}
@@ -1625,12 +1479,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// If we have reset, then we should not continue to restore the previous state
mSaveInstanceStateItemIndex = -1;
- AppsCustomizeTabHost tabHost = getTabHost();
- String tag = tabHost.getCurrentTabTag();
- if (tag != null) {
- if (!tag.equals(tabHost.getTabTagForContentType(ContentType.Applications))) {
- tabHost.setCurrentTabFromContent(ContentType.Applications);
- }
+ if (mContentType != ContentType.Applications) {
+ setContentType(ContentType.Applications);
}
if (mCurrentPage != 0) {
@@ -1674,23 +1524,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
cancelAllTasks();
}
- @Override
- public void iconPressed(PagedViewIcon icon) {
- // Reset the previously pressed icon and store a reference to the pressed icon so that
- // we can reset it on return to Launcher (in Launcher.onResume())
- if (mPressedIcon != null) {
- mPressedIcon.resetDrawableState();
- }
- mPressedIcon = icon;
- }
-
- public void resetDrawableState() {
- if (mPressedIcon != null) {
- mPressedIcon.resetDrawableState();
- mPressedIcon = null;
- }
- }
-
/*
* We load an extra page on each side to prevent flashes from scrolling and loading of the
* widget previews in the background with the AsyncTasks.
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index bb7f045ce..9a516fd41 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -29,6 +29,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TabHost;
@@ -37,35 +38,20 @@ import android.widget.TextView;
import java.util.ArrayList;
-public class AppsCustomizeTabHost extends TabHost implements LauncherTransitionable,
- TabHost.OnTabChangeListener, Insettable {
+public class AppsCustomizeTabHost extends FrameLayout implements LauncherTransitionable, Insettable {
static final String LOG_TAG = "AppsCustomizeTabHost";
private static final String APPS_TAB_TAG = "APPS";
private static final String WIDGETS_TAB_TAG = "WIDGETS";
- private final LayoutInflater mLayoutInflater;
- private ViewGroup mTabs;
- private ViewGroup mTabsContainer;
- private AppsCustomizePagedView mAppsCustomizePane;
- private FrameLayout mAnimationBuffer;
- private LinearLayout mContent;
-
- private boolean mInTransition;
- private boolean mTransitioningToWorkspace;
- private boolean mResetAfterTransition;
- private Runnable mRelayoutAndMakeVisible;
+ private AppsCustomizePagedView mPagedView;
+ private View mContent;
+ private boolean mInTransition = false;
+
private final Rect mInsets = new Rect();
public AppsCustomizeTabHost(Context context, AttributeSet attrs) {
super(context, attrs);
- mLayoutInflater = LayoutInflater.from(context);
- mRelayoutAndMakeVisible = new Runnable() {
- public void run() {
- mTabs.requestLayout();
- mTabsContainer.setAlpha(1f);
- }
- };
}
/**
@@ -75,17 +61,17 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
* tabs manually).
*/
void setContentTypeImmediate(AppsCustomizePagedView.ContentType type) {
- setOnTabChangedListener(null);
- onTabChangedStart();
- onTabChangedEnd(type);
- setCurrentTabByTag(getTabTagForContentType(type));
- setOnTabChangedListener(this);
+ mPagedView.setContentType(type);
+ }
+
+ public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) {
+ setContentTypeImmediate(type);
}
@Override
public void setInsets(Rect insets) {
mInsets.set(insets);
- FrameLayout.LayoutParams flp = (LayoutParams) mContent.getLayoutParams();
+ LayoutParams flp = (LayoutParams) mContent.getLayoutParams();
flp.topMargin = insets.top;
flp.bottomMargin = insets.bottom;
flp.leftMargin = insets.left;
@@ -98,212 +84,12 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
*/
@Override
protected void onFinishInflate() {
- // Setup the tab host
- setup();
-
- final ViewGroup tabsContainer = (ViewGroup) findViewById(R.id.tabs_container);
- final TabWidget tabs = getTabWidget();
- final AppsCustomizePagedView appsCustomizePane = (AppsCustomizePagedView)
- findViewById(R.id.apps_customize_pane_content);
- mTabs = tabs;
- mTabsContainer = tabsContainer;
- mAppsCustomizePane = appsCustomizePane;
- mAnimationBuffer = (FrameLayout) findViewById(R.id.animation_buffer);
- mContent = (LinearLayout) findViewById(R.id.apps_customize_content);
- if (tabs == null || mAppsCustomizePane == null) throw new Resources.NotFoundException();
-
- // Configure the tabs content factory to return the same paged view (that we change the
- // content filter on)
- TabContentFactory contentFactory = new TabContentFactory() {
- public View createTabContent(String tag) {
- return appsCustomizePane;
- }
- };
-
- // Create the tabs
- TextView tabView;
- String label;
- label = getContext().getString(R.string.all_apps_button_label);
- tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false);
- tabView.setText(label);
- tabView.setContentDescription(label);
- addTab(newTabSpec(APPS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));
- label = getContext().getString(R.string.widgets_tab_label);
- tabView = (TextView) mLayoutInflater.inflate(R.layout.tab_widget_indicator, tabs, false);
- tabView.setText(label);
- tabView.setContentDescription(label);
- addTab(newTabSpec(WIDGETS_TAB_TAG).setIndicator(tabView).setContent(contentFactory));
- setOnTabChangedListener(this);
-
- // Setup the key listener to jump between the last tab view and the market icon
- AppsCustomizeTabKeyEventListener keyListener = new AppsCustomizeTabKeyEventListener();
- View lastTab = tabs.getChildTabViewAt(tabs.getTabCount() - 1);
- lastTab.setOnKeyListener(keyListener);
- View shopButton = findViewById(R.id.market_button);
- shopButton.setOnKeyListener(keyListener);
-
- // Hide the tab bar until we measure
- mTabsContainer.setAlpha(0f);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- boolean remeasureTabWidth = (mTabs.getLayoutParams().width <= 0);
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
-
- // Set the width of the tab list to the content width
- if (remeasureTabWidth) {
- int contentWidth = mAppsCustomizePane.getPageContentWidth();
- if (contentWidth > 0 && mTabs.getLayoutParams().width != contentWidth) {
- // Set the width and show the tab bar
- mTabs.getLayoutParams().width = contentWidth;
- mRelayoutAndMakeVisible.run();
- }
-
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- }
- }
-
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- // If we are mid transitioning to the workspace, then intercept touch events here so we
- // can ignore them, otherwise we just let all apps handle the touch events.
- if (mInTransition && mTransitioningToWorkspace) {
- return true;
- }
- return super.onInterceptTouchEvent(ev);
- };
-
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // Allow touch events to fall through to the workspace if we are transitioning there
- if (mInTransition && mTransitioningToWorkspace) {
- return super.onTouchEvent(event);
- }
-
- // Intercept all touch events up to the bottom of the AppsCustomizePane so they do not fall
- // through to the workspace and trigger showWorkspace()
- if (event.getY() < mAppsCustomizePane.getBottom()) {
- return true;
- }
- return super.onTouchEvent(event);
- }
-
- private void onTabChangedStart() {
+ mPagedView = (AppsCustomizePagedView) findViewById(R.id.apps_customize_pane_content);
+ mContent = findViewById(R.id.content);
}
- private void reloadCurrentPage() {
- mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
- mAppsCustomizePane.requestFocus();
- }
-
- private void onTabChangedEnd(AppsCustomizePagedView.ContentType type) {
- int bgAlpha = (int) (255 * (getResources().getInteger(
- R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f));
- setBackgroundColor(Color.argb(bgAlpha, 0, 0, 0));
- mAppsCustomizePane.setContentType(type);
- }
-
- @Override
- public void onTabChanged(String tabId) {
- final AppsCustomizePagedView.ContentType type = getContentTypeForTabTag(tabId);
-
- // Animate the changing of the tab content by fading pages in and out
- final Resources res = getResources();
- final int duration = res.getInteger(R.integer.config_tabTransitionDuration);
-
- // We post a runnable here because there is a delay while the first page is loading and
- // the feedback from having changed the tab almost feels better than having it stick
- post(new Runnable() {
- @Override
- public void run() {
- if (mAppsCustomizePane.getMeasuredWidth() <= 0 ||
- mAppsCustomizePane.getMeasuredHeight() <= 0) {
- reloadCurrentPage();
- return;
- }
-
- // Take the visible pages and re-parent them temporarily to mAnimatorBuffer
- // and then cross fade to the new pages
- int[] visiblePageRange = new int[2];
- mAppsCustomizePane.getVisiblePages(visiblePageRange);
- if (visiblePageRange[0] == -1 && visiblePageRange[1] == -1) {
- // If we can't get the visible page ranges, then just skip the animation
- reloadCurrentPage();
- return;
- }
- ArrayList<View> visiblePages = new ArrayList<View>();
- for (int i = visiblePageRange[0]; i <= visiblePageRange[1]; i++) {
- visiblePages.add(mAppsCustomizePane.getPageAt(i));
- }
-
- // We want the pages to be rendered in exactly the same way as they were when
- // their parent was mAppsCustomizePane -- so set the scroll on mAnimationBuffer
- // to be exactly the same as mAppsCustomizePane, and below, set the left/top
- // parameters to be correct for each of the pages
- mAnimationBuffer.scrollTo(mAppsCustomizePane.getScrollX(), 0);
-
- // mAppsCustomizePane renders its children in reverse order, so
- // add the pages to mAnimationBuffer in reverse order to match that behavior
- for (int i = visiblePages.size() - 1; i >= 0; i--) {
- View child = visiblePages.get(i);
- if (child instanceof AppsCustomizeCellLayout) {
- ((AppsCustomizeCellLayout) child).resetChildrenOnKeyListeners();
- } else if (child instanceof PagedViewGridLayout) {
- ((PagedViewGridLayout) child).resetChildrenOnKeyListeners();
- }
- PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(false);
- mAppsCustomizePane.removeView(child);
- PagedViewWidget.setDeletePreviewsWhenDetachedFromWindow(true);
- mAnimationBuffer.setAlpha(1f);
- mAnimationBuffer.setVisibility(View.VISIBLE);
- LayoutParams p = new FrameLayout.LayoutParams(child.getMeasuredWidth(),
- child.getMeasuredHeight());
- p.setMargins((int) child.getLeft(), (int) child.getTop(), 0, 0);
- mAnimationBuffer.addView(child, p);
- }
-
- // Toggle the new content
- onTabChangedStart();
- onTabChangedEnd(type);
-
- // Animate the transition
- ObjectAnimator outAnim = LauncherAnimUtils.ofFloat(mAnimationBuffer, "alpha", 0f);
- outAnim.addListener(new AnimatorListenerAdapter() {
- private void clearAnimationBuffer() {
- mAnimationBuffer.setVisibility(View.GONE);
- PagedViewWidget.setRecyclePreviewsWhenDetachedFromWindow(false);
- mAnimationBuffer.removeAllViews();
- PagedViewWidget.setRecyclePreviewsWhenDetachedFromWindow(true);
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- clearAnimationBuffer();
- }
- @Override
- public void onAnimationCancel(Animator animation) {
- clearAnimationBuffer();
- }
- });
- ObjectAnimator inAnim = LauncherAnimUtils.ofFloat(mAppsCustomizePane, "alpha", 1f);
- inAnim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- reloadCurrentPage();
- }
- });
-
- final AnimatorSet animSet = LauncherAnimUtils.createAnimatorSet();
- animSet.playTogether(outAnim, inAnim);
- animSet.setDuration(duration);
- animSet.start();
- }
- });
- }
-
- public void setCurrentTabFromContent(AppsCustomizePagedView.ContentType type) {
- setOnTabChangedListener(null);
- setCurrentTabByTag(getTabTagForContentType(type));
- setOnTabChangedListener(this);
+ public String getContentTag() {
+ return getTabTagForContentType(mPagedView.getContentType());
}
/**
@@ -342,44 +128,41 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
}
void reset() {
- if (mInTransition) {
- // Defer to after the transition to reset
- mResetAfterTransition = true;
- } else {
- // Reset immediately
- mAppsCustomizePane.reset();
- }
+ // Reset immediately
+ mPagedView.reset();
}
- private void enableAndBuildHardwareLayer() {
- // isHardwareAccelerated() checks if we're attached to a window and if that
- // window is HW accelerated-- we were sometimes not attached to a window
- // and buildLayer was throwing an IllegalStateException
- if (isHardwareAccelerated()) {
- // Turn on hardware layers for performance
- setLayerType(LAYER_TYPE_HARDWARE, null);
-
- // force building the layer, so you don't get a blip early in an animation
- // when the layer is created layer
- buildLayer();
+ public void onWindowVisible() {
+ if (getVisibility() == VISIBLE) {
+ mContent.setVisibility(VISIBLE);
+ // We unload the widget previews when the UI is hidden, so need to reload pages
+ // Load the current page synchronously, and the neighboring pages asynchronously
+ mPagedView.loadAssociatedPages(mPagedView.getCurrentPage(), true);
+ mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
}
}
+ public void onTrimMemory() {
+ mContent.setVisibility(GONE);
+ // Clear the widget pages of all their subviews - this will trigger the widget previews
+ // to delete their bitmaps
+ mPagedView.clearAllWidgetPages();
+ }
+
@Override
- public View getContent() {
- View appsCustomizeContent = mAppsCustomizePane.getContent();
- if (appsCustomizeContent != null) {
- return appsCustomizeContent;
- }
- return mContent;
+ public ViewGroup getContent() {
+ return mPagedView;
+ }
+
+ public boolean isInTransition() {
+ return mInTransition;
}
/* LauncherTransitionable overrides */
@Override
public void onLauncherTransitionPrepare(Launcher l, boolean animated, boolean toWorkspace) {
- mAppsCustomizePane.onLauncherTransitionPrepare(l, animated, toWorkspace);
+ mPagedView.onLauncherTransitionPrepare(l, animated, toWorkspace);
mInTransition = true;
- mTransitioningToWorkspace = toWorkspace;
if (toWorkspace) {
// Going from All Apps -> Workspace
@@ -390,45 +173,38 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
// Make sure the current page is loaded (we start loading the side pages after the
// transition to prevent slowing down the animation)
- mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
- }
-
- if (mResetAfterTransition) {
- mAppsCustomizePane.reset();
- mResetAfterTransition = false;
+ // TODO: revisit this
+ mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
}
}
@Override
public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
- mAppsCustomizePane.onLauncherTransitionStart(l, animated, toWorkspace);
- if (animated) {
- enableAndBuildHardwareLayer();
- }
-
- // Dismiss the workspace cling
- l.getLauncherClings().dismissWorkspaceCling(null);
+ mPagedView.onLauncherTransitionStart(l, animated, toWorkspace);
}
@Override
public void onLauncherTransitionStep(Launcher l, float t) {
- mAppsCustomizePane.onLauncherTransitionStep(l, t);
+ mPagedView.onLauncherTransitionStep(l, t);
}
@Override
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
- mAppsCustomizePane.onLauncherTransitionEnd(l, animated, toWorkspace);
+ mPagedView.onLauncherTransitionEnd(l, animated, toWorkspace);
mInTransition = false;
- if (animated) {
- setLayerType(LAYER_TYPE_NONE, null);
- }
if (!toWorkspace) {
- // Show the all apps cling (if not already shown)
- mAppsCustomizePane.showAllAppsCling();
// Make sure adjacent pages are loaded (we wait until after the transition to
// prevent slowing down the animation)
- mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
+ mPagedView.loadAssociatedPages(mPagedView.getCurrentPage());
+
+ // Opening apps, need to announce what page we are on.
+ AccessibilityManager am = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (am.isEnabled()) {
+ // Notify the user when the page changes
+ announceForAccessibility(mPagedView.getCurrentPageDescription());
+ }
// Going from Workspace -> All Apps
// NOTE: We should do this at the end since we check visibility state in some of the
@@ -459,25 +235,4 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
throw new RuntimeException("Failed; can't get z-order of views");
}
}
-
- public void onWindowVisible() {
- if (getVisibility() == VISIBLE) {
- mContent.setVisibility(VISIBLE);
- // We unload the widget previews when the UI is hidden, so need to reload pages
- // Load the current page synchronously, and the neighboring pages asynchronously
- mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage(), true);
- mAppsCustomizePane.loadAssociatedPages(mAppsCustomizePane.getCurrentPage());
- }
- }
-
- public void onTrimMemory() {
- mContent.setVisibility(GONE);
- // Clear the widget pages of all their subviews - this will trigger the widget previews
- // to delete their bitmaps
- mAppsCustomizePane.clearAllWidgetPages();
- }
-
- boolean isTransitioning() {
- return mInTransition;
- }
}
diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java
new file mode 100644
index 000000000..00f0cf36f
--- /dev/null
+++ b/src/com/android/launcher3/AutoInstallsLayout.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.database.sqlite.SQLiteDatabase;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Patterns;
+
+import com.android.launcher3.LauncherProvider.SqlArguments;
+import com.android.launcher3.LauncherProvider.WorkspaceLoader;
+import com.android.launcher3.LauncherSettings.Favorites;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+/**
+ * This class contains contains duplication of functionality as found in
+ * LauncherProvider#DatabaseHelper. It has been isolated and differentiated in order
+ * to cleanly and separately represent AutoInstall default layout format and policy.
+ */
+public class AutoInstallsLayout implements WorkspaceLoader {
+ private static final String TAG = "AutoInstalls";
+ private static final boolean LOGD = true;
+
+ /** Marker action used to discover a package which defines launcher customization */
+ static final String ACTION_LAUNCHER_CUSTOMIZATION =
+ "android.autoinstalls.config.action.PLAY_AUTO_INSTALL";
+
+ private static final String LAYOUT_RES = "default_layout";
+
+ static AutoInstallsLayout get(Context context, AppWidgetHost appWidgetHost,
+ LayoutParserCallback callback) {
+ Pair<String, Resources> customizationApkInfo = Utilities.findSystemApk(
+ ACTION_LAUNCHER_CUSTOMIZATION, context.getPackageManager());
+ if (customizationApkInfo == null) {
+ return null;
+ }
+
+ String pkg = customizationApkInfo.first;
+ Resources res = customizationApkInfo.second;
+ int layoutId = res.getIdentifier(LAYOUT_RES, "xml", pkg);
+ if (layoutId == 0) {
+ Log.e(TAG, "Layout definition not found in package: " + pkg);
+ return null;
+ }
+ return new AutoInstallsLayout(context, appWidgetHost, callback, pkg, res, layoutId);
+ }
+
+ // Object Tags
+ private static final String TAG_WORKSPACE = "workspace";
+ private static final String TAG_APP_ICON = "appicon";
+ private static final String TAG_AUTO_INSTALL = "autoinstall";
+ private static final String TAG_FOLDER = "folder";
+ private static final String TAG_APPWIDGET = "appwidget";
+ private static final String TAG_SHORTCUT = "shortcut";
+ private static final String TAG_EXTRA = "extra";
+
+ private static final String ATTR_CONTAINER = "container";
+ private static final String ATTR_RANK = "rank";
+
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_CLASS_NAME = "className";
+ private static final String ATTR_TITLE = "title";
+ private static final String ATTR_SCREEN = "screen";
+ private static final String ATTR_X = "x";
+ private static final String ATTR_Y = "y";
+ private static final String ATTR_SPAN_X = "spanX";
+ private static final String ATTR_SPAN_Y = "spanY";
+ private static final String ATTR_ICON = "icon";
+ private static final String ATTR_URL = "url";
+
+ // Style attrs -- "Extra"
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_VALUE = "value";
+
+ private static final String HOTSEAT_CONTAINER_NAME =
+ Favorites.containerToString(Favorites.CONTAINER_HOTSEAT);
+
+ private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
+ "com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
+
+ private final Context mContext;
+ private final AppWidgetHost mAppWidgetHost;
+ private final LayoutParserCallback mCallback;
+
+ private final PackageManager mPackageManager;
+ private final ContentValues mValues;
+
+ private final Resources mRes;
+ private final int mLayoutId;
+
+ private SQLiteDatabase mDb;
+
+ public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
+ LayoutParserCallback callback, String packageName, Resources res, int layoutId) {
+ mContext = context;
+ mAppWidgetHost = appWidgetHost;
+ mCallback = callback;
+
+ mPackageManager = context.getPackageManager();
+ mValues = new ContentValues();
+
+ mRes = res;
+ mLayoutId = layoutId;
+ }
+
+ @Override
+ public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
+ mDb = db;
+ try {
+ return parseLayout(mRes, mLayoutId, screenIds);
+ } catch (XmlPullParserException | IOException | RuntimeException e) {
+ Log.w(TAG, "Got exception parsing layout.", e);
+ return -1;
+ }
+ }
+
+ private int parseLayout(Resources res, int layoutId, ArrayList<Long> screenIds)
+ throws XmlPullParserException, IOException {
+ final int hotseatAllAppsRank = LauncherAppState.getInstance()
+ .getDynamicGrid().getDeviceProfile().hotseatAllAppsRank;
+
+ XmlResourceParser parser = res.getXml(layoutId);
+ beginDocument(parser, TAG_WORKSPACE);
+ final int depth = parser.getDepth();
+ int type;
+ HashMap<String, TagParser> tagParserMap = getLayoutElementsMap();
+ int count = 0;
+
+ while (((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ mValues.clear();
+ final int container;
+ final long screenId;
+
+ if (HOTSEAT_CONTAINER_NAME.equals(getAttributeValue(parser, ATTR_CONTAINER))) {
+ container = Favorites.CONTAINER_HOTSEAT;
+
+ // Hack: hotseat items are stored using screen ids
+ long rank = Long.parseLong(getAttributeValue(parser, ATTR_RANK));
+ screenId = (rank < hotseatAllAppsRank) ? rank : (rank + 1);
+
+ } else {
+ container = Favorites.CONTAINER_DESKTOP;
+ screenId = Long.parseLong(getAttributeValue(parser, ATTR_SCREEN));
+
+ mValues.put(Favorites.CELLX, getAttributeValue(parser, ATTR_X));
+ mValues.put(Favorites.CELLY, getAttributeValue(parser, ATTR_Y));
+ }
+
+ mValues.put(Favorites.CONTAINER, container);
+ mValues.put(Favorites.SCREEN, screenId);
+
+ TagParser tagParser = tagParserMap.get(parser.getName());
+ if (tagParser == null) {
+ if (LOGD) Log.d(TAG, "Ignoring unknown element tag: " + parser.getName());
+ continue;
+ }
+ long newElementId = tagParser.parseAndAdd(parser, res);
+ if (newElementId >= 0) {
+ // Keep track of the set of screens which need to be added to the db.
+ if (!screenIds.contains(screenId) &&
+ container == Favorites.CONTAINER_DESKTOP) {
+ screenIds.add(screenId);
+ }
+ count++;
+ }
+ }
+ return count;
+ }
+
+ protected long addShortcut(String title, Intent intent, int type) {
+ long id = mCallback.generateNewItemId();
+ mValues.put(Favorites.INTENT, intent.toUri(0));
+ mValues.put(Favorites.TITLE, title);
+ mValues.put(Favorites.ITEM_TYPE, type);
+ mValues.put(Favorites.SPANX, 1);
+ mValues.put(Favorites.SPANY, 1);
+ mValues.put(Favorites._ID, id);
+ if (mCallback.insertAndCheck(mDb, mValues) < 0) {
+ return -1;
+ } else {
+ return id;
+ }
+ }
+
+ protected HashMap<String, TagParser> getFolderElementsMap() {
+ HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ parsers.put(TAG_APP_ICON, new AppShortcutParser());
+ parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
+ parsers.put(TAG_SHORTCUT, new ShortcutParser());
+ return parsers;
+ }
+
+ protected HashMap<String, TagParser> getLayoutElementsMap() {
+ HashMap<String, TagParser> parsers = new HashMap<String, TagParser>();
+ parsers.put(TAG_APP_ICON, new AppShortcutParser());
+ parsers.put(TAG_AUTO_INSTALL, new AutoInstallParser());
+ parsers.put(TAG_FOLDER, new FolderParser());
+ parsers.put(TAG_APPWIDGET, new AppWidgetParser());
+ parsers.put(TAG_SHORTCUT, new ShortcutParser());
+ return parsers;
+ }
+
+ private interface TagParser {
+ /**
+ * Parses the tag and adds to the db
+ * @return the id of the row added or -1;
+ */
+ long parseAndAdd(XmlResourceParser parser, Resources res)
+ throws XmlPullParserException, IOException;
+ }
+
+ private class AppShortcutParser implements TagParser {
+
+ @Override
+ public long parseAndAdd(XmlResourceParser parser, Resources res) {
+ final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+ ActivityInfo info;
+ try {
+ ComponentName cn;
+ try {
+ cn = new ComponentName(packageName, className);
+ info = mPackageManager.getActivityInfo(cn, 0);
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ String[] packages = mPackageManager.currentToCanonicalPackageNames(
+ new String[] { packageName });
+ cn = new ComponentName(packages[0], className);
+ info = mPackageManager.getActivityInfo(cn, 0);
+ }
+ final Intent intent = new Intent(Intent.ACTION_MAIN, null)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(cn)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ return addShortcut(info.loadLabel(mPackageManager).toString(),
+ intent, Favorites.ITEM_TYPE_APPLICATION);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e);
+ }
+ return -1;
+ } else {
+ if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component or uri");
+ return -1;
+ }
+ }
+ }
+
+ private class AutoInstallParser implements TagParser {
+
+ @Override
+ public long parseAndAdd(XmlResourceParser parser, Resources res) {
+ final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+ if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
+ if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+ return -1;
+ }
+
+ mValues.put(Favorites.RESTORED, ShortcutInfo.FLAG_AUTOINTALL_ICON);
+ final Intent intent = new Intent(Intent.ACTION_MAIN, null)
+ .addCategory(Intent.CATEGORY_LAUNCHER)
+ .setComponent(new ComponentName(packageName, className))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ return addShortcut(mContext.getString(R.string.package_state_unknown), intent,
+ Favorites.ITEM_TYPE_APPLICATION);
+ }
+ }
+
+ private class ShortcutParser implements TagParser {
+
+ @Override
+ public long parseAndAdd(XmlResourceParser parser, Resources res) {
+ final String url = getAttributeValue(parser, ATTR_URL);
+ final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
+ final int iconId = getAttributeResourceValue(parser, ATTR_ICON, 0);
+
+ if (titleResId == 0 || iconId == 0) {
+ if (LOGD) Log.d(TAG, "Ignoring shortcut");
+ return -1;
+ }
+
+ if (TextUtils.isEmpty(url) || !Patterns.WEB_URL.matcher(url).matches()) {
+ if (LOGD) Log.d(TAG, "Ignoring shortcut, invalid url: " + url);
+ return -1;
+ }
+ Drawable icon = res.getDrawable(iconId);
+ if (icon == null) {
+ if (LOGD) Log.d(TAG, "Ignoring shortcut, can't load icon");
+ return -1;
+ }
+
+ ItemInfo.writeBitmap(mValues, Utilities.createIconBitmap(icon, mContext));
+ final Intent intent = new Intent(Intent.ACTION_VIEW, null)
+ .setData(Uri.parse(url))
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ return addShortcut(res.getString(titleResId), intent, Favorites.ITEM_TYPE_SHORTCUT);
+ }
+ }
+
+ private class AppWidgetParser implements TagParser {
+
+ @Override
+ public long parseAndAdd(XmlResourceParser parser, Resources res)
+ throws XmlPullParserException, IOException {
+ final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+ if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(className)) {
+ if (LOGD) Log.d(TAG, "Skipping invalid <favorite> with no component");
+ return -1;
+ }
+
+ ComponentName cn = new ComponentName(packageName, className);
+ try {
+ mPackageManager.getReceiverInfo(cn, 0);
+ } catch (Exception e) {
+ String[] packages = mPackageManager.currentToCanonicalPackageNames(
+ new String[] { packageName });
+ cn = new ComponentName(packages[0], className);
+ try {
+ mPackageManager.getReceiverInfo(cn, 0);
+ } catch (Exception e1) {
+ if (LOGD) Log.d(TAG, "Can't find widget provider: " + className);
+ return -1;
+ }
+ }
+
+ mValues.put(Favorites.SPANX, getAttributeValue(parser, ATTR_SPAN_X));
+ mValues.put(Favorites.SPANY, getAttributeValue(parser, ATTR_SPAN_Y));
+
+ // Read the extras
+ Bundle extras = new Bundle();
+ int widgetDepth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > widgetDepth) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (TAG_EXTRA.equals(parser.getName())) {
+ String key = getAttributeValue(parser, ATTR_KEY);
+ String value = getAttributeValue(parser, ATTR_VALUE);
+ if (key != null && value != null) {
+ extras.putString(key, value);
+ } else {
+ throw new RuntimeException("Widget extras must have a key and value");
+ }
+ } else {
+ throw new RuntimeException("Widgets can contain only extras");
+ }
+ }
+
+ final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+ long insertedId = -1;
+ try {
+ int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+
+ if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, cn)) {
+ if (LOGD) Log.e(TAG, "Unable to bind app widget id " + cn);
+ return -1;
+ }
+
+ mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
+ mValues.put(Favorites.APPWIDGET_ID, appWidgetId);
+ mValues.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
+ mValues.put(Favorites._ID, mCallback.generateNewItemId());
+ insertedId = mCallback.insertAndCheck(mDb, mValues);
+ if (insertedId < 0) {
+ mAppWidgetHost.deleteAppWidgetId(appWidgetId);
+ return insertedId;
+ }
+
+ // Send a broadcast to configure the widget
+ if (!extras.isEmpty()) {
+ Intent intent = new Intent(ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE);
+ intent.setComponent(cn);
+ intent.putExtras(extras);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ mContext.sendBroadcast(intent);
+ }
+ } catch (RuntimeException ex) {
+ if (LOGD) Log.e(TAG, "Problem allocating appWidgetId", ex);
+ }
+ return insertedId;
+ }
+ }
+
+ private class FolderParser implements TagParser {
+ private final HashMap<String, TagParser> mFolderElements = getFolderElementsMap();
+
+ @Override
+ public long parseAndAdd(XmlResourceParser parser, Resources res)
+ throws XmlPullParserException, IOException {
+ final String title;
+ final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
+ if (titleResId != 0) {
+ title = res.getString(titleResId);
+ } else {
+ title = mContext.getResources().getString(R.string.folder_name);
+ }
+
+ mValues.put(Favorites.TITLE, title);
+ mValues.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_FOLDER);
+ mValues.put(Favorites.SPANX, 1);
+ mValues.put(Favorites.SPANY, 1);
+ mValues.put(Favorites._ID, mCallback.generateNewItemId());
+ long folderId = mCallback.insertAndCheck(mDb, mValues);
+ if (folderId < 0) {
+ if (LOGD) Log.e(TAG, "Unable to add folder");
+ return -1;
+ }
+
+ final ContentValues myValues = new ContentValues(mValues);
+ ArrayList<Long> folderItems = new ArrayList<Long>();
+
+ int type;
+ int folderDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > folderDepth) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ mValues.clear();
+ mValues.put(Favorites.CONTAINER, folderId);
+
+ TagParser tagParser = mFolderElements.get(parser.getName());
+ if (tagParser != null) {
+ final long id = tagParser.parseAndAdd(parser, res);
+ if (id >= 0) {
+ folderItems.add(id);
+ }
+ } else {
+ throw new RuntimeException("Invalid folder item " + parser.getName());
+ }
+ }
+
+ long addedId = folderId;
+
+ // We can only have folders with >= 2 items, so we need to remove the
+ // folder and clean up if less than 2 items were included, or some
+ // failed to add, and less than 2 were actually added
+ if (folderItems.size() < 2) {
+ // Delete the folder
+ Uri uri = Favorites.getContentUri(folderId, false);
+ SqlArguments args = new SqlArguments(uri, null, null);
+ mDb.delete(args.table, args.where, args.args);
+ addedId = -1;
+
+ // If we have a single item, promote it to where the folder
+ // would have been.
+ if (folderItems.size() == 1) {
+ final ContentValues childValues = new ContentValues();
+ copyInteger(myValues, childValues, Favorites.CONTAINER);
+ copyInteger(myValues, childValues, Favorites.SCREEN);
+ copyInteger(myValues, childValues, Favorites.CELLX);
+ copyInteger(myValues, childValues, Favorites.CELLY);
+
+ addedId = folderItems.get(0);
+ mDb.update(LauncherProvider.TABLE_FAVORITES, childValues,
+ Favorites._ID + "=" + addedId, null);
+ }
+ }
+ return addedId;
+ }
+ }
+
+ private static final void beginDocument(XmlPullParser parser, String firstElementName)
+ throws XmlPullParserException, IOException {
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT);
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ if (!parser.getName().equals(firstElementName)) {
+ throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
+ ", expected " + firstElementName);
+ }
+ }
+
+ /**
+ * Return attribute value, attempting launcher-specific namespace first
+ * before falling back to anonymous attribute.
+ */
+ private static String getAttributeValue(XmlResourceParser parser, String attribute) {
+ String value = parser.getAttributeValue(
+ "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
+ if (value == null) {
+ value = parser.getAttributeValue(null, attribute);
+ }
+ return value;
+ }
+
+ /**
+ * Return attribute resource value, attempting launcher-specific namespace
+ * first before falling back to anonymous attribute.
+ */
+ private static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+ int defaultValue) {
+ int value = parser.getAttributeResourceValue(
+ "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
+ defaultValue);
+ if (value == defaultValue) {
+ value = parser.getAttributeResourceValue(null, attribute, defaultValue);
+ }
+ return value;
+ }
+
+ public static interface LayoutParserCallback {
+ long generateNewItemId();
+
+ long insertAndCheck(SQLiteDatabase db, ContentValues values);
+ }
+
+ private static void copyInteger(ContentValues from, ContentValues to, String key) {
+ to.put(key, from.getAsInteger(key));
+ }
+}
diff --git a/src/com/android/launcher3/BorderCropDrawable.java b/src/com/android/launcher3/BorderCropDrawable.java
new file mode 100644
index 000000000..caf497d9b
--- /dev/null
+++ b/src/com/android/launcher3/BorderCropDrawable.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+public class BorderCropDrawable extends Drawable {
+
+ private final Drawable mChild;
+ private final Rect mBoundsShift;
+ private final Rect mPadding;
+
+ BorderCropDrawable(Drawable child, boolean cropLeft,
+ boolean cropTop, boolean cropRight, boolean cropBottom) {
+ mChild = child;
+
+ mBoundsShift = new Rect();
+ mPadding = new Rect();
+ mChild.getPadding(mPadding);
+
+ if (cropLeft) {
+ mBoundsShift.left = -mPadding.left;
+ mPadding.left = 0;
+ }
+ if (cropTop) {
+ mBoundsShift.top = -mPadding.top;
+ mPadding.top = 0;
+ }
+ if (cropRight) {
+ mBoundsShift.right = mPadding.right;
+ mPadding.right = 0;
+ }
+ if (cropBottom) {
+ mBoundsShift.bottom = mPadding.bottom;
+ mPadding.bottom = 0;
+ }
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mChild.setBounds(
+ bounds.left + mBoundsShift.left,
+ bounds.top + mBoundsShift.top,
+ bounds.right + mBoundsShift.right,
+ bounds.bottom + mBoundsShift.bottom);
+ }
+
+ @Override
+ public boolean getPadding(Rect padding) {
+ padding.set(mPadding);
+ return (padding.left | padding.top | padding.right | padding.bottom) != 0;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ mChild.draw(canvas);
+ }
+
+ @Override
+ public int getOpacity() {
+ return mChild.getOpacity();
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mChild.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mChild.setColorFilter(cf);
+ }
+}
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ee42904dd..a368796bd 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -17,16 +17,20 @@
package com.android.launcher3;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
-import android.graphics.Rect;
import android.graphics.Region;
-import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.util.SparseArray;
import android.util.TypedValue;
+import android.view.KeyEvent;
import android.view.MotionEvent;
+import android.view.ViewConfiguration;
import android.widget.TextView;
/**
@@ -35,48 +39,57 @@ import android.widget.TextView;
* too aggressive.
*/
public class BubbleTextView extends TextView {
- static final float SHADOW_LARGE_RADIUS = 4.0f;
- static final float SHADOW_SMALL_RADIUS = 1.75f;
- static final float SHADOW_Y_OFFSET = 2.0f;
- static final int SHADOW_LARGE_COLOUR = 0xDD000000;
- static final int SHADOW_SMALL_COLOUR = 0xCC000000;
- static final float PADDING_H = 8.0f;
- static final float PADDING_V = 3.0f;
- private int mPrevAlpha = -1;
+ private static SparseArray<Theme> sPreloaderThemes = new SparseArray<>(2);
+
+ private static final float SHADOW_LARGE_RADIUS = 4.0f;
+ private static final float SHADOW_SMALL_RADIUS = 1.75f;
+ private static final float SHADOW_Y_OFFSET = 2.0f;
+ private static final int SHADOW_LARGE_COLOUR = 0xDD000000;
+ private static final int SHADOW_SMALL_COLOUR = 0xCC000000;
+ static final float PADDING_V = 3.0f;
private HolographicOutlineHelper mOutlineHelper;
- private final Canvas mTempCanvas = new Canvas();
- private final Rect mTempRect = new Rect();
- private boolean mDidInvalidateForPressedState;
- private Bitmap mPressedOrFocusedBackground;
- private int mFocusedOutlineColor;
- private int mFocusedGlowColor;
- private int mPressedOutlineColor;
- private int mPressedGlowColor;
+ private Bitmap mPressedBackground;
+
+ private float mSlop;
private int mTextColor;
- private boolean mShadowsEnabled = true;
+ private final boolean mCustomShadowsEnabled;
private boolean mIsTextVisible;
+ // TODO: Remove custom background handling code, as no instance of BubbleTextView use any
+ // background.
private boolean mBackgroundSizeChanged;
- private Drawable mBackground;
+ private final Drawable mBackground;
private boolean mStayPressed;
+ private boolean mIgnorePressedStateChange;
private CheckLongPressHelper mLongPressHelper;
public BubbleTextView(Context context) {
- super(context);
- init();
+ this(context, null, 0);
}
public BubbleTextView(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
+ this(context, attrs, 0);
}
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.BubbleTextView, defStyle, 0);
+ mCustomShadowsEnabled = a.getBoolean(R.styleable.BubbleTextView_customShadows, true);
+ a.recycle();
+
+ if (mCustomShadowsEnabled) {
+ // Draw the background itself as the parent is drawn twice.
+ mBackground = getBackground();
+ setBackground(null);
+ } else {
+ mBackground = null;
+ }
init();
}
@@ -87,34 +100,62 @@ public class BubbleTextView extends TextView {
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
- setTextColor(getResources().getColor(R.color.workspace_icon_text_color));
}
private void init() {
mLongPressHelper = new CheckLongPressHelper(this);
- mBackground = getBackground();
mOutlineHelper = HolographicOutlineHelper.obtain(getContext());
+ if (mCustomShadowsEnabled) {
+ setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
+ }
+ }
- final Resources res = getContext().getResources();
- mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor =
- res.getColor(R.color.outline_color);
-
- setShadowLayer(SHADOW_LARGE_RADIUS, 0.0f, SHADOW_Y_OFFSET, SHADOW_LARGE_COLOUR);
+ public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
+ boolean setDefaultPadding) {
+ applyFromShortcutInfo(info, iconCache, setDefaultPadding, false);
}
- public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache) {
+ public void applyFromShortcutInfo(ShortcutInfo info, IconCache iconCache,
+ boolean setDefaultPadding, boolean promiseStateChanged) {
Bitmap b = info.getIcon(iconCache);
LauncherAppState app = LauncherAppState.getInstance();
+
+ FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b);
+ iconDrawable.setGhostModeEnabled(info.isDisabled);
+
+ setCompoundDrawables(null, iconDrawable, null, null);
+ if (setDefaultPadding) {
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
+ }
+ if (info.contentDescription != null) {
+ setContentDescription(info.contentDescription);
+ }
+ setText(info.title);
+ setTag(info);
+
+ if (promiseStateChanged || info.isPromise()) {
+ applyState(promiseStateChanged);
+ }
+ }
+
+ public void applyFromApplicationInfo(AppInfo info) {
+ LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- setCompoundDrawables(null,
- Utilities.createIconDrawable(b), null, null);
+ Drawable topDrawable = Utilities.createIconDrawable(info.iconBitmap);
+ topDrawable.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
+ setCompoundDrawables(null, topDrawable, null, null);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
setText(info.title);
+ if (info.contentDescription != null) {
+ setContentDescription(info.contentDescription);
+ }
setTag(info);
}
+
@Override
protected boolean setFrame(int left, int top, int right, int bottom) {
if (getLeft() != left || getRight() != right || getTop() != top || getBottom() != bottom) {
@@ -137,90 +178,19 @@ public class BubbleTextView extends TextView {
}
@Override
- protected void drawableStateChanged() {
- if (isPressed()) {
- // In this case, we have already created the pressed outline on ACTION_DOWN,
- // so we just need to do an invalidate to trigger draw
- if (!mDidInvalidateForPressedState) {
- setCellLayoutPressedOrFocusedIcon();
- }
- } else {
- // Otherwise, either clear the pressed/focused background, or create a background
- // for the focused state
- final boolean backgroundEmptyBefore = mPressedOrFocusedBackground == null;
- if (!mStayPressed) {
- mPressedOrFocusedBackground = null;
- }
- if (isFocused()) {
- if (getLayout() == null) {
- // In some cases, we get focus before we have been layed out. Set the
- // background to null so that it will get created when the view is drawn.
- mPressedOrFocusedBackground = null;
- } else {
- mPressedOrFocusedBackground = createGlowingOutline(
- mTempCanvas, mFocusedGlowColor, mFocusedOutlineColor);
- }
- mStayPressed = false;
- setCellLayoutPressedOrFocusedIcon();
- }
- final boolean backgroundEmptyNow = mPressedOrFocusedBackground == null;
- if (!backgroundEmptyBefore && backgroundEmptyNow) {
- setCellLayoutPressedOrFocusedIcon();
- }
- }
+ public void setPressed(boolean pressed) {
+ super.setPressed(pressed);
- Drawable d = mBackground;
- if (d != null && d.isStateful()) {
- d.setState(getDrawableState());
+ if (!mIgnorePressedStateChange) {
+ updateIconState();
}
- super.drawableStateChanged();
}
- /**
- * Draw this BubbleTextView into the given Canvas.
- *
- * @param destCanvas the canvas to draw on
- * @param padding the horizontal and vertical padding to use when drawing
- */
- private void drawWithPadding(Canvas destCanvas, int padding) {
- final Rect clipRect = mTempRect;
- getDrawingRect(clipRect);
-
- // adjust the clip rect so that we don't include the text label
- clipRect.bottom =
- getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V + getLayout().getLineTop(0);
-
- // Draw the View into the bitmap.
- // The translate of scrollX and scrollY is necessary when drawing TextViews, because
- // they set scrollX and scrollY to large values to achieve centered text
- destCanvas.save();
- destCanvas.scale(getScaleX(), getScaleY(),
- (getWidth() + padding) / 2, (getHeight() + padding) / 2);
- destCanvas.translate(-getScrollX() + padding / 2, -getScrollY() + padding / 2);
- destCanvas.clipRect(clipRect, Op.REPLACE);
- draw(destCanvas);
- destCanvas.restore();
- }
-
- public void setGlowColor(int color) {
- mFocusedOutlineColor = mFocusedGlowColor = mPressedOutlineColor = mPressedGlowColor = color;
- }
-
- /**
- * Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
- * Responsibility for the bitmap is transferred to the caller.
- */
- private Bitmap createGlowingOutline(Canvas canvas, int outlineColor, int glowColor) {
- final int padding = mOutlineHelper.mMaxOuterBlurRadius;
- final Bitmap b = Bitmap.createBitmap(
- getWidth() + padding, getHeight() + padding, Bitmap.Config.ARGB_8888);
-
- canvas.setBitmap(b);
- drawWithPadding(canvas, padding);
- mOutlineHelper.applyExtraThickExpensiveOutlineWithBlur(b, canvas, glowColor, outlineColor);
- canvas.setBitmap(null);
-
- return b;
+ private void updateIconState() {
+ Drawable top = getCompoundDrawables()[1];
+ if (top instanceof FastBitmapDrawable) {
+ ((FastBitmapDrawable) top).setPressed(isPressed() || mStayPressed);
+ }
}
@Override
@@ -231,20 +201,11 @@ public class BubbleTextView extends TextView {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
- // So that the pressed outline is visible immediately when isPressed() is true,
+ // So that the pressed outline is visible immediately on setStayPressed(),
// we pre-create it on ACTION_DOWN (it takes a small but perceptible amount of time
// to create it)
- if (mPressedOrFocusedBackground == null) {
- mPressedOrFocusedBackground = createGlowingOutline(
- mTempCanvas, mPressedGlowColor, mPressedOutlineColor);
- }
- // Invalidate so the pressed state is visible, or set a flag so we know that we
- // have to call invalidate as soon as the state is "pressed"
- if (isPressed()) {
- mDidInvalidateForPressedState = true;
- setCellLayoutPressedOrFocusedIcon();
- } else {
- mDidInvalidateForPressedState = false;
+ if (mPressedBackground == null) {
+ mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
}
mLongPressHelper.postCheckForLongPress();
@@ -254,11 +215,16 @@ public class BubbleTextView extends TextView {
// If we've touched down and up on an item, and it's still not "pressed", then
// destroy the pressed outline
if (!isPressed()) {
- mPressedOrFocusedBackground = null;
+ mPressedBackground = null;
}
mLongPressHelper.cancelLongPress();
break;
+ case MotionEvent.ACTION_MOVE:
+ if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
+ mLongPressHelper.cancelLongPress();
+ }
+ break;
}
return result;
}
@@ -266,37 +232,52 @@ public class BubbleTextView extends TextView {
void setStayPressed(boolean stayPressed) {
mStayPressed = stayPressed;
if (!stayPressed) {
- mPressedOrFocusedBackground = null;
+ mPressedBackground = null;
}
- setCellLayoutPressedOrFocusedIcon();
- }
- void setCellLayoutPressedOrFocusedIcon() {
+ // Only show the shadow effect when persistent pressed state is set.
if (getParent() instanceof ShortcutAndWidgetContainer) {
- ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) getParent();
- if (parent != null) {
- CellLayout layout = (CellLayout) parent.getParent();
- layout.setPressedOrFocusedIcon((mPressedOrFocusedBackground != null) ? this : null);
- }
+ CellLayout layout = (CellLayout) getParent().getParent();
+ layout.setPressedIcon(this, mPressedBackground, mOutlineHelper.shadowBitmapPadding);
}
+
+ updateIconState();
}
- void clearPressedOrFocusedBackground() {
- mPressedOrFocusedBackground = null;
- setCellLayoutPressedOrFocusedIcon();
+ void clearPressedBackground() {
+ setPressed(false);
+ setStayPressed(false);
}
- Bitmap getPressedOrFocusedBackground() {
- return mPressedOrFocusedBackground;
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ if (super.onKeyDown(keyCode, event)) {
+ // Pre-create shadow so show immediately on click.
+ if (mPressedBackground == null) {
+ mPressedBackground = mOutlineHelper.createMediumDropShadow(this);
+ }
+ return true;
+ }
+ return false;
}
- int getPressedOrFocusedBackgroundPadding() {
- return mOutlineHelper.mMaxOuterBlurRadius / 2;
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ // Unlike touch events, keypress event propagate pressed state change immediately,
+ // without waiting for onClickHandler to execute. Disable pressed state changes here
+ // to avoid flickering.
+ mIgnorePressedStateChange = true;
+ boolean result = super.onKeyUp(keyCode, event);
+
+ mPressedBackground = null;
+ mIgnorePressedStateChange = false;
+ updateIconState();
+ return result;
}
@Override
public void draw(Canvas canvas) {
- if (!mShadowsEnabled) {
+ if (!mCustomShadowsEnabled) {
super.draw(canvas);
return;
}
@@ -342,7 +323,14 @@ public class BubbleTextView extends TextView {
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
+
if (mBackground != null) mBackground.setCallback(this);
+ Drawable top = getCompoundDrawables()[1];
+
+ if (top instanceof PreloadIconDrawable) {
+ ((PreloadIconDrawable) top).applyTheme(getPreloaderTheme());
+ }
+ mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
@Override
@@ -357,10 +345,10 @@ public class BubbleTextView extends TextView {
super.setTextColor(color);
}
- public void setShadowsEnabled(boolean enabled) {
- mShadowsEnabled = enabled;
- getPaint().clearShadowLayer();
- invalidate();
+ @Override
+ public void setTextColor(ColorStateList colors) {
+ mTextColor = colors.getDefaultColor();
+ super.setTextColor(colors);
}
public void setTextVisibility(boolean visible) {
@@ -379,10 +367,6 @@ public class BubbleTextView extends TextView {
@Override
protected boolean onSetAlpha(int alpha) {
- if (mPrevAlpha != alpha) {
- mPrevAlpha = alpha;
- super.onSetAlpha(alpha);
- }
return true;
}
@@ -392,4 +376,45 @@ public class BubbleTextView extends TextView {
mLongPressHelper.cancelLongPress();
}
+
+ public void applyState(boolean promiseStateChanged) {
+ if (getTag() instanceof ShortcutInfo) {
+ ShortcutInfo info = (ShortcutInfo) getTag();
+ final boolean isPromise = info.isPromise();
+ final int progressLevel = isPromise ?
+ ((info.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE) ?
+ info.getInstallProgress() : 0)) : 100;
+
+ Drawable[] drawables = getCompoundDrawables();
+ Drawable top = drawables[1];
+ if (top != null) {
+ final PreloadIconDrawable preloadDrawable;
+ if (top instanceof PreloadIconDrawable) {
+ preloadDrawable = (PreloadIconDrawable) top;
+ } else {
+ preloadDrawable = new PreloadIconDrawable(top, getPreloaderTheme());
+ setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]);
+ }
+
+ preloadDrawable.setLevel(progressLevel);
+ if (promiseStateChanged) {
+ preloadDrawable.maybePerformFinishedAnimation();
+ }
+ }
+ }
+ }
+
+ private Theme getPreloaderTheme() {
+ Object tag = getTag();
+ int style = ((tag != null) && (tag instanceof ShortcutInfo) &&
+ (((ShortcutInfo) tag).container >= 0)) ? R.style.PreloadIcon_Folder
+ : R.style.PreloadIcon;
+ Theme theme = sPreloaderThemes.get(style);
+ if (theme == null) {
+ theme = getResources().newTheme();
+ theme.applyStyle(style, true);
+ sPreloaderThemes.put(style, theme);
+ }
+ return theme;
+ }
}
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index 2436a51a3..0ff1ef4ad 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -30,8 +30,6 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
@@ -73,11 +71,8 @@ public class CellLayout extends ViewGroup {
private int mWidthGap;
private int mHeightGap;
private int mMaxGap;
- private boolean mScrollingTransformsDirty = false;
private boolean mDropPending = false;
-
- private final Rect mRect = new Rect();
- private final CellInfo mCellInfo = new CellInfo();
+ private boolean mIsDragTarget = true;
// These are temporary variables to prevent having to allocate a new object just to
// return an (x, y) value from helper functions. Do NOT use them to maintain other state.
@@ -128,7 +123,7 @@ public class CellLayout extends ViewGroup {
private int mDragOutlineCurrent = 0;
private final Paint mDragOutlinePaint = new Paint();
- private BubbleTextView mPressedOrFocusedIcon;
+ private final FastBitmapView mTouchFeedbackView;
private HashMap<CellLayout.LayoutParams, Animator> mReorderAnimators = new
HashMap<CellLayout.LayoutParams, Animator>();
@@ -172,8 +167,6 @@ public class CellLayout extends ViewGroup {
private Rect mTempRect = new Rect();
- private final static PorterDuffXfermode sAddBlendMode =
- new PorterDuffXfermode(PorterDuff.Mode.ADD);
private final static Paint sPaint = new Paint();
public CellLayout(Context context) {
@@ -295,6 +288,9 @@ public class CellLayout extends ViewGroup {
mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mWidthGap, mHeightGap,
mCountX, mCountY);
+ mTouchFeedbackView = new FastBitmapView(context);
+ // Make the feedback view large enough to hold the blur bitmap.
+ addView(mTouchFeedbackView, (int) (grid.cellWidthPx * 1.5), (int) (grid.cellHeightPx * 1.5));
addView(mShortcutsAndWidgets);
}
@@ -341,14 +337,6 @@ public class CellLayout extends ViewGroup {
return mDropPending;
}
- private void invalidateBubbleTextView(BubbleTextView icon) {
- final int padding = icon.getPressedOrFocusedBackgroundPadding();
- invalidate(icon.getLeft() + getPaddingLeft() - padding,
- icon.getTop() + getPaddingTop() - padding,
- icon.getRight() + getPaddingLeft() + padding,
- icon.getBottom() + getPaddingTop() + padding);
- }
-
void setOverScrollAmount(float r, boolean left) {
if (left && mOverScrollForegroundDrawable != mOverScrollLeft) {
mOverScrollForegroundDrawable = mOverScrollLeft;
@@ -362,24 +350,23 @@ public class CellLayout extends ViewGroup {
invalidate();
}
- void setPressedOrFocusedIcon(BubbleTextView icon) {
- // We draw the pressed or focused BubbleTextView's background in CellLayout because it
- // requires an expanded clip rect (due to the glow's blur radius)
- BubbleTextView oldIcon = mPressedOrFocusedIcon;
- mPressedOrFocusedIcon = icon;
- if (oldIcon != null) {
- invalidateBubbleTextView(oldIcon);
- }
- if (mPressedOrFocusedIcon != null) {
- invalidateBubbleTextView(mPressedOrFocusedIcon);
- }
- }
-
- void setIsDragOverlapping(boolean isDragOverlapping) {
- if (mIsDragOverlapping != isDragOverlapping) {
- mIsDragOverlapping = isDragOverlapping;
- setUseActiveGlowBackground(mIsDragOverlapping);
- invalidate();
+ void setPressedIcon(BubbleTextView icon, Bitmap background, int padding) {
+ if (icon == null || background == null) {
+ mTouchFeedbackView.setBitmap(null);
+ mTouchFeedbackView.animate().cancel();
+ } else {
+ int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight()
+ - (mCountX * mCellWidth);
+ mTouchFeedbackView.setTranslationX(icon.getLeft() + (int) Math.ceil(offset / 2f)
+ - padding);
+ mTouchFeedbackView.setTranslationY(icon.getTop() - padding);
+ if (mTouchFeedbackView.setBitmap(background)) {
+ mTouchFeedbackView.setAlpha(0);
+ mTouchFeedbackView.animate().alpha(1)
+ .setDuration(FastBitmapDrawable.CLICK_FEEDBACK_DURATION)
+ .setInterpolator(FastBitmapDrawable.CLICK_FEEDBACK_INTERPOLATOR)
+ .start();
+ }
}
}
@@ -391,27 +378,26 @@ public class CellLayout extends ViewGroup {
mDrawBackground = false;
}
- boolean getIsDragOverlapping() {
- return mIsDragOverlapping;
+ void disableDragTarget() {
+ mIsDragTarget = false;
}
- protected void setOverscrollTransformsDirty(boolean dirty) {
- mScrollingTransformsDirty = dirty;
+ boolean isDragTarget() {
+ return mIsDragTarget;
}
- protected void resetOverscrollTransforms() {
- if (mScrollingTransformsDirty) {
- setOverscrollTransformsDirty(false);
- setTranslationX(0);
- setRotationY(0);
- // It doesn't matter if we pass true or false here, the important thing is that we
- // pass 0, which results in the overscroll drawable not being drawn any more.
- setOverScrollAmount(0, false);
- setPivotX(getMeasuredWidth() / 2);
- setPivotY(getMeasuredHeight() / 2);
+ void setIsDragOverlapping(boolean isDragOverlapping) {
+ if (mIsDragOverlapping != isDragOverlapping) {
+ mIsDragOverlapping = isDragOverlapping;
+ setUseActiveGlowBackground(mIsDragOverlapping);
+ invalidate();
}
}
+ boolean getIsDragOverlapping() {
+ return mIsDragOverlapping;
+ }
+
@Override
protected void onDraw(Canvas canvas) {
// When we're large, we are either drawn in a "hover" state (ie when dragging an item to
@@ -447,23 +433,6 @@ public class CellLayout extends ViewGroup {
}
}
- // We draw the pressed or focused BubbleTextView's background in CellLayout because it
- // requires an expanded clip rect (due to the glow's blur radius)
- if (mPressedOrFocusedIcon != null) {
- final int padding = mPressedOrFocusedIcon.getPressedOrFocusedBackgroundPadding();
- final Bitmap b = mPressedOrFocusedIcon.getPressedOrFocusedBackground();
- if (b != null) {
- int offset = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() -
- (mCountX * mCellWidth);
- int left = getPaddingLeft() + (int) Math.ceil(offset / 2f);
- int top = getPaddingTop();
- canvas.drawBitmap(b,
- mPressedOrFocusedIcon.getLeft() + left - padding,
- mPressedOrFocusedIcon.getTop() + top - padding,
- null);
- }
- }
-
if (DEBUG_VISUALIZE_OCCUPIED) {
int[] pt = new int[2];
ColorDrawable cd = new ColorDrawable(Color.RED);
@@ -582,7 +551,15 @@ public class CellLayout extends ViewGroup {
}
public void restoreInstanceState(SparseArray<Parcelable> states) {
- dispatchRestoreInstanceState(states);
+ try {
+ dispatchRestoreInstanceState(states);
+ } catch (IllegalArgumentException ex) {
+ if (LauncherAppState.isDogfoodBuild()) {
+ throw ex;
+ }
+ // Mismatched viewId / viewType preventing restore. Skip restore on production builds.
+ Log.e(TAG, "Ignoring an error while restoring a view instance state", ex);
+ }
}
@Override
@@ -699,103 +676,17 @@ public class CellLayout extends ViewGroup {
}
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- if (getParent() instanceof Workspace) {
- Workspace workspace = (Workspace) getParent();
- mCellInfo.screenId = workspace.getIdForScreen(this);
- }
- }
-
- public void setTagToCellInfoForPoint(int touchX, int touchY) {
- final CellInfo cellInfo = mCellInfo;
- Rect frame = mRect;
- final int x = touchX + getScrollX();
- final int y = touchY + getScrollY();
- final int count = mShortcutsAndWidgets.getChildCount();
-
- boolean found = false;
- for (int i = count - 1; i >= 0; i--) {
- final View child = mShortcutsAndWidgets.getChildAt(i);
- final LayoutParams lp = (LayoutParams) child.getLayoutParams();
-
- if ((child.getVisibility() == VISIBLE || child.getAnimation() != null) &&
- lp.isLockedToGrid) {
- child.getHitRect(frame);
-
- float scale = child.getScaleX();
- frame = new Rect(child.getLeft(), child.getTop(), child.getRight(),
- child.getBottom());
- // The child hit rect is relative to the CellLayoutChildren parent, so we need to
- // offset that by this CellLayout's padding to test an (x,y) point that is relative
- // to this view.
- frame.offset(getPaddingLeft(), getPaddingTop());
- frame.inset((int) (frame.width() * (1f - scale) / 2),
- (int) (frame.height() * (1f - scale) / 2));
-
- if (frame.contains(x, y)) {
- cellInfo.cell = child;
- cellInfo.cellX = lp.cellX;
- cellInfo.cellY = lp.cellY;
- cellInfo.spanX = lp.cellHSpan;
- cellInfo.spanY = lp.cellVSpan;
- found = true;
- break;
- }
- }
- }
-
- mLastDownOnOccupiedCell = found;
-
- if (!found) {
- final int cellXY[] = mTmpXY;
- pointToCellExact(x, y, cellXY);
-
- cellInfo.cell = null;
- cellInfo.cellX = cellXY[0];
- cellInfo.cellY = cellXY[1];
- cellInfo.spanX = 1;
- cellInfo.spanY = 1;
- }
- setTag(cellInfo);
- }
-
- @Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// First we clear the tag to ensure that on every touch down we start with a fresh slate,
// even in the case where we return early. Not clearing here was causing bugs whereby on
// long-press we'd end up picking up an item from a previous drag operation.
- final int action = ev.getAction();
-
- if (action == MotionEvent.ACTION_DOWN) {
- clearTagCellInfo();
- }
-
if (mInterceptTouchListener != null && mInterceptTouchListener.onTouch(this, ev)) {
return true;
}
- if (action == MotionEvent.ACTION_DOWN) {
- setTagToCellInfoForPoint((int) ev.getX(), (int) ev.getY());
- }
-
return false;
}
- private void clearTagCellInfo() {
- final CellInfo cellInfo = mCellInfo;
- cellInfo.cell = null;
- cellInfo.cellX = -1;
- cellInfo.cellY = -1;
- cellInfo.spanX = 0;
- cellInfo.spanY = 0;
- setTag(cellInfo);
- }
-
- public CellInfo getTag() {
- return (CellInfo) super.getTag();
- }
-
/**
* Given a point, return the cell that strictly encloses that point
* @param x X coordinate of the point
@@ -1049,6 +940,7 @@ public class CellLayout extends ViewGroup {
}
public void setBackgroundAlphaMultiplier(float multiplier) {
+
if (mBackgroundAlphaMultiplier != multiplier) {
mBackgroundAlphaMultiplier = multiplier;
invalidate();
@@ -1067,17 +959,11 @@ public class CellLayout extends ViewGroup {
}
public void setShortcutAndWidgetAlpha(float alpha) {
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- getChildAt(i).setAlpha(alpha);
- }
+ mShortcutsAndWidgets.setAlpha(alpha);
}
public ShortcutAndWidgetContainer getShortcutsAndWidgets() {
- if (getChildCount() > 0) {
- return (ShortcutAndWidgetContainer) getChildAt(0);
- }
- return null;
+ return mShortcutsAndWidgets;
}
public View getChildAt(int x, int y) {
@@ -3360,6 +3246,16 @@ out: for (int i = x; i < x + spanX - 1 && x < xCount; i++) {
long screenId;
long container;
+ CellInfo(View v, ItemInfo info) {
+ cell = v;
+ cellX = info.cellX;
+ cellY = info.cellY;
+ spanX = info.spanX;
+ spanY = info.spanY;
+ screenId = info.screenId;
+ container = info.container;
+ }
+
@Override
public String toString() {
return "Cell[view=" + (cell == null ? "null" : cell.getClass())
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
deleted file mode 100644
index a6139ccbc..000000000
--- a/src/com/android/launcher3/Cling.java
+++ /dev/null
@@ -1,571 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.launcher3;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.graphics.*;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
-import android.view.FocusFinder;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.animation.AccelerateInterpolator;
-import android.widget.FrameLayout;
-import android.widget.TextView;
-
-public class Cling extends FrameLayout implements Insettable, View.OnClickListener,
- View.OnLongClickListener, View.OnTouchListener {
-
- private static String FIRST_RUN_PORTRAIT = "first_run_portrait";
- private static String FIRST_RUN_LANDSCAPE = "first_run_landscape";
-
- private static String WORKSPACE_PORTRAIT = "workspace_portrait";
- private static String WORKSPACE_LANDSCAPE = "workspace_landscape";
- private static String WORKSPACE_LARGE = "workspace_large";
- private static String WORKSPACE_CUSTOM = "workspace_custom";
-
- private static String MIGRATION_PORTRAIT = "migration_portrait";
- private static String MIGRATION_LANDSCAPE = "migration_landscape";
-
- private static String MIGRATION_WORKSPACE_PORTRAIT = "migration_workspace_portrait";
- private static String MIGRATION_WORKSPACE_LARGE_PORTRAIT = "migration_workspace_large_portrait";
- private static String MIGRATION_WORKSPACE_LANDSCAPE = "migration_workspace_landscape";
-
- private static String FOLDER_PORTRAIT = "folder_portrait";
- private static String FOLDER_LANDSCAPE = "folder_landscape";
- private static String FOLDER_LARGE = "folder_large";
-
- private static float FIRST_RUN_CIRCLE_BUFFER_DPS = 60;
- private static float FIRST_RUN_MAX_CIRCLE_RADIUS_DPS = 180;
- private static float WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 50;
- private static float WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 60;
- private static float WORKSPACE_CIRCLE_Y_OFFSET_DPS = 30;
- private static float MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS = 42;
- private static float MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS = 46;
-
- private Launcher mLauncher;
- private boolean mIsInitialized;
- private String mDrawIdentifier;
- private Drawable mBackground;
-
- private int[] mTouchDownPt = new int[2];
-
- private Drawable mFocusedHotseatApp;
- private ComponentName mFocusedHotseatAppComponent;
- private Rect mFocusedHotseatAppBounds;
-
- private Paint mErasePaint;
- private Paint mBorderPaint;
- private Paint mBubblePaint;
- private Paint mDotPaint;
-
- private View mScrimView;
- private int mBackgroundColor;
-
- private final Rect mInsets = new Rect();
-
- public Cling(Context context) {
- this(context, null, 0);
- }
-
- public Cling(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public Cling(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
-
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Cling, defStyle, 0);
- mDrawIdentifier = a.getString(R.styleable.Cling_drawIdentifier);
- a.recycle();
-
- setClickable(true);
-
- }
-
- void init(Launcher l, View scrim) {
- if (!mIsInitialized) {
- mLauncher = l;
- mScrimView = scrim;
- mBackgroundColor = 0xcc000000;
- setOnLongClickListener(this);
- setOnClickListener(this);
- setOnTouchListener(this);
-
- mErasePaint = new Paint();
- mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
- mErasePaint.setColor(0xFFFFFF);
- mErasePaint.setAlpha(0);
- mErasePaint.setAntiAlias(true);
-
- mBorderPaint = new Paint();
- mBorderPaint.setColor(0xFFFFFFFF);
- mBorderPaint.setAntiAlias(true);
-
- int circleColor = getResources().getColor(
- R.color.first_run_cling_circle_background_color);
- mBubblePaint = new Paint();
- mBubblePaint.setColor(circleColor);
- mBubblePaint.setAntiAlias(true);
-
- mDotPaint = new Paint();
- mDotPaint.setColor(0x72BBED);
- mDotPaint.setAntiAlias(true);
-
- mIsInitialized = true;
- }
- }
-
- void setFocusedHotseatApp(int drawableId, int appRank, ComponentName cn, String title,
- String description) {
- // Get the app to draw
- Resources r = getResources();
- int appIconId = drawableId;
- Hotseat hotseat = mLauncher.getHotseat();
- // Skip the focused app in the large layouts
- if (!mDrawIdentifier.equals(WORKSPACE_LARGE) &&
- hotseat != null && appIconId > -1 && appRank > -1 && !title.isEmpty() &&
- !description.isEmpty()) {
- // Set the app bounds
- int x = hotseat.getCellXFromOrder(appRank);
- int y = hotseat.getCellYFromOrder(appRank);
- Rect pos = hotseat.getCellCoordinates(x, y);
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- mFocusedHotseatApp = getResources().getDrawable(appIconId);
- mFocusedHotseatAppComponent = cn;
- mFocusedHotseatAppBounds = new Rect(pos.left, pos.top,
- pos.left + Utilities.sIconTextureWidth,
- pos.top + Utilities.sIconTextureHeight);
- Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds,
- ((float) grid.hotseatIconSizePx / grid.iconSizePx));
-
- // Set the title
- TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title);
- if (v != null) {
- v.setText(title);
- }
-
- // Set the description
- v = (TextView) findViewById(R.id.focused_hotseat_app_description);
- if (v != null) {
- v.setText(description);
- }
-
- // Show the bubble
- View bubble = findViewById(R.id.focused_hotseat_app_bubble);
- bubble.setVisibility(View.VISIBLE);
- }
- }
-
- void setOpenFolderRect(Rect r) {
- if (mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
- mDrawIdentifier.equals(FOLDER_LARGE)) {
- ViewGroup vg = (ViewGroup) findViewById(R.id.folder_bubble);
- ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) vg.getLayoutParams();
- lp.topMargin = r.top - mInsets.bottom;
- lp.leftMargin = r.right;
- vg.setLayoutDirection(View.LAYOUT_DIRECTION_LTR);
- vg.requestLayout();
- }
- }
-
- void updateMigrationWorkspaceBubblePosition() {
- DisplayMetrics metrics = new DisplayMetrics();
- mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- // Get the page indicator bounds
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets);
-
- if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT)) {
- View bubble = findViewById(R.id.migration_workspace_cling_bubble);
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
- lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top;
- bubble.requestLayout();
- } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT)) {
- View bubble = findViewById(R.id.content);
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
- lp.bottomMargin = grid.heightPx - pageIndicatorBounds.top;
- bubble.requestLayout();
- } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
- View bubble = findViewById(R.id.content);
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
- if (grid.isLayoutRtl) {
- lp.leftMargin = pageIndicatorBounds.right;
- } else {
- lp.rightMargin = (grid.widthPx - pageIndicatorBounds.left);
- }
- bubble.requestLayout();
- }
- }
-
- void updateWorkspaceBubblePosition() {
- DisplayMetrics metrics = new DisplayMetrics();
- mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
-
- // Get the cut-out bounds
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- Rect cutOutBounds = getWorkspaceCutOutBounds(metrics);
-
- if (mDrawIdentifier.equals(WORKSPACE_LARGE)) {
- View bubble = findViewById(R.id.workspace_cling_bubble);
- ViewGroup.MarginLayoutParams lp =
- (ViewGroup.MarginLayoutParams) bubble.getLayoutParams();
- lp.bottomMargin = grid.heightPx - cutOutBounds.top - mInsets.bottom;
- bubble.requestLayout();
- }
- }
-
- private Rect getWorkspaceCutOutBounds(DisplayMetrics metrics) {
- int halfWidth = metrics.widthPixels / 2;
- int halfHeight = metrics.heightPixels / 2;
- int yOffset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
- if (mDrawIdentifier.equals(WORKSPACE_LARGE)) {
- yOffset = 0;
- }
- int radius = DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics);
- return new Rect(halfWidth - radius, halfHeight - yOffset - radius, halfWidth + radius,
- halfHeight - yOffset + radius);
- }
-
- void show(boolean animate, int duration) {
- setVisibility(View.VISIBLE);
- setLayerType(View.LAYER_TYPE_HARDWARE, null);
- if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
- mDrawIdentifier.equals(WORKSPACE_LARGE) ||
- mDrawIdentifier.equals(WORKSPACE_CUSTOM) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
- View content = getContent();
- content.setAlpha(0f);
- content.animate()
- .alpha(1f)
- .setDuration(duration)
- .setListener(null)
- .start();
- setAlpha(1f);
- } else {
- if (animate) {
- buildLayer();
- setAlpha(0f);
- animate()
- .alpha(1f)
- .setInterpolator(new AccelerateInterpolator())
- .setDuration(duration)
- .setListener(null)
- .start();
- } else {
- setAlpha(1f);
- }
- }
-
- // Show the scrim if necessary
- if (mScrimView != null) {
- mScrimView.setVisibility(View.VISIBLE);
- mScrimView.setAlpha(0f);
- mScrimView.animate()
- .alpha(1f)
- .setDuration(duration)
- .setListener(null)
- .start();
- }
-
- setFocusableInTouchMode(true);
- post(new Runnable() {
- public void run() {
- setFocusable(true);
- requestFocus();
- }
- });
- }
-
- void hide(final int duration, final Runnable postCb) {
- if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
- mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE) ||
- mDrawIdentifier.equals(MIGRATION_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_LANDSCAPE)) {
- View content = getContent();
- content.animate()
- .alpha(0f)
- .setDuration(duration)
- .setListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- // We are about to trigger the workspace cling, so don't do anything else
- setVisibility(View.GONE);
- postCb.run();
- };
- })
- .start();
- } else {
- animate()
- .alpha(0f)
- .setDuration(duration)
- .setListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- // We are about to trigger the workspace cling, so don't do anything else
- setVisibility(View.GONE);
- postCb.run();
- };
- })
- .start();
- }
-
- // Show the scrim if necessary
- if (mScrimView != null) {
- mScrimView.animate()
- .alpha(0f)
- .setDuration(duration)
- .setListener(new AnimatorListenerAdapter() {
- public void onAnimationEnd(Animator animation) {
- mScrimView.setVisibility(View.GONE);
- };
- })
- .start();
- }
- }
-
- void cleanup() {
- mBackground = null;
- mIsInitialized = false;
- }
-
- void bringScrimToFront() {
- if (mScrimView != null) {
- mScrimView.bringToFront();
- }
- }
-
- @Override
- public void setInsets(Rect insets) {
- mInsets.set(insets);
- setPadding(insets.left, insets.top, insets.right, insets.bottom);
- }
-
- View getContent() {
- return findViewById(R.id.content);
- }
-
- String getDrawIdentifier() {
- return mDrawIdentifier;
- }
-
- @Override
- public View focusSearch(int direction) {
- return this.focusSearch(this, direction);
- }
-
- @Override
- public View focusSearch(View focused, int direction) {
- return FocusFinder.getInstance().findNextFocus(this, focused, direction);
- }
-
- @Override
- public boolean onHoverEvent(MotionEvent event) {
- return (mDrawIdentifier.equals(WORKSPACE_PORTRAIT)
- || mDrawIdentifier.equals(WORKSPACE_LANDSCAPE)
- || mDrawIdentifier.equals(WORKSPACE_LARGE)
- || mDrawIdentifier.equals(WORKSPACE_CUSTOM));
- }
-
- @Override
- public boolean onTouchEvent(android.view.MotionEvent event) {
- if (mDrawIdentifier.equals(FOLDER_PORTRAIT) ||
- mDrawIdentifier.equals(FOLDER_LANDSCAPE) ||
- mDrawIdentifier.equals(FOLDER_LARGE)) {
- Folder f = mLauncher.getWorkspace().getOpenFolder();
- if (f != null) {
- Rect r = new Rect();
- f.getHitRect(r);
- if (r.contains((int) event.getX(), (int) event.getY())) {
- return false;
- }
- }
- }
- return super.onTouchEvent(event);
- };
-
- @Override
- public boolean onTouch(View v, MotionEvent ev) {
- if (ev.getAction() == MotionEvent.ACTION_DOWN) {
- mTouchDownPt[0] = (int) ev.getX();
- mTouchDownPt[1] = (int) ev.getY();
- }
- return false;
- }
-
- @Override
- public void onClick(View v) {
- if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
- mDrawIdentifier.equals(WORKSPACE_LARGE)) {
- if (mFocusedHotseatAppBounds != null &&
- mFocusedHotseatAppBounds.contains(mTouchDownPt[0], mTouchDownPt[1])) {
- // Launch the activity that is being highlighted
- Intent intent = new Intent(Intent.ACTION_MAIN);
- intent.setComponent(mFocusedHotseatAppComponent);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- mLauncher.startActivity(intent, null);
- mLauncher.getLauncherClings().dismissWorkspaceCling(this);
- }
- }
- }
-
- @Override
- public boolean onLongClick(View v) {
- if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
- mDrawIdentifier.equals(WORKSPACE_LARGE)) {
- mLauncher.getLauncherClings().dismissWorkspaceCling(null);
- return true;
- } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
- mLauncher.getLauncherClings().dismissMigrationWorkspaceCling(null);
- return true;
- }
- return false;
- }
-
- @Override
- protected void dispatchDraw(Canvas canvas) {
- if (mIsInitialized) {
- canvas.save();
-
- // Get the page indicator bounds
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- Rect pageIndicatorBounds = grid.getWorkspacePageIndicatorBounds(mInsets);
-
- // Get the background override if there is one
- if (mBackground == null) {
- if (mDrawIdentifier.equals(WORKSPACE_CUSTOM)) {
- mBackground = getResources().getDrawable(R.drawable.bg_cling5);
- }
- }
- // Draw the background
- Bitmap eraseBg = null;
- Canvas eraseCanvas = null;
- if (mScrimView != null) {
- // Skip drawing the background
- mScrimView.setBackgroundColor(mBackgroundColor);
- } else if (mBackground != null) {
- mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
- mBackground.draw(canvas);
- } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
- mDrawIdentifier.equals(WORKSPACE_LARGE) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
- // Initialize the draw buffer (to allow punching through)
- eraseBg = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(),
- Bitmap.Config.ARGB_8888);
- eraseCanvas = new Canvas(eraseBg);
- eraseCanvas.drawColor(mBackgroundColor);
- } else {
- canvas.drawColor(mBackgroundColor);
- }
-
- // Draw everything else
- DisplayMetrics metrics = new DisplayMetrics();
- mLauncher.getWindowManager().getDefaultDisplay().getMetrics(metrics);
- float alpha = getAlpha();
- View content = getContent();
- if (content != null) {
- alpha *= content.getAlpha();
- }
- if (mDrawIdentifier.equals(FIRST_RUN_PORTRAIT) ||
- mDrawIdentifier.equals(FIRST_RUN_LANDSCAPE)) {
- // Draw the circle
- View bubbleContent = findViewById(R.id.bubble_content);
- Rect bubbleRect = new Rect();
- bubbleContent.getGlobalVisibleRect(bubbleRect);
- mBubblePaint.setAlpha((int) (255 * alpha));
- float buffer = DynamicGrid.pxFromDp(FIRST_RUN_CIRCLE_BUFFER_DPS, metrics);
- float maxRadius = DynamicGrid.pxFromDp(FIRST_RUN_MAX_CIRCLE_RADIUS_DPS, metrics);
- float radius = Math.min(maxRadius, (bubbleContent.getMeasuredWidth() + buffer) / 2);
- canvas.drawCircle(metrics.widthPixels / 2,
- bubbleRect.centerY(), radius,
- mBubblePaint);
- } else if (mDrawIdentifier.equals(WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(WORKSPACE_LANDSCAPE) ||
- mDrawIdentifier.equals(WORKSPACE_LARGE)) {
- Rect cutOutBounds = getWorkspaceCutOutBounds(metrics);
- // Draw the outer circle
- mErasePaint.setAlpha(128);
- eraseCanvas.drawCircle(cutOutBounds.centerX(), cutOutBounds.centerY(),
- DynamicGrid.pxFromDp(WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
- mErasePaint);
- // Draw the inner circle
- mErasePaint.setAlpha(0);
- eraseCanvas.drawCircle(cutOutBounds.centerX(), cutOutBounds.centerY(),
- DynamicGrid.pxFromDp(WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
- mErasePaint);
- canvas.drawBitmap(eraseBg, 0, 0, null);
- eraseCanvas.setBitmap(null);
- eraseBg = null;
-
- // Draw the focused hotseat app icon
- if (mFocusedHotseatAppBounds != null && mFocusedHotseatApp != null) {
- mFocusedHotseatApp.setBounds(mFocusedHotseatAppBounds.left,
- mFocusedHotseatAppBounds.top, mFocusedHotseatAppBounds.right,
- mFocusedHotseatAppBounds.bottom);
- mFocusedHotseatApp.setAlpha((int) (255 * alpha));
- mFocusedHotseatApp.draw(canvas);
- }
- } else if (mDrawIdentifier.equals(MIGRATION_WORKSPACE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LARGE_PORTRAIT) ||
- mDrawIdentifier.equals(MIGRATION_WORKSPACE_LANDSCAPE)) {
- int offset = DynamicGrid.pxFromDp(WORKSPACE_CIRCLE_Y_OFFSET_DPS, metrics);
- // Draw the outer circle
- eraseCanvas.drawCircle(pageIndicatorBounds.centerX(),
- pageIndicatorBounds.centerY(),
- DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_OUTER_CIRCLE_RADIUS_DPS, metrics),
- mBorderPaint);
- // Draw the inner circle
- mErasePaint.setAlpha(0);
- eraseCanvas.drawCircle(pageIndicatorBounds.centerX(),
- pageIndicatorBounds.centerY(),
- DynamicGrid.pxFromDp(MIGRATION_WORKSPACE_INNER_CIRCLE_RADIUS_DPS, metrics),
- mErasePaint);
- canvas.drawBitmap(eraseBg, 0, 0, null);
- eraseCanvas.setBitmap(null);
- eraseBg = null;
- }
- canvas.restore();
- }
-
- // Draw the rest of the cling
- super.dispatchDraw(canvas);
- };
-}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 75d906bc2..05e8906cb 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -30,6 +30,9 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.drawable.TransitionDrawable;
import android.os.AsyncTask;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserManager;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewConfiguration;
@@ -38,6 +41,9 @@ import android.view.animation.AnimationUtils;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.LinearInterpolator;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.UserHandleCompat;
+
import java.util.List;
import java.util.Set;
@@ -125,11 +131,15 @@ public class DeleteDropTarget extends ButtonDropTarget {
}
private void setHoverColor() {
- mCurrentDrawable.startTransition(mTransitionDuration);
+ if (mCurrentDrawable != null) {
+ mCurrentDrawable.startTransition(mTransitionDuration);
+ }
setTextColor(mHoverColor);
}
private void resetHoverColor() {
- mCurrentDrawable.resetTransition();
+ if (mCurrentDrawable != null) {
+ mCurrentDrawable.resetTransition();
+ }
setTextColor(mOriginalTextColor);
}
@@ -184,6 +194,17 @@ public class DeleteDropTarget extends ButtonDropTarget {
if (!willAcceptDrop(info) || isAllAppsWidget(source, info)) {
isVisible = false;
}
+ if (useUninstallLabel) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ UserManager userManager = (UserManager)
+ getContext().getSystemService(Context.USER_SERVICE);
+ Bundle restrictions = userManager.getUserRestrictions();
+ if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+ || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false)) {
+ isVisible = false;
+ }
+ }
+ }
if (useUninstallLabel) {
setCompoundDrawablesRelativeWithIntrinsicBounds(mUninstallDrawable, null, null, null);
@@ -230,8 +251,11 @@ public class DeleteDropTarget extends ButtonDropTarget {
final DragLayer dragLayer = mLauncher.getDragLayer();
final Rect from = new Rect();
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
+
+ int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
+ int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
- mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight());
+ width, height);
final float scale = (float) to.width() / from.width();
mSearchDropTargetBar.deferOnDragEnd();
@@ -279,25 +303,24 @@ public class DeleteDropTarget extends ButtonDropTarget {
if (isAllAppsApplication(d.dragSource, item)) {
// Uninstall the application if it is being dragged from AppsCustomize
AppInfo appInfo = (AppInfo) item;
- mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags);
+ mLauncher.startApplicationUninstallActivity(appInfo.componentName, appInfo.flags,
+ appInfo.user);
} else if (isUninstallFromWorkspace(d)) {
ShortcutInfo shortcut = (ShortcutInfo) item;
if (shortcut.intent != null && shortcut.intent.getComponent() != null) {
final ComponentName componentName = shortcut.intent.getComponent();
final DragSource dragSource = d.dragSource;
- int flags = AppInfo.initFlags(
- ShortcutInfo.getPackageInfo(getContext(), componentName.getPackageName()));
- mWaitingForUninstall =
- mLauncher.startApplicationUninstallActivity(componentName, flags);
+ final UserHandleCompat user = shortcut.user;
+ mWaitingForUninstall = mLauncher.startApplicationUninstallActivity(
+ componentName, shortcut.flags, user);
if (mWaitingForUninstall) {
final Runnable checkIfUninstallWasSuccess = new Runnable() {
@Override
public void run() {
mWaitingForUninstall = false;
String packageName = componentName.getPackageName();
- List<ResolveInfo> activities =
- AllAppsList.findActivitiesForPackage(getContext(), packageName);
- boolean uninstallSuccessful = activities.size() == 0;
+ boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
+ getContext(), packageName, user);
if (dragSource instanceof Folder) {
((Folder) dragSource).
onUninstallActivityReturned(uninstallSuccessful);
@@ -324,7 +347,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
- if (appWidgetHost != null) {
+ if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
new AsyncTask<Void, Void, Void>() {
@@ -353,8 +376,11 @@ public class DeleteDropTarget extends ButtonDropTarget {
*/
private AnimatorUpdateListener createFlingToTrashAnimatorListener(final DragLayer dragLayer,
DragObject d, PointF vel, ViewConfiguration config) {
+
+ int width = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicWidth();
+ int height = mCurrentDrawable == null ? 0 : mCurrentDrawable.getIntrinsicHeight();
final Rect to = getIconRect(d.dragView.getMeasuredWidth(), d.dragView.getMeasuredHeight(),
- mCurrentDrawable.getIntrinsicWidth(), mCurrentDrawable.getIntrinsicHeight());
+ width, height);
final Rect from = new Rect();
dragLayer.getViewRectRelativeToSelf(d.dragView, from);
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index e67ec197a..daf5556d4 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -43,16 +43,18 @@ import java.util.Comparator;
class DeviceProfileQuery {
+ DeviceProfile profile;
float widthDps;
float heightDps;
float value;
PointF dimens;
- DeviceProfileQuery(float w, float h, float v) {
- widthDps = w;
- heightDps = h;
+ DeviceProfileQuery(DeviceProfile p, float v) {
+ widthDps = p.minWidthDps;
+ heightDps = p.minHeightDps;
value = v;
- dimens = new PointF(w, h);
+ dimens = new PointF(widthDps, heightDps);
+ profile = p;
}
}
@@ -67,11 +69,14 @@ public class DeviceProfile {
float numRows;
float numColumns;
float numHotseatIcons;
- private float iconSize;
+ float iconSize;
private float iconTextSize;
private int iconDrawablePaddingOriginalPx;
private float hotseatIconSize;
+ int defaultLayoutId;
+ int defaultNoAllAppsLayoutId;
+
boolean isLandscape;
boolean isTablet;
boolean isLargeTablet;
@@ -121,13 +126,17 @@ public class DeviceProfile {
int searchBarSpaceHeightPx;
int searchBarHeightPx;
int pageIndicatorHeightPx;
+ int allAppsButtonVisualSize;
float dragViewScale;
+ int allAppsShortEdgeCount = -1;
+ int allAppsLongEdgeCount = -1;
+
private ArrayList<DeviceProfileCallbacks> mCallbacks = new ArrayList<DeviceProfileCallbacks>();
DeviceProfile(String n, float w, float h, float r, float c,
- float is, float its, float hs, float his) {
+ float is, float its, float hs, float his, int dlId, int dnalId) {
// Ensure that we have an odd number of hotseat items (since we need to place all apps)
if (!LauncherAppState.isDisableAllApps() && hs % 2 == 0) {
throw new RuntimeException("All Device Profiles must have an odd number of hotseat spaces");
@@ -142,6 +151,11 @@ public class DeviceProfile {
iconTextSize = its;
numHotseatIcons = hs;
hotseatIconSize = his;
+ defaultLayoutId = dlId;
+ defaultNoAllAppsLayoutId = dnalId;
+ }
+
+ DeviceProfile() {
}
DeviceProfile(Context context,
@@ -182,38 +196,42 @@ public class DeviceProfile {
overviewModeScaleFactor =
res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f;
- // Interpolate the rows
+ // Find the closes profile given the width/height
for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numRows));
+ points.add(new DeviceProfileQuery(p, 0f));
}
- numRows = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
- // Interpolate the columns
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numColumns));
- }
- numColumns = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
- // Interpolate the hotseat length
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
- }
- numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
+ DeviceProfile closestProfile = findClosestDeviceProfile(minWidth, minHeight, points);
+
+ // Snap to the closest row count
+ numRows = closestProfile.numRows;
+
+ // Snap to the closest column count
+ numColumns = closestProfile.numColumns;
+
+ // Snap to the closest hotseat size
+ numHotseatIcons = closestProfile.numHotseatIcons;
hotseatAllAppsRank = (int) (numHotseatIcons / 2);
+ // Snap to the closest default layout id
+ defaultLayoutId = closestProfile.defaultLayoutId;
+
+ // Snap to the closest default no all-apps layout id
+ defaultNoAllAppsLayoutId = closestProfile.defaultNoAllAppsLayoutId;
+
// Interpolate the icon size
points.clear();
for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
+ points.add(new DeviceProfileQuery(p, p.iconSize));
}
iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+
// AllApps uses the original non-scaled icon size
allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
// Interpolate the icon text size
points.clear();
for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
+ points.add(new DeviceProfileQuery(p, p.iconTextSize));
}
iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
iconDrawablePaddingOriginalPx =
@@ -224,14 +242,56 @@ public class DeviceProfile {
// Interpolate the hotseat icon size
points.clear();
for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.hotseatIconSize));
+ points.add(new DeviceProfileQuery(p, p.hotseatIconSize));
}
// Hotseat
hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
+ // If the partner customization apk contains any grid overrides, apply them
+ applyPartnerDeviceProfileOverrides(context, dm);
+
// Calculate the remaining vars
updateFromConfiguration(context, res, wPx, hPx, awPx, ahPx);
updateAvailableDimensions(context);
+ computeAllAppsButtonSize(context);
+ }
+
+ /**
+ * Apply any Partner customization grid overrides.
+ *
+ * Currently we support: all apps row / column count.
+ */
+ private void applyPartnerDeviceProfileOverrides(Context ctx, DisplayMetrics dm) {
+ Partner p = Partner.get(ctx.getPackageManager());
+ if (p != null) {
+ DeviceProfile partnerDp = p.getDeviceProfileOverride(dm);
+ if (partnerDp != null) {
+ if (partnerDp.numRows > 0 && partnerDp.numColumns > 0) {
+ numRows = partnerDp.numRows;
+ numColumns = partnerDp.numColumns;
+ }
+ if (partnerDp.allAppsShortEdgeCount > 0 && partnerDp.allAppsLongEdgeCount > 0) {
+ allAppsShortEdgeCount = partnerDp.allAppsShortEdgeCount;
+ allAppsLongEdgeCount = partnerDp.allAppsLongEdgeCount;
+ }
+ if (partnerDp.iconSize > 0) {
+ iconSize = partnerDp.iconSize;
+ // AllApps uses the original non-scaled icon size
+ allAppsIconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
+ }
+ }
+ }
+ }
+
+ /**
+ * Determine the exact visual footprint of the all apps button, taking into account scaling
+ * and internal padding of the drawable.
+ */
+ private void computeAllAppsButtonSize(Context context) {
+ Resources res = context.getResources();
+ float padding = res.getInteger(R.integer.config_allAppsButtonPaddingPercent) / 100f;
+ LauncherAppState app = LauncherAppState.getInstance();
+ allAppsButtonVisualSize = (int) (hotseatIconSizePx * (1 - padding));
}
void addCallback(DeviceProfileCallbacks cb) {
@@ -357,12 +417,17 @@ public class DeviceProfile {
int maxRows = (isLandscape ? maxShortEdgeCellCount : maxLongEdgeCellCount);
int maxCols = (isLandscape ? maxLongEdgeCellCount : maxShortEdgeCellCount);
- allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
- (allAppsCellHeightPx + allAppsCellPaddingPx);
- allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
- allAppsNumCols = (availableWidthPx) /
- (allAppsCellWidthPx + allAppsCellPaddingPx);
- allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
+ if (allAppsShortEdgeCount > 0 && allAppsLongEdgeCount > 0) {
+ allAppsNumRows = isLandscape ? allAppsShortEdgeCount : allAppsLongEdgeCount;
+ allAppsNumCols = isLandscape ? allAppsLongEdgeCount : allAppsShortEdgeCount;
+ } else {
+ allAppsNumRows = (availableHeightPx - pageIndicatorHeightPx) /
+ (allAppsCellHeightPx + allAppsCellPaddingPx);
+ allAppsNumRows = Math.max(minEdgeCellCount, Math.min(maxRows, allAppsNumRows));
+ allAppsNumCols = (availableWidthPx) /
+ (allAppsCellWidthPx + allAppsCellPaddingPx);
+ allAppsNumCols = Math.max(minEdgeCellCount, Math.min(maxCols, allAppsNumCols));
+ }
}
void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
@@ -398,14 +463,18 @@ public class DeviceProfile {
return (float) (1f / Math.pow(d, pow));
}
- private float invDistWeightedInterpolate(float width, float height,
- ArrayList<DeviceProfileQuery> points) {
- float sum = 0;
- float weights = 0;
- float pow = 5;
- float kNearestNeighbors = 3;
+ /** Returns the closest device profile given the width and height and a list of profiles */
+ private DeviceProfile findClosestDeviceProfile(float width, float height,
+ ArrayList<DeviceProfileQuery> points) {
+ return findClosestDeviceProfiles(width, height, points).get(0).profile;
+ }
+
+ /** Returns the closest device profiles ordered by closeness to the specified width and height */
+ private ArrayList<DeviceProfileQuery> findClosestDeviceProfiles(float width, float height,
+ ArrayList<DeviceProfileQuery> points) {
final PointF xy = new PointF(width, height);
+ // Sort the profiles by their closeness to the dimensions
ArrayList<DeviceProfileQuery> pointsByNearness = points;
Collections.sort(pointsByNearness, new Comparator<DeviceProfileQuery>() {
public int compare(DeviceProfileQuery a, DeviceProfileQuery b) {
@@ -413,6 +482,20 @@ public class DeviceProfile {
}
});
+ return pointsByNearness;
+ }
+
+ private float invDistWeightedInterpolate(float width, float height,
+ ArrayList<DeviceProfileQuery> points) {
+ float sum = 0;
+ float weights = 0;
+ float pow = 5;
+ float kNearestNeighbors = 3;
+ final PointF xy = new PointF(width, height);
+
+ ArrayList<DeviceProfileQuery> pointsByNearness = findClosestDeviceProfiles(width, height,
+ points);
+
for (int i = 0; i < pointsByNearness.size(); ++i) {
DeviceProfileQuery p = pointsByNearness.get(i);
if (i < kNearestNeighbors) {
@@ -739,15 +822,19 @@ public class DeviceProfile {
(allAppsIconSizePx / DynamicGrid.DEFAULT_ICON_SIZE_PX)));
pageIndicator = host.findViewById(R.id.apps_customize_page_indicator);
if (pageIndicator != null) {
- lp = (FrameLayout.LayoutParams) pageIndicator.getLayoutParams();
- lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;
- lp.width = LayoutParams.WRAP_CONTENT;
- lp.height = pageIndicatorHeight;
- pageIndicator.setLayoutParams(lp);
+ LinearLayout.LayoutParams lllp = (LinearLayout.LayoutParams) pageIndicator.getLayoutParams();
+ lllp.width = LayoutParams.WRAP_CONTENT;
+ lllp.height = pageIndicatorHeight;
+ pageIndicator.setLayoutParams(lllp);
}
AppsCustomizePagedView pagedView = (AppsCustomizePagedView)
host.findViewById(R.id.apps_customize_pane_content);
+
+ FrameLayout fakePageContainer = (FrameLayout)
+ host.findViewById(R.id.fake_page_container);
+ FrameLayout fakePage = (FrameLayout) host.findViewById(R.id.fake_page);
+
padding = new Rect();
if (pagedView != null) {
// Constrain the dimensions of all apps so that it does not span the full width
@@ -763,11 +850,24 @@ public class DeviceProfile {
if ((isTablet() || isLandscape) && gridPaddingLR > (allAppsCellWidthPx / 4)) {
padding.left = padding.right = gridPaddingLR;
}
+
// The icons are centered, so we can't just offset by the page indicator height
// because the empty space will actually be pageIndicatorHeight + paddingTB
padding.bottom = Math.max(0, pageIndicatorHeight - paddingTB);
- pagedView.setAllAppsPadding(padding);
+
pagedView.setWidgetsPageIndicatorPadding(pageIndicatorHeight);
+ fakePage.setBackground(res.getDrawable(R.drawable.quantum_panel));
+
+ // Horizontal padding for the whole paged view
+ int pagedFixedViewPadding =
+ res.getDimensionPixelSize(R.dimen.apps_customize_horizontal_padding);
+
+ padding.left += pagedFixedViewPadding;
+ padding.right += pagedFixedViewPadding;
+
+ pagedView.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+ fakePageContainer.setPadding(padding.left, padding.top, padding.right, padding.bottom);
+
}
}
diff --git a/src/com/android/launcher3/DragController.java b/src/com/android/launcher3/DragController.java
index 4c3ea2a0a..6d0a2be63 100644
--- a/src/com/android/launcher3/DragController.java
+++ b/src/com/android/launcher3/DragController.java
@@ -329,8 +329,8 @@ public class DragController {
if (dragInfo != null &&
dragInfo.intent != null && info != null) {
ComponentName cn = dragInfo.intent.getComponent();
- boolean isSameComponent = cn.equals(info.componentName) ||
- packageNames.contains(cn.getPackageName());
+ boolean isSameComponent = cn != null && (cn.equals(info.componentName) ||
+ packageNames.contains(cn.getPackageName()));
if (isSameComponent) {
cancelDrag();
return;
diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java
index 862ceca28..a8a61ea89 100644
--- a/src/com/android/launcher3/DragLayer.java
+++ b/src/com/android/launcher3/DragLayer.java
@@ -73,7 +73,21 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
private final Rect mInsets = new Rect();
- private int mDragViewIndex;
+ private View mOverlayView;
+ private int mTopViewIndex;
+ private int mChildCountOnLastUpdate = -1;
+
+ // Darkening scrim
+ private Drawable mBackground;
+ private float mBackgroundAlpha = 0;
+
+ // Related to adjacent page hints
+ private boolean mInScrollArea;
+ private boolean mShowPageHints;
+ private Drawable mLeftHoverDrawable;
+ private Drawable mRightHoverDrawable;
+ private Drawable mLeftHoverDrawableActive;
+ private Drawable mRightHoverDrawableActive;
/**
* Used to create a new DragLayer from XML.
@@ -89,8 +103,12 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
setChildrenDrawingOrderEnabled(true);
setOnHierarchyChangeListener(this);
- mLeftHoverDrawable = getResources().getDrawable(R.drawable.page_hover_left_holo);
- mRightHoverDrawable = getResources().getDrawable(R.drawable.page_hover_right_holo);
+ final Resources res = getResources();
+ mLeftHoverDrawable = res.getDrawable(R.drawable.page_hover_left);
+ mRightHoverDrawable = res.getDrawable(R.drawable.page_hover_right);
+ mLeftHoverDrawableActive = res.getDrawable(R.drawable.page_hover_left_active);
+ mRightHoverDrawableActive = res.getDrawable(R.drawable.page_hover_right_active);
+ mBackground = res.getDrawable(R.drawable.apps_customize_bg);
}
public void setup(Launcher launcher, DragController controller) {
@@ -114,12 +132,30 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
return true; // I'll take it from here
}
+ Rect getInsets() {
+ return mInsets;
+ }
+
@Override
public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
super.addView(child, index, params);
setInsets(child, mInsets, new Rect());
}
+ public void showOverlayView(View overlayView) {
+ LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
+ mOverlayView = overlayView;
+ addView(overlayView, lp);
+
+ // ensure that the overlay view stays on top. we can't use drawing order for this
+ // because in API level 16 touch dispatch doesn't respect drawing order.
+ mOverlayView.bringToFront();
+ }
+
+ public void dismissOverlayView() {
+ removeView(mOverlayView);
+ }
+
private void setInsets(View child, Rect newInsets, Rect oldInsets) {
final FrameLayout.LayoutParams flp = (FrameLayout.LayoutParams) child.getLayoutParams();
if (child instanceof Insettable) {
@@ -168,8 +204,7 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
}
Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
- if (currentFolder != null && !mLauncher.getLauncherClings().isFolderClingVisible() &&
- intercept) {
+ if (currentFolder != null && intercept) {
if (currentFolder.isEditingName()) {
if (!isEventOverFolderTextRegion(currentFolder, ev)) {
currentFolder.dismissEditingName();
@@ -544,6 +579,10 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
// the drag view about the scaled child view.
toY += Math.round(toScale * tv.getPaddingTop());
toY -= dragView.getMeasuredHeight() * (1 - toScale) / 2;
+ if (dragView.getDragVisualizeOffset() != null) {
+ toY -= Math.round(toScale * dragView.getDragVisualizeOffset().y);
+ }
+
toX -= (dragView.getMeasuredWidth() - Math.round(scale * child.getMeasuredWidth())) / 2;
} else if (child instanceof FolderIcon) {
// Account for holographic blur padding on the drag view
@@ -762,6 +801,11 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
@Override
public void onChildViewAdded(View parent, View child) {
+ if (mOverlayView != null) {
+ // ensure that the overlay view stays on top. we can't use drawing order for this
+ // because in API level 16 touch dispatch doesn't respect drawing order.
+ mOverlayView.bringToFront();
+ }
updateChildIndices();
}
@@ -770,34 +814,54 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
updateChildIndices();
}
+ @Override
+ public void bringChildToFront(View child) {
+ super.bringChildToFront(child);
+ if (child != mOverlayView && mOverlayView != null) {
+ // ensure that the overlay view stays on top. we can't use drawing order for this
+ // because in API level 16 touch dispatch doesn't respect drawing order.
+ mOverlayView.bringToFront();
+ }
+ updateChildIndices();
+ }
+
private void updateChildIndices() {
- mDragViewIndex = -1;
+ mTopViewIndex = -1;
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
if (getChildAt(i) instanceof DragView) {
- mDragViewIndex = i;
+ mTopViewIndex = i;
}
}
+ mChildCountOnLastUpdate = childCount;
}
@Override
protected int getChildDrawingOrder(int childCount, int i) {
- if (mDragViewIndex == -1) {
+ if (mChildCountOnLastUpdate != childCount) {
+ // between platform versions 17 and 18, behavior for onChildViewRemoved / Added changed.
+ // Pre-18, the child was not added / removed by the time of those callbacks. We need to
+ // force update our representation of things here to avoid crashing on pre-18 devices
+ // in certain instances.
+ updateChildIndices();
+ }
+
+ // i represents the current draw iteration
+ if (mTopViewIndex == -1) {
+ // in general we do nothing
return i;
- } else if (i == mDragViewIndex) {
- return getChildCount()-1;
- } else if (i < mDragViewIndex) {
+ } else if (i == childCount - 1) {
+ // if we have a top index, we return it when drawing last item (highest z-order)
+ return mTopViewIndex;
+ } else if (i < mTopViewIndex) {
return i;
} else {
- // i > mDragViewIndex
- return i-1;
+ // for indexes greater than the top index, we fetch one item above to shift for the
+ // displacement of the top index
+ return i + 1;
}
}
- private boolean mInScrollArea;
- private Drawable mLeftHoverDrawable;
- private Drawable mRightHoverDrawable;
-
void onEnterScrollArea(int direction) {
mInScrollArea = true;
invalidate();
@@ -808,6 +872,16 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
invalidate();
}
+ void showPageHints() {
+ mShowPageHints = true;
+ invalidate();
+ }
+
+ void hidePageHints() {
+ mShowPageHints = false;
+ invalidate();
+ }
+
/**
* Note: this is a reimplementation of View.isLayoutRtl() since that is currently hidden api.
*/
@@ -817,31 +891,68 @@ public class DragLayer extends FrameLayout implements ViewGroup.OnHierarchyChang
@Override
protected void dispatchDraw(Canvas canvas) {
+ // Draw the background gradient below children.
+ if (mBackground != null && mBackgroundAlpha > 0.0f) {
+ int alpha = (int) (mBackgroundAlpha * 255);
+ mBackground.setAlpha(alpha);
+ mBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
+ mBackground.draw(canvas);
+ }
+
super.dispatchDraw(canvas);
+ }
- if (mInScrollArea && !LauncherAppState.getInstance().isScreenLarge()) {
+ private void drawPageHints(Canvas canvas) {
+ if (mShowPageHints) {
Workspace workspace = mLauncher.getWorkspace();
int width = getMeasuredWidth();
Rect childRect = new Rect();
- getDescendantRectRelativeToSelf(workspace.getChildAt(0), childRect);
+ getDescendantRectRelativeToSelf(workspace.getChildAt(workspace.getChildCount() - 1),
+ childRect);
int page = workspace.getNextPage();
final boolean isRtl = isLayoutRtl();
CellLayout leftPage = (CellLayout) workspace.getChildAt(isRtl ? page + 1 : page - 1);
CellLayout rightPage = (CellLayout) workspace.getChildAt(isRtl ? page - 1 : page + 1);
- if (leftPage != null && leftPage.getIsDragOverlapping()) {
- mLeftHoverDrawable.setBounds(0, childRect.top,
- mLeftHoverDrawable.getIntrinsicWidth(), childRect.bottom);
- mLeftHoverDrawable.draw(canvas);
- } else if (rightPage != null && rightPage.getIsDragOverlapping()) {
- mRightHoverDrawable.setBounds(width - mRightHoverDrawable.getIntrinsicWidth(),
+ if (leftPage != null && leftPage.isDragTarget()) {
+ Drawable left = mInScrollArea && leftPage.getIsDragOverlapping() ?
+ mLeftHoverDrawableActive : mLeftHoverDrawable;
+ left.setBounds(0, childRect.top,
+ left.getIntrinsicWidth(), childRect.bottom);
+ left.draw(canvas);
+ }
+ if (rightPage != null && rightPage.isDragTarget()) {
+ Drawable right = mInScrollArea && rightPage.getIsDragOverlapping() ?
+ mRightHoverDrawableActive : mRightHoverDrawable;
+ right.setBounds(width - right.getIntrinsicWidth(),
childRect.top, width, childRect.bottom);
- mRightHoverDrawable.draw(canvas);
+ right.draw(canvas);
}
}
}
+ protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+ boolean ret = super.drawChild(canvas, child, drawingTime);
+
+ // We want to draw the page hints above the workspace, but below the drag view.
+ if (child instanceof Workspace) {
+ drawPageHints(canvas);
+ }
+ return ret;
+ }
+
+ public void setBackgroundAlpha(float alpha) {
+ if (alpha != mBackgroundAlpha) {
+ mBackgroundAlpha = alpha;
+ invalidate();
+ }
+ }
+
+ public float getBackgroundAlpha() {
+ return mBackgroundAlpha;
+ }
+
public void setTouchCompleteListener(TouchCompleteListener listener) {
mTouchCompleteListener = listener;
}
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 447bb1cd8..94a07d706 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -60,36 +60,41 @@ public class DynamicGrid {
DEFAULT_ICON_SIZE_PX = pxFromDp(DEFAULT_ICON_SIZE_DP, dm);
// Our phone profiles include the bar sizes in each orientation
deviceProfiles.add(new DeviceProfile("Super Short Stubby",
- 255, 300, 2, 3, 48, 13, (hasAA ? 5 : 5), 48));
+ 255, 300, 2, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Shorter Stubby",
- 255, 400, 3, 3, 48, 13, (hasAA ? 5 : 5), 48));
+ 255, 400, 3, 3, 48, 13, (hasAA ? 3 : 5), 48, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Short Stubby",
- 275, 420, 3, 4, 48, 13, (hasAA ? 5 : 5), 48));
+ 275, 420, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Stubby",
- 255, 450, 3, 4, 48, 13, (hasAA ? 5 : 5), 48));
+ 255, 450, 3, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Nexus S",
- 296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 5), 48));
+ 296, 491.33f, 4, 4, 48, 13, (hasAA ? 5 : 5), 48, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Nexus 4",
- 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56));
+ 335, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Nexus 5",
- 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56));
+ 359, 567, 4, 4, DEFAULT_ICON_SIZE_DP, 13, (hasAA ? 5 : 5), 56, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
deviceProfiles.add(new DeviceProfile("Large Phone",
- 406, 694, 5, 5, 64, 14.4f, 5, 56));
+ 406, 694, 5, 5, 64, 14.4f, 5, 56, R.xml.default_workspace_5x5,
+ R.xml.default_workspace_5x5_no_all_apps));
// The tablet profile is odd in that the landscape orientation
// also includes the nav bar on the side
deviceProfiles.add(new DeviceProfile("Nexus 7",
- 575, 904, 5, 6, 72, 14.4f, 7, 60));
+ 575, 904, 5, 6, 72, 14.4f, 7, 60, R.xml.default_workspace_5x6,
+ R.xml.default_workspace_5x6_no_all_apps));
// Larger tablet profiles always have system bars on the top & bottom
deviceProfiles.add(new DeviceProfile("Nexus 10",
- 727, 1207, 5, 6, 76, 14.4f, 7, 64));
- /*
- deviceProfiles.add(new DeviceProfile("Nexus 7",
- 600, 960, 5, 5, 72, 14.4f, 5, 60));
- deviceProfiles.add(new DeviceProfile("Nexus 10",
- 800, 1280, 5, 5, 80, 14.4f, (hasAA ? 7 : 6), 64));
- */
+ 727, 1207, 5, 6, 76, 14.4f, 7, 64, R.xml.default_workspace_5x6,
+ R.xml.default_workspace_5x6_no_all_apps));
deviceProfiles.add(new DeviceProfile("20-inch Tablet",
- 1527, 2527, 7, 7, 100, 20, 7, 72));
+ 1527, 2527, 7, 7, 100, 20, 7, 72, R.xml.default_workspace_4x4,
+ R.xml.default_workspace_4x4_no_all_apps));
mMinWidth = dpiFromPx(minWidthPx, dm);
mMinHeight = dpiFromPx(minHeightPx, dm);
mProfile = new DeviceProfile(context, deviceProfiles,
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 85e90202b..ff02bbbc3 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -16,18 +16,61 @@
package com.android.launcher3;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.ColorFilter;
+import android.graphics.ColorMatrix;
+import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.util.SparseArray;
class FastBitmapDrawable extends Drawable {
- private Bitmap mBitmap;
- private int mAlpha;
+
+ static final TimeInterpolator CLICK_FEEDBACK_INTERPOLATOR = new TimeInterpolator() {
+
+ @Override
+ public float getInterpolation(float input) {
+ if (input < 0.05f) {
+ return input / 0.05f;
+ } else if (input < 0.3f){
+ return 1;
+ } else {
+ return (1 - input) / 0.7f;
+ }
+ }
+ };
+ static final long CLICK_FEEDBACK_DURATION = 2000;
+
+ private static final int PRESSED_BRIGHTNESS = 100;
+ private static ColorMatrix sGhostModeMatrix;
+ private static final ColorMatrix sTempMatrix = new ColorMatrix();
+
+ /**
+ * Store the brightness colors filters to optimize animations during icon press. This
+ * only works for non-ghost-mode icons.
+ */
+ private static final SparseArray<ColorFilter> sCachedBrightnessFilter =
+ new SparseArray<ColorFilter>();
+
+ private static final int GHOST_MODE_MIN_COLOR_RANGE = 130;
+
private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ private final Bitmap mBitmap;
+ private int mAlpha;
+
+ private int mBrightness = 0;
+ private boolean mGhostModeEnabled = false;
+
+ private boolean mPressed = false;
+ private ObjectAnimator mPressedAnimator;
FastBitmapDrawable(Bitmap b) {
mAlpha = 255;
@@ -44,7 +87,7 @@ class FastBitmapDrawable extends Drawable {
@Override
public void setColorFilter(ColorFilter cf) {
- mPaint.setColorFilter(cf);
+ // No op
}
@Override
@@ -58,6 +101,7 @@ class FastBitmapDrawable extends Drawable {
mPaint.setAlpha(alpha);
}
+ @Override
public void setFilterBitmap(boolean filterBitmap) {
mPaint.setFilterBitmap(filterBitmap);
mPaint.setAntiAlias(filterBitmap);
@@ -69,12 +113,12 @@ class FastBitmapDrawable extends Drawable {
@Override
public int getIntrinsicWidth() {
- return getBounds().width();
+ return mBitmap.getWidth();
}
@Override
public int getIntrinsicHeight() {
- return getBounds().height();
+ return mBitmap.getHeight();
}
@Override
@@ -90,4 +134,98 @@ class FastBitmapDrawable extends Drawable {
public Bitmap getBitmap() {
return mBitmap;
}
+
+ /**
+ * When enabled, the icon is grayed out and the contrast is increased to give it a 'ghost'
+ * appearance.
+ */
+ public void setGhostModeEnabled(boolean enabled) {
+ if (mGhostModeEnabled != enabled) {
+ mGhostModeEnabled = enabled;
+ updateFilter();
+ }
+ }
+
+ public void setPressed(boolean pressed) {
+ if (mPressed != pressed) {
+ mPressed = pressed;
+ if (mPressed) {
+ mPressedAnimator = ObjectAnimator
+ .ofInt(this, "brightness", PRESSED_BRIGHTNESS)
+ .setDuration(CLICK_FEEDBACK_DURATION);
+ mPressedAnimator.setInterpolator(CLICK_FEEDBACK_INTERPOLATOR);
+ mPressedAnimator.start();
+ } else if (mPressedAnimator != null) {
+ mPressedAnimator.cancel();
+ setBrightness(0);
+ }
+ }
+ invalidateSelf();
+ }
+
+ public boolean isGhostModeEnabled() {
+ return mGhostModeEnabled;
+ }
+
+ public int getBrightness() {
+ return mBrightness;
+ }
+
+ public void setBrightness(int brightness) {
+ if (mBrightness != brightness) {
+ mBrightness = brightness;
+ updateFilter();
+ invalidateSelf();
+ }
+ }
+
+ private void updateFilter() {
+ if (mGhostModeEnabled) {
+ if (sGhostModeMatrix == null) {
+ sGhostModeMatrix = new ColorMatrix();
+ sGhostModeMatrix.setSaturation(0);
+
+ // For ghost mode, set the color range to [GHOST_MODE_MIN_COLOR_RANGE, 255]
+ float range = (255 - GHOST_MODE_MIN_COLOR_RANGE) / 255.0f;
+ sTempMatrix.set(new float[] {
+ range, 0, 0, 0, GHOST_MODE_MIN_COLOR_RANGE,
+ 0, range, 0, 0, GHOST_MODE_MIN_COLOR_RANGE,
+ 0, 0, range, 0, GHOST_MODE_MIN_COLOR_RANGE,
+ 0, 0, 0, 1, 0 });
+ sGhostModeMatrix.preConcat(sTempMatrix);
+ }
+
+ if (mBrightness == 0) {
+ mPaint.setColorFilter(new ColorMatrixColorFilter(sGhostModeMatrix));
+ } else {
+ setBrightnessMatrix(sTempMatrix, mBrightness);
+ sTempMatrix.postConcat(sGhostModeMatrix);
+ mPaint.setColorFilter(new ColorMatrixColorFilter(sTempMatrix));
+ }
+ } else if (mBrightness != 0) {
+ ColorFilter filter = sCachedBrightnessFilter.get(mBrightness);
+ if (filter == null) {
+ filter = new PorterDuffColorFilter(Color.argb(mBrightness, 255, 255, 255),
+ PorterDuff.Mode.SRC_ATOP);
+ sCachedBrightnessFilter.put(mBrightness, filter);
+ }
+ mPaint.setColorFilter(filter);
+ } else {
+ mPaint.setColorFilter(null);
+ }
+ }
+
+ private static void setBrightnessMatrix(ColorMatrix matrix, int brightness) {
+ // Brightness: C-new = C-old*(1-amount) + amount
+ float scale = 1 - brightness / 255.0f;
+ matrix.setScale(scale, scale, scale, 1);
+ float[] array = matrix.getArray();
+
+ // Add the amount to RGB components of the matrix, as per the above formula.
+ // Fifth elements in the array correspond to the constant being added to
+ // red, blue, green, and alpha channel respectively.
+ array[4] = brightness;
+ array[9] = brightness;
+ array[14] = brightness;
+ }
}
diff --git a/src/com/android/launcher3/FastBitmapView.java b/src/com/android/launcher3/FastBitmapView.java
new file mode 100644
index 000000000..0937eb75e
--- /dev/null
+++ b/src/com/android/launcher3/FastBitmapView.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.view.View;
+
+public class FastBitmapView extends View {
+
+ private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+ private Bitmap mBitmap;
+
+ public FastBitmapView(Context context) {
+ super(context);
+ }
+
+ /**
+ * Applies the new bitmap.
+ * @return true if the view was invalidated.
+ */
+ public boolean setBitmap(Bitmap b) {
+ if (b != mBitmap){
+ if (mBitmap != null) {
+ invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+ }
+ mBitmap = b;
+ if (mBitmap != null) {
+ invalidate(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
+ }
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mBitmap != null) {
+ canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/FocusHelper.java b/src/com/android/launcher3/FocusHelper.java
index bb62bac65..d529b3901 100644
--- a/src/com/android/launcher3/FocusHelper.java
+++ b/src/com/android/launcher3/FocusHelper.java
@@ -17,13 +17,13 @@
package com.android.launcher3;
import android.content.res.Configuration;
+import android.util.Log;
import android.view.KeyEvent;
+import android.view.SoundEffectConstants;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.ScrollView;
-import android.widget.TabHost;
-import android.widget.TabWidget;
import java.util.ArrayList;
import java.util.Collections;
@@ -57,61 +57,16 @@ class HotseatIconKeyEventListener implements View.OnKeyListener {
}
}
-/**
- * A keyboard listener we set on the last tab button in AppsCustomize to jump to then
- * market icon and vice versa.
- */
-class AppsCustomizeTabKeyEventListener implements View.OnKeyListener {
- public boolean onKey(View v, int keyCode, KeyEvent event) {
- return FocusHelper.handleAppsCustomizeTabKeyEvent(v, keyCode, event);
- }
-}
-
public class FocusHelper {
/**
* Private helper to get the parent TabHost in the view hiearchy.
*/
- private static TabHost findTabHostParent(View v) {
+ private static AppsCustomizeTabHost findTabHostParent(View v) {
ViewParent p = v.getParent();
- while (p != null && !(p instanceof TabHost)) {
+ while (p != null && !(p instanceof AppsCustomizeTabHost)) {
p = p.getParent();
}
- return (TabHost) p;
- }
-
- /**
- * Handles key events in a AppsCustomize tab between the last tab view and the shop button.
- */
- static boolean handleAppsCustomizeTabKeyEvent(View v, int keyCode, KeyEvent e) {
- final TabHost tabHost = findTabHostParent(v);
- final ViewGroup contents = tabHost.getTabContentView();
- final View shop = tabHost.findViewById(R.id.market_button);
-
- final int action = e.getAction();
- final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
- boolean wasHandled = false;
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- if (handleKeyEvent) {
- // Select the shop button if we aren't on it
- if (v != shop) {
- shop.requestFocus();
- }
- }
- wasHandled = true;
- break;
- case KeyEvent.KEYCODE_DPAD_DOWN:
- if (handleKeyEvent) {
- // Select the content view (down is handled by the tab key handler otherwise)
- if (v == shop) {
- contents.requestFocus();
- wasHandled = true;
- }
- }
- break;
- default: break;
- }
- return wasHandled;
+ return (AppsCustomizeTabHost) p;
}
/**
@@ -134,8 +89,6 @@ public class FocusHelper {
final PagedViewGridLayout parent = (PagedViewGridLayout) w.getParent();
final PagedView container = (PagedView) parent.getParent();
- final TabHost tabHost = findTabHostParent(container);
- final TabWidget tabs = tabHost.getTabWidget();
final int widgetIndex = parent.indexOfChild(w);
final int widgetCount = parent.getChildCount();
final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parent));
@@ -194,8 +147,6 @@ public class FocusHelper {
int newWidgetIndex = ((y - 1) * cellCountX) + x;
child = parent.getChildAt(newWidgetIndex);
if (child != null) child.requestFocus();
- } else {
- tabs.requestFocus();
}
}
wasHandled = true;
@@ -294,8 +245,6 @@ public class FocusHelper {
// Note we have an extra parent because of the
// PagedViewCellLayout/PagedViewCellLayoutChildren relationship
final PagedView container = (PagedView) parentLayout.getParent();
- final TabHost tabHost = findTabHostParent(container);
- final TabWidget tabs = tabHost.getTabWidget();
final int iconIndex = itemContainer.indexOfChild(v);
final int itemCount = itemContainer.getChildCount();
final int pageIndex = ((PagedView) container).indexToPage(container.indexOfChild(parentLayout));
@@ -317,13 +266,17 @@ public class FocusHelper {
// Select the previous icon or the last icon on the previous page
if (iconIndex > 0) {
itemContainer.getChildAt(iconIndex - 1).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
} else {
if (pageIndex > 0) {
newParent = getAppsCustomizePage(container, pageIndex - 1);
if (newParent != null) {
container.snapToPage(pageIndex - 1);
child = newParent.getChildAt(newParent.getChildCount() - 1);
- if (child != null) child.requestFocus();
+ if (child != null) {
+ child.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
+ }
}
}
}
@@ -335,13 +288,17 @@ public class FocusHelper {
// Select the next icon or the first icon on the next page
if (iconIndex < (itemCount - 1)) {
itemContainer.getChildAt(iconIndex + 1).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
} else {
if (pageIndex < (pageCount - 1)) {
newParent = getAppsCustomizePage(container, pageIndex + 1);
if (newParent != null) {
container.snapToPage(pageIndex + 1);
child = newParent.getChildAt(0);
- if (child != null) child.requestFocus();
+ if (child != null) {
+ child.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
+ }
}
}
}
@@ -354,31 +311,25 @@ public class FocusHelper {
if (y > 0) {
int newiconIndex = ((y - 1) * countX) + x;
itemContainer.getChildAt(newiconIndex).requestFocus();
- } else {
- tabs.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
if (handleKeyEvent) {
- // Select the closest icon in the previous row, otherwise do nothing
+ // Select the closest icon in the next row, otherwise do nothing
if (y < (countY - 1)) {
int newiconIndex = Math.min(itemCount - 1, ((y + 1) * countX) + x);
- itemContainer.getChildAt(newiconIndex).requestFocus();
+ int newIconY = newiconIndex / countX;
+ if (newIconY != y) {
+ itemContainer.getChildAt(newiconIndex).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
+ }
}
}
wasHandled = true;
break;
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- if (handleKeyEvent) {
- // Simulate a click on the icon
- View.OnClickListener clickListener = (View.OnClickListener) container;
- clickListener.onClick(v);
- }
- wasHandled = true;
- break;
case KeyEvent.KEYCODE_PAGE_UP:
if (handleKeyEvent) {
// Select the first icon on the previous page, or the first icon on this page
@@ -388,10 +339,14 @@ public class FocusHelper {
if (newParent != null) {
container.snapToPage(pageIndex - 1);
child = newParent.getChildAt(0);
- if (child != null) child.requestFocus();
+ if (child != null) {
+ child.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
+ }
}
} else {
itemContainer.getChildAt(0).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
}
wasHandled = true;
@@ -405,10 +360,14 @@ public class FocusHelper {
if (newParent != null) {
container.snapToPage(pageIndex + 1);
child = newParent.getChildAt(0);
- if (child != null) child.requestFocus();
+ if (child != null) {
+ child.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
+ }
}
} else {
itemContainer.getChildAt(itemCount - 1).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
}
wasHandled = true;
@@ -417,6 +376,7 @@ public class FocusHelper {
if (handleKeyEvent) {
// Select the first icon on this page
itemContainer.getChildAt(0).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
wasHandled = true;
break;
@@ -424,6 +384,7 @@ public class FocusHelper {
if (handleKeyEvent) {
// Select the last icon on this page
itemContainer.getChildAt(itemCount - 1).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
wasHandled = true;
break;
@@ -439,8 +400,8 @@ public class FocusHelper {
if (!LauncherAppState.getInstance().isScreenLarge()) return false;
final FocusOnlyTabWidget parent = (FocusOnlyTabWidget) v.getParent();
- final TabHost tabHost = findTabHostParent(parent);
- final ViewGroup contents = tabHost.getTabContentView();
+ final AppsCustomizeTabHost tabHost = findTabHostParent(parent);
+ final ViewGroup contents = tabHost.getContent();
final int tabCount = parent.getTabCount();
final int tabIndex = parent.getChildTabIndex(v);
@@ -490,53 +451,56 @@ public class FocusHelper {
* Handles key events in the workspace hotseat (bottom of the screen).
*/
static boolean handleHotseatButtonKeyEvent(View v, int keyCode, KeyEvent e, int orientation) {
- final ViewGroup parent = (ViewGroup) v.getParent();
- final ViewGroup launcher = (ViewGroup) parent.getParent();
- final Workspace workspace = (Workspace) launcher.findViewById(R.id.workspace);
- final int buttonIndex = parent.indexOfChild(v);
- final int buttonCount = parent.getChildCount();
- final int pageIndex = workspace.getCurrentPage();
+ ShortcutAndWidgetContainer parent = (ShortcutAndWidgetContainer) v.getParent();
+ final CellLayout layout = (CellLayout) parent.getParent();
// NOTE: currently we don't special case for the phone UI in different
- // orientations, even though the hotseat is on the side in landscape mode. This
+ // orientations, even though the hotseat is on the side in landscape mode. This
// is to ensure that accessibility consistency is maintained across rotations.
-
final int action = e.getAction();
final boolean handleKeyEvent = (action != KeyEvent.ACTION_UP);
boolean wasHandled = false;
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (handleKeyEvent) {
- // Select the previous button, otherwise snap to the previous page
- if (buttonIndex > 0) {
- parent.getChildAt(buttonIndex - 1).requestFocus();
- } else {
- workspace.snapToPage(pageIndex - 1);
+ ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
+ int myIndex = views.indexOf(v);
+ // Select the previous button, otherwise do nothing
+ if (myIndex > 0) {
+ views.get(myIndex - 1).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
}
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (handleKeyEvent) {
- // Select the next button, otherwise snap to the next page
- if (buttonIndex < (buttonCount - 1)) {
- parent.getChildAt(buttonIndex + 1).requestFocus();
- } else {
- workspace.snapToPage(pageIndex + 1);
+ ArrayList<View> views = getCellLayoutChildrenSortedSpatially(layout, parent);
+ int myIndex = views.indexOf(v);
+ // Select the next button, otherwise do nothing
+ if (myIndex < views.size() - 1) {
+ views.get(myIndex + 1).requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
}
}
wasHandled = true;
break;
case KeyEvent.KEYCODE_DPAD_UP:
if (handleKeyEvent) {
- // Select the first bubble text view in the current page of the workspace
- final CellLayout layout = (CellLayout) workspace.getChildAt(pageIndex);
- final ShortcutAndWidgetContainer children = layout.getShortcutsAndWidgets();
- final View newIcon = getIconInDirection(layout, children, -1, 1);
- if (newIcon != null) {
- newIcon.requestFocus();
- } else {
- workspace.requestFocus();
+ final Workspace workspace = (Workspace)
+ v.getRootView().findViewById(R.id.workspace);
+ if (workspace != null) {
+ int pageIndex = workspace.getCurrentPage();
+ CellLayout topLayout = (CellLayout) workspace.getChildAt(pageIndex);
+ ShortcutAndWidgetContainer children = topLayout.getShortcutsAndWidgets();
+ final View newIcon = getIconInDirection(layout, children, -1, 1);
+ // Select the first bubble text view in the current page of the workspace
+ if (newIcon != null) {
+ newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
+ } else {
+ workspace.requestFocus();
+ }
}
}
wasHandled = true;
@@ -555,8 +519,8 @@ public class FocusHelper {
*/
private static ShortcutAndWidgetContainer getCellLayoutChildrenForIndex(
ViewGroup container, int i) {
- ViewGroup parent = (ViewGroup) container.getChildAt(i);
- return (ShortcutAndWidgetContainer) parent.getChildAt(0);
+ CellLayout parent = (CellLayout) container.getChildAt(i);
+ return parent.getShortcutsAndWidgets();
}
/**
@@ -680,6 +644,7 @@ public class FocusHelper {
View newIcon = getIconInDirection(layout, parent, v, -1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
} else {
if (pageIndex > 0) {
parent = getCellLayoutChildrenForIndex(workspace, pageIndex - 1);
@@ -691,6 +656,7 @@ public class FocusHelper {
// Snap to the previous page
workspace.snapToPage(pageIndex - 1);
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
}
}
}
@@ -702,6 +668,7 @@ public class FocusHelper {
View newIcon = getIconInDirection(layout, parent, v, 1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
} else {
if (pageIndex < (pageCount - 1)) {
parent = getCellLayoutChildrenForIndex(workspace, pageIndex + 1);
@@ -712,6 +679,7 @@ public class FocusHelper {
// Snap to the next page
workspace.snapToPage(pageIndex + 1);
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
}
}
}
@@ -727,6 +695,7 @@ public class FocusHelper {
} else {
tabs.requestFocus();
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
@@ -735,9 +704,11 @@ public class FocusHelper {
View newIcon = getClosestIconOnLine(layout, parent, v, 1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
wasHandled = true;
} else if (hotseat != null) {
hotseat.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
}
break;
@@ -754,10 +725,12 @@ public class FocusHelper {
// Snap to the previous page
workspace.snapToPage(pageIndex - 1);
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
} else {
View newIcon = getIconInDirection(layout, parent, -1, 1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
}
}
@@ -776,11 +749,13 @@ public class FocusHelper {
// Snap to the next page
workspace.snapToPage(pageIndex + 1);
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
} else {
View newIcon = getIconInDirection(layout, parent,
parent.getChildCount(), -1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
}
}
@@ -792,6 +767,7 @@ public class FocusHelper {
View newIcon = getIconInDirection(layout, parent, -1, 1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
}
wasHandled = true;
@@ -803,6 +779,7 @@ public class FocusHelper {
parent.getChildCount(), -1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
}
wasHandled = true;
@@ -832,6 +809,7 @@ public class FocusHelper {
View newIcon = getIconInDirection(layout, parent, v, -1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_LEFT);
}
}
wasHandled = true;
@@ -845,6 +823,7 @@ public class FocusHelper {
} else {
title.requestFocus();
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_RIGHT);
}
wasHandled = true;
break;
@@ -854,6 +833,7 @@ public class FocusHelper {
View newIcon = getClosestIconOnLine(layout, parent, v, -1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
}
wasHandled = true;
@@ -867,6 +847,7 @@ public class FocusHelper {
} else {
title.requestFocus();
}
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
wasHandled = true;
break;
@@ -876,6 +857,7 @@ public class FocusHelper {
View newIcon = getIconInDirection(layout, parent, -1, 1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_UP);
}
}
wasHandled = true;
@@ -887,6 +869,7 @@ public class FocusHelper {
parent.getChildCount(), -1);
if (newIcon != null) {
newIcon.requestFocus();
+ v.playSoundEffect(SoundEffectConstants.NAVIGATION_DOWN);
}
}
wasHandled = true;
diff --git a/src/com/android/launcher3/FocusIndicatorView.java b/src/com/android/launcher3/FocusIndicatorView.java
new file mode 100644
index 000000000..12b7a4076
--- /dev/null
+++ b/src/com/android/launcher3/FocusIndicatorView.java
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+import android.util.Pair;
+import android.view.View;
+import android.view.ViewParent;
+
+public class FocusIndicatorView extends View implements View.OnFocusChangeListener {
+
+ // It can be any number >0. The view is resized using scaleX and scaleY.
+ static final int DEFAULT_LAYOUT_SIZE = 100;
+ private static final float MIN_VISIBLE_ALPHA = 0.2f;
+
+ private static final int[] sTempPos = new int[2];
+ private static final int[] sTempShift = new int[2];
+
+ private final int[] mIndicatorPos = new int[2];
+ private final int[] mTargetViewPos = new int[2];
+
+ private View mLastFocusedView;
+ private boolean mInitiated;
+
+ private Pair<View, Boolean> mPendingCall;
+
+ public FocusIndicatorView(Context context) {
+ this(context, null);
+ }
+
+ public FocusIndicatorView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ setAlpha(0);
+ setBackgroundColor(getResources().getColor(R.color.focused_background));
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+
+ // Redraw if it is already showing. This avoids a bug where the height changes by a small
+ // amount on connecting/disconnecting a bluetooth keyboard.
+ if (mLastFocusedView != null) {
+ mPendingCall = Pair.create(mLastFocusedView, Boolean.TRUE);
+ invalidate();
+ }
+ }
+
+ @Override
+ public void onFocusChange(View v, boolean hasFocus) {
+ mPendingCall = null;
+ if (!mInitiated && (getWidth() == 0)) {
+ // View not yet laid out. Wait until the view is ready to be drawn, so that be can
+ // get the location on screen.
+ mPendingCall = Pair.create(v, hasFocus);
+ invalidate();
+ return;
+ }
+
+ if (!mInitiated) {
+ getLocationRelativeToParentPagedView(this, mIndicatorPos);
+ mInitiated = true;
+ }
+
+ if (hasFocus) {
+ int indicatorWidth = getWidth();
+ int indicatorHeight = getHeight();
+
+ float scaleX = v.getScaleX() * v.getWidth() / indicatorWidth;
+ float scaleY = v.getScaleY() * v.getHeight() / indicatorHeight;
+
+ getLocationRelativeToParentPagedView(v, mTargetViewPos);
+ float x = mTargetViewPos[0] - mIndicatorPos[0] - (1 - scaleX) * indicatorWidth / 2;
+ float y = mTargetViewPos[1] - mIndicatorPos[1] - (1 - scaleY) * indicatorHeight / 2;
+
+ if (getAlpha() > MIN_VISIBLE_ALPHA) {
+ animate()
+ .translationX(x)
+ .translationY(y)
+ .scaleX(scaleX)
+ .scaleY(scaleY)
+ .alpha(1);
+ } else {
+ setTranslationX(x);
+ setTranslationY(y);
+ setScaleX(scaleX);
+ setScaleY(scaleY);
+ animate().alpha(1);
+ }
+ mLastFocusedView = v;
+ } else {
+ if (mLastFocusedView == v) {
+ mLastFocusedView = null;
+ animate().alpha(0);
+ }
+ }
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mPendingCall != null) {
+ onFocusChange(mPendingCall.first, mPendingCall.second);
+ }
+ }
+
+ /**
+ * Gets the location of a view relative in the window, off-setting any shift due to
+ * page view scroll
+ */
+ private static void getLocationRelativeToParentPagedView(View v, int[] pos) {
+ getPagedViewScrollShift(v, sTempShift);
+ v.getLocationInWindow(sTempPos);
+ pos[0] = sTempPos[0] + sTempShift[0];
+ pos[1] = sTempPos[1] + sTempShift[1];
+ }
+
+ private static void getPagedViewScrollShift(View child, int[] shift) {
+ ViewParent parent = child.getParent();
+ if (parent instanceof PagedView) {
+ View parentView = (View) parent;
+ child.getLocationInWindow(sTempPos);
+ shift[0] = parentView.getPaddingLeft() - sTempPos[0];
+ shift[1] = -(int) child.getTranslationY();
+ } else if (parent instanceof View) {
+ getPagedViewScrollShift((View) parent, shift);
+ } else {
+ shift[0] = shift[1] = 0;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index b4c399266..1890af47d 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -18,6 +18,7 @@ package com.android.launcher3;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -40,6 +41,7 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.LinearLayout;
@@ -72,6 +74,8 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private static final int CLOSE_FOLDER_DELAY_MS = 150;
private int mExpandDuration;
+ private int mMaterialExpandDuration;
+ private int mMaterialExpandStagger;
protected CellLayout mContent;
private ScrollView mScrollView;
private final LayoutInflater mInflater;
@@ -112,9 +116,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
private static String sDefaultFolderName;
private static String sHintText;
- private int DRAG_MODE_NONE = 0;
- private int DRAG_MODE_REORDER = 1;
- private int mDragMode = DRAG_MODE_NONE;
+ private FocusIndicatorView mFocusIndicatorHandler;
// We avoid measuring the scroll view with a 0 width or height, as this
// results in CellLayout being measured as UNSPECIFIED, which it does
@@ -157,7 +159,9 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mInputMethodManager = (InputMethodManager)
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
- mExpandDuration = res.getInteger(R.integer.config_folderAnimDuration);
+ mExpandDuration = res.getInteger(R.integer.config_folderExpandDuration);
+ mMaterialExpandDuration = res.getInteger(R.integer.config_materialFolderExpandDuration);
+ mMaterialExpandStagger = res.getInteger(R.integer.config_materialFolderExpandStagger);
if (sDefaultFolderName == null) {
sDefaultFolderName = res.getString(R.string.folder_name);
@@ -178,6 +182,11 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mScrollView = (ScrollView) findViewById(R.id.scroll_view);
mContent = (CellLayout) findViewById(R.id.folder_content);
+ mFocusIndicatorHandler = new FocusIndicatorView(getContext());
+ mContent.addView(mFocusIndicatorHandler, 0);
+ mFocusIndicatorHandler.getLayoutParams().height = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
+ mFocusIndicatorHandler.getLayoutParams().width = FocusIndicatorView.DEFAULT_LAYOUT_SIZE;
+
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@@ -239,9 +248,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return false;
}
- mLauncher.getLauncherClings().dismissFolderCling(null);
-
- mLauncher.getWorkspace().onDragStartedWithItem(v);
mLauncher.getWorkspace().beginDragShared(v, this);
mCurrentDragInfo = item;
@@ -303,6 +309,10 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
return mFolderName;
}
+ public CellLayout getContent() {
+ return mContent;
+ }
+
/**
* We need to handle touch events to prevent them from falling through to the workspace below.
*/
@@ -387,7 +397,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
// We rearrange the items in case there are any empty gaps
setupContentForNumItems(count);
- // If our folder has too many items we prune them from the list. This is an issue
+ // If our folder has too many items we prune them from the list. This is an issue
// when upgrading from the old Folders implementation which could contain an unlimited
// number of items.
for (ShortcutInfo item: overflow) {
@@ -439,18 +449,93 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mState = STATE_SMALL;
}
- public void animateOpen() {
- positionAndSizeAsIcon();
+ private void prepareReveal() {
+ setScaleX(1f);
+ setScaleY(1f);
+ setAlpha(1f);
+ mState = STATE_SMALL;
+ }
+ public void animateOpen() {
if (!(getParent() instanceof DragLayer)) return;
- centerAboutIcon();
- PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1);
- PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
- PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
- final ObjectAnimator oa =
- LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
- oa.addListener(new AnimatorListenerAdapter() {
+ Animator openFolderAnim = null;
+ final Runnable onCompleteRunnable;
+ if (!Utilities.isLmpOrAbove()) {
+ positionAndSizeAsIcon();
+ centerAboutIcon();
+
+ PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 1);
+ PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1.0f);
+ PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1.0f);
+ final ObjectAnimator oa =
+ LauncherAnimUtils.ofPropertyValuesHolder(this, alpha, scaleX, scaleY);
+ oa.setDuration(mExpandDuration);
+ openFolderAnim = oa;
+
+ setLayerType(LAYER_TYPE_HARDWARE, null);
+ onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ setLayerType(LAYER_TYPE_NONE, null);
+ }
+ };
+ } else {
+ prepareReveal();
+ centerAboutIcon();
+
+ int width = getPaddingLeft() + getPaddingRight() + mContent.getDesiredWidth();
+ int height = getFolderHeight();
+
+ float transX = - 0.075f * (width / 2 - getPivotX());
+ float transY = - 0.075f * (height / 2 - getPivotY());
+ setTranslationX(transX);
+ setTranslationY(transY);
+ PropertyValuesHolder tx = PropertyValuesHolder.ofFloat("translationX", transX, 0);
+ PropertyValuesHolder ty = PropertyValuesHolder.ofFloat("translationY", transY, 0);
+
+ int rx = (int) Math.max(Math.max(width - getPivotX(), 0), getPivotX());
+ int ry = (int) Math.max(Math.max(height - getPivotY(), 0), getPivotY());
+ float radius = (float) Math.sqrt(rx * rx + ry * ry);
+ AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
+ Animator reveal = LauncherAnimUtils.createCircularReveal(this, (int) getPivotX(),
+ (int) getPivotY(), 0, radius);
+ reveal.setDuration(mMaterialExpandDuration);
+ reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ mContent.setAlpha(0f);
+ Animator iconsAlpha = LauncherAnimUtils.ofFloat(mContent, "alpha", 0f, 1f);
+ iconsAlpha.setDuration(mMaterialExpandDuration);
+ iconsAlpha.setStartDelay(mMaterialExpandStagger);
+ iconsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+
+ mFolderName.setAlpha(0f);
+ Animator textAlpha = LauncherAnimUtils.ofFloat(mFolderName, "alpha", 0f, 1f);
+ textAlpha.setDuration(mMaterialExpandDuration);
+ textAlpha.setStartDelay(mMaterialExpandStagger);
+ textAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+
+ Animator drift = LauncherAnimUtils.ofPropertyValuesHolder(this, tx, ty);
+ drift.setDuration(mMaterialExpandDuration);
+ drift.setStartDelay(mMaterialExpandStagger);
+ drift.setInterpolator(new LogDecelerateInterpolator(60, 0));
+
+ anim.play(drift);
+ anim.play(iconsAlpha);
+ anim.play(textAlpha);
+ anim.play(reveal);
+
+ openFolderAnim = anim;
+
+ mContent.setLayerType(LAYER_TYPE_HARDWARE, null);
+ onCompleteRunnable = new Runnable() {
+ @Override
+ public void run() {
+ mContent.setLayerType(LAYER_TYPE_NONE, null);
+ }
+ };
+ }
+ openFolderAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
@@ -461,23 +546,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
@Override
public void onAnimationEnd(Animator animation) {
mState = STATE_OPEN;
- setLayerType(LAYER_TYPE_NONE, null);
- // Only show cling if we are not in the middle of a drag - this would be quite jarring.
- if (!mDragController.isDragging()) {
- Cling cling = mLauncher.getLauncherClings().showFoldersCling();
- if (cling != null) {
- cling.bringScrimToFront();
- bringToFront();
- cling.bringToFront();
- }
+ if (onCompleteRunnable != null) {
+ onCompleteRunnable.run();
}
+
setFocusOnFirstChild();
}
});
- oa.setDuration(mExpandDuration);
- setLayerType(LAYER_TYPE_HARDWARE, null);
- oa.start();
+ openFolderAnim.start();
// Make sure the folder picks up the last drag move even if the finger doesn't move.
if (mDragController.isDragging()) {
@@ -563,23 +640,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
protected View createAndAddShortcut(ShortcutInfo item) {
final BubbleTextView textView =
- (BubbleTextView) mInflater.inflate(R.layout.application, this, false);
- textView.setCompoundDrawables(null,
- Utilities.createIconDrawable(item.getIcon(mIconCache)), null, null);
- textView.setText(item.title);
- textView.setTag(item);
- textView.setTextColor(getResources().getColor(R.color.folder_items_text_color));
- textView.setShadowsEnabled(false);
- textView.setGlowColor(getResources().getColor(R.color.folder_items_glow_color));
+ (BubbleTextView) mInflater.inflate(R.layout.folder_application, this, false);
+ textView.applyFromShortcutInfo(item, mIconCache, false);
textView.setOnClickListener(this);
textView.setOnLongClickListener(this);
+ textView.setOnFocusChangeListener(mFocusIndicatorHandler);
// We need to check here to verify that the given item's location isn't already occupied
// by another item.
if (mContent.getChildAt(item.cellX, item.cellY) != null || item.cellX < 0 || item.cellY < 0
|| item.cellX >= mContent.getCountX() || item.cellY >= mContent.getCountY()) {
- // This shouldn't happen, log it.
+ // This shouldn't happen, log it.
Log.e(TAG, "Folder order not properly persisted during bind");
if (!findAndSetEmptyCells(item)) {
return null;
@@ -695,9 +767,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mReorderAlarm.setAlarm(REORDER_DELAY);
mPreviousTargetCell[0] = mTargetCell[0];
mPreviousTargetCell[1] = mTargetCell[1];
- mDragMode = DRAG_MODE_REORDER;
- } else {
- mDragMode = DRAG_MODE_NONE;
}
}
}
@@ -753,7 +822,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mOnExitAlarm.setAlarm(ON_EXIT_CLOSE_DELAY);
}
mReorderAlarm.cancelAlarm();
- mDragMode = DRAG_MODE_NONE;
}
public void onDropCompleted(final View target, final DragObject d,
@@ -793,12 +861,6 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
}
}
- // This is kind of hacky, but in general, dropping on the workspace handles removing
- // the extra screen, but dropping elsewhere (back to self, or onto delete) doesn't.
- if (target != mLauncher.getWorkspace()) {
- mLauncher.getWorkspace().removeExtraEmptyScreen(true, null);
- }
-
mDeleteFolderOnDropCompleted = false;
mDragInProgress = false;
mItemAddedBackToSelfViaIcon = false;
@@ -1172,21 +1234,15 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
public void onDrop(DragObject d) {
Runnable cleanUpRunnable = null;
- // If we are coming from All Apps space, we need to remove the extra empty screen (which is
- // normally done in Workspace#onDropExternal, as well zoom back in and close the folder.
+ // If we are coming from All Apps space, we defer removing the extra empty screen
+ // until the folder closes
if (d.dragSource != mLauncher.getWorkspace() && !(d.dragSource instanceof Folder)) {
cleanUpRunnable = new Runnable() {
@Override
public void run() {
- mLauncher.getWorkspace().removeExtraEmptyScreen(false, new Runnable() {
- @Override
- public void run() {
- mLauncher.closeFolder();
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE,
- null);
- }
- }, CLOSE_FOLDER_DELAY_MS, false);
+ mLauncher.exitSpringLoadedDragModeDelayed(true,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
+ null);
}
};
}
@@ -1196,6 +1252,18 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
if (mIsExternalDrag) {
si.cellX = mEmptyCell[0];
si.cellY = mEmptyCell[1];
+
+ // 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);
+
+ // We only need to update the locations if it doesn't get handled in #onDropCompleted.
+ if (d.dragSource != this) {
+ updateItemLocationsInDatabaseBatch();
+ }
+ mIsExternalDrag = false;
+
currentDragView = createAndAddShortcut(si);
} else {
currentDragView = mCurrentDragView;
@@ -1223,22 +1291,12 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
mItemsInvalidated = true;
setupContentDimensions(getItemCount());
- // Actually move the item in the database if it was an external drag.
- if (mIsExternalDrag) {
- 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;
- }
-
// Temporarily suppress the listener, as we did all the work already here.
mSuppressOnAdd = true;
mInfo.add(si);
mSuppressOnAdd = false;
+ // Clear the drag info, as it is no longer being dragged.
+ mCurrentDragInfo = null;
}
// This is used so the item doesn't immediately appear in the folder when added. In one case
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 78026f162..a359f1180 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -33,6 +33,7 @@ import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
@@ -70,7 +71,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
private static final float OUTER_RING_GROWTH_FACTOR = 0.3f;
// The amount of vertical spread between items in the stack [0...1]
- private static final float PERSPECTIVE_SHIFT_FACTOR = 0.24f;
+ private static final float PERSPECTIVE_SHIFT_FACTOR = 0.18f;
// Flag as to whether or not to draw an outer ring. Currently none is designed.
public static final boolean HAS_OUTER_RING = true;
@@ -105,6 +106,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
boolean mAnimating = false;
private Rect mOldBounds = new Rect();
+ private float mSlop;
+
private PreviewItemDrawingParams mParams = new PreviewItemDrawingParams(0, 0, 0, 0);
private PreviewItemDrawingParams mAnimParams = new PreviewItemDrawingParams(0, 0, 0, 0);
private ArrayList<ShortcutInfo> mHiddenItems = new ArrayList<ShortcutInfo>();
@@ -130,7 +133,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
final ViewGroup cellLayoutChildren = (ViewGroup) getParent();
final ViewGroup cellLayout = (ViewGroup) cellLayoutChildren.getParent();
final Workspace workspace = (Workspace) cellLayout.getParent();
- return !workspace.isSmall();
+ return !workspace.workspaceInModalState();
}
static FolderIcon fromXml(int resId, Launcher launcher, ViewGroup group,
@@ -175,6 +178,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
icon.mFolderRingAnimator = new FolderRingAnimator(launcher, icon);
folderInfo.addListener(icon);
+ icon.setOnFocusChangeListener(launcher.mFocusHandler);
return icon;
}
@@ -308,7 +312,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
}
- Folder getFolder() {
+ public Folder getFolder() {
return mFolder;
}
@@ -341,7 +345,12 @@ public class FolderIcon extends FrameLayout implements FolderListener {
mFolderRingAnimator.animateToAcceptState();
layout.showFolderAccept(mFolderRingAnimator);
mOpenAlarm.setOnAlarmListener(mOnOpenListener);
- if (SPRING_LOADING_ENABLED) {
+ if (SPRING_LOADING_ENABLED &&
+ ((dragInfo instanceof AppInfo) || (dragInfo instanceof ShortcutInfo))) {
+ // TODO: we currently don't support spring-loading for PendingAddShortcutInfos even
+ // though widget-style shortcuts can be added to folders. The issue is that we need
+ // to deal with configuration activities which are currently handled in
+ // Workspace#onDropExternal.
mOpenAlarm.setAlarm(ON_OPEN_DELAY);
}
mDragInfo = (ItemInfo) dragInfo;
@@ -359,6 +368,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
item.spanX = 1;
item.spanY = 1;
} else {
+ // ShortcutInfo
item = (ShortcutInfo) mDragInfo;
}
mFolder.beginExternalDrag(item);
@@ -371,7 +381,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
float scaleRelativeToDragLayer, Runnable postAnimationRunnable) {
// These correspond two the drawable and view that the icon was dropped _onto_
- Drawable animateDrawable = ((TextView) destView).getCompoundDrawables()[1];
+ Drawable animateDrawable = getTopDrawable((TextView) destView);
computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
destView.getMeasuredWidth());
@@ -385,8 +395,8 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
public void performDestroyAnimation(final View finalView, Runnable onCompleteRunnable) {
- Drawable animateDrawable = ((TextView) finalView).getCompoundDrawables()[1];
- computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
+ Drawable animateDrawable = getTopDrawable((TextView) finalView);
+ computePreviewDrawingParams(animateDrawable.getIntrinsicWidth(),
finalView.getMeasuredWidth());
// This will animate the first item from it's position as an icon into its
@@ -492,6 +502,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
int adjustedAvailableSpace = (int) ((mAvailableSpaceInPreview / 2) * (1 + 0.8f));
int unscaledHeight = (int) (mIntrinsicIconSize * (1 + PERSPECTIVE_SHIFT_FACTOR));
+
mBaselineIconScale = (1.0f * adjustedAvailableSpace / unscaledHeight);
mBaselineIconSize = (int) (mIntrinsicIconSize * mBaselineIconScale);
@@ -546,7 +557,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
// We want to imagine our coordinates from the bottom left, growing up and to the
// right. This is natural for the x-axis, but for the y-axis, we have to invert things.
float transY = mAvailableSpaceInPreview - (offset + scaledSize + scaleOffsetCorrection) + getPaddingTop();
- float transX = offset + scaleOffsetCorrection;
+ float transX = (mAvailableSpaceInPreview - scaledSize) / 2;
float totalScale = mBaselineIconScale * scale;
final int overlayAlpha = (int) (80 * (1 - r));
@@ -570,10 +581,18 @@ public class FolderIcon extends FrameLayout implements FolderListener {
if (d != null) {
mOldBounds.set(d.getBounds());
d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
- d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
- PorterDuff.Mode.SRC_ATOP);
- d.draw(canvas);
- d.clearColorFilter();
+ if (d instanceof FastBitmapDrawable) {
+ FastBitmapDrawable fd = (FastBitmapDrawable) d;
+ int oldBrightness = fd.getBrightness();
+ fd.setBrightness(params.overlayAlpha);
+ d.draw(canvas);
+ fd.setBrightness(oldBrightness);
+ } else {
+ d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
+ PorterDuff.Mode.SRC_ATOP);
+ d.draw(canvas);
+ d.clearColorFilter();
+ }
d.setBounds(mOldBounds);
}
canvas.restore();
@@ -595,7 +614,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
computePreviewDrawingParams(mAnimParams.drawable);
} else {
v = (TextView) items.get(0);
- d = v.getCompoundDrawables()[1];
+ d = getTopDrawable(v);
computePreviewDrawingParams(d);
}
@@ -604,7 +623,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
for (int i = nItemsInPreview - 1; i >= 0; i--) {
v = (TextView) items.get(i);
if (!mHiddenItems.contains(v.getTag())) {
- d = v.getCompoundDrawables()[1];
+ d = getTopDrawable(v);
mParams = computePreviewItemDrawingParams(i, mParams);
mParams.drawable = d;
drawPreviewItem(canvas, mParams);
@@ -615,6 +634,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
}
}
+ private Drawable getTopDrawable(TextView v) {
+ Drawable d = v.getCompoundDrawables()[1];
+ return (d instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) d).mIcon : d;
+ }
+
private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
final Runnable onCompleteRunnable) {
final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
@@ -703,11 +727,22 @@ public class FolderIcon extends FrameLayout implements FolderListener {
case MotionEvent.ACTION_UP:
mLongPressHelper.cancelLongPress();
break;
+ case MotionEvent.ACTION_MOVE:
+ if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
+ mLongPressHelper.cancelLongPress();
+ }
+ break;
}
return result;
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
+
+ @Override
public void cancelLongPress() {
super.cancelLongPress();
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index d45e4e47b..85a792f4b 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -17,13 +17,17 @@
package com.android.launcher3;
import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.compat.UserHandleCompat;
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Represents a folder containing shortcuts or apps.
*/
-class FolderInfo extends ItemInfo {
+public class FolderInfo extends ItemInfo {
/**
* Whether this folder has been opened
@@ -39,6 +43,7 @@ class FolderInfo extends ItemInfo {
FolderInfo() {
itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
+ user = UserHandleCompat.myUserHandle();
}
/**
@@ -75,8 +80,8 @@ class FolderInfo extends ItemInfo {
}
@Override
- void onAddToDatabase(ContentValues values) {
- super.onAddToDatabase(values);
+ void onAddToDatabase(Context context, ContentValues values) {
+ super.onAddToDatabase(context, values);
values.put(LauncherSettings.Favorites.TITLE, title.toString());
}
@@ -114,6 +119,6 @@ class FolderInfo extends ItemInfo {
return "FolderInfo(id=" + this.id + " type=" + this.itemType
+ " container=" + this.container + " screen=" + screenId
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + " dropPos=" + dropPos + ")";
+ + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos) + ")";
}
}
diff --git a/src/com/android/launcher3/HideFromAccessibilityHelper.java b/src/com/android/launcher3/HideFromAccessibilityHelper.java
deleted file mode 100644
index 75cbb1b1e..000000000
--- a/src/com/android/launcher3/HideFromAccessibilityHelper.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.view.View;
-import android.view.ViewGroup;
-import android.view.ViewGroup.OnHierarchyChangeListener;
-
-import java.util.HashMap;
-
-public class HideFromAccessibilityHelper implements OnHierarchyChangeListener {
- private HashMap<View, Integer> mPreviousValues;
- boolean mHide;
- boolean mOnlyAllApps;
-
- public HideFromAccessibilityHelper() {
- mPreviousValues = new HashMap<View, Integer>();
- mHide = false;
- }
-
- public void setImportantForAccessibilityToNo(View v, boolean onlyAllApps) {
- mOnlyAllApps = onlyAllApps;
- setImportantForAccessibilityToNoHelper(v);
- mHide = true;
- }
-
- private void setImportantForAccessibilityToNoHelper(View v) {
- mPreviousValues.put(v, v.getImportantForAccessibility());
- v.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO);
-
- // Call method on children recursively
- if (v instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) v;
- vg.setOnHierarchyChangeListener(this);
- for (int i = 0; i < vg.getChildCount(); i++) {
- View child = vg.getChildAt(i);
-
- if (includeView(child)) {
- setImportantForAccessibilityToNoHelper(child);
- }
- }
- }
- }
-
- public void restoreImportantForAccessibility(View v) {
- if (mHide) {
- restoreImportantForAccessibilityHelper(v);
- }
- mHide = false;
- }
-
- private void restoreImportantForAccessibilityHelper(View v) {
- Integer important = mPreviousValues.get(v);
- v.setImportantForAccessibility(important);
- mPreviousValues.remove(v);
-
- // Call method on children recursively
- if (v instanceof ViewGroup) {
- ViewGroup vg = (ViewGroup) v;
-
- // We assume if a class implements OnHierarchyChangeListener, it listens
- // to changes to any of its children (happens to be the case in Launcher)
- if (vg instanceof OnHierarchyChangeListener) {
- vg.setOnHierarchyChangeListener((OnHierarchyChangeListener) vg);
- } else {
- vg.setOnHierarchyChangeListener(null);
- }
- for (int i = 0; i < vg.getChildCount(); i++) {
- View child = vg.getChildAt(i);
- if (includeView(child)) {
- restoreImportantForAccessibilityHelper(child);
- }
- }
- }
- }
-
- public void onChildViewAdded(View parent, View child) {
- if (mHide && includeView(child)) {
- setImportantForAccessibilityToNoHelper(child);
- }
- }
-
- public void onChildViewRemoved(View parent, View child) {
- if (mHide && includeView(child)) {
- restoreImportantForAccessibilityHelper(child);
- }
- }
-
- private boolean includeView(View v) {
- return !hasAncestorOfType(v, Cling.class) &&
- (!mOnlyAllApps || hasAncestorOfType(v, AppsCustomizeTabHost.class));
- }
-
- private boolean hasAncestorOfType(View v, Class c) {
- return v != null &&
- (v.getClass().equals(c) ||
- (v.getParent() instanceof ViewGroup &&
- hasAncestorOfType((ViewGroup) v.getParent(), c)));
- }
-} \ No newline at end of file
diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java
index d7b960aba..b1e0e68a4 100644
--- a/src/com/android/launcher3/HolographicOutlineHelper.java
+++ b/src/com/android/launcher3/HolographicOutlineHelper.java
@@ -20,48 +20,49 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Region.Op;
public class HolographicOutlineHelper {
- private final Paint mHolographicPaint = new Paint();
+
+ private static final Rect sTempRect = new Rect();
+
+ private final Canvas mCanvas = new Canvas();
+ private final Paint mDrawPaint = new Paint();
private final Paint mBlurPaint = new Paint();
private final Paint mErasePaint = new Paint();
- public int mMaxOuterBlurRadius;
- public int mMinOuterBlurRadius;
+ private final BlurMaskFilter mMediumOuterBlurMaskFilter;
+ private final BlurMaskFilter mThinOuterBlurMaskFilter;
+ private final BlurMaskFilter mMediumInnerBlurMaskFilter;
- private BlurMaskFilter mExtraThickOuterBlurMaskFilter;
- private BlurMaskFilter mThickOuterBlurMaskFilter;
- private BlurMaskFilter mMediumOuterBlurMaskFilter;
- private BlurMaskFilter mThinOuterBlurMaskFilter;
- private BlurMaskFilter mThickInnerBlurMaskFilter;
- private BlurMaskFilter mExtraThickInnerBlurMaskFilter;
- private BlurMaskFilter mMediumInnerBlurMaskFilter;
+ private final BlurMaskFilter mShaowBlurMaskFilter;
+ private final int mShadowOffset;
- private static final int THICK = 0;
- private static final int MEDIUM = 1;
- private static final int EXTRA_THICK = 2;
+ /**
+ * Padding used when creating shadow bitmap;
+ */
+ final int shadowBitmapPadding;
static HolographicOutlineHelper INSTANCE;
private HolographicOutlineHelper(Context context) {
final float scale = LauncherAppState.getInstance().getScreenDensity();
- mMinOuterBlurRadius = (int) (scale * 1.0f);
- mMaxOuterBlurRadius = (int) (scale * 12.0f);
-
- mExtraThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 12.0f, BlurMaskFilter.Blur.OUTER);
- mThickOuterBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.OUTER);
mMediumOuterBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.OUTER);
mThinOuterBlurMaskFilter = new BlurMaskFilter(scale * 1.0f, BlurMaskFilter.Blur.OUTER);
- mExtraThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 6.0f, BlurMaskFilter.Blur.NORMAL);
- mThickInnerBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
mMediumInnerBlurMaskFilter = new BlurMaskFilter(scale * 2.0f, BlurMaskFilter.Blur.NORMAL);
- mHolographicPaint.setFilterBitmap(true);
- mHolographicPaint.setAntiAlias(true);
+ mShaowBlurMaskFilter = new BlurMaskFilter(scale * 4.0f, BlurMaskFilter.Blur.NORMAL);
+ mShadowOffset = (int) (scale * 2.0f);
+ shadowBitmapPadding = (int) (scale * 4.0f);
+
+ mDrawPaint.setFilterBitmap(true);
+ mDrawPaint.setAntiAlias(true);
mBlurPaint.setFilterBitmap(true);
mBlurPaint.setAntiAlias(true);
mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
@@ -77,37 +78,15 @@ public class HolographicOutlineHelper {
}
/**
- * Returns the interpolated holographic highlight alpha for the effect we want when scrolling
- * pages.
- */
- public static float highlightAlphaInterpolator(float r) {
- float maxAlpha = 0.6f;
- return (float) Math.pow(maxAlpha * (1.0f - r), 1.5f);
- }
-
- /**
- * Returns the interpolated view alpha for the effect we want when scrolling pages.
- */
- public static float viewAlphaInterpolator(float r) {
- final float pivot = 0.95f;
- if (r < pivot) {
- return (float) Math.pow(r / pivot, 1.5f);
- } else {
- return 1.0f;
- }
- }
-
- /**
* Applies a more expensive and accurate outline to whatever is currently drawn in a specified
* bitmap.
*/
void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor, int thickness) {
- applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true,
- thickness);
+ int outlineColor) {
+ applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, true);
}
void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor, boolean clipAlpha, int thickness) {
+ int outlineColor, boolean clipAlpha) {
// We start by removing most of the alpha channel so as to ignore shadows, and
// other types of partial transparency when defining the shape of the object
@@ -127,50 +106,18 @@ public class HolographicOutlineHelper {
Bitmap glowShape = srcDst.extractAlpha();
// calculate the outer blur first
- BlurMaskFilter outerBlurMaskFilter;
- switch (thickness) {
- case EXTRA_THICK:
- outerBlurMaskFilter = mExtraThickOuterBlurMaskFilter;
- break;
- case THICK:
- outerBlurMaskFilter = mThickOuterBlurMaskFilter;
- break;
- case MEDIUM:
- outerBlurMaskFilter = mMediumOuterBlurMaskFilter;
- break;
- default:
- throw new RuntimeException("Invalid blur thickness");
- }
- mBlurPaint.setMaskFilter(outerBlurMaskFilter);
+ mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
int[] outerBlurOffset = new int[2];
Bitmap thickOuterBlur = glowShape.extractAlpha(mBlurPaint, outerBlurOffset);
- if (thickness == EXTRA_THICK) {
- mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter);
- } else {
- mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
- }
+ mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter);
int[] brightOutlineOffset = new int[2];
Bitmap brightOutline = glowShape.extractAlpha(mBlurPaint, brightOutlineOffset);
// calculate the inner blur
srcDstCanvas.setBitmap(glowShape);
srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT);
- BlurMaskFilter innerBlurMaskFilter;
- switch (thickness) {
- case EXTRA_THICK:
- innerBlurMaskFilter = mExtraThickInnerBlurMaskFilter;
- break;
- case THICK:
- innerBlurMaskFilter = mThickInnerBlurMaskFilter;
- break;
- case MEDIUM:
- innerBlurMaskFilter = mMediumInnerBlurMaskFilter;
- break;
- default:
- throw new RuntimeException("Invalid blur thickness");
- }
- mBlurPaint.setMaskFilter(innerBlurMaskFilter);
+ mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter);
int[] thickInnerBlurOffset = new int[2];
Bitmap thickInnerBlur = glowShape.extractAlpha(mBlurPaint, thickInnerBlurOffset);
@@ -186,16 +133,16 @@ public class HolographicOutlineHelper {
// draw the inner and outer blur
srcDstCanvas.setBitmap(srcDst);
srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
- mHolographicPaint.setColor(color);
+ mDrawPaint.setColor(color);
srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1],
- mHolographicPaint);
+ mDrawPaint);
srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1],
- mHolographicPaint);
+ mDrawPaint);
// draw the bright outline
- mHolographicPaint.setColor(outlineColor);
+ mDrawPaint.setColor(outlineColor);
srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1],
- mHolographicPaint);
+ mDrawPaint);
// cleanup
srcDstCanvas.setBitmap(null);
@@ -205,25 +152,52 @@ public class HolographicOutlineHelper {
glowShape.recycle();
}
- void applyExtraThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor) {
- applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, EXTRA_THICK);
- }
-
- void applyThickExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor) {
- applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, THICK);
+ Bitmap createMediumDropShadow(BubbleTextView view) {
+ final Bitmap result = Bitmap.createBitmap(
+ view.getWidth() + shadowBitmapPadding + shadowBitmapPadding,
+ view.getHeight() + shadowBitmapPadding + shadowBitmapPadding + mShadowOffset,
+ Bitmap.Config.ARGB_8888);
+
+ mCanvas.setBitmap(result);
+
+ final Rect clipRect = sTempRect;
+ view.getDrawingRect(sTempRect);
+ // adjust the clip rect so that we don't include the text label
+ clipRect.bottom = view.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V
+ + view.getLayout().getLineTop(0);
+
+ // Draw the View into the bitmap.
+ // The translate of scrollX and scrollY is necessary when drawing TextViews, because
+ // they set scrollX and scrollY to large values to achieve centered text
+ mCanvas.save();
+ mCanvas.scale(view.getScaleX(), view.getScaleY(),
+ view.getWidth() / 2 + shadowBitmapPadding,
+ view.getHeight() / 2 + shadowBitmapPadding);
+ mCanvas.translate(-view.getScrollX() + shadowBitmapPadding,
+ -view.getScrollY() + shadowBitmapPadding);
+ mCanvas.clipRect(clipRect, Op.REPLACE);
+ view.draw(mCanvas);
+ mCanvas.restore();
+
+ int[] blurOffst = new int[2];
+ mBlurPaint.setMaskFilter(mShaowBlurMaskFilter);
+ Bitmap blurBitmap = result.extractAlpha(mBlurPaint, blurOffst);
+
+ mCanvas.save();
+ mCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
+ mCanvas.translate(blurOffst[0], blurOffst[1]);
+
+ mDrawPaint.setColor(Color.BLACK);
+ mDrawPaint.setAlpha(30);
+ mCanvas.drawBitmap(blurBitmap, 0, 0, mDrawPaint);
+
+ mDrawPaint.setAlpha(60);
+ mCanvas.drawBitmap(blurBitmap, 0, mShadowOffset, mDrawPaint);
+ mCanvas.restore();
+
+ mCanvas.setBitmap(null);
+ blurBitmap.recycle();
+
+ return result;
}
-
- void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor, boolean clipAlpha) {
- applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, clipAlpha,
- MEDIUM);
- }
-
- void applyMediumExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, int color,
- int outlineColor) {
- applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, color, outlineColor, MEDIUM);
- }
-
}
diff --git a/src/com/android/launcher3/Hotseat.java b/src/com/android/launcher3/Hotseat.java
index 59d60e381..b08272f36 100644
--- a/src/com/android/launcher3/Hotseat.java
+++ b/src/com/android/launcher3/Hotseat.java
@@ -64,7 +64,6 @@ public class Hotseat extends FrameLayout {
public void setup(Launcher launcher) {
mLauncher = launcher;
- setOnKeyListener(new HotseatIconKeyEventListener());
}
CellLayout getLayout() {
@@ -150,21 +149,18 @@ public class Hotseat extends FrameLayout {
TextView allAppsButton = (TextView)
inflater.inflate(R.layout.all_apps_button, mContent, false);
Drawable d = context.getResources().getDrawable(R.drawable.all_apps_button_icon);
+
Utilities.resizeIconDrawable(d);
allAppsButton.setCompoundDrawables(null, d, null, null);
allAppsButton.setContentDescription(context.getString(R.string.all_apps_button_label));
+ allAppsButton.setOnKeyListener(new HotseatIconKeyEventListener());
if (mLauncher != null) {
allAppsButton.setOnTouchListener(mLauncher.getHapticFeedbackTouchListener());
+ mLauncher.setAllAppsButton(allAppsButton);
+ allAppsButton.setOnClickListener(mLauncher);
+ allAppsButton.setOnFocusChangeListener(mLauncher.mFocusHandler);
}
- allAppsButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(android.view.View v) {
- if (mLauncher != null) {
- mLauncher.onClickAllAppsButton(v);
- }
- }
- });
// Note: We do this to ensure that the hotseat is always laid out in the orientation of
// the hotseat in order regardless of which orientation they were added
@@ -172,7 +168,7 @@ public class Hotseat extends FrameLayout {
int y = getCellYFromOrder(mAllAppsButtonRank);
CellLayout.LayoutParams lp = new CellLayout.LayoutParams(x,y,1,1);
lp.canReorder = false;
- mContent.addViewToCellLayout(allAppsButton, -1, 0, lp, true);
+ mContent.addViewToCellLayout(allAppsButton, -1, allAppsButton.getId(), lp, true);
}
}
@@ -180,7 +176,7 @@ public class Hotseat extends FrameLayout {
public boolean onInterceptTouchEvent(MotionEvent ev) {
// We don't want any clicks to go through to the hotseat unless the workspace is in
// the normal state.
- if (mLauncher.getWorkspace().isSmall()) {
+ if (mLauncher.getWorkspace().workspaceInModalState()) {
return true;
}
return false;
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 827718b9e..bb71d776c 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -16,23 +16,29 @@
package com.android.launcher3;
-import com.android.launcher3.backup.BackupProtos;
-
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.Paint;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
import android.util.Log;
+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 java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
@@ -48,24 +54,52 @@ import java.util.Map.Entry;
* Cache of application icons. Icons can be made from any thread.
*/
public class IconCache {
- @SuppressWarnings("unused")
+
private static final String TAG = "Launcher.IconCache";
private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
private static final String RESOURCE_FILE_PREFIX = "icon_";
- private static final boolean DEBUG = true;
+ // Empty class name is used for storing package default entry.
+ private static final String EMPTY_CLASS_NAME = ".";
+
+ private static final boolean DEBUG = false;
private static class CacheEntry {
public Bitmap icon;
- public String title;
+ public CharSequence title;
+ public CharSequence contentDescription;
+ }
+
+ private static class CacheKey {
+ public ComponentName componentName;
+ public UserHandleCompat user;
+
+ CacheKey(ComponentName componentName, UserHandleCompat user) {
+ this.componentName = componentName;
+ this.user = user;
+ }
+
+ @Override
+ public int hashCode() {
+ return componentName.hashCode() + user.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ CacheKey other = (CacheKey) o;
+ return other.componentName.equals(componentName) && other.user.equals(user);
+ }
}
- private final Bitmap mDefaultIcon;
+ private final HashMap<UserHandleCompat, Bitmap> mDefaultIcons =
+ new HashMap<UserHandleCompat, Bitmap>();
private final Context mContext;
private final PackageManager mPackageManager;
- private final HashMap<ComponentName, CacheEntry> mCache =
- new HashMap<ComponentName, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
+ private final UserManagerCompat mUserManager;
+ private final LauncherAppsCompat mLauncherApps;
+ private final HashMap<CacheKey, CacheEntry> mCache =
+ new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
private int mIconDpi;
public IconCache(Context context) {
@@ -74,10 +108,13 @@ public class IconCache {
mContext = context;
mPackageManager = context.getPackageManager();
+ mUserManager = UserManagerCompat.getInstance(mContext);
+ mLauncherApps = LauncherAppsCompat.getInstance(mContext);
mIconDpi = activityManager.getLauncherLargeIconDensity();
// need to set mIconDpi before getting default icon
- mDefaultIcon = makeDefaultIcon();
+ UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+ mDefaultIcons.put(myUser, makeDefaultIcon(myUser));
}
public Drawable getFullResDefaultActivityIcon() {
@@ -111,6 +148,10 @@ public class IconCache {
return getFullResDefaultActivityIcon();
}
+ public int getFullResIconDpi() {
+ return mIconDpi;
+ }
+
public Drawable getFullResIcon(ResolveInfo info) {
return getFullResIcon(info.activityInfo);
}
@@ -134,8 +175,9 @@ public class IconCache {
return getFullResDefaultActivityIcon();
}
- private Bitmap makeDefaultIcon() {
- Drawable d = getFullResDefaultActivityIcon();
+ private Bitmap makeDefaultIcon(UserHandleCompat user) {
+ Drawable unbadged = getFullResDefaultActivityIcon();
+ Drawable d = mUserManager.getBadgedDrawableForUser(unbadged, user);
Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
Math.max(d.getIntrinsicHeight(), 1),
Bitmap.Config.ARGB_8888);
@@ -149,24 +191,25 @@ public class IconCache {
/**
* Remove any records for the supplied ComponentName.
*/
- public void remove(ComponentName componentName) {
+ public void remove(ComponentName componentName, UserHandleCompat user) {
synchronized (mCache) {
- mCache.remove(componentName);
+ mCache.remove(new CacheKey(componentName, user));
}
}
/**
* Remove any records for the supplied package name.
*/
- public void remove(String packageName) {
- HashSet<ComponentName> forDeletion = new HashSet<ComponentName>();
- for (ComponentName componentName: mCache.keySet()) {
- if (componentName.getPackageName().equals(packageName)) {
- forDeletion.add(componentName);
+ public void remove(String packageName, UserHandleCompat user) {
+ HashSet<CacheKey> forDeletion = new HashSet<CacheKey>();
+ for (CacheKey key: mCache.keySet()) {
+ if (key.componentName.getPackageName().equals(packageName)
+ && key.user.equals(user)) {
+ forDeletion.add(key);
}
}
- for (ComponentName condemned: forDeletion) {
- remove(condemned);
+ for (CacheKey condemned: forDeletion) {
+ mCache.remove(condemned);
}
}
@@ -184,10 +227,11 @@ public class IconCache {
*/
public void flushInvalidIcons(DeviceProfile grid) {
synchronized (mCache) {
- Iterator<Entry<ComponentName, CacheEntry>> it = mCache.entrySet().iterator();
+ Iterator<Entry<CacheKey, CacheEntry>> it = mCache.entrySet().iterator();
while (it.hasNext()) {
final CacheEntry e = it.next().getValue();
- if (e.icon.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
+ if ((e.icon != null) && (e.icon.getWidth() < grid.iconSizePx
+ || e.icon.getHeight() < grid.iconSizePx)) {
it.remove();
}
}
@@ -197,100 +241,193 @@ public class IconCache {
/**
* Fill in "application" with the icon and label for "info."
*/
- public void getTitleAndIcon(AppInfo application, ResolveInfo info,
+ public void getTitleAndIcon(AppInfo application, LauncherActivityInfoCompat info,
HashMap<Object, CharSequence> labelCache) {
synchronized (mCache) {
- CacheEntry entry = cacheLocked(application.componentName, info, labelCache);
+ CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
+ info.getUser(), false);
application.title = entry.title;
application.iconBitmap = entry.icon;
+ application.contentDescription = entry.contentDescription;
}
}
- public Bitmap getIcon(Intent intent) {
- return getIcon(intent, null);
+ public Bitmap getIcon(Intent intent, UserHandleCompat user) {
+ return getIcon(intent, null, user, true);
}
- public Bitmap getIcon(Intent intent, String title) {
+ private Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
synchronized (mCache) {
- final ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, 0);
ComponentName component = intent.getComponent();
-
+ // null info means not installed, but if we have a component from the intent then
+ // we should still look in the cache for restored app icons.
if (component == null) {
- return mDefaultIcon;
+ return getDefaultIcon(user);
}
- CacheEntry entry = cacheLocked(component, resolveInfo, null);
+ LauncherActivityInfoCompat launcherActInfo = mLauncherApps.resolveActivity(intent, user);
+ CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
if (title != null) {
entry.title = title;
+ entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
}
return entry.icon;
}
}
- public Bitmap getIcon(ComponentName component, ResolveInfo resolveInfo,
+ /**
+ * Fill in "shortcutInfo" with the icon and label for "info."
+ */
+ public void getTitleAndIcon(ShortcutInfo shortcutInfo, Intent intent, UserHandleCompat user,
+ boolean usePkgIcon) {
+ synchronized (mCache) {
+ ComponentName component = intent.getComponent();
+ // null info means not installed, but if we have a component from the intent then
+ // we should still look in the cache for restored app icons.
+ if (component == null) {
+ shortcutInfo.setIcon(getDefaultIcon(user));
+ shortcutInfo.title = "";
+ shortcutInfo.usingFallbackIcon = true;
+ } else {
+ LauncherActivityInfoCompat launcherActInfo =
+ mLauncherApps.resolveActivity(intent, user);
+ CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
+
+ shortcutInfo.setIcon(entry.icon);
+ shortcutInfo.title = entry.title;
+ shortcutInfo.usingFallbackIcon = isDefaultIcon(entry.icon, user);
+ }
+ }
+ }
+
+
+ public Bitmap getDefaultIcon(UserHandleCompat user) {
+ if (!mDefaultIcons.containsKey(user)) {
+ mDefaultIcons.put(user, makeDefaultIcon(user));
+ }
+ return mDefaultIcons.get(user);
+ }
+
+ public Bitmap getIcon(ComponentName component, LauncherActivityInfoCompat info,
HashMap<Object, CharSequence> labelCache) {
synchronized (mCache) {
- if (resolveInfo == null || component == null) {
+ if (info == null || component == null) {
return null;
}
- CacheEntry entry = cacheLocked(component, resolveInfo, labelCache);
+ CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
return entry.icon;
}
}
- public boolean isDefaultIcon(Bitmap icon) {
- return mDefaultIcon == icon;
+ public boolean isDefaultIcon(Bitmap icon, UserHandleCompat user) {
+ return mDefaultIcons.get(user) == icon;
}
- private CacheEntry cacheLocked(ComponentName componentName, ResolveInfo info,
- HashMap<Object, CharSequence> labelCache) {
- CacheEntry entry = mCache.get(componentName);
+ private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
+ HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
+ CacheKey cacheKey = new CacheKey(componentName, user);
+ CacheEntry entry = mCache.get(cacheKey);
if (entry == null) {
entry = new CacheEntry();
- mCache.put(componentName, entry);
+ mCache.put(cacheKey, entry);
if (info != null) {
- ComponentName key = LauncherModel.getComponentNameFromResolveInfo(info);
- if (labelCache != null && labelCache.containsKey(key)) {
- entry.title = labelCache.get(key).toString();
+ ComponentName labelKey = info.getComponentName();
+ if (labelCache != null && labelCache.containsKey(labelKey)) {
+ entry.title = labelCache.get(labelKey).toString();
} else {
- entry.title = info.loadLabel(mPackageManager).toString();
+ entry.title = info.getLabel().toString();
if (labelCache != null) {
- labelCache.put(key, entry.title);
+ labelCache.put(labelKey, entry.title);
}
}
- if (entry.title == null) {
- entry.title = info.activityInfo.name;
- }
+ entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user);
entry.icon = Utilities.createIconBitmap(
- getFullResIcon(info), mContext);
+ info.getBadgedIcon(mIconDpi), mContext);
} else {
entry.title = "";
- Bitmap preloaded = getPreloadedIcon(componentName);
+ Bitmap preloaded = getPreloadedIcon(componentName, user);
if (preloaded != null) {
if (DEBUG) Log.d(TAG, "using preloaded icon for " +
componentName.toShortString());
entry.icon = preloaded;
} else {
- if (DEBUG) Log.d(TAG, "using default icon for " +
- componentName.toShortString());
- entry.icon = mDefaultIcon;
+ if (usePackageIcon) {
+ CacheEntry packageEntry = getEntryForPackage(
+ componentName.getPackageName(), user);
+ if (packageEntry != null) {
+ if (DEBUG) Log.d(TAG, "using package default icon for " +
+ componentName.toShortString());
+ entry.icon = packageEntry.icon;
+ entry.title = packageEntry.title;
+ }
+ }
+ if (entry.icon == null) {
+ if (DEBUG) Log.d(TAG, "using default icon for " +
+ componentName.toShortString());
+ entry.icon = getDefaultIcon(user);
+ }
}
}
}
return entry;
}
+ /**
+ * Adds a default package entry in the cache. This entry is not persisted and will be removed
+ * when the cache is flushed.
+ */
+ public void cachePackageInstallInfo(String packageName, UserHandleCompat user,
+ Bitmap icon, CharSequence title) {
+ remove(packageName, user);
+
+ CacheEntry entry = getEntryForPackage(packageName, user);
+ if (!TextUtils.isEmpty(title)) {
+ entry.title = title;
+ }
+ if (icon != null) {
+ entry.icon = Utilities.createIconBitmap(
+ new BitmapDrawable(mContext.getResources(), icon), mContext);
+ }
+ }
+
+ /**
+ * Gets an entry for the package, which can be used as a fallback entry for various components.
+ */
+ private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
+ ComponentName cn = getPackageComponent(packageName);
+ CacheKey cacheKey = new CacheKey(cn, user);
+ CacheEntry entry = mCache.get(cacheKey);
+ if (entry == null) {
+ entry = new CacheEntry();
+ entry.title = "";
+ mCache.put(cacheKey, entry);
+
+ try {
+ ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
+ entry.title = info.loadLabel(mPackageManager);
+ entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
+ } catch (NameNotFoundException e) {
+ if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
+ }
+
+ if (entry.icon == null) {
+ entry.icon = getPreloadedIcon(cn, user);
+ }
+ }
+ return entry;
+ }
+
public HashMap<ComponentName,Bitmap> getAllIcons() {
synchronized (mCache) {
HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
- for (ComponentName cn : mCache.keySet()) {
- final CacheEntry e = mCache.get(cn);
- set.put(cn, e.icon);
+ for (CacheKey ck : mCache.keySet()) {
+ final CacheEntry e = mCache.get(ck);
+ set.put(ck.componentName, e.icon);
}
return set;
}
@@ -353,9 +490,14 @@ public class IconCache {
* @param componentName the component that should own the icon
* @returns a bitmap if one is cached, or null.
*/
- private Bitmap getPreloadedIcon(ComponentName componentName) {
+ private Bitmap getPreloadedIcon(ComponentName componentName, UserHandleCompat user) {
final String key = componentName.flattenToShortString();
+ // We don't keep icons for other profiles in persistent cache.
+ if (!user.equals(UserHandleCompat.myUserHandle())) {
+ return null;
+ }
+
if (DEBUG) Log.v(TAG, "looking for pre-load icon for " + key);
Bitmap icon = null;
FileInputStream resourceFile = null;
@@ -374,7 +516,7 @@ public class IconCache {
Log.w(TAG, "failed to decode pre-load icon for " + key);
}
} catch (FileNotFoundException e) {
- if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key, e);
+ if (DEBUG) Log.d(TAG, "there is no restored icon for: " + key);
} catch (IOException e) {
Log.w(TAG, "failed to read pre-load icon for: " + key, e);
} finally {
@@ -387,20 +529,6 @@ public class IconCache {
}
}
- if (icon != null) {
- // TODO: handle alpha mask in the view layer
- Bitmap b = Bitmap.createBitmap(Math.max(icon.getWidth(), 1),
- Math.max(icon.getHeight(), 1),
- Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
- Paint paint = new Paint();
- paint.setAlpha(127);
- c.drawBitmap(icon, 0, 0, paint);
- c.setBitmap(null);
- icon.recycle();
- icon = b;
- }
-
return icon;
}
@@ -410,7 +538,11 @@ public class IconCache {
* @param componentName the component that should own the icon
* @returns true on success
*/
- public boolean deletePreloadedIcon(ComponentName componentName) {
+ public boolean deletePreloadedIcon(ComponentName componentName, UserHandleCompat user) {
+ // We don't keep icons for other profiles in persistent cache.
+ if (!user.equals(UserHandleCompat.myUserHandle())) {
+ return false;
+ }
if (componentName == null) {
return false;
}
@@ -428,4 +560,8 @@ public class IconCache {
String filename = resourceName.replace(File.separatorChar, '_');
return RESOURCE_FILE_PREFIX + filename;
}
+
+ static ComponentName getPackageComponent(String packageName) {
+ return new ComponentName(packageName, EMPTY_CLASS_NAME);
+ }
}
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index 374238c49..7e55af228 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -26,6 +26,8 @@ import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import com.android.launcher3.compat.UserHandleCompat;
+
public class InfoDropTarget extends ButtonDropTarget {
private ColorStateList mOriginalTextColor;
@@ -49,6 +51,13 @@ public class InfoDropTarget extends ButtonDropTarget {
Resources r = getResources();
mHoverColor = r.getColor(R.color.info_target_hover_tint);
mDrawable = (TransitionDrawable) getCurrentDrawable();
+
+ if (mDrawable == null) {
+ // TODO: investigate why this is ever happening. Presently only on one known device.
+ mDrawable = (TransitionDrawable) r.getDrawable(R.drawable.info_target_selector);
+ setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null);
+ }
+
if (null != mDrawable) {
mDrawable.setCrossFadeEnabled(true);
}
@@ -75,8 +84,15 @@ public class InfoDropTarget extends ButtonDropTarget {
} else if (d.dragInfo instanceof PendingAddItemInfo) {
componentName = ((PendingAddItemInfo) d.dragInfo).componentName;
}
+ final UserHandleCompat user;
+ if (d.dragInfo instanceof ItemInfo) {
+ user = ((ItemInfo) d.dragInfo).user;
+ } else {
+ user = UserHandleCompat.myUserHandle();
+ }
+
if (componentName != null) {
- mLauncher.startApplicationDetailsActivity(componentName);
+ mLauncher.startApplicationDetailsActivity(componentName, user);
}
// There is no post-drop animation, so clean up the DragView now
diff --git a/src/com/android/launcher3/InstallShortcutReceiver.java b/src/com/android/launcher3/InstallShortcutReceiver.java
index 28cef1346..2edde4fae 100644
--- a/src/com/android/launcher3/InstallShortcutReceiver.java
+++ b/src/com/android/launcher3/InstallShortcutReceiver.java
@@ -17,6 +17,7 @@
package com.android.launcher3;
import android.content.BroadcastReceiver;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -29,6 +30,8 @@ import android.util.Base64;
import android.util.Log;
import android.widget.Toast;
+import com.android.launcher3.compat.UserHandleCompat;
+
import org.json.JSONObject;
import org.json.JSONStringer;
import org.json.JSONTokener;
@@ -280,19 +283,27 @@ public class InstallShortcutReceiver extends BroadcastReceiver {
final boolean exists = LauncherModel.shortcutExists(context, name, intent);
//final boolean allowDuplicate = data.getBooleanExtra(Launcher.EXTRA_SHORTCUT_DUPLICATE, true);
- // TODO-XXX: Disable duplicates for now
- if (!exists /* && allowDuplicate */) {
+ // If the intent specifies a package, make sure the package exists
+ String packageName = intent.getPackage();
+ if (packageName == null) {
+ packageName = intent.getComponent() == null ? null :
+ intent.getComponent().getPackageName();
+ }
+ if (packageName != null && !packageName.isEmpty()) {
+ UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
+ if (!LauncherModel.isValidPackage(context, packageName, myUserHandle)) {
+ if (DBG) Log.d(TAG, "Ignoring shortcut for absent package:" + intent);
+ continue;
+ }
+ }
+
+ if (!exists) {
// Generate a shortcut info to add into the model
ShortcutInfo info = getShortcutInfo(context, pendingInfo.data,
pendingInfo.launchIntent);
addShortcuts.add(info);
}
- /*
- else if (exists && !allowDuplicate) {
- result = INSTALL_SHORTCUT_IS_DUPLICATE;
- duplicateName = name;
- }
- */
+
}
// Notify the user once if we weren't able to place any duplicates
diff --git a/src/com/android/launcher3/ItemInfo.java b/src/com/android/launcher3/ItemInfo.java
index 3dc92c9c2..09b77f756 100644
--- a/src/com/android/launcher3/ItemInfo.java
+++ b/src/com/android/launcher3/ItemInfo.java
@@ -17,24 +17,34 @@
package com.android.launcher3;
import android.content.ContentValues;
+import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.util.Log;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
+import java.util.Arrays;
/**
* Represents an item in the launcher.
*/
public class ItemInfo {
+
+ /**
+ * Intent extra to store the profile. Format: UserHandle
+ */
+ static final String EXTRA_PROFILE = "profile";
static final int NO_ID = -1;
/**
* The id in the settings database for this item
*/
- long id = NO_ID;
+ public long id = NO_ID;
/**
* One of {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
@@ -42,7 +52,7 @@ public class ItemInfo {
* {@link LauncherSettings.Favorites#ITEM_TYPE_FOLDER}, or
* {@link LauncherSettings.Favorites#ITEM_TYPE_APPWIDGET}.
*/
- int itemType;
+ public int itemType;
/**
* The id of the container that holds this item. For the desktop, this will be
@@ -50,27 +60,27 @@ public class ItemInfo {
* will be {@link #NO_ID} (since it is not stored in the settings DB). For user folders
* it will be the id of the folder.
*/
- long container = NO_ID;
+ public long container = NO_ID;
/**
* Iindicates the screen in which the shortcut appears.
*/
- long screenId = -1;
+ public long screenId = -1;
/**
* Indicates the X position of the associated cell.
*/
- int cellX = -1;
+ public int cellX = -1;
/**
* Indicates the Y position of the associated cell.
*/
- int cellY = -1;
+ public int cellY = -1;
/**
* Indicates the X cell span.
*/
- int spanX = 1;
+ public int spanX = 1;
/**
* Indicates the Y cell span.
@@ -80,17 +90,17 @@ public class ItemInfo {
/**
* Indicates the minimum X cell span.
*/
- int minSpanX = 1;
+ public int minSpanX = 1;
/**
* Indicates the minimum Y cell span.
*/
- int minSpanY = 1;
+ public int minSpanY = 1;
/**
* Indicates that this item needs to be updated in the db
*/
- boolean requiresDbUpdate = false;
+ public boolean requiresDbUpdate = false;
/**
* Title of the item
@@ -98,14 +108,28 @@ public class ItemInfo {
CharSequence title;
/**
+ * Content description of the item.
+ */
+ CharSequence contentDescription;
+
+ /**
* The position of the item in a drag-and-drop operation.
*/
int[] dropPos = null;
+ UserHandleCompat user;
+
ItemInfo() {
+ user = UserHandleCompat.myUserHandle();
}
ItemInfo(ItemInfo info) {
+ copyFrom(info);
+ // tempdebug:
+ LauncherModel.checkItemInfo(this);
+ }
+
+ public void copyFrom(ItemInfo info) {
id = info.id;
cellX = info.cellX;
cellY = info.cellY;
@@ -114,24 +138,22 @@ public class ItemInfo {
screenId = info.screenId;
itemType = info.itemType;
container = info.container;
- // tempdebug:
- LauncherModel.checkItemInfo(this);
- }
-
- protected Intent getIntent() {
- throw new RuntimeException("Unexpected Intent");
+ user = info.user;
+ contentDescription = info.contentDescription;
}
- protected Intent getRestoredIntent() {
+ public Intent getIntent() {
throw new RuntimeException("Unexpected Intent");
}
/**
* Write the fields of this item to the DB
*
+ * @param context A context object to use for getting UserManagerCompat
* @param values
*/
- void onAddToDatabase(ContentValues values) {
+
+ void onAddToDatabase(Context context, ContentValues values) {
values.put(LauncherSettings.BaseLauncherColumns.ITEM_TYPE, itemType);
values.put(LauncherSettings.Favorites.CONTAINER, container);
values.put(LauncherSettings.Favorites.SCREEN, screenId);
@@ -139,6 +161,13 @@ public class ItemInfo {
values.put(LauncherSettings.Favorites.CELLY, cellY);
values.put(LauncherSettings.Favorites.SPANX, spanX);
values.put(LauncherSettings.Favorites.SPANY, spanY);
+ long serialNumber = UserManagerCompat.getInstance(context).getSerialNumberForUser(user);
+ values.put(LauncherSettings.Favorites.PROFILE_ID, serialNumber);
+
+ if (screenId == Workspace.EXTRA_EMPTY_SCREEN_ID) {
+ // We should never persist an item on the extra empty screen.
+ throw new RuntimeException("Screen id should not be EXTRA_EMPTY_SCREEN_ID");
+ }
}
void updateValuesWithCoordinates(ContentValues values, int cellX, int cellY) {
@@ -182,6 +211,7 @@ public class ItemInfo {
public String toString() {
return "Item(id=" + this.id + " type=" + this.itemType + " container=" + this.container
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
- + " spanY=" + spanY + " dropPos=" + dropPos + ")";
+ + " spanY=" + spanY + " dropPos=" + Arrays.toString(dropPos)
+ + " user=" + user + ")";
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c22a6bf3f..42ec4fb48 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -22,11 +22,13 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.AlertDialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -37,6 +39,7 @@ import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
@@ -44,25 +47,25 @@ import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.os.SystemClock;
-import android.provider.Settings;
import android.speech.RecognizerIntent;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -70,6 +73,7 @@ import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -81,13 +85,16 @@ import android.view.Surface;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
+import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
-import android.view.animation.AccelerateDecelerateInterpolator;
+import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.Advanceable;
import android.widget.FrameLayout;
@@ -96,6 +103,14 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.PagedView.PageSwitchListener;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -105,6 +120,9 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -113,13 +131,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
-
/**
* Default launcher application.
*/
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
- View.OnTouchListener {
+ View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -133,12 +150,12 @@ public class Launcher extends Activity
private static final int REQUEST_CREATE_SHORTCUT = 1;
private static final int REQUEST_CREATE_APPWIDGET = 5;
- private static final int REQUEST_PICK_APPLICATION = 6;
private static final int REQUEST_PICK_SHORTCUT = 7;
private static final int REQUEST_PICK_APPWIDGET = 9;
private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
+ private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
/**
* IntentStarter uses request codes starting with this. This must be greater than all activity
@@ -189,9 +206,13 @@ public class Launcher extends Activity
// Type: int[]
private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
-
+ static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
+ static final String FIRST_LOAD_COMPLETE = "launcher.first_load_complete";
+ static final String ACTION_FIRST_LOAD_COMPLETE =
+ "com.android.launcher3.action.FIRST_LOAD_COMPLETE";
+
private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
"com.android.launcher.toolbar_search_icon";
@@ -208,10 +229,12 @@ public class Launcher extends Activity
private State mState = State.WORKSPACE;
private AnimatorSet mStateAnimation;
+ private boolean mIsSafeModeEnabled;
+
static final int APPWIDGET_HOST_ID = 1024;
public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
- public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE = 400;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
+ private static final int ACTIVITY_START_DELAY = 1000;
private static final Object sLock = new Object();
private static int sScreen = DEFAULT_SCREEN;
@@ -223,6 +246,7 @@ public class Launcher extends Activity
private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
private static int NEW_APPS_ANIMATION_DELAY = 500;
+ private static final int SINGLE_FRAME_DELAY = 16;
private final BroadcastReceiver mCloseSystemDialogsReceiver
= new CloseSystemDialogsIntentReceiver();
@@ -236,9 +260,8 @@ public class Launcher extends Activity
private DragLayer mDragLayer;
private DragController mDragController;
private View mWeightWatcher;
- private LauncherClings mLauncherClings;
- private AppWidgetManager mAppWidgetManager;
+ private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
private ItemInfo mPendingAddInfo = new ItemInfo();
@@ -278,9 +301,6 @@ public class Launcher extends Activity
private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
- // Keep track of whether the user has left launcher
- private static boolean sPausedFromUserAction = false;
-
private Bundle mSavedInstanceState;
private LauncherModel mModel;
@@ -312,10 +332,6 @@ public class Launcher extends Activity
// External icons saved in case of resource changes, orientation, etc.
private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2];
private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
- private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
-
- private Intent mAppMarketIntent = null;
- private static final boolean DISABLE_MARKET_BUTTON = true;
private Drawable mWorkspaceBackgroundDrawable;
@@ -352,8 +368,7 @@ public class Launcher extends Activity
}
};
- private static ArrayList<PendingAddArguments> sPendingAddList
- = new ArrayList<PendingAddArguments>();
+ private static PendingAddArguments sPendingAddItem;
public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
@@ -364,10 +379,13 @@ public class Launcher extends Activity
long screenId;
int cellX;
int cellY;
+ int appWidgetId;
}
private Stats mStats;
+ FocusIndicatorView mFocusHandler;
+
static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
@@ -393,7 +411,7 @@ public class Launcher extends Activity
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
-
+ LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
// Determine the dynamic grid properties
Point smallestSize = new Point();
Point largestSize = new Point();
@@ -414,16 +432,16 @@ public class Launcher extends Activity
// the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
Context.MODE_PRIVATE);
+ mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
mIconCache.flushInvalidIcons(grid);
mDragController = new DragController(this);
- mLauncherClings = new LauncherClings(this);
mInflater = getLayoutInflater();
mStats = new Stats(this);
- mAppWidgetManager = AppWidgetManager.getInstance(this);
+ mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
@@ -438,7 +456,6 @@ public class Launcher extends Activity
Environment.getExternalStorageDirectory() + "/launcher");
}
-
checkForLocaleChange();
setContentView(R.layout.launcher);
@@ -457,7 +474,7 @@ public class Launcher extends Activity
}
if (!mRestoring) {
- if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE || sPausedFromUserAction) {
+ if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
@@ -480,25 +497,16 @@ public class Launcher extends Activity
// On large interfaces, we want the screen to auto-rotate based on the current orientation
unlockScreenOrientation(true);
- // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
- // on the device, then we always show the first run cling experience (or if there is no
- // launcher2). Otherwise, we prompt the user upon started for migration
- showFirstRunActivity();
- if (mLauncherClings.shouldShowFirstRunOrMigrationClings()) {
- if (mModel.canMigrateFromOldLauncherDb(this)) {
- mLauncherClings.showMigrationCling();
- } else {
- mLauncherClings.showFirstRunCling();
- }
+ if (shouldShowIntroScreen()) {
+ showIntroScreen();
} else {
- mLauncherClings.removeFirstRunAndMigrationClings();
+ showFirstRunActivity();
+ showFirstRunClings();
}
}
- protected void onUserLeaveHint() {
- super.onUserLeaveHint();
- sPausedFromUserAction = true;
- }
+ @Override
+ public void onLauncherProviderChange() { }
/** To be overriden by subclasses to hint to Launcher that we have custom content */
protected boolean hasCustomContentToLeft() {
@@ -514,21 +522,6 @@ public class Launcher extends Activity
}
/**
- * To be overridden by subclasses to indicate that there is an activity to launch
- * before showing the standard launcher experience.
- */
- protected boolean hasFirstRunActivity() {
- return false;
- }
-
- /**
- * To be overridden by subclasses to launch any first run activity
- */
- protected Intent getFirstRunActivity() {
- return null;
- }
-
- /**
* Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
* ensure the custom content page is added or removed if necessary.
*/
@@ -552,11 +545,7 @@ public class Launcher extends Activity
boolean voiceVisible = false;
// If we have a saved version of these external icons, we load them up immediately
int coi = getCurrentOrientationIndexForGlobalIcons();
- if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
- sAppMarketIcon[coi] == null) {
- if (!DISABLE_MARKET_BUTTON) {
- updateAppMarketIcon();
- }
+ if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null) {
searchVisible = updateGlobalSearchIcon();
voiceVisible = updateVoiceSearchIcon(searchVisible);
}
@@ -568,9 +557,6 @@ public class Launcher extends Activity
updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
voiceVisible = true;
}
- if (!DISABLE_MARKET_BUTTON && sAppMarketIcon[coi] != null) {
- updateAppMarketIcon(sAppMarketIcon[coi]);
- }
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
}
@@ -703,19 +689,20 @@ public class Launcher extends Activity
}
}
- /**
- * Copied from View -- the View version of the method isn't called
- * anywhere else in our process and only exists for API level 17+,
- * so it's ok to keep our own version with no API requirement.
- */
public static int generateViewId() {
- for (;;) {
- final int result = sNextGeneratedId.get();
- // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
- int newValue = result + 1;
- if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
- if (sNextGeneratedId.compareAndSet(result, newValue)) {
- return result;
+ if (Build.VERSION.SDK_INT >= 17) {
+ return View.generateViewId();
+ } else {
+ // View.generateViewId() is not available. The following fallback logic is a copy
+ // of its implementation.
+ for (;;) {
+ final int result = sNextGeneratedId.get();
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
}
}
}
@@ -735,40 +722,39 @@ public class Launcher extends Activity
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
*/
- private boolean completeAdd(PendingAddArguments args) {
- boolean result = false;
+ private long completeAdd(PendingAddArguments args) {
+ long screenId = args.screenId;
+ if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ // When the screen id represents an actual screen (as opposed to a rank) we make sure
+ // that the drop page actually exists.
+ screenId = ensurePendingDropLayoutExists(args.screenId);
+ }
+
switch (args.requestCode) {
- case REQUEST_PICK_APPLICATION:
- completeAddApplication(args.intent, args.container, args.screenId, args.cellX,
- args.cellY);
- break;
- case REQUEST_PICK_SHORTCUT:
- processShortcut(args.intent);
- break;
case REQUEST_CREATE_SHORTCUT:
- completeAddShortcut(args.intent, args.container, args.screenId, args.cellX,
+ completeAddShortcut(args.intent, args.container, screenId, args.cellX,
args.cellY);
- result = true;
break;
case REQUEST_CREATE_APPWIDGET:
- int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
- completeAddAppWidget(appWidgetId, args.container, args.screenId, null, null);
- result = true;
+ completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
+ break;
+ case REQUEST_RECONFIGURE_APPWIDGET:
+ completeRestoreAppWidget(args.appWidgetId);
break;
}
// Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
// if you turned the screen off and then back while in All Apps, Launcher would not
// return to the workspace. Clearing mAddInfo.container here fixes this issue
resetAddInfo();
- return result;
+ return screenId;
}
@Override
protected void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
// Reset the startActivity waiting flag
- mWaitingForResult = false;
- int pendingAddWidgetId = mPendingAddWidgetId;
+ setWaitingForResult(false);
+ final int pendingAddWidgetId = mPendingAddWidgetId;
mPendingAddWidgetId = -1;
Runnable exitSpringLoaded = new Runnable() {
@@ -784,7 +770,7 @@ public class Launcher extends Activity
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
- mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_OK) {
addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
@@ -801,6 +787,7 @@ public class Launcher extends Activity
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
requestCode == REQUEST_CREATE_APPWIDGET);
+ final boolean workspaceLocked = isWorkspaceLocked();
// We have special handling for widgets
if (isWidgetDrop) {
final int appWidgetId;
@@ -813,33 +800,66 @@ public class Launcher extends Activity
}
final int result;
- final Runnable onComplete;
if (appWidgetId < 0 || resultCode == RESULT_CANCELED) {
- Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" +
- "widget configuration activity.");
+ Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
+ "returned from the widget configuration activity.");
result = RESULT_CANCELED;
completeTwoStageWidgetDrop(result, appWidgetId);
- onComplete = new Runnable() {
+ final Runnable onComplete = new Runnable() {
@Override
public void run() {
exitSpringLoadedDragModeDelayed(false, 0, null);
}
};
+ if (workspaceLocked) {
+ // No need to remove the empty screen if we're mid-binding, as the
+ // the bind will not add the empty screen.
+ mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
+ } else {
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ }
} else {
- result = resultCode;
- final CellLayout dropLayout =
- (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
- dropLayout.setDropPending(true);
- onComplete = new Runnable() {
- @Override
- public void run() {
- completeTwoStageWidgetDrop(result, appWidgetId);
- dropLayout.setDropPending(false);
+ if (!workspaceLocked) {
+ if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ // When the screen id represents an actual screen (as opposed to a rank)
+ // we make sure that the drop page actually exists.
+ mPendingAddInfo.screenId =
+ ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
}
- };
+ final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+
+ dropLayout.setDropPending(true);
+ final Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ completeTwoStageWidgetDrop(resultCode, appWidgetId);
+ dropLayout.setDropPending(false);
+ }
+ };
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ } else {
+ PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
+ mPendingAddInfo);
+ sPendingAddItem = args;
+ }
+ }
+ return;
+ }
+
+ if (requestCode == REQUEST_RECONFIGURE_APPWIDGET) {
+ if (resultCode == RESULT_OK) {
+ // Update the widget view.
+ PendingAddArguments args = preparePendingAddArgs(requestCode, data,
+ pendingAddWidgetId, mPendingAddInfo);
+ if (workspaceLocked) {
+ sPendingAddItem = args;
+ } else {
+ completeAdd(args);
+ }
}
- mWorkspace.removeExtraEmptyScreen(true, onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY,
- false);
+ // Leave the widget in the pending state if the user canceled the configure.
return;
}
@@ -849,27 +869,54 @@ public class Launcher extends Activity
// For example, the user would PICK_SHORTCUT for "Music playlist", and we
// launch over to the Music app to actually CREATE_SHORTCUT.
if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
- final PendingAddArguments args = new PendingAddArguments();
- args.requestCode = requestCode;
- args.intent = data;
- args.container = mPendingAddInfo.container;
- args.screenId = mPendingAddInfo.screenId;
- args.cellX = mPendingAddInfo.cellX;
- args.cellY = mPendingAddInfo.cellY;
+ final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
+ mPendingAddInfo);
if (isWorkspaceLocked()) {
- sPendingAddList.add(args);
+ sPendingAddItem = args;
} else {
completeAdd(args);
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
- mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_CANCELED) {
- mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
mDragLayer.clearAnimatedView();
}
+ private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
+ appWidgetId, ItemInfo info) {
+ PendingAddArguments args = new PendingAddArguments();
+ args.requestCode = requestCode;
+ args.intent = data;
+ args.container = info.container;
+ args.screenId = info.screenId;
+ args.cellX = info.cellX;
+ args.cellY = info.cellY;
+ args.appWidgetId = appWidgetId;
+ return args;
+ }
+
+ /**
+ * Check to see if a given screen id exists. If not, create it at the end, return the new id.
+ *
+ * @param screenId the screen id to check
+ * @return the new screen, or screenId if it exists
+ */
+ private long ensurePendingDropLayoutExists(long screenId) {
+ CellLayout dropLayout =
+ (CellLayout) mWorkspace.getScreenWithId(screenId);
+ if (dropLayout == null) {
+ // it's possible that the add screen was removed because it was
+ // empty and a re-bind occurred
+ mWorkspace.addExtraEmptyScreen();
+ return mWorkspace.commitExtraEmptyScreen();
+ } else {
+ return screenId;
+ }
+ }
+
private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
CellLayout cellLayout =
(CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
@@ -938,9 +985,8 @@ public class Launcher extends Activity
setWorkspaceBackground(mState == State.WORKSPACE);
mPaused = false;
- sPausedFromUserAction = false;
if (mRestoring || mOnResumeNeedsLoad) {
- mWorkspaceLoading = true;
+ setWorkspaceLoading(true);
mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
mRestoring = false;
mOnResumeNeedsLoad = false;
@@ -981,10 +1027,6 @@ public class Launcher extends Activity
// Resets the previous workspace icon press state
mWaitingForResume.setStayPressed(false);
}
- if (mAppsCustomizeContent != null) {
- // Resets the previous all apps icon press state
- mAppsCustomizeContent.resetDrawableState();
- }
// It is possible that widgets can receive updates while launcher is not in the foreground.
// Consequently, the widgets will be inflated in the orientation of the foreground activity
@@ -1010,17 +1052,20 @@ public class Launcher extends Activity
// It is also poassible that onShow will instead be called slightly after first layout
// if PagedView#setRestorePage was set to the custom content page in onCreate().
if (mWorkspace.isOnOrMovingToCustomContent()) {
- mWorkspace.getCustomContentCallbacks().onShow();
+ mWorkspace.getCustomContentCallbacks().onShow(true);
}
}
mWorkspace.updateInteractionForState();
mWorkspace.onResume();
+
+ PackageInstallerCompat.getInstance(this).onResume();
}
@Override
protected void onPause() {
// Ensure that items added to Launcher are queued until Launcher returns
InstallShortcutReceiver.enableInstallQueue();
+ PackageInstallerCompat.getInstance(this).onPause();
super.onPause();
mPaused = true;
@@ -1054,23 +1099,24 @@ public class Launcher extends Activity
}
public interface CustomContentCallbacks {
- // Custom content is completely shown
- public void onShow();
+ // Custom content is completely shown. {@code fromResume} indicates whether this was caused
+ // by a onResume or by scrolling otherwise.
+ public void onShow(boolean fromResume);
// Custom content is completely hidden
public void onHide();
// Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
public void onScrollProgressChanged(float progress);
+
+ // Indicates whether the user is allowed to scroll away from the custom content.
+ boolean isScrollingAllowed();
}
protected boolean hasSettings() {
return false;
}
- protected void startSettings() {
- }
-
public interface QSBScroller {
public void setScrollY(int scrollY);
}
@@ -1089,7 +1135,9 @@ public class Launcher extends Activity
@Override
public Object onRetainNonConfigurationInstance() {
// Flag the loader to stop early before switching
- mModel.stopLoader();
+ if (mModel.isCurrentCallbacks(this)) {
+ mModel.stopLoader();
+ }
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.surrender();
}
@@ -1196,7 +1244,7 @@ public class Launcher extends Activity
mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
- mWaitingForResult = true;
+ setWaitingForResult(true);
mRestoring = true;
}
@@ -1231,8 +1279,10 @@ public class Launcher extends Activity
final DragController dragController = mDragController;
mLauncherView = findViewById(R.id.launcher);
+ mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
+ mWorkspace.setPageSwitchListener(this);
mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
mLauncherView.setSystemUiVisibility(
@@ -1255,7 +1305,7 @@ public class Launcher extends Activity
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
- showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
+ onClickAddWidgetButton(arg0);
}
}
});
@@ -1266,7 +1316,7 @@ public class Launcher extends Activity
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
- startWallpaper();
+ onClickWallpaperPicker(arg0);
}
}
});
@@ -1278,9 +1328,9 @@ public class Launcher extends Activity
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
- startSettings();
- }
+ onClickSettingsButton(arg0);
}
+ }
});
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
} else {
@@ -1334,6 +1384,17 @@ public class Launcher extends Activity
}
/**
+ * Sets the all apps button. This method is called from {@link Hotseat}.
+ */
+ public void setAllAppsButton(View allAppsButton) {
+ mAllAppsButton = allAppsButton;
+ }
+
+ public View getAllAppsButton() {
+ return mAllAppsButton;
+ }
+
+ /**
* Creates a view representing a shortcut.
*
* @param info The data structure describing the shortcut.
@@ -1356,44 +1417,13 @@ public class Launcher extends Activity
*/
View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
- favorite.applyFromShortcutInfo(info, mIconCache);
+ favorite.applyFromShortcutInfo(info, mIconCache, true);
favorite.setOnClickListener(this);
+ favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
/**
- * Add an application shortcut to the workspace.
- *
- * @param data The intent describing the application.
- * @param cellInfo The position on screen where to create the shortcut.
- */
- void completeAddApplication(Intent data, long container, long screenId, int cellX, int cellY) {
- final int[] cellXY = mTmpAddItemCellCoordinates;
- final CellLayout layout = getCellLayout(container, screenId);
-
- // First we check if we already know the exact location where we want to add this item.
- if (cellX >= 0 && cellY >= 0) {
- cellXY[0] = cellX;
- cellXY[1] = cellY;
- } else if (!layout.findCellForSpan(cellXY, 1, 1)) {
- showOutOfSpaceMessage(isHotseatLayout(layout));
- return;
- }
-
- final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
-
- if (info != null) {
- info.setActivity(this, data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- info.container = ItemInfo.NO_ID;
- mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1],
- isWorkspaceLocked(), cellX, cellY);
- } else {
- Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
- }
- }
-
- /**
* Add a shortcut to the workspace.
*
* @param data The intent describing the shortcut.
@@ -1543,6 +1573,7 @@ public class Launcher extends Activity
launcherInfo.spanY = spanXY[1];
launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
+ launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
LauncherModel.addItemToDatabase(this, launcherInfo,
container, screenId, cellXY[0], cellXY[1], false);
@@ -1595,6 +1626,9 @@ public class Launcher extends Activity
mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
| LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
+ } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+ || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ getModel().forceReload();
}
}
};
@@ -1607,16 +1641,61 @@ public class Launcher extends Activity
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ // For handling managed profiles
+ filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
+ filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
if (ENABLE_DEBUG_INTENTS) {
filter.addAction(DebugIntents.DELETE_DATABASE);
filter.addAction(DebugIntents.MIGRATE_DATABASE);
}
registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
+ setupTransparentSystemBarsForLmp();
mAttached = true;
mVisible = true;
}
+ /**
+ * Sets up transparent navigation and status bars in LMP.
+ * This method is a no-op for other platform versions.
+ */
+ @TargetApi(19)
+ private void setupTransparentSystemBarsForLmp() {
+ // TODO(sansid): use the APIs directly when compiling against L sdk.
+ // Currently we use reflection to access the flags and the API to set the transparency
+ // on the System bars.
+ if (Utilities.isLmpOrAbove()) {
+ try {
+ getWindow().getAttributes().systemUiVisibility |=
+ (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+ Field drawsSysBackgroundsField = WindowManager.LayoutParams.class.getField(
+ "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS");
+ getWindow().addFlags(drawsSysBackgroundsField.getInt(null));
+
+ Method setStatusBarColorMethod =
+ Window.class.getDeclaredMethod("setStatusBarColor", int.class);
+ Method setNavigationBarColorMethod =
+ Window.class.getDeclaredMethod("setNavigationBarColor", int.class);
+ setStatusBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
+ setNavigationBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
+ } catch (NoSuchFieldException e) {
+ Log.w(TAG, "NoSuchFieldException while setting up transparent bars");
+ } catch (NoSuchMethodException ex) {
+ Log.w(TAG, "NoSuchMethodException while setting up transparent bars");
+ } catch (IllegalAccessException e) {
+ Log.w(TAG, "IllegalAccessException while setting up transparent bars");
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "IllegalArgumentException while setting up transparent bars");
+ } catch (InvocationTargetException e) {
+ Log.w(TAG, "InvocationTargetException while setting up transparent bars");
+ } finally {}
+ }
+ }
+
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -1667,11 +1746,6 @@ public class Launcher extends Activity
}
});
}
- // When Launcher comes back to foreground, a different Activity might be responsible for
- // the app market intent, so refresh the icon
- if (!DISABLE_MARKET_BUTTON) {
- updateAppMarketIcon();
- }
clearTypedText();
}
}
@@ -1778,10 +1852,6 @@ public class Launcher extends Activity
return mModel;
}
- public LauncherClings getLauncherClings() {
- return mLauncherClings;
- }
-
protected SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
@@ -1790,7 +1860,7 @@ public class Launcher extends Activity
getWindow().closeAllPanels();
// Whatever we were doing is hereby canceled.
- mWaitingForResult = false;
+ setWaitingForResult(false);
}
@Override
@@ -1930,8 +2000,13 @@ public class Launcher extends Activity
// Stop callbacks from LauncherModel
LauncherAppState app = (LauncherAppState.getInstance());
- mModel.stopLoader();
- app.setLauncher(null);
+
+ // It's possible to receive onDestroy after a new Launcher activity has
+ // been created. In this case, don't interfere with the new Launcher.
+ if (mModel.isCurrentCallbacks(this)) {
+ mModel.stopLoader();
+ app.setLauncher(null);
+ }
try {
mAppWidgetHost.stopListening();
@@ -1959,6 +2034,7 @@ public class Launcher extends Activity
mWorkspace = null;
mDragController = null;
+ PackageInstallerCompat.getInstance(this).onStop();
LauncherAnimUtils.onDestroyActivity();
}
@@ -1968,7 +2044,9 @@ public class Launcher extends Activity
@Override
public void startActivityForResult(Intent intent, int requestCode) {
- if (requestCode >= 0) mWaitingForResult = true;
+ if (requestCode >= 0) {
+ setWaitingForResult(true);
+ }
super.startActivityForResult(intent, requestCode);
}
@@ -1995,14 +2073,25 @@ public class Launcher extends Activity
sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
}
- startSearch(initialQuery, selectInitialQuery,
+ boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
appSearchData, sourceBounds);
+ if (clearTextImmediately) {
+ clearTypedText();
+ }
}
- public void startSearch(String initialQuery,
+ /**
+ * Start a text search.
+ *
+ * @return {@code true} if the search will start immediately, so any further keypresses
+ * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
+ * to buffer keypresses.
+ */
+ public boolean startSearch(String initialQuery,
boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
startGlobalSearch(initialQuery, selectInitialQuery,
appSearchData, sourceBounds);
+ return false;
}
/**
@@ -2082,6 +2171,24 @@ public class Launcher extends Activity
return mWorkspaceLoading;
}
+ private void setWorkspaceLoading(boolean value) {
+ boolean isLocked = isWorkspaceLocked();
+ mWorkspaceLoading = value;
+ if (isLocked != isWorkspaceLocked()) {
+ onWorkspaceLockedChanged();
+ }
+ }
+
+ private void setWaitingForResult(boolean value) {
+ boolean isLocked = isWorkspaceLocked();
+ mWaitingForResult = value;
+ if (isLocked != isWorkspaceLocked()) {
+ onWorkspaceLockedChanged();
+ }
+ }
+
+ protected void onWorkspaceLockedChanged() { }
+
private void resetAddInfo() {
mPendingAddInfo.container = ItemInfo.NO_ID;
mPendingAddInfo.screenId = -1;
@@ -2104,10 +2211,9 @@ public class Launcher extends Activity
mPendingAddWidgetId = appWidgetId;
// Launch over to configure widget, if needed
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
- intent.setComponent(appWidgetInfo.configure);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET);
+ mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
+ mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
+
} else {
// Otherwise just add it
Runnable onComplete = new Runnable() {
@@ -2120,7 +2226,7 @@ public class Launcher extends Activity
};
completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
appWidgetInfo);
- mWorkspace.removeExtraEmptyScreen(true, onComplete, delay, false);
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
@@ -2191,14 +2297,8 @@ public class Launcher extends Activity
appWidgetId = getAppWidgetHost().allocateAppWidgetId();
Bundle options = info.bindOptions;
- boolean success = false;
- if (options != null) {
- success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
- info.componentName, options);
- } else {
- success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
- info.componentName);
- }
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ appWidgetId, info.info, options);
if (success) {
addAppWidgetImpl(appWidgetId, info, null, info.info);
} else {
@@ -2206,6 +2306,8 @@ public class Launcher extends Activity
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
+ mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+ .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
@@ -2214,21 +2316,7 @@ public class Launcher extends Activity
}
void processShortcut(Intent intent) {
- // Handle case where user selected "Applications"
- String applicationName = getResources().getString(R.string.group_applications);
- String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-
- if (applicationName != null && applicationName.equals(shortcutName)) {
- Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
- pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
- pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
- Utilities.startActivityForResultSafely(this, pickIntent, REQUEST_PICK_APPLICATION);
- } else {
- Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
- }
+ Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
}
void processWallpaper(Intent intent) {
@@ -2260,12 +2348,6 @@ public class Launcher extends Activity
sFolders.remove(folder.id);
}
- protected void startWallpaper() {
- final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
- pickWallpaper.setComponent(getWallpaperPickerComponent());
- startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
- }
-
protected ComponentName getWallpaperPickerComponent() {
return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName());
}
@@ -2369,59 +2451,63 @@ public class Launcher extends Activity
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
- // Open shortcut
- final ShortcutInfo shortcut = (ShortcutInfo) tag;
- final Intent intent = shortcut.intent;
-
- // Check for special shortcuts
- if (intent.getComponent() != null) {
- final String shortcutClass = intent.getComponent().getClassName();
-
- if (shortcutClass.equals(WidgetAdder.class.getName())) {
- onClickAddWidgetButton();
- return;
- } else if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
- MemoryDumpActivity.startDump(this);
- return;
- } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
- toggleShowWeightWatcher();
- return;
- }
- }
-
- // Start activities
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- intent.setSourceBounds(new Rect(pos[0], pos[1],
- pos[0] + v.getWidth(), pos[1] + v.getHeight()));
-
- boolean success = startActivitySafely(v, intent, tag);
-
- mStats.recordLaunch(intent, shortcut);
-
- if (success && v instanceof BubbleTextView) {
- mWaitingForResume = (BubbleTextView) v;
- mWaitingForResume.setStayPressed(true);
- }
+ onClickAppShortcut(v);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
- FolderIcon fi = (FolderIcon) v;
- handleFolderClick(fi);
+ onClickFolderIcon(v);
}
} else if (v == mAllAppsButton) {
- if (isAllAppsVisible()) {
- showWorkspace(true);
- } else {
- onClickAllAppsButton(v);
+ onClickAllAppsButton(v);
+ } else if (tag instanceof AppInfo) {
+ startAppShortcutOrInfoActivity(v);
+ } else if (tag instanceof LauncherAppWidgetInfo) {
+ if (v instanceof PendingAppWidgetHostView) {
+ onClickPendingWidget((PendingAppWidgetHostView) v);
}
}
}
+ public void onClickPagedViewIcon(View v) {
+ startAppShortcutOrInfoActivity(v);
+ }
+
public boolean onTouch(View v, MotionEvent event) {
return false;
}
/**
+ * Event handler for the app widget view which has not fully restored.
+ */
+ public void onClickPendingWidget(final PendingAppWidgetHostView v) {
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
+ if (v.isReadyForClickSetup()) {
+ int widgetId = info.appWidgetId;
+ AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
+ if (appWidgetInfo != null) {
+ mPendingAddWidgetInfo = appWidgetInfo;
+ mPendingAddInfo.copyFrom(info);
+ mPendingAddWidgetId = widgetId;
+
+ AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo,
+ info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
+ }
+ } else if (info.installProgress < 0) {
+ // The install has not been queued
+ final String packageName = info.providerName.getPackageName();
+ showBrokenAppInstallDialog(packageName,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ }
+ });
+ } else {
+ // Download has started.
+ final String packageName = info.providerName.getPackageName();
+ startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ }
+ }
+
+ /**
* Event handler for the search button
*
* @param v The view that was clicked.
@@ -2467,18 +2553,180 @@ public class Launcher extends Activity
*
* @param v The view that was clicked.
*/
- public void onClickAllAppsButton(View v) {
- showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+ protected void onClickAllAppsButton(View v) {
+ if (LOGD) Log.d(TAG, "onClickAllAppsButton");
+ if (isAllAppsVisible()) {
+ showWorkspace(true);
+ } else {
+ showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+ }
+ }
+
+ private void showBrokenAppInstallDialog(final String packageName,
+ DialogInterface.OnClickListener onSearchClickListener) {
+ new AlertDialog.Builder(new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault))
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search, onSearchClickListener)
+ .setNeutralButton(R.string.abandoned_clean_this,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ final UserHandleCompat user = UserHandleCompat.myUserHandle();
+ mWorkspace.removeAbandonedPromise(packageName, user);
+ }
+ })
+ .create().show();
+ return;
+ }
+
+ /**
+ * Event handler for an app shortcut click.
+ *
+ * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+ */
+ protected void onClickAppShortcut(final View v) {
+ if (LOGD) Log.d(TAG, "onClickAppShortcut");
+ Object tag = v.getTag();
+ if (!(tag instanceof ShortcutInfo)) {
+ throw new IllegalArgumentException("Input must be a Shortcut");
+ }
+
+ // Open shortcut
+ final ShortcutInfo shortcut = (ShortcutInfo) tag;
+ final Intent intent = shortcut.intent;
+
+ // Check for special shortcuts
+ if (intent.getComponent() != null) {
+ final String shortcutClass = intent.getComponent().getClassName();
+
+ if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
+ MemoryDumpActivity.startDump(this);
+ return;
+ } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
+ toggleShowWeightWatcher();
+ return;
+ }
+ }
+
+ // Check for abandoned promise
+ if ((v instanceof BubbleTextView)
+ && shortcut.isPromise()
+ && !shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE)) {
+ showBrokenAppInstallDialog(
+ shortcut.getTargetComponent().getPackageName(),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ startAppShortcutOrInfoActivity(v);
+ }
+ });
+ return;
+ }
+
+ // Start activities
+ startAppShortcutOrInfoActivity(v);
+ }
+
+ private void startAppShortcutOrInfoActivity(View v) {
+ Object tag = v.getTag();
+ final ShortcutInfo shortcut;
+ final Intent intent;
+ if (tag instanceof ShortcutInfo) {
+ shortcut = (ShortcutInfo) tag;
+ intent = shortcut.intent;
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ intent.setSourceBounds(new Rect(pos[0], pos[1],
+ pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+
+ } else if (tag instanceof AppInfo) {
+ shortcut = null;
+ intent = ((AppInfo) tag).intent;
+ } else {
+ throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
+ }
+
+ boolean success = startActivitySafely(v, intent, tag);
+ mStats.recordLaunch(intent, shortcut);
+
+ if (success && v instanceof BubbleTextView) {
+ mWaitingForResume = (BubbleTextView) v;
+ mWaitingForResume.setStayPressed(true);
+ }
+ }
+
+ /**
+ * Event handler for a folder icon click.
+ *
+ * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
+ */
+ protected void onClickFolderIcon(View v) {
+ if (LOGD) Log.d(TAG, "onClickFolder");
+ if (!(v instanceof FolderIcon)){
+ throw new IllegalArgumentException("Input must be a FolderIcon");
+ }
+
+ FolderIcon folderIcon = (FolderIcon) v;
+ final FolderInfo info = folderIcon.getFolderInfo();
+ Folder openFolder = mWorkspace.getFolderForTag(info);
+
+ // 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) {
+ Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
+ + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
+ info.opened = false;
+ }
+
+ if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
+ // Close any open folder
+ closeFolder();
+ // Open the requested folder
+ openFolder(folderIcon);
+ } else {
+ // Find the open folder...
+ int folderScreen;
+ if (openFolder != null) {
+ folderScreen = mWorkspace.getPageForView(openFolder);
+ // .. and close it
+ closeFolder(openFolder);
+ if (folderScreen != mWorkspace.getCurrentPage()) {
+ // Close any folder open on the current screen
+ closeFolder();
+ // Pull the folder onto this screen
+ openFolder(folderIcon);
+ }
+ }
+ }
}
/**
* Event handler for the (Add) Widgets button that appears after a long press
* on the home screen.
*/
- protected void onClickAddWidgetButton() {
+ protected void onClickAddWidgetButton(View view) {
+ if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
}
+ /**
+ * Event handler for the wallpaper picker button that appears after a long press
+ * on the home screen.
+ */
+ protected void onClickWallpaperPicker(View v) {
+ if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
+ final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
+ pickWallpaper.setComponent(getWallpaperPickerComponent());
+ startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
+ }
+
+ /**
+ * Event handler for a click on the settings button that appears after a long press
+ * on the home screen.
+ */
+ protected void onClickSettingsButton(View v) {
+ if (LOGD) Log.d(TAG, "onClickSettingsButton");
+ }
+
public void onTouchDownAllAppsButton(View v) {
// Provide the same haptic feedback that the system offers for virtual keys.
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
@@ -2504,15 +2752,7 @@ public class Launcher extends Activity
return mHapticFeedbackTouchListener;
}
- public void onClickAppMarketButton(View v) {
- if (!DISABLE_MARKET_BUTTON) {
- if (mAppMarketIntent != null) {
- startActivitySafely(v, mAppMarketIntent, "app market");
- } else {
- Log.e(TAG, "Invalid app market intent.");
- }
- }
- }
+ public void onDragStarted(View view) {}
/**
* Called when the user stops interacting with the launcher.
@@ -2531,17 +2771,24 @@ public class Launcher extends Activity
*/
protected void onInteractionBegin() {}
- void startApplicationDetailsActivity(ComponentName componentName) {
+ void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
String packageName = componentName.getPackageName();
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.fromParts("package", packageName, null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
- Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- startActivitySafely(null, intent, "startApplicationDetailsActivity");
+ try {
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+ UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+ launcherApps.showAppDetailsForProfile(componentName, user);
+ } catch (SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Launcher does not have permission to launch settings");
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Unable to launch settings");
+ }
}
// returns true if the activity was started
- boolean startApplicationUninstallActivity(ComponentName componentName, int flags) {
+ boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
+ UserHandleCompat user) {
if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
@@ -2555,6 +2802,9 @@ public class Launcher extends Activity
Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ if (user != null) {
+ user.addToIntent(intent, Intent.EXTRA_USER);
+ }
startActivity(intent);
return true;
}
@@ -2562,19 +2812,35 @@ public class Launcher extends Activity
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+ UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+
+ UserHandleCompat user = null;
+ if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
+ long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
+ user = userManager.getUserForSerialNumber(serialNumber);
+ }
+
+ Bundle optsBundle = null;
if (useLaunchAnimation) {
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
- v.getMeasuredWidth(), v.getMeasuredHeight());
+ ActivityOptions opts = Utilities.isLmpOrAbove() ?
+ ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
+ ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
+ optsBundle = opts.toBundle();
+ }
- startActivity(intent, opts.toBundle());
+ if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
} else {
- startActivity(intent);
+ // TODO Component can be null when shortcuts are supported for secondary user
+ launcherApps.startActivityForProfile(intent.getComponent(), user,
+ intent.getSourceBounds(), optsBundle);
}
return true;
} catch (SecurityException e) {
@@ -2589,6 +2855,10 @@ public class Launcher extends Activity
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
+ if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+ Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+ return false;
+ }
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
@@ -2598,40 +2868,6 @@ public class Launcher extends Activity
return success;
}
- private void handleFolderClick(FolderIcon folderIcon) {
- final FolderInfo info = folderIcon.getFolderInfo();
- Folder openFolder = mWorkspace.getFolderForTag(info);
-
- // 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) {
- Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
- + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
- info.opened = false;
- }
-
- if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
- // Close any open folder
- closeFolder();
- // Open the requested folder
- openFolder(folderIcon);
- } else {
- // Find the open folder...
- int folderScreen;
- if (openFolder != null) {
- folderScreen = mWorkspace.getPageForView(openFolder);
- // .. and close it
- closeFolder(openFolder);
- if (folderScreen != mWorkspace.getCurrentPage()) {
- // Close any folder open on the current screen
- closeFolder();
- // Pull the folder onto this screen
- openFolder(folderIcon);
- }
- }
- }
- }
-
/**
* This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
* in the DragLayer in the exact absolute location of the original FolderIcon.
@@ -2703,7 +2939,10 @@ public class Launcher extends Activity
ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
scaleX, scaleY);
- oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
+ if (Utilities.isLmpOrAbove()) {
+ oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ }
+ oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.start();
}
@@ -2720,7 +2959,7 @@ public class Launcher extends Activity
copyFolderIconToImage(fi);
ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
scaleX, scaleY);
- oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
+ oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -2773,9 +3012,6 @@ public class Launcher extends Activity
folder.dismissEditingName();
}
closeFolder(folder);
-
- // Dismiss the folder cling
- mLauncherClings.dismissFolderCling(null);
}
}
@@ -2808,23 +3044,22 @@ public class Launcher extends Activity
} else {
return false;
}
+ } else {
+ return false;
}
}
- if (!(v instanceof CellLayout)) {
- v = (View) v.getParent().getParent();
- }
-
- resetAddInfo();
- CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
- // This happens when long clicking an item with the dpad/trackball
- if (longClickCellInfo == null) {
- return true;
+ CellLayout.CellInfo longClickCellInfo = null;
+ View itemUnderLongClick = null;
+ if (v.getTag() instanceof ItemInfo) {
+ ItemInfo info = (ItemInfo) v.getTag();
+ longClickCellInfo = new CellLayout.CellInfo(v, info);;
+ itemUnderLongClick = longClickCellInfo.cell;
+ resetAddInfo();
}
// The hotseat touch handling does not go through Workspace, and we always allow long press
// on hotseat items.
- final View itemUnderLongClick = longClickCellInfo.cell;
final boolean inHotseat = isHotseatLayout(v);
boolean allowLongPress = inHotseat || mWorkspace.allowLongPress();
if (allowLongPress && !mDragController.isDragging()) {
@@ -2832,7 +3067,6 @@ public class Launcher extends Activity
// User long pressed on empty space
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- // Disabling reordering until we sort out some issues.
if (mWorkspace.isInOverviewMode()) {
mWorkspace.startReordering(v);
} else {
@@ -2876,22 +3110,12 @@ public class Launcher extends Activity
return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
}
- /**
- * Helper method for the cameraZoomIn/cameraZoomOut animations
- * @param view The view being animated
- * @param scaleFactor The scale factor used for the zoom
- */
- private void setPivotsForZoom(View view, float scaleFactor) {
- view.setPivotX(view.getWidth() / 2.0f);
- view.setPivotY(view.getHeight() / 2.0f);
- }
-
private void setWorkspaceBackground(boolean workspace) {
mLauncherView.setBackground(workspace ?
mWorkspaceBackgroundDrawable : null);
}
- void updateWallpaperVisibility(boolean visible) {
+ protected void changeWallpaperVisiblity(boolean visible) {
int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
int curflags = getWindow().getAttributes().flags
& WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -2980,6 +3204,7 @@ public class Launcher extends Activity
AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType();
showAppsCustomizeHelper(animated, springLoaded, contentType);
}
+
private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
final AppsCustomizePagedView.ContentType contentType) {
if (mStateAnimation != null) {
@@ -2987,98 +3212,178 @@ public class Launcher extends Activity
mStateAnimation.cancel();
mStateAnimation = null;
}
+
+ boolean material = Utilities.isLmpOrAbove();
+
final Resources res = getResources();
final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
+ final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+ final int itemsAlphaStagger =
+ res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+
final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mWorkspace;
final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
- final int startDelay =
- res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
- setPivotsForZoom(toView, scale);
+ final ArrayList<View> layerViews = new ArrayList<View>();
- // Shrink workspaces away if going to AppsCustomize from workspace
+ Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
+ Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
Animator workspaceAnim =
- mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
+ mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews);
if (!LauncherAppState.isDisableAllApps()
|| contentType == AppsCustomizePagedView.ContentType.Widgets) {
// Set the content type for the all apps/widgets space
mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
}
- if (animated) {
- toView.setScaleX(scale);
- toView.setScaleY(scale);
- final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
- scaleAnim.
- scaleX(1f).scaleY(1f).
- setDuration(duration).
- setInterpolator(new Workspace.ZoomOutInterpolator());
-
- toView.setVisibility(View.VISIBLE);
- toView.setAlpha(0f);
- final ObjectAnimator alphaAnim = LauncherAnimUtils
- .ofFloat(toView, "alpha", 0f, 1f)
- .setDuration(fadeDuration);
- alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
- alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (animation == null) {
- throw new RuntimeException("animation is null");
- }
- float t = (Float) animation.getAnimatedValue();
- dispatchOnLauncherTransitionStep(fromView, t);
- dispatchOnLauncherTransitionStep(toView, t);
- }
- });
+ // If for some reason our views aren't initialized, don't animate
+ boolean initialized = getAllAppsButton() != null;
- // toView should appear right at the end of the workspace shrink
- // animation
+ if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
- mStateAnimation.play(scaleAnim).after(startDelay);
- mStateAnimation.play(alphaAnim).after(startDelay);
+ final AppsCustomizePagedView content = (AppsCustomizePagedView)
+ toView.findViewById(R.id.apps_customize_pane_content);
+
+ final View page = content.getPageAt(content.getCurrentPage());
+ final View revealView = toView.findViewById(R.id.fake_page);
+
+ final float initialPanelAlpha = 1f;
+
+ final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
+ if (isWidgetTray) {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
+ } else {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
+ }
+
+ // Hide the real page background, and swap in the fake one
+ content.setPageBackgroundsVisible(false);
+ revealView.setVisibility(View.VISIBLE);
+ // We need to hide this view as the animation start will be posted.
+ revealView.setAlpha(0);
+
+ int width = revealView.getMeasuredWidth();
+ int height = revealView.getMeasuredHeight();
+ float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
+
+ revealView.setTranslationY(0);
+ revealView.setTranslationX(0);
+
+ // Get the y delta between the center of the page and the center of the all apps button
+ int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+ getAllAppsButton(), null);
+
+ float alpha = 0;
+ float xDrift = 0;
+ float yDrift = 0;
+ if (material) {
+ alpha = isWidgetTray ? 0.3f : 1f;
+ yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
+ xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
+ } else {
+ yDrift = 2 * height / 3;
+ xDrift = 0;
+ }
+ final float initAlpha = alpha;
+
+ revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ layerViews.add(revealView);
+ PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);
+ PropertyValuesHolder panelDriftY =
+ PropertyValuesHolder.ofFloat("translationY", yDrift, 0);
+ PropertyValuesHolder panelDriftX =
+ PropertyValuesHolder.ofFloat("translationX", xDrift, 0);
+
+ ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
+ panelAlpha, panelDriftY, panelDriftX);
+
+ panelAlphaAndDrift.setDuration(revealDuration);
+ panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ mStateAnimation.play(panelAlphaAndDrift);
+
+ if (page != null) {
+ page.setVisibility(View.VISIBLE);
+ page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ layerViews.add(page);
+
+ ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);
+ page.setTranslationY(yDrift);
+ pageDrift.setDuration(revealDuration);
+ pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ pageDrift.setStartDelay(itemsAlphaStagger);
+ mStateAnimation.play(pageDrift);
+
+ page.setAlpha(0f);
+ ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f);
+ itemsAlpha.setDuration(revealDuration);
+ itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+ itemsAlpha.setStartDelay(itemsAlphaStagger);
+ mStateAnimation.play(itemsAlpha);
+ }
+
+ View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
+ pageIndicators.setAlpha(0.01f);
+ ObjectAnimator indicatorsAlpha =
+ ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
+ indicatorsAlpha.setDuration(revealDuration);
+ mStateAnimation.play(indicatorsAlpha);
+
+ if (material) {
+ final View allApps = getAllAppsButton();
+ int allAppsButtonSize = LauncherAppState.getInstance().
+ getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+ float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
+ Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
+ height / 2, startRadius, revealRadius);
+ reveal.setDuration(revealDuration);
+ reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ reveal.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart(Animator animation) {
+ if (!isWidgetTray) {
+ allApps.setVisibility(View.INVISIBLE);
+ }
+ }
+ public void onAnimationEnd(Animator animation) {
+ if (!isWidgetTray) {
+ allApps.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ mStateAnimation.play(reveal);
+ }
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationStart(Animator animation) {
- // Prepare the position
- toView.setTranslationX(0.0f);
- toView.setTranslationY(0.0f);
- toView.setVisibility(View.VISIBLE);
- toView.bringToFront();
- }
- @Override
public void onAnimationEnd(Animator animation) {
dispatchOnLauncherTransitionEnd(fromView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
+ revealView.setVisibility(View.INVISIBLE);
+ revealView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (page != null) {
+ page.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ content.setPageBackgroundsVisible(true);
+
// Hide the search bar
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.hideSearchBar(false);
}
}
+
});
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
}
- boolean delayAnim = false;
-
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
-
- // If any of the objects being animated haven't been measured/laid out
- // yet, delay the animation until we get a layout pass
- if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
- (mWorkspace.getMeasuredWidth() == 0) ||
- (toView.getMeasuredWidth() == 0)) {
- delayAnim = true;
- }
-
final AnimatorSet stateAnimation = mStateAnimation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
@@ -3086,23 +3391,28 @@ public class Launcher extends Activity
// we waited for a layout/draw pass
if (mStateAnimation != stateAnimation)
return;
- setPivotsForZoom(toView, scale);
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
- LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
+
+ revealView.setAlpha(initAlpha);
+ if (Utilities.isLmpOrAbove()) {
+ for (int i = 0; i < layerViews.size(); i++) {
+ View v = layerViews.get(i);
+ if (v != null) {
+ boolean attached = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ attached = v.isAttachedToWindow();
+ }
+ if (attached) v.buildLayer();
+ }
+ }
+ }
+ mStateAnimation.start();
}
};
- if (delayAnim) {
- final ViewTreeObserver observer = toView.getViewTreeObserver();
- observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- public void onGlobalLayout() {
- startAnimRunnable.run();
- toView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- }
- });
- } else {
- startAnimRunnable.run();
- }
+ toView.bringToFront();
+ toView.setVisibility(View.VISIBLE);
+ toView.post(startAnimRunnable);
} else {
toView.setTranslationX(0.0f);
toView.setTranslationY(0.0f);
@@ -3139,54 +3449,184 @@ public class Launcher extends Activity
mStateAnimation.cancel();
mStateAnimation = null;
}
+
+ boolean material = Utilities.isLmpOrAbove();
Resources res = getResources();
final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
- final int fadeOutDuration =
- res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
+ final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
+ final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
+ final int itemsAlphaStagger =
+ res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+
final float scaleFactor = (float)
res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mAppsCustomizeTabHost;
final View toView = mWorkspace;
Animator workspaceAnim = null;
+ final ArrayList<View> layerViews = new ArrayList<View>();
+
if (toState == Workspace.State.NORMAL) {
- int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
workspaceAnim = mWorkspace.getChangeStateAnimation(
- toState, animated, stagger, -1);
+ toState, animated, layerViews);
} else if (toState == Workspace.State.SPRING_LOADED ||
toState == Workspace.State.OVERVIEW) {
workspaceAnim = mWorkspace.getChangeStateAnimation(
- toState, animated);
- }
-
- setPivotsForZoom(fromView, scaleFactor);
- showHotseat(animated);
- if (animated) {
- final LauncherViewPropertyAnimator scaleAnim =
- new LauncherViewPropertyAnimator(fromView);
- scaleAnim.
- scaleX(scaleFactor).scaleY(scaleFactor).
- setDuration(duration).
- setInterpolator(new Workspace.ZoomInInterpolator());
-
- final ObjectAnimator alphaAnim = LauncherAnimUtils
- .ofFloat(fromView, "alpha", 1f, 0f)
- .setDuration(fadeOutDuration);
- alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
- alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = 1f - (Float) animation.getAnimatedValue();
- dispatchOnLauncherTransitionStep(fromView, t);
- dispatchOnLauncherTransitionStep(toView, t);
- }
- });
+ toState, animated, layerViews);
+ }
+
+ // If for some reason our views aren't initialized, don't animate
+ boolean initialized = getAllAppsButton() != null;
+ if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+ if (workspaceAnim != null) {
+ mStateAnimation.play(workspaceAnim);
+ }
- dispatchOnLauncherTransitionPrepare(fromView, animated, true);
- dispatchOnLauncherTransitionPrepare(toView, animated, true);
- mAppsCustomizeContent.stopScrolling();
+ final AppsCustomizePagedView content = (AppsCustomizePagedView)
+ fromView.findViewById(R.id.apps_customize_pane_content);
+
+ final View page = content.getPageAt(content.getNextPage());
+
+ // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
+ int count = content.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = content.getChildAt(i);
+ if (child != page) {
+ child.setVisibility(View.INVISIBLE);
+ }
+ }
+ final View revealView = fromView.findViewById(R.id.fake_page);
+
+ // 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) {
+ AppsCustomizePagedView.ContentType contentType = content.getContentType();
+ final boolean isWidgetTray =
+ contentType == AppsCustomizePagedView.ContentType.Widgets;
+
+ if (isWidgetTray) {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
+ } else {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
+ }
+
+ int width = revealView.getMeasuredWidth();
+ int height = revealView.getMeasuredHeight();
+ float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
+
+ // Hide the real page background, and swap in the fake one
+ revealView.setVisibility(View.VISIBLE);
+ content.setPageBackgroundsVisible(false);
+
+ final View allAppsButton = getAllAppsButton();
+ revealView.setTranslationY(0);
+ int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+ allAppsButton, null);
+
+ float xDrift = 0;
+ float yDrift = 0;
+ if (material) {
+ yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
+ xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
+ } else {
+ yDrift = 5 * height / 4;
+ xDrift = 0;
+ }
+
+ revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ TimeInterpolator decelerateInterpolator = material ?
+ new LogDecelerateInterpolator(100, 0) :
+ new LogDecelerateInterpolator(30, 0);
+
+ // The vertical motion of the apps panel should be delayed by one frame
+ // from the conceal animation in order to give the right feel. We correpsondingly
+ // shorten the duration so that the slide and conceal end at the same time.
+ ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
+ 0, yDrift);
+ panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+ panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+ panelDriftY.setInterpolator(decelerateInterpolator);
+ mStateAnimation.play(panelDriftY);
+
+ ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
+ 0, xDrift);
+ panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+ panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+ panelDriftX.setInterpolator(decelerateInterpolator);
+ mStateAnimation.play(panelDriftX);
+
+ if (isWidgetTray || !material) {
+ float finalAlpha = material ? 0.4f : 0f;
+ revealView.setAlpha(1f);
+ ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
+ 1f, finalAlpha);
+ panelAlpha.setDuration(revealDuration);
+ panelAlpha.setInterpolator(material ? decelerateInterpolator :
+ new AccelerateInterpolator(1.5f));
+ mStateAnimation.play(panelAlpha);
+ }
+
+ if (page != null) {
+ page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY",
+ 0, yDrift);
+ page.setTranslationY(0);
+ pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+ pageDrift.setInterpolator(decelerateInterpolator);
+ pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+ mStateAnimation.play(pageDrift);
+
+ page.setAlpha(1f);
+ ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f);
+ itemsAlpha.setDuration(100);
+ itemsAlpha.setInterpolator(decelerateInterpolator);
+ mStateAnimation.play(itemsAlpha);
+ }
+
+ View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
+ pageIndicators.setAlpha(1f);
+ ObjectAnimator indicatorsAlpha =
+ LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
+ indicatorsAlpha.setDuration(revealDuration);
+ indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
+ mStateAnimation.play(indicatorsAlpha);
+
+ width = revealView.getMeasuredWidth();
+
+ if (material) {
+ if (!isWidgetTray) {
+ allAppsButton.setVisibility(View.INVISIBLE);
+ }
+ int allAppsButtonSize = LauncherAppState.getInstance().
+ getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+ float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
+ Animator reveal =
+ LauncherAnimUtils.createCircularReveal(revealView, width / 2,
+ height / 2, revealRadius, finalRadius);
+ reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ reveal.setDuration(revealDuration);
+ reveal.setStartDelay(itemsAlphaStagger);
+
+ reveal.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ revealView.setVisibility(View.INVISIBLE);
+ if (!isWidgetTray) {
+ allAppsButton.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+
+ mStateAnimation.play(reveal);
+ }
+
+ dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+ dispatchOnLauncherTransitionPrepare(toView, animated, true);
+ mAppsCustomizeContent.stopScrolling();
+ }
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -3197,17 +3637,57 @@ public class Launcher extends Activity
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
+
+ revealView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (page != null) {
+ page.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ content.setPageBackgroundsVisible(true);
+ // Unhide side pages
+ int count = content.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = content.getChildAt(i);
+ child.setVisibility(View.VISIBLE);
+ }
+
+ // Reset page transforms
+ if (page != null) {
+ page.setTranslationX(0);
+ page.setTranslationY(0);
+ page.setAlpha(1);
+ }
+ content.setCurrentPage(content.getNextPage());
+
mAppsCustomizeContent.updateCurrentPageScroll();
}
});
- mStateAnimation.playTogether(scaleAnim, alphaAnim);
- if (workspaceAnim != null) {
- mStateAnimation.play(workspaceAnim);
- }
- dispatchOnLauncherTransitionStart(fromView, animated, true);
- dispatchOnLauncherTransitionStart(toView, animated, true);
- LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
+ final AnimatorSet stateAnimation = mStateAnimation;
+ final Runnable startAnimRunnable = new Runnable() {
+ public void run() {
+ // Check that mStateAnimation hasn't changed while
+ // we waited for a layout/draw pass
+ if (mStateAnimation != stateAnimation)
+ return;
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+
+ if (Utilities.isLmpOrAbove()) {
+ for (int i = 0; i < layerViews.size(); i++) {
+ View v = layerViews.get(i);
+ if (v != null) {
+ boolean attached = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ attached = v.isAttachedToWindow();
+ }
+ if (attached) v.buildLayer();
+ }
+ }
+ }
+ mStateAnimation.start();
+ }
+ };
+ fromView.post(startAnimRunnable);
} else {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
@@ -3236,10 +3716,7 @@ public class Launcher extends Activity
}
void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
- if (mWorkspace.isInOverviewMode()) {
- mWorkspace.exitOverviewMode(animated);
- }
- if (mState != State.WORKSPACE) {
+ if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) {
boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
mWorkspace.setVisibility(View.VISIBLE);
hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable);
@@ -3288,7 +3765,13 @@ public class Launcher extends Activity
mAppsCustomizeTabHost.reset();
}
showAppsCustomizeHelper(animated, false, contentType);
- mAppsCustomizeTabHost.requestFocus();
+ mAppsCustomizeTabHost.post(new Runnable() {
+ @Override
+ public void run() {
+ // We post this in-case the all apps view isn't yet constructed.
+ mAppsCustomizeTabHost.requestFocus();
+ }
+ });
// Change the state *after* we've called all the transition code
mState = State.APPS_CUSTOMIZE;
@@ -3328,7 +3811,6 @@ public class Launcher extends Activity
}
}
}, delay);
-
}
void exitSpringLoadedDragMode() {
@@ -3350,25 +3832,6 @@ public class Launcher extends Activity
}
/**
- * Shows the hotseat area.
- */
- void showHotseat(boolean animated) {
- if (!LauncherAppState.getInstance().isScreenLarge()) {
- if (animated) {
- if (mHotseat.getAlpha() != 1f) {
- int duration = 0;
- if (mSearchDropTargetBar != null) {
- duration = mSearchDropTargetBar.getTransitionInDuration();
- }
- mHotseat.animate().alpha(1f).setDuration(duration);
- }
- } else {
- mHotseat.setAlpha(1f);
- }
- }
- }
-
- /**
* Hides the hotseat area.
*/
void hideHotseat(boolean animated) {
@@ -3621,44 +4084,6 @@ public class Launcher extends Activity
public void disableVoiceButtonProxy(boolean disabled) {
updateVoiceButtonProxyVisible(disabled);
}
- /**
- * Sets the app market icon
- */
- private void updateAppMarketIcon() {
- if (!DISABLE_MARKET_BUTTON) {
- final View marketButton = findViewById(R.id.market_button);
- Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
- // Find the app market activity by resolving an intent.
- // (If multiple app markets are installed, it will return the ResolverActivity.)
- ComponentName activityName = intent.resolveActivity(getPackageManager());
- if (activityName != null) {
- int coi = getCurrentOrientationIndexForGlobalIcons();
- mAppMarketIntent = intent;
- sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity(
- R.id.market_button, activityName, R.drawable.ic_launcher_market_holo,
- TOOLBAR_ICON_METADATA_NAME);
- marketButton.setVisibility(View.VISIBLE);
- } else {
- // We should hide and disable the view so that we don't try and restore the visibility
- // of it when we swap between drag & normal states from IconDropTarget subclasses.
- marketButton.setVisibility(View.GONE);
- marketButton.setEnabled(false);
- }
- }
- }
-
- private void updateAppMarketIcon(Drawable.ConstantState d) {
- if (!DISABLE_MARKET_BUTTON) {
- // Ensure that the new drawable we are creating has the approprate toolbar icon bounds
- Resources r = getResources();
- Drawable marketIconDrawable = d.newDrawable(r);
- int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
- int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
- marketIconDrawable.setBounds(0, 0, w, h);
-
- updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable);
- }
- }
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
@@ -3667,7 +4092,7 @@ public class Launcher extends Activity
text.clear();
// Populate event with a fake title based on the current state.
if (mState == State.APPS_CUSTOMIZE) {
- text.add(mAppsCustomizeTabHost.getCurrentTabView().getContentDescription());
+ text.add(mAppsCustomizeTabHost.getContentTag());
} else {
text.add(getString(R.string.all_apps_home_button_label));
}
@@ -3775,7 +4200,7 @@ public class Launcher extends Activity
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
- mWorkspaceLoading = true;
+ setWorkspaceLoading(true);
// If we're starting binding all over again, clear any bind calls we'd postponed in
// the past (see waitUntilResume) -- we don't need them since we're starting binding
@@ -3875,7 +4300,7 @@ public class Launcher extends Activity
}
// Remove the extra empty screen
- mWorkspace.removeExtraEmptyScreen(false, null);
+ mWorkspace.removeExtraEmptyScreen(false, false);
if (!LauncherAppState.isDisableAllApps() &&
addedApps != null && mAppsCustomizeContent != null) {
@@ -3926,7 +4351,15 @@ public class Launcher extends Activity
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
- throw new RuntimeException("OCCUPIED");
+ View v = cl.getChildAt(item.cellX, item.cellY);
+ Object tag = v.getTag();
+ String desc = "Collision while binding workspace item: " + item
+ + ". Collides with " + tag;
+ if (LauncherAppState.isDogfoodBuild()) {
+ throw (new RuntimeException(desc));
+ } else {
+ Log.d(TAG, desc);
+ }
}
}
@@ -4021,13 +4454,74 @@ public class Launcher extends Activity
}
final Workspace workspace = mWorkspace;
- final int appWidgetId = item.appWidgetId;
- final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+ AppWidgetProviderInfo appWidgetInfo;
+ if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) &&
+ ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
+
+ appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);
+ if (appWidgetInfo == null) {
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the povider is null");
+ }
+ LauncherModel.deleteItemFromDatabase(this, item);
+ return;
+ }
+ // Note: This assumes that the id remap broadcast is received before this step.
+ // If that is not the case, the id remap will be ignored and user may see the
+ // click to setup view.
+ PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);
+ pendingInfo.spanX = item.spanX;
+ pendingInfo.spanY = item.spanY;
+ pendingInfo.minSpanX = item.minSpanX;
+ pendingInfo.minSpanY = item.minSpanY;
+ Bundle options =
+ AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
+
+ int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ newWidgetId, appWidgetInfo, options);
+
+ // TODO consider showing a permission dialog when the widget is clicked.
+ if (!success) {
+ mAppWidgetHost.deleteAppWidgetId(newWidgetId);
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the launcher is unable to bing a new widget id");
+ }
+ LauncherModel.deleteItemFromDatabase(this, item);
+ return;
+ }
+
+ item.appWidgetId = newWidgetId;
+
+ // If the widget has a configure activity, it is still needs to set it up, otherwise
+ // the widget is ready to go.
+ item.restoreStatus = (appWidgetInfo.configure == null)
+ ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+ : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
+ LauncherModel.updateItemInDatabase(this, item);
}
- item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ final int appWidgetId = item.appWidgetId;
+ appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+ }
+
+ item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ } else {
+ appWidgetInfo = null;
+ PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item);
+ view.updateIcon(mIconCache);
+ item.hostView = view;
+ item.hostView.updateAppWidget(null);
+ item.hostView.setOnClickListener(this);
+ }
item.hostView.setTag(item);
item.onBindAppWidget(this);
@@ -4044,6 +4538,26 @@ public class Launcher extends Activity
}
}
+ /**
+ * Restores a pending widget.
+ *
+ * @param appWidgetId The app widget id
+ * @param cellInfo The position on screen where to create the widget.
+ */
+ private void completeRestoreAppWidget(final int appWidgetId) {
+ LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
+ if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
+ Log.e(TAG, "Widget update called, when the widget no longer exists.");
+ return;
+ }
+
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
+ info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+
+ mWorkspace.reinflateWidgetsIfNecessary();
+ LauncherModel.updateItemInDatabase(this, info);
+ }
+
public void onPageBoundSynchronously(int page) {
mSynchronouslyBoundPages.add(page);
}
@@ -4071,24 +4585,44 @@ public class Launcher extends Activity
mWorkspace.restoreInstanceStateForRemainingPages();
+ setWorkspaceLoading(false);
+ sendLoadingCompleteBroadcastIfNecessary();
+
// If we received the result of any pending adds while the loader was running (e.g. the
// widget configuration forced an orientation change), process them now.
- for (int i = 0; i < sPendingAddList.size(); i++) {
- completeAdd(sPendingAddList.get(i));
- }
- sPendingAddList.clear();
+ if (sPendingAddItem != null) {
+ final long screenId = completeAdd(sPendingAddItem);
- // Update the market app icon as necessary (the other icons will be managed in response to
- // package changes in bindSearchablesChanged()
- if (!DISABLE_MARKET_BUTTON) {
- updateAppMarketIcon();
+ // TODO: this moves the user to the page where the pending item was added. Ideally,
+ // the screen would be guaranteed to exist after bind, and the page would be set through
+ // the workspace restore process.
+ mWorkspace.post(new Runnable() {
+ @Override
+ public void run() {
+ mWorkspace.snapToScreenId(screenId);
+ }
+ });
+ sPendingAddItem = null;
}
- mWorkspaceLoading = false;
if (upgradePath) {
mWorkspace.getUniqueComponents(true, null);
mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
}
+ PackageInstallerCompat.getInstance(this).onFinishBind();
+ mModel.recheckRestoredItems(this);
+ }
+
+ private void sendLoadingCompleteBroadcastIfNecessary() {
+ if (!mSharedPrefs.getBoolean(FIRST_LOAD_COMPLETE, false)) {
+ String permission =
+ getResources().getString(R.string.receive_first_load_broadcast_permission);
+ Intent intent = new Intent(ACTION_FIRST_LOAD_COMPLETE);
+ sendBroadcast(intent, permission);
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ editor.putBoolean(FIRST_LOAD_COMPLETE, true);
+ editor.apply();
+ }
}
public boolean isAllAppsButtonRank(int rank) {
@@ -4176,7 +4710,7 @@ public class Launcher extends Activity
}
if (mWorkspace != null) {
- mWorkspace.updateShortcuts(apps);
+ mWorkspace.updateShortcutsAndWidgets(apps);
}
if (!LauncherAppState.isDisableAllApps() &&
@@ -4186,6 +4720,48 @@ public class Launcher extends Activity
}
/**
+ * Packages were restored
+ */
+ public void bindAppsRestored(final ArrayList<AppInfo> apps) {
+ Runnable r = new Runnable() {
+ public void run() {
+ bindAppsRestored(apps);
+ }
+ };
+ if (waitUntilResume(r)) {
+ return;
+ }
+
+ if (mWorkspace != null) {
+ mWorkspace.updateShortcutsAndWidgets(apps);
+ }
+ }
+
+ /**
+ * Update the state of a package, typically related to install state.
+ *
+ * Implementation of the method from LauncherModel.Callbacks.
+ */
+ @Override
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfo) {
+ if (mWorkspace != null) {
+ mWorkspace.updatePackageState(installInfo);
+ }
+ }
+
+ /**
+ * Update the label and icon of all the icons in a package
+ *
+ * Implementation of the method from LauncherModel.Callbacks.
+ */
+ @Override
+ public void updatePackageBadge(String packageName) {
+ if (mWorkspace != null) {
+ mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle());
+ }
+ }
+
+ /**
* A package was uninstalled. We take both the super set of packageNames
* in addition to specific applications to remove, the reason being that
* this can be called when a package is updated as well. In that scenario,
@@ -4195,10 +4771,10 @@ public class Launcher extends Activity
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindComponentsRemoved(final ArrayList<String> packageNames,
- final ArrayList<AppInfo> appInfos) {
+ final ArrayList<AppInfo> appInfos, final UserHandleCompat user) {
Runnable r = new Runnable() {
public void run() {
- bindComponentsRemoved(packageNames, appInfos);
+ bindComponentsRemoved(packageNames, appInfos, user);
}
};
if (waitUntilResume(r)) {
@@ -4206,10 +4782,10 @@ public class Launcher extends Activity
}
if (!packageNames.isEmpty()) {
- mWorkspace.removeItemsByPackageName(packageNames);
+ mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!appInfos.isEmpty()) {
- mWorkspace.removeItemsByApplicationInfo(appInfos);
+ mWorkspace.removeItemsByApplicationInfo(appInfos, user);
}
// Notify the drag controller
@@ -4307,7 +4883,7 @@ public class Launcher extends Activity
* @param hint the hint to be displayed in the search bar.
*/
protected void onSearchBarHintChanged(String hint) {
- mLauncherClings.updateSearchBarHint(hint);
+
}
protected boolean isLauncherPreinstalled() {
@@ -4325,6 +4901,17 @@ public class Launcher extends Activity
}
}
+ /**
+ * This method indicates whether or not we should suggest default wallpaper dimensions
+ * when our wallpaper cropper was not yet used to set a wallpaper.
+ */
+ protected boolean overrideWallpaperDimensions() {
+ return true;
+ }
+
+ protected boolean shouldClingFocusHotseatApp() {
+ return false;
+ }
protected String getFirstRunClingSearchBarHint() {
return "";
}
@@ -4347,23 +4934,19 @@ public class Launcher extends Activity
return "";
}
- public void dismissFirstRunCling(View v) {
- mLauncherClings.dismissFirstRunCling(v);
- }
- public void dismissMigrationClingCopyApps(View v) {
- mLauncherClings.dismissMigrationClingCopyApps(v);
- }
- public void dismissMigrationClingUseDefault(View v) {
- mLauncherClings.dismissMigrationClingUseDefault(v);
- }
- public void dismissMigrationWorkspaceCling(View v) {
- mLauncherClings.dismissMigrationWorkspaceCling(v);
- }
- public void dismissWorkspaceCling(View v) {
- mLauncherClings.dismissWorkspaceCling(v);
+ /**
+ * To be overridden by subclasses to indicate that there is an activity to launch
+ * before showing the standard launcher experience.
+ */
+ protected boolean hasFirstRunActivity() {
+ return false;
}
- public void dismissFolderCling(View v) {
- mLauncherClings.dismissFolderCling(v);
+
+ /**
+ * To be overridden by subclasses to launch any first run activity
+ */
+ protected Intent getFirstRunActivity() {
+ return null;
}
private boolean shouldRunFirstRunActivity() {
@@ -4371,15 +4954,21 @@ public class Launcher extends Activity
!mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
}
- public void showFirstRunActivity() {
+ protected boolean hasRunFirstRunActivity() {
+ return mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
+ }
+
+ public boolean showFirstRunActivity() {
if (shouldRunFirstRunActivity() &&
hasFirstRunActivity()) {
Intent firstRunIntent = getFirstRunActivity();
if (firstRunIntent != null) {
startActivity(firstRunIntent);
markFirstRunActivityShown();
+ return true;
}
}
+ return false;
}
private void markFirstRunActivityShown() {
@@ -4388,6 +4977,77 @@ public class Launcher extends Activity
editor.apply();
}
+ /**
+ * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
+ * screen that must be displayed and dismissed.
+ */
+ protected boolean hasDismissableIntroScreen() {
+ return false;
+ }
+
+ /**
+ * Full screen intro screen to be shown and dismissed before the launcher can be used.
+ */
+ protected View getIntroScreen() {
+ return null;
+ }
+
+ /**
+ * To be overriden by subclasses to indicate whether the in-activity intro screen has been
+ * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
+ */
+ private boolean shouldShowIntroScreen() {
+ return hasDismissableIntroScreen() &&
+ !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
+ }
+
+ protected void showIntroScreen() {
+ View introScreen = getIntroScreen();
+ changeWallpaperVisiblity(false);
+ if (introScreen != null) {
+ mDragLayer.showOverlayView(introScreen);
+ }
+ }
+
+ public void dismissIntroScreen() {
+ markIntroScreenDismissed();
+ if (showFirstRunActivity()) {
+ // We delay hiding the intro view until the first run activity is showing. This
+ // avoids a blip.
+ mWorkspace.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mDragLayer.dismissOverlayView();
+ showFirstRunClings();
+ }
+ }, ACTIVITY_START_DELAY);
+ } else {
+ mDragLayer.dismissOverlayView();
+ showFirstRunClings();
+ }
+ changeWallpaperVisiblity(true);
+ }
+
+ private void markIntroScreenDismissed() {
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ editor.putBoolean(INTRO_SCREEN_DISMISSED, true);
+ editor.apply();
+ }
+
+ private void showFirstRunClings() {
+ // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
+ // on the device, then we always show the first run cling experience (or if there is no
+ // launcher2). Otherwise, we prompt the user upon started for migration
+ LauncherClings launcherClings = new LauncherClings(this);
+ if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
+ if (mModel.canMigrateFromOldLauncherDb(this)) {
+ launcherClings.showMigrationCling();
+ } else {
+ launcherClings.showLongPressCling(true);
+ }
+ }
+ }
+
void showWorkspaceSearchAndHotseat() {
if (mWorkspace != null) mWorkspace.setAlpha(1f);
if (mHotseat != null) mHotseat.setAlpha(1f);
@@ -4402,24 +5062,44 @@ public class Launcher extends Activity
if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
}
-
public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
- ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0);
- if (ri == null) {
+ // Called from search suggestion, not supported in other profiles.
+ final UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+ LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(appLaunchIntent,
+ myUser);
+ if (activityInfo == null) {
return null;
}
- return new AppInfo(getPackageManager(), ri, mIconCache, null);
+ return new AppInfo(this, activityInfo, myUser, mIconCache, null);
}
public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
Bitmap icon) {
- return new ShortcutInfo(shortcutIntent, caption, icon);
+ // Called from search suggestion, not supported in other profiles.
+ return createShortcutDragInfo(shortcutIntent, caption, icon,
+ UserHandleCompat.myUserHandle());
+ }
+
+ public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
+ Bitmap icon, UserHandleCompat user) {
+ UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+ CharSequence contentDescription = userManager.getBadgedLabelForUser(caption, user);
+ return new ShortcutInfo(shortcutIntent, caption, contentDescription, icon, user);
+ }
+
+ protected void moveWorkspaceToDefaultScreen() {
+ mWorkspace.moveToDefaultScreen(false);
}
public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
dragView.setTag(dragInfo);
- mWorkspace.onDragStartedWithItem(dragView);
- mWorkspace.beginDragShared(dragView, source);
+ mWorkspace.onExternalDragStartedWithItem(dragView);
+ mWorkspace.beginExternalDragShared(dragView, source);
+ }
+
+ @Override
+ public void onPageSwitch(View newPage, int newPageIndex) {
}
/**
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index e6c220b2a..be295f8b3 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -22,6 +22,7 @@ import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.view.View;
+import android.view.ViewAnimationUtils;
import android.view.ViewTreeObserver;
import java.util.HashSet;
@@ -126,4 +127,14 @@ public class LauncherAnimUtils {
new FirstFrameAnimatorHelper(anim, view);
return anim;
}
+
+ public static Animator createCircularReveal(View view, int centerX,
+ int centerY, float startRadius, float endRadius) {
+ Animator anim = ViewAnimationUtils.createCircularReveal(view, centerX,
+ centerY, startRadius, endRadius);
+ if (anim instanceof ValueAnimator) {
+ new FirstFrameAnimatorHelper((ValueAnimator) anim, view);
+ }
+ return anim;
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 29e18f9c0..246278fa2 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,19 +17,29 @@
package com.android.launcher3;
import android.app.SearchManager;
-import android.content.*;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.util.Log;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
private static final String TAG = "LauncherAppState";
private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
+ private static final boolean DEBUG = false;
+
private final AppFilter mAppFilter;
private final BuildInfo mBuildInfo;
private LauncherModel mModel;
@@ -90,16 +100,11 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
mAppFilter = AppFilter.loadByName(sContext.getString(R.string.app_filter_class));
mBuildInfo = BuildInfo.loadByName(sContext.getString(R.string.build_info_class));
mModel = new LauncherModel(this, mIconCache, mAppFilter);
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext);
+ launcherApps.addOnAppsChangedCallback(mModel);
// Register intent receivers
- IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
- filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- filter.addDataScheme("package");
- sContext.registerReceiver(mModel, filter);
- filter = new IntentFilter();
- filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
- filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
sContext.registerReceiver(mModel, filter);
@@ -115,7 +120,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
resolver.registerContentObserver(LauncherSettings.Favorites.CONTENT_URI, true,
mFavoritesObserver);
}
-
+
public void recreateWidgetPreviewDb() {
if (mWidgetPreviewCacheDb != null) {
mWidgetPreviewCacheDb.close();
@@ -128,6 +133,8 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
*/
public void onTerminate() {
sContext.unregisterReceiver(mModel);
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(sContext);
+ launcherApps.removeOnAppsChangedCallback(mModel);
ContentResolver resolver = sContext.getContentResolver();
resolver.unregisterContentObserver(mFavoritesObserver);
@@ -154,7 +161,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
return mModel;
}
- IconCache getIconCache() {
+ public IconCache getIconCache() {
return mIconCache;
}
@@ -249,4 +256,15 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
public static boolean isDogfoodBuild() {
return getInstance().mBuildInfo.isDogfoodBuild();
}
+
+ public void setPackageState(ArrayList<PackageInstallInfo> installInfo) {
+ mModel.setPackageState(installInfo);
+ }
+
+ /**
+ * Updates the icons and label of all icons for the provided package name.
+ */
+ public void updatePackageBadge(String packageName) {
+ mModel.updatePackageBadge(packageName);
+ }
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index 7b08d4403..a309f268c 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -20,6 +20,9 @@ import android.appwidget.AppWidgetHost;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.Context;
+import android.os.TransactionTooLargeException;
+
+import java.util.ArrayList;
/**
* Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
@@ -28,6 +31,8 @@ import android.content.Context;
*/
public class LauncherAppWidgetHost extends AppWidgetHost {
+ private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
+
Launcher mLauncher;
public LauncherAppWidgetHost(Launcher launcher, int hostId) {
@@ -42,14 +47,42 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
}
@Override
+ public void startListening() {
+ try {
+ super.startListening();
+ } catch (Exception e) {
+ if (e.getCause() instanceof TransactionTooLargeException) {
+ // We're willing to let this slide. The exception is being caused by the list of
+ // RemoteViews which is being passed back. The startListening relationship will
+ // have been established by this point, and we will end up populating the
+ // widgets upon bind anyway. See issue 14255011 for more context.
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+ @Override
public void stopListening() {
super.stopListening();
clearViews();
}
+ public void addProviderChangeListener(Runnable callback) {
+ mProviderChangeListeners.add(callback);
+ }
+
+ public void removeProviderChangeListener(Runnable callback) {
+ mProviderChangeListeners.remove(callback);
+ }
+
protected void onProvidersChanged() {
// Once we get the message that widget packages are updated, we need to rebind items
// in AppsCustomize accordingly.
mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher));
+
+ for (Runnable callback : mProviderChangeListeners) {
+ callback.run();
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index 51a649a07..e39727b17 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -21,6 +21,7 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.RemoteViews;
@@ -30,12 +31,16 @@ import com.android.launcher3.DragLayer.TouchCompleteListener;
* {@inheritDoc}
*/
public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
+
+ LayoutInflater mInflater;
+
private CheckLongPressHelper mLongPressHelper;
- private LayoutInflater mInflater;
private Context mContext;
private int mPreviousOrientation;
private DragLayer mDragLayer;
+ private float mSlop;
+
public LauncherAppWidgetHostView(Context context) {
super(context);
mContext = context;
@@ -56,7 +61,8 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
super.updateAppWidget(remoteViews);
}
- public boolean orientationChangedSincedInflation() {
+ public boolean isReinflateRequired() {
+ // Re-inflate is required if the orientation has changed since last inflated.
int orientation = mContext.getResources().getConfiguration().orientation;
if (mPreviousOrientation != orientation) {
return true;
@@ -90,6 +96,11 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
case MotionEvent.ACTION_CANCEL:
mLongPressHelper.cancelLongPress();
break;
+ case MotionEvent.ACTION_MOVE:
+ if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
+ mLongPressHelper.cancelLongPress();
+ }
+ break;
}
// Otherwise continue letting touch events fall through to children
@@ -104,11 +115,22 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
case MotionEvent.ACTION_CANCEL:
mLongPressHelper.cancelLongPress();
break;
+ case MotionEvent.ACTION_MOVE:
+ if (!Utilities.pointInView(this, ev.getX(), ev.getY(), mSlop)) {
+ mLongPressHelper.cancelLongPress();
+ }
+ break;
}
return false;
}
@Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+ }
+
+ @Override
public void cancelLongPress() {
super.cancelLongPress();
mLongPressHelper.cancelLongPress();
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index 28df90fb0..5c6535a24 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -19,11 +19,36 @@ package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.content.ComponentName;
import android.content.ContentValues;
+import android.content.Context;
+
+import com.android.launcher3.compat.UserHandleCompat;
/**
* Represents a widget (either instantiated or about to be) in the Launcher.
*/
-class LauncherAppWidgetInfo extends ItemInfo {
+public class LauncherAppWidgetInfo extends ItemInfo {
+
+ public static final int RESTORE_COMPLETED = 0;
+
+ /**
+ * This is set during the package backup creation.
+ */
+ public static final int FLAG_ID_NOT_VALID = 1;
+
+ /**
+ * Indicates that the provider is not available yet.
+ */
+ public static final int FLAG_PROVIDER_NOT_READY = 2;
+
+ /**
+ * Indicates that the widget UI is not yet ready, and user needs to set it up again.
+ */
+ public static final int FLAG_UI_NOT_READY = 4;
+
+ /**
+ * Indicates that the widget restore has started.
+ */
+ public static final int FLAG_RESTORE_STARTED = 8;
/**
* Indicates that the widget hasn't been instantiated yet.
@@ -42,6 +67,16 @@ class LauncherAppWidgetInfo extends ItemInfo {
int minWidth = -1;
int minHeight = -1;
+ /**
+ * Indicates the restore status of the widget.
+ */
+ int restoreStatus;
+
+ /**
+ * Indicates the installation progress of the widget provider
+ */
+ int installProgress = -1;
+
private boolean mHasNotifiedInitialWidgetSizeChanged;
/**
@@ -59,13 +94,17 @@ class LauncherAppWidgetInfo extends ItemInfo {
// to indicate that they should be calculated based on the layout and minWidth/minHeight
spanX = -1;
spanY = -1;
+ // We only support app widgets on current user.
+ user = UserHandleCompat.myUserHandle();
+ restoreStatus = RESTORE_COMPLETED;
}
@Override
- void onAddToDatabase(ContentValues values) {
- super.onAddToDatabase(values);
+ void onAddToDatabase(Context context, ContentValues values) {
+ super.onAddToDatabase(context, values);
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
+ values.put(LauncherSettings.Favorites.RESTORED, restoreStatus);
}
/**
@@ -96,4 +135,12 @@ class LauncherAppWidgetInfo extends ItemInfo {
super.unbind();
hostView = null;
}
+
+ public final boolean isWidgetIdValid() {
+ return (restoreStatus & FLAG_ID_NOT_VALID) == 0;
+ }
+
+ public final boolean hasRestoreFlag(int flag) {
+ return (restoreStatus & flag) == flag;
+ }
}
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
index de6aedddd..c20c6939d 100644
--- a/src/com/android/launcher3/LauncherBackupAgentHelper.java
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -17,13 +17,16 @@
package com.android.launcher3;
import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupDataInput;
import android.app.backup.BackupManager;
-import android.app.backup.SharedPreferencesBackupHelper;
import android.content.Context;
-import android.content.SharedPreferences;
+import android.database.Cursor;
+import android.os.ParcelFileDescriptor;
import android.provider.Settings;
import android.util.Log;
+import java.io.IOException;
+
public class LauncherBackupAgentHelper extends BackupAgentHelper {
private static final String TAG = "LauncherBackupAgentHelper";
@@ -54,14 +57,14 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
// modifies the file outside the normal codepaths, so it looks like another
// process. This forces a reload of the file, in case this process persists.
String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
+ getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
super.onDestroy();
}
@Override
public void onCreate() {
boolean restoreEnabled = 0 != Settings.Secure.getInt(
- getContentResolver(), SETTING_RESTORE_ENABLED, 0);
+ getContentResolver(), SETTING_RESTORE_ENABLED, 1);
if (VERBOSE) Log.v(TAG, "restore is " + (restoreEnabled ? "enabled" : "disabled"));
addHelper(LauncherBackupHelper.LAUNCHER_PREFS_PREFIX,
@@ -71,4 +74,21 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper {
addHelper(LauncherBackupHelper.LAUNCHER_PREFIX,
new LauncherBackupHelper(this, restoreEnabled));
}
+
+ @Override
+ public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+ throws IOException {
+ super.onRestore(data, appVersionCode, newState);
+
+ // If no favorite was migrated, clear the data and start fresh.
+ final Cursor c = getContentResolver().query(
+ LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION, null, null, null, null);
+ boolean hasData = c.moveToNext();
+ c.close();
+
+ if (!hasData) {
+ if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB");
+ LauncherAppState.getLauncherProvider().createEmptyDB();
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index 62e6f3102..201f3e9bb 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -28,6 +28,8 @@ import com.android.launcher3.backup.BackupProtos.Key;
import com.android.launcher3.backup.BackupProtos.Resource;
import com.android.launcher3.backup.BackupProtos.Screen;
import com.android.launcher3.backup.BackupProtos.Widget;
+import com.android.launcher3.compat.UserManagerCompat;
+import com.android.launcher3.compat.UserHandleCompat;
import android.app.backup.BackupDataInputStream;
import android.app.backup.BackupDataOutput;
@@ -108,6 +110,7 @@ public class LauncherBackupHelper implements BackupHelper {
Favorites.SPANX, // 14
Favorites.SPANY, // 15
Favorites.TITLE, // 16
+ Favorites.PROFILE_ID, // 17
};
private static final int ID_INDEX = 0;
@@ -127,6 +130,7 @@ public class LauncherBackupHelper implements BackupHelper {
private static final int SPANX_INDEX = 14;
private static final int SPANY_INDEX = 15;
private static final int TITLE_INDEX = 16;
+ private static final int PROFILE_ID_INDEX = 17;
private static final String[] SCREEN_PROJECTION = {
WorkspaceScreens._ID, // 0
@@ -144,11 +148,12 @@ public class LauncherBackupHelper implements BackupHelper {
private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap;
- private ArrayList<Key> mKeys;
+ private final ArrayList<Key> mKeys;
public LauncherBackupHelper(Context context, boolean restoreEnabled) {
mContext = context;
mRestoreEnabled = restoreEnabled;
+ mKeys = new ArrayList<Key>();
}
private void dataChanged() {
@@ -214,9 +219,6 @@ public class LauncherBackupHelper implements BackupHelper {
@Override
public void restoreEntity(BackupDataInputStream data) {
if (VERBOSE) Log.v(TAG, "restoreEntity");
- if (mKeys == null) {
- mKeys = new ArrayList<Key>();
- }
byte[] buffer = new byte[512];
String backupKey = data.getKey();
int dataSize = data.size();
@@ -297,8 +299,9 @@ public class LauncherBackupHelper implements BackupHelper {
// persist things that have changed since the last backup
ContentResolver cr = mContext.getContentResolver();
+ // Don't backup apps in other profiles for now.
Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
- null, null, null);
+ getUserSelectionArg(), null, null);
Set<String> currentIds = new HashSet<String>(cursor.getCount());
try {
cursor.moveToPosition(-1);
@@ -349,7 +352,7 @@ public class LauncherBackupHelper implements BackupHelper {
try {
ContentResolver cr = mContext.getContentResolver();
ContentValues values = unpackFavorite(buffer, 0, dataSize);
- cr.insert(Favorites.CONTENT_URI, values);
+ cr.insert(Favorites.CONTENT_URI_NO_NOTIFICATION, values);
} catch (InvalidProtocolBufferNanoException e) {
Log.e(TAG, "failed to decode favorite", e);
}
@@ -454,14 +457,19 @@ public class LauncherBackupHelper implements BackupHelper {
}
final ContentResolver cr = mContext.getContentResolver();
final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
+ final UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
// read the old ID set
Set<String> savedIds = getSavedIdsByType(Key.ICON, in);
if (DEBUG) Log.d(TAG, "icon savedIds.size()=" + savedIds.size());
+ // Don't backup apps in other profiles for now.
int startRows = out.rows;
if (DEBUG) Log.d(TAG, "starting here: " + startRows);
- String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION;
+
+ String where = "(" + Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPLICATION + " OR " +
+ Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_SHORTCUT + ") AND " +
+ getUserSelectionArg();
Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
where, null, null);
Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -491,9 +499,9 @@ public class LauncherBackupHelper implements BackupHelper {
if (DEBUG) Log.d(TAG, "I can count this high: " + out.rows);
if ((out.rows - startRows) < MAX_ICONS_PER_PASS) {
if (VERBOSE) Log.v(TAG, "saving icon " + backupKey);
- Bitmap icon = mIconCache.getIcon(intent);
+ Bitmap icon = mIconCache.getIcon(intent, myUserHandle);
keys.add(key);
- if (icon != null && !mIconCache.isDefaultIcon(icon)) {
+ if (icon != null && !mIconCache.isDefaultIcon(icon, myUserHandle)) {
byte[] blob = packIcon(dpi, icon);
writeRowToBackup(key, blob, out, data);
}
@@ -556,6 +564,7 @@ public class LauncherBackupHelper implements BackupHelper {
}
return;
} else {
+ if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name);
IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(key.name),
icon, res.dpi);
}
@@ -595,7 +604,8 @@ public class LauncherBackupHelper implements BackupHelper {
int startRows = out.rows;
if (DEBUG) Log.d(TAG, "starting here: " + startRows);
- String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET;
+ String where = Favorites.ITEM_TYPE + "=" + Favorites.ITEM_TYPE_APPWIDGET + " AND "
+ + getUserSelectionArg();
Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
where, null, null);
Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -670,6 +680,9 @@ public class LauncherBackupHelper implements BackupHelper {
.decodeByteArray(widget.icon.data, 0, widget.icon.data.length);
if (icon == null) {
Log.w(TAG, "failed to unpack widget icon for " + key.name);
+ } else {
+ IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider),
+ icon, widget.icon.dpi);
}
}
@@ -798,9 +811,15 @@ public class LauncherBackupHelper implements BackupHelper {
if (!TextUtils.isEmpty(title)) {
favorite.title = title;
}
- String intent = c.getString(INTENT_INDEX);
- if (!TextUtils.isEmpty(intent)) {
- favorite.intent = intent;
+ String intentDescription = c.getString(INTENT_INDEX);
+ if (!TextUtils.isEmpty(intentDescription)) {
+ try {
+ Intent intent = Intent.parseUri(intentDescription, 0);
+ intent.removeExtra(ItemInfo.EXTRA_PROFILE);
+ favorite.intent = intent.toUri(0);
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Invalid intent", e);
+ }
}
favorite.itemType = c.getInt(ITEM_TYPE_INDEX);
if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
@@ -846,16 +865,26 @@ public class LauncherBackupHelper implements BackupHelper {
values.put(Favorites.INTENT, favorite.intent);
}
values.put(Favorites.ITEM_TYPE, favorite.itemType);
+
+ UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
+ long userSerialNumber =
+ UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
+ values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
+
if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
if (!TextUtils.isEmpty(favorite.appWidgetProvider)) {
values.put(Favorites.APPWIDGET_PROVIDER, favorite.appWidgetProvider);
}
values.put(Favorites.APPWIDGET_ID, favorite.appWidgetId);
+ values.put(LauncherSettings.Favorites.RESTORED,
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
+ LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
+ } else {
+ // Let LauncherModel know we've been here.
+ values.put(LauncherSettings.Favorites.RESTORED, 1);
}
- // Let LauncherModel know we've been here.
- values.put(LauncherSettings.Favorites.RESTORED, 1);
-
return values;
}
@@ -1151,6 +1180,11 @@ public class LauncherBackupHelper implements BackupHelper {
return true;
}
+ private String getUserSelectionArg() {
+ return Favorites.PROFILE_ID + '=' + UserManagerCompat.getInstance(mContext)
+ .getSerialNumberForUser(UserHandleCompat.myUserHandle());
+ }
+
private class KeyParsingException extends Throwable {
private KeyParsingException(Throwable cause) {
super(cause);
diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java
index 952edfd06..458d81f61 100644
--- a/src/com/android/launcher3/LauncherClings.java
+++ b/src/com/android/launcher3/LauncherClings.java
@@ -18,324 +18,186 @@ package com.android.launcher3;
import android.accounts.Account;
import android.accounts.AccountManager;
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.app.ActivityManager;
import android.content.Context;
import android.content.SharedPreferences;
-import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.UserManager;
+import android.provider.Settings;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
+import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.accessibility.AccessibilityManager;
-import android.widget.TextView;
-class LauncherClings {
- private static final String FIRST_RUN_CLING_DISMISSED_KEY = "cling_gel.first_run.dismissed";
+class LauncherClings implements OnClickListener {
private static final String MIGRATION_CLING_DISMISSED_KEY = "cling_gel.migration.dismissed";
- private static final String MIGRATION_WORKSPACE_CLING_DISMISSED_KEY =
- "cling_gel.migration_workspace.dismissed";
private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed";
- private static final String FOLDER_CLING_DISMISSED_KEY = "cling_gel.folder.dismissed";
+
+ private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides";
private static final boolean DISABLE_CLINGS = false;
private static final int SHOW_CLING_DURATION = 250;
private static final int DISMISS_CLING_DURATION = 200;
+ // New Secure Setting in L
+ private static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
+
private Launcher mLauncher;
private LayoutInflater mInflater;
- private HideFromAccessibilityHelper mHideFromAccessibilityHelper
- = new HideFromAccessibilityHelper();
/** Ctor */
public LauncherClings(Launcher launcher) {
mLauncher = launcher;
- mInflater = mLauncher.getLayoutInflater();
+ mInflater = LayoutInflater.from(new
+ ContextThemeWrapper(mLauncher, android.R.style.Theme_DeviceDefault));
}
- /** Initializes a cling */
- private Cling initCling(int clingId, int scrimId, boolean animate,
- boolean dimNavBarVisibilty) {
- Cling cling = (Cling) mLauncher.findViewById(clingId);
- View scrim = null;
- if (scrimId > 0) {
- scrim = mLauncher.findViewById(scrimId);
- }
- if (cling != null) {
- cling.init(mLauncher, scrim);
- cling.show(animate, SHOW_CLING_DURATION);
-
- if (dimNavBarVisibilty) {
- cling.setSystemUiVisibility(cling.getSystemUiVisibility() |
- View.SYSTEM_UI_FLAG_LOW_PROFILE);
- }
+ @Override
+ public void onClick(View v) {
+ int id = v.getId();
+ if (id == R.id.cling_dismiss_migration_use_default) {
+ // Disable the migration cling
+ dismissMigrationCling();
+ } else if (id == R.id.cling_dismiss_migration_copy_apps) {
+ // Copy the shortcuts from the old database
+ LauncherModel model = mLauncher.getModel();
+ model.resetLoadedState(false, true);
+ model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
+ LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
+ | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
+ // Set the flag to skip the folder cling
+ String spKey = LauncherAppState.getSharedPreferencesKey();
+ SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = sp.edit();
+ editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
+ editor.apply();
+ // Disable the migration cling
+ dismissMigrationCling();
+ } else if (id == R.id.cling_dismiss_longpress_info) {
+ dismissLongPressCling();
}
- return cling;
}
- /** Returns whether the clings are enabled or should be shown */
- private boolean areClingsEnabled() {
- if (DISABLE_CLINGS) {
- return false;
- }
-
- // disable clings when running in a test harness
- if(ActivityManager.isRunningInTestHarness()) return false;
+ /**
+ * Shows the migration cling.
+ *
+ * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
+ * package was not preinstalled and there exists a db to migrate from.
+ */
+ public void showMigrationCling() {
+ mLauncher.hideWorkspaceSearchAndHotseat();
- // Disable clings for accessibility when explore by touch is enabled
- final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
- Launcher.ACCESSIBILITY_SERVICE);
- if (a11yManager.isTouchExplorationEnabled()) {
- return false;
- }
+ ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
+ View inflated = mInflater.inflate(R.layout.migration_cling, root);
+ inflated.findViewById(R.id.cling_dismiss_migration_copy_apps).setOnClickListener(this);
+ inflated.findViewById(R.id.cling_dismiss_migration_use_default).setOnClickListener(this);
+ }
- // Restricted secondary users (child mode) will potentially have very few apps
- // seeded when they start up for the first time. Clings won't work well with that
- boolean supportsLimitedUsers =
- android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
- Account[] accounts = AccountManager.get(mLauncher).getAccounts();
- if (supportsLimitedUsers && accounts.length == 0) {
- UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
- Bundle restrictions = um.getUserRestrictions();
- if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
- return false;
+ private void dismissMigrationCling() {
+ mLauncher.showWorkspaceSearchAndHotseat();
+ Runnable dismissCb = new Runnable() {
+ public void run() {
+ Runnable cb = new Runnable() {
+ public void run() {
+ // Show the longpress cling next
+ showLongPressCling(false);
+ }
+ };
+ dismissCling(mLauncher.findViewById(R.id.migration_cling), cb,
+ MIGRATION_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
}
- }
- return true;
+ };
+ mLauncher.getWorkspace().post(dismissCb);
}
- /** Returns whether the folder cling is visible. */
- public boolean isFolderClingVisible() {
- Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
- if (cling != null) {
- return cling.getVisibility() == View.VISIBLE;
- }
- return false;
- }
+ public void showLongPressCling(boolean showWelcome) {
+ ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher);
+ View cling = mInflater.inflate(R.layout.longpress_cling, root, false);
- private boolean skipCustomClingIfNoAccounts() {
- Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
- boolean customCling = cling.getDrawIdentifier().equals("workspace_custom");
- if (customCling) {
- AccountManager am = AccountManager.get(mLauncher);
- if (am == null) return false;
- Account[] accounts = am.getAccountsByType("com.google");
- return accounts.length == 0;
- }
- return false;
- }
+ cling.setOnLongClickListener(new OnLongClickListener() {
- /** Updates the first run cling custom content hint */
- private void setCustomContentHintVisibility(Cling cling, String ccHintStr, boolean visible,
- boolean animate) {
- final TextView ccHint = (TextView) cling.findViewById(R.id.custom_content_hint);
- if (ccHint != null) {
- if (visible && !ccHintStr.isEmpty()) {
- ccHint.setText(ccHintStr);
- ccHint.setVisibility(View.VISIBLE);
- if (animate) {
- ccHint.setAlpha(0f);
- ccHint.animate().alpha(1f)
- .setDuration(SHOW_CLING_DURATION)
- .start();
- } else {
- ccHint.setAlpha(1f);
- }
- } else {
- if (animate) {
- ccHint.animate().alpha(0f)
- .setDuration(SHOW_CLING_DURATION)
- .setListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- ccHint.setVisibility(View.GONE);
- }
- })
- .start();
- } else {
- ccHint.setAlpha(0f);
- ccHint.setVisibility(View.GONE);
- }
+ @Override
+ public boolean onLongClick(View v) {
+ mLauncher.getWorkspace().enterOverviewMode();
+ dismissLongPressCling();
+ return true;
}
- }
- }
+ });
- /** Updates the first run cling custom content hint */
- public void updateCustomContentHintVisibility() {
- Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
- String ccHintStr = mLauncher.getFirstRunCustomContentHint();
+ final ViewGroup content = (ViewGroup) cling.findViewById(R.id.cling_content);
+ mInflater.inflate(showWelcome ? R.layout.longpress_cling_welcome_content
+ : R.layout.longpress_cling_content, content);
+ content.findViewById(R.id.cling_dismiss_longpress_info).setOnClickListener(this);
- if (mLauncher.getWorkspace().hasCustomContent()) {
- // Show the custom content hint if ccHintStr is not empty
- if (cling != null) {
- setCustomContentHintVisibility(cling, ccHintStr, true, true);
- }
- } else {
- // Hide the custom content hint
- if (cling != null) {
- setCustomContentHintVisibility(cling, ccHintStr, false, true);
- }
+ if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
+ Drawable bg = new BorderCropDrawable(mLauncher.getResources().getDrawable(R.drawable.cling_bg),
+ true, true, true, false);
+ content.setBackground(bg);
}
- }
- /** Updates the first run cling search bar hint. */
- public void updateSearchBarHint(String hint) {
- Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
- if (cling != null && cling.getVisibility() == View.VISIBLE && !hint.isEmpty()) {
- TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
- sbHint.setText(hint);
- sbHint.setVisibility(View.VISIBLE);
- }
- }
+ root.addView(cling);
- public boolean shouldShowFirstRunOrMigrationClings() {
- SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
- return areClingsEnabled() &&
- !sharedPrefs.getBoolean(FIRST_RUN_CLING_DISMISSED_KEY, false) &&
- !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
- }
-
- public void removeFirstRunAndMigrationClings() {
- removeCling(R.id.first_run_cling);
- removeCling(R.id.migration_cling);
- }
-
- /**
- * Shows the first run cling.
- *
- * This flow is mutually exclusive with showMigrationCling, and only runs if this Launcher
- * package was preinstalled or there is no db to migrate from.
- */
- public void showFirstRunCling() {
- if (!skipCustomClingIfNoAccounts()) {
- Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
- if (cling != null) {
- String sbHintStr = mLauncher.getFirstRunClingSearchBarHint();
- String ccHintStr = mLauncher.getFirstRunCustomContentHint();
- if (!sbHintStr.isEmpty()) {
- TextView sbHint = (TextView) cling.findViewById(R.id.search_bar_hint);
- sbHint.setText(sbHintStr);
- sbHint.setVisibility(View.VISIBLE);
- }
- setCustomContentHintVisibility(cling, ccHintStr, true, false);
- }
- initCling(R.id.first_run_cling, 0, false, true);
- } else {
- removeFirstRunAndMigrationClings();
+ if (showWelcome) {
+ // This is the first cling being shown. No need to animate.
+ return;
}
- }
- /**
- * Shows the migration cling.
- *
- * This flow is mutually exclusive with showFirstRunCling, and only runs if this Launcher
- * package was not preinstalled and there exists a db to migrate from.
- */
- public void showMigrationCling() {
- mLauncher.hideWorkspaceSearchAndHotseat();
-
- Cling c = initCling(R.id.migration_cling, 0, false, true);
- c.bringScrimToFront();
- c.bringToFront();
- }
+ // Animate
+ content.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- public void showMigrationWorkspaceCling() {
- // Enable the clings only if they have not been dismissed before
- if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
- MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, false)) {
- Cling c = initCling(R.id.migration_workspace_cling, 0, false, true);
- c.updateMigrationWorkspaceBubblePosition();
- c.bringScrimToFront();
- c.bringToFront();
- } else {
- removeCling(R.id.migration_workspace_cling);
- }
- }
+ @Override
+ public void onGlobalLayout() {
+ content.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- public void showWorkspaceCling() {
- // Enable the clings only if they have not been dismissed before
- if (areClingsEnabled() && !mLauncher.getSharedPrefs().getBoolean(
- WORKSPACE_CLING_DISMISSED_KEY, false)) {
- Cling c = initCling(R.id.workspace_cling, 0, false, true);
- c.updateWorkspaceBubblePosition();
-
- // Set the focused hotseat app if there is one
- c.setFocusedHotseatApp(mLauncher.getFirstRunFocusedHotseatAppDrawableId(),
- mLauncher.getFirstRunFocusedHotseatAppRank(),
- mLauncher.getFirstRunFocusedHotseatAppComponentName(),
- mLauncher.getFirstRunFocusedHotseatAppBubbleTitle(),
- mLauncher.getFirstRunFocusedHotseatAppBubbleDescription());
- } else {
- removeCling(R.id.workspace_cling);
- }
- }
+ ObjectAnimator anim;
+ if (TAG_CROP_TOP_AND_SIDES.equals(content.getTag())) {
+ content.setTranslationY(-content.getMeasuredHeight());
+ anim = LauncherAnimUtils.ofFloat(content, "translationY", 0);
+ } else {
+ content.setScaleX(0);
+ content.setScaleY(0);
+ PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("scaleX", 1);
+ PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("scaleY", 1);
+ anim = LauncherAnimUtils.ofPropertyValuesHolder(content, scaleX, scaleY);
+ }
- public Cling showFoldersCling() {
- SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
- // Enable the clings only if they have not been dismissed before
- if (areClingsEnabled() &&
- !sharedPrefs.getBoolean(FOLDER_CLING_DISMISSED_KEY, false) &&
- !sharedPrefs.getBoolean(Launcher.USER_HAS_MIGRATED, false)) {
- Cling cling = initCling(R.id.folder_cling, R.id.cling_scrim,
- true, true);
- Folder openFolder = mLauncher.getWorkspace().getOpenFolder();
- if (openFolder != null) {
- Rect openFolderRect = new Rect();
- openFolder.getHitRect(openFolderRect);
- cling.setOpenFolderRect(openFolderRect);
- openFolder.bringToFront();
+ anim.setDuration(SHOW_CLING_DURATION);
+ anim.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ anim.start();
}
- return cling;
- } else {
- removeCling(R.id.folder_cling);
- return null;
- }
+ });
}
- public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
- SharedPreferences prefs = ctx.getSharedPreferences(
- LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putBoolean(LauncherClings.FIRST_RUN_CLING_DISMISSED_KEY, true);
- editor.commit();
- }
-
- public void markFolderClingDismissed() {
- SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
- editor.putBoolean(LauncherClings.FOLDER_CLING_DISMISSED_KEY, true);
- editor.apply();
- }
-
- /** Removes the cling outright from the DragLayer */
- private void removeCling(int id) {
- final View cling = mLauncher.findViewById(id);
- if (cling != null) {
- final ViewGroup parent = (ViewGroup) cling.getParent();
- parent.post(new Runnable() {
- @Override
- public void run() {
- parent.removeView(cling);
- }
- });
- mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
- }
+ private void dismissLongPressCling() {
+ Runnable dismissCb = new Runnable() {
+ public void run() {
+ dismissCling(mLauncher.findViewById(R.id.longpress_cling), null,
+ WORKSPACE_CLING_DISMISSED_KEY, DISMISS_CLING_DURATION);
+ }
+ };
+ mLauncher.getWorkspace().post(dismissCb);
}
/** Hides the specified Cling */
- private void dismissCling(final Cling cling, final Runnable postAnimationCb,
- final String flag, int duration, boolean restoreNavBarVisibilty) {
+ private void dismissCling(final View cling, final Runnable postAnimationCb,
+ final String flag, int duration) {
// To catch cases where siblings of top-level views are made invisible, just check whether
// the cling is directly set to GONE before dismissing it.
if (cling != null && cling.getVisibility() != View.GONE) {
final Runnable cleanUpClingCb = new Runnable() {
public void run() {
- cling.cleanup();
- SharedPreferences.Editor editor = mLauncher.getSharedPrefs().edit();
- editor.putBoolean(flag, true);
- editor.apply();
+ cling.setVisibility(View.GONE);
+ mLauncher.getSharedPrefs().edit()
+ .putBoolean(flag, true)
+ .apply();
if (postAnimationCb != null) {
postAnimationCb.run();
}
@@ -344,108 +206,58 @@ class LauncherClings {
if (duration <= 0) {
cleanUpClingCb.run();
} else {
- cling.hide(duration, cleanUpClingCb);
- }
- mHideFromAccessibilityHelper.restoreImportantForAccessibility(mLauncher.getDragLayer());
-
- if (restoreNavBarVisibilty) {
- cling.setSystemUiVisibility(cling.getSystemUiVisibility() &
- ~View.SYSTEM_UI_FLAG_LOW_PROFILE);
+ cling.animate().alpha(0).setDuration(duration).withEndAction(cleanUpClingCb);
}
}
}
- public void dismissFirstRunCling(View v) {
- Cling cling = (Cling) mLauncher.findViewById(R.id.first_run_cling);
- Runnable cb = new Runnable() {
- public void run() {
- // Show the workspace cling next
- showWorkspaceCling();
- }
- };
- dismissCling(cling, cb, FIRST_RUN_CLING_DISMISSED_KEY,
- DISMISS_CLING_DURATION, false);
-
- // Fade out the search bar for the workspace cling coming up
- mLauncher.getSearchBar().hideSearchBar(true);
- }
-
- private void dismissMigrationCling() {
- mLauncher.showWorkspaceSearchAndHotseat();
- Runnable dismissCb = new Runnable() {
- public void run() {
- Cling cling = (Cling) mLauncher.findViewById(R.id.migration_cling);
- Runnable cb = new Runnable() {
- public void run() {
- // Show the migration workspace cling next
- showMigrationWorkspaceCling();
- }
- };
- dismissCling(cling, cb, MIGRATION_CLING_DISMISSED_KEY,
- DISMISS_CLING_DURATION, true);
- }
- };
- mLauncher.getWorkspace().post(dismissCb);
- }
-
- private void dismissAnyWorkspaceCling(Cling cling, String key, View v) {
- Runnable cb = null;
- if (v == null) {
- cb = new Runnable() {
- public void run() {
- mLauncher.getWorkspace().enterOverviewMode();
- }
- };
+ /** Returns whether the clings are enabled or should be shown */
+ private boolean areClingsEnabled() {
+ if (DISABLE_CLINGS) {
+ return false;
}
- dismissCling(cling, cb, key, DISMISS_CLING_DURATION, true);
-
- // Fade in the search bar
- mLauncher.getSearchBar().showSearchBar(true);
- }
-
- public void dismissMigrationClingCopyApps(View v) {
- // Copy the shortcuts from the old database
- LauncherModel model = mLauncher.getModel();
- model.resetLoadedState(false, true);
- model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
- LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
- | LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
-
- // Set the flag to skip the folder cling
- String spKey = LauncherAppState.getSharedPreferencesKey();
- SharedPreferences sp = mLauncher.getSharedPreferences(spKey, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = sp.edit();
- editor.putBoolean(Launcher.USER_HAS_MIGRATED, true);
- editor.apply();
-
- // Disable the migration cling
- dismissMigrationCling();
- }
- public void dismissMigrationClingUseDefault(View v) {
- // Clear the workspace
- LauncherModel model = mLauncher.getModel();
- model.resetLoadedState(false, true);
- model.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
- LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE);
+ // disable clings when running in a test harness
+ if(ActivityManager.isRunningInTestHarness()) return false;
- // Disable the migration cling
- dismissMigrationCling();
- }
+ // Disable clings for accessibility when explore by touch is enabled
+ final AccessibilityManager a11yManager = (AccessibilityManager) mLauncher.getSystemService(
+ Launcher.ACCESSIBILITY_SERVICE);
+ if (a11yManager.isTouchExplorationEnabled()) {
+ return false;
+ }
- public void dismissMigrationWorkspaceCling(View v) {
- Cling cling = (Cling) mLauncher.findViewById(R.id.migration_workspace_cling);
- dismissAnyWorkspaceCling(cling, MIGRATION_WORKSPACE_CLING_DISMISSED_KEY, v);
+ // Restricted secondary users (child mode) will potentially have very few apps
+ // seeded when they start up for the first time. Clings won't work well with that
+ boolean supportsLimitedUsers =
+ android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
+ Account[] accounts = AccountManager.get(mLauncher).getAccounts();
+ if (supportsLimitedUsers && accounts.length == 0) {
+ UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE);
+ Bundle restrictions = um.getUserRestrictions();
+ if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) {
+ return false;
+ }
+ }
+ if (Settings.Secure.getInt(mLauncher.getContentResolver(), SKIP_FIRST_USE_HINTS, 0)
+ == 1) {
+ return false;
+ }
+ return true;
}
- public void dismissWorkspaceCling(View v) {
- Cling cling = (Cling) mLauncher.findViewById(R.id.workspace_cling);
- dismissAnyWorkspaceCling(cling, WORKSPACE_CLING_DISMISSED_KEY, v);
+ public boolean shouldShowFirstRunOrMigrationClings() {
+ SharedPreferences sharedPrefs = mLauncher.getSharedPrefs();
+ return areClingsEnabled() &&
+ !sharedPrefs.getBoolean(WORKSPACE_CLING_DISMISSED_KEY, false) &&
+ !sharedPrefs.getBoolean(MIGRATION_CLING_DISMISSED_KEY, false);
}
- public void dismissFolderCling(View v) {
- Cling cling = (Cling) mLauncher.findViewById(R.id.folder_cling);
- dismissCling(cling, null, FOLDER_CLING_DISMISSED_KEY,
- DISMISS_CLING_DURATION, true);
+ public static void synchonouslyMarkFirstRunClingDismissed(Context ctx) {
+ SharedPreferences prefs = ctx.getSharedPreferences(
+ LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true);
+ editor.commit();
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 007fd7a4a..c64506d80 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -19,12 +19,19 @@ package com.android.launcher3;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProviderInfo;
-import android.content.*;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
import android.content.Intent.ShortcutIconResource;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageInfo;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -44,10 +51,17 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+import com.android.launcher3.compat.LauncherActivityInfoCompat;
+import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
import java.lang.ref.WeakReference;
import java.net.URISyntaxException;
+import java.security.InvalidParameterException;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
@@ -58,6 +72,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -67,14 +82,17 @@ import java.util.concurrent.atomic.AtomicBoolean;
* LauncherModel object held in a static. Also provide APIs for updating the database state
* for the Launcher.
*/
-public class LauncherModel extends BroadcastReceiver {
+public class LauncherModel extends BroadcastReceiver
+ implements LauncherAppsCompat.OnAppsChangedCallbackCompat {
static final boolean DEBUG_LOADERS = false;
+ private static final boolean DEBUG_RECEIVER = false;
+ private static final boolean REMOVE_UNRESTORED_ICONS = true;
+
static final String TAG = "Launcher.Model";
// true = use a "More Apps" folder for non-workspace apps on upgrade
// false = strew non-workspace apps across the workspace on upgrade
public static final boolean UPGRADE_USE_MORE_APPS_FOLDER = false;
-
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;
@@ -97,6 +115,7 @@ public class LauncherModel extends BroadcastReceiver {
private static final int MAIN_THREAD_NORMAL_RUNNABLE = 0;
private static final int MAIN_THREAD_BINDING_RUNNABLE = 1;
+ private static final String MIGRATE_AUTHORITY = "com.android.launcher2.settings";
private static final HandlerThread sWorkerThread = new HandlerThread("launcher-loader");
static {
@@ -149,13 +168,19 @@ public class LauncherModel extends BroadcastReceiver {
// sBgWorkspaceScreens is the ordered set of workspace screens.
static final ArrayList<Long> sBgWorkspaceScreens = new ArrayList<Long>();
+ // sPendingPackages is a set of packages which could be on sdcard and are not available yet
+ static final HashMap<UserHandleCompat, HashSet<String>> sPendingPackages =
+ new HashMap<UserHandleCompat, HashSet<String>>();
+
// </ only access in worker thread >
private IconCache mIconCache;
- private Bitmap mDefaultIcon;
protected int mPreviousConfigMcc;
+ private final LauncherAppsCompat mLauncherApps;
+ private final UserManagerCompat mUserManager;
+
public interface Callbacks {
public boolean setLoadOnResume();
public int getCurrentWorkspaceScreen();
@@ -173,8 +198,11 @@ public class LauncherModel extends BroadcastReceiver {
ArrayList<ItemInfo> addAnimated,
ArrayList<AppInfo> addedApps);
public void bindAppsUpdated(ArrayList<AppInfo> apps);
+ public void bindAppsRestored(ArrayList<AppInfo> apps);
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
+ public void updatePackageBadge(String packageName);
public void bindComponentsRemoved(ArrayList<String> packageNames,
- ArrayList<AppInfo> appInfos);
+ ArrayList<AppInfo> appInfos, UserHandleCompat user);
public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
public void bindSearchablesChanged();
public boolean isAllAppsButtonRank(int rank);
@@ -188,11 +216,26 @@ public class LauncherModel extends BroadcastReceiver {
LauncherModel(LauncherAppState app, IconCache iconCache, AppFilter appFilter) {
Context context = app.getContext();
- ContentResolver contentResolver = context.getContentResolver();
mAppsCanBeOnRemoveableStorage = Environment.isExternalStorageRemovable();
- mOldContentProviderExists = (contentResolver.acquireContentProviderClient(
- LauncherSettings.Favorites.OLD_CONTENT_URI) != null);
+ String oldProvider = context.getString(R.string.old_launcher_provider_uri);
+ // This may be the same as MIGRATE_AUTHORITY, or it may be replaced by a different
+ // resource string.
+ String redirectAuthority = Uri.parse(oldProvider).getAuthority();
+ ProviderInfo providerInfo =
+ context.getPackageManager().resolveContentProvider(MIGRATE_AUTHORITY, 0);
+ ProviderInfo redirectProvider =
+ context.getPackageManager().resolveContentProvider(redirectAuthority, 0);
+
+ Log.d(TAG, "Old launcher provider: " + oldProvider);
+ mOldContentProviderExists = (providerInfo != null) && (redirectProvider != null);
+
+ if (mOldContentProviderExists) {
+ Log.d(TAG, "Old launcher provider exists.");
+ } else {
+ Log.d(TAG, "Old launcher provider does not exist.");
+ }
+
mApp = app;
mBgAllAppsList = new AllAppsList(iconCache, appFilter);
mIconCache = iconCache;
@@ -200,6 +243,8 @@ public class LauncherModel extends BroadcastReceiver {
final Resources res = context.getResources();
Configuration config = res.getConfiguration();
mPreviousConfigMcc = config.mcc;
+ mLauncherApps = LauncherAppsCompat.getInstance(context);
+ mUserManager = UserManagerCompat.getInstance(context);
}
/** Runs the specified runnable immediately if called from the main thread, otherwise it is
@@ -292,6 +337,32 @@ public class LauncherModel extends BroadcastReceiver {
return null;
}
+ public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
+ // Process the updated package state
+ Runnable r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks != null) {
+ callbacks.updatePackageState(installInfo);
+ }
+ }
+ };
+ mHandler.post(r);
+ }
+
+ public void updatePackageBadge(final String packageName) {
+ // Process the updated package badge
+ Runnable r = new Runnable() {
+ public void run() {
+ Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks != null) {
+ callbacks.updatePackageBadge(packageName);
+ }
+ }
+ };
+ mHandler.post(r);
+ }
+
public void addAppsToAllApps(final Context ctx, final ArrayList<AppInfo> allAppsApps) {
final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
@@ -306,7 +377,7 @@ public class LauncherModel extends BroadcastReceiver {
Iterator<AppInfo> iter = allAppsApps.iterator();
while (iter.hasNext()) {
ItemInfo a = iter.next();
- if (LauncherModel.appWasRestored(ctx, a.getIntent())) {
+ if (LauncherModel.appWasPromise(ctx, a.getIntent(), a.user)) {
restoredAppsFinal.add((AppInfo) a);
}
}
@@ -322,7 +393,8 @@ public class LauncherModel extends BroadcastReceiver {
for (AppInfo info : restoredAppsFinal) {
final Intent intent = info.getIntent();
if (intent != null) {
- mIconCache.deletePreloadedIcon(intent.getComponent());
+ mIconCache.deletePreloadedIcon(intent.getComponent(),
+ info.user);
}
}
callbacks.bindAppsUpdated(restoredAppsFinal);
@@ -374,7 +446,7 @@ public class LauncherModel extends BroadcastReceiver {
if (LauncherModel.shortcutExists(context, name, launchIntent)) {
// Only InstallShortcutReceiver sends us shortcutInfos, ignore them
if (a instanceof AppInfo &&
- LauncherModel.appWasRestored(context, launchIntent)) {
+ LauncherModel.appWasPromise(context, launchIntent, a.user)) {
restoredAppsFinal.add((AppInfo) a);
}
continue;
@@ -464,15 +536,6 @@ public class LauncherModel extends BroadcastReceiver {
runOnWorkerThread(r);
}
- public Bitmap getFallbackIcon() {
- if (mDefaultIcon == null) {
- final Context context = LauncherAppState.getInstance().getContext();
- mDefaultIcon = Utilities.createIconBitmap(
- mIconCache.getFullResDefaultActivityIcon(), context);
- }
- return Bitmap.createBitmap(mDefaultIcon);
- }
-
public void unbindItemInfosAndClearQueuedBindRunnables() {
if (sWorkerThread.getThreadId() == Process.myTid()) {
throw new RuntimeException("Expected unbindLauncherItemInfos() to be called from the " +
@@ -480,7 +543,9 @@ public class LauncherModel extends BroadcastReceiver {
}
// Clear any deferred bind runnables
- mDeferredBindRunnables.clear();
+ synchronized (mDeferredBindRunnables) {
+ mDeferredBindRunnables.clear();
+ }
// Remove any queued bind runnables
mHandler.cancelAllRunnablesOfType(MAIN_THREAD_BINDING_RUNNABLE);
// Unbind all the workspace items
@@ -796,7 +861,7 @@ public class LauncherModel extends BroadcastReceiver {
*/
static void updateItemInDatabase(Context context, final ItemInfo item) {
final ContentValues values = new ContentValues();
- item.onAddToDatabase(values);
+ item.onAddToDatabase(context, values);
item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
updateItemInDatabaseHelper(context, values, item, "updateItemInDatabase");
}
@@ -807,9 +872,26 @@ public class LauncherModel extends BroadcastReceiver {
*/
static boolean shortcutExists(Context context, String title, Intent intent) {
final ContentResolver cr = context.getContentResolver();
+ final Intent intentWithPkg, intentWithoutPkg;
+
+ if (intent.getComponent() != null) {
+ // If component is not null, an intent with null package will produce
+ // the same result and should also be a match.
+ if (intent.getPackage() != null) {
+ intentWithPkg = intent;
+ intentWithoutPkg = new Intent(intent).setPackage(null);
+ } else {
+ intentWithPkg = new Intent(intent).setPackage(
+ intent.getComponent().getPackageName());
+ intentWithoutPkg = intent;
+ }
+ } else {
+ intentWithPkg = intent;
+ intentWithoutPkg = intent;
+ }
Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
- new String[] { "title", "intent" }, "title=? and intent=?",
- new String[] { title, intent.toUri(0) }, null);
+ new String[] { "title", "intent" }, "title=? and (intent=? or intent=?)",
+ new String[] { title, intentWithPkg.toUri(0), intentWithoutPkg.toUri(0) }, null);
boolean result = false;
try {
result = c.moveToFirst();
@@ -820,27 +902,14 @@ public class LauncherModel extends BroadcastReceiver {
}
/**
- * Returns true if the shortcuts already exists in the database.
- * we identify a shortcut by the component name of the intent.
+ * Returns true if the promise shortcuts with the same package name exists on the workspace.
*/
- static boolean appWasRestored(Context context, Intent intent) {
- final ContentResolver cr = context.getContentResolver();
+ static boolean appWasPromise(Context context, Intent intent, UserHandleCompat user) {
final ComponentName component = intent.getComponent();
if (component == null) {
return false;
}
- String componentName = component.flattenToString();
- final String where = "intent glob \"*component=" + componentName + "*\" and restored = 1";
- Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI,
- new String[]{"intent", "restored"}, where, null, null);
- boolean result = false;
- try {
- result = c.moveToFirst();
- } finally {
- c.close();
- }
- Log.d(TAG, "shortcutWasRestored is " + result + " for " + componentName);
- return result;
+ return !getItemsByPackageName(component.getPackageName(), user).isEmpty();
}
/**
@@ -852,8 +921,10 @@ public class LauncherModel extends BroadcastReceiver {
final ContentResolver cr = context.getContentResolver();
Cursor c = cr.query(LauncherSettings.Favorites.CONTENT_URI, new String[] {
LauncherSettings.Favorites.ITEM_TYPE, LauncherSettings.Favorites.CONTAINER,
- LauncherSettings.Favorites.SCREEN, LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
- LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY }, null, null, null);
+ LauncherSettings.Favorites.SCREEN,
+ LauncherSettings.Favorites.CELLX, LauncherSettings.Favorites.CELLY,
+ LauncherSettings.Favorites.SPANX, LauncherSettings.Favorites.SPANY,
+ LauncherSettings.Favorites.PROFILE_ID }, null, null, null);
final int itemTypeIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.ITEM_TYPE);
final int containerIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CONTAINER);
@@ -862,7 +933,8 @@ public class LauncherModel extends BroadcastReceiver {
final int cellYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.CELLY);
final int spanXIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANX);
final int spanYIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.SPANY);
-
+ final int profileIdIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.PROFILE_ID);
+ UserManagerCompat userManager = UserManagerCompat.getInstance(context);
try {
while (c.moveToNext()) {
ItemInfo item = new ItemInfo();
@@ -873,8 +945,12 @@ public class LauncherModel extends BroadcastReceiver {
item.container = c.getInt(containerIndex);
item.itemType = c.getInt(itemTypeIndex);
item.screenId = c.getInt(screenIndex);
-
- items.add(item);
+ long serialNumber = c.getInt(profileIdIndex);
+ item.user = userManager.getUserForSerialNumber(serialNumber);
+ // Skip if user has been deleted.
+ if (item.user != null) {
+ items.add(item);
+ }
}
} catch (Exception e) {
items.clear();
@@ -947,12 +1023,13 @@ public class LauncherModel extends BroadcastReceiver {
final ContentValues values = new ContentValues();
final ContentResolver cr = context.getContentResolver();
- item.onAddToDatabase(values);
+ item.onAddToDatabase(context, values);
item.id = LauncherAppState.getLauncherProvider().generateNewItemId();
values.put(LauncherSettings.Favorites._ID, item.id);
item.updateValuesWithCoordinates(values, item.cellX, item.cellY);
+ final StackTraceElement[] stackTrace = new Throwable().getStackTrace();
Runnable r = new Runnable() {
public void run() {
cr.insert(notify ? LauncherSettings.Favorites.CONTENT_URI :
@@ -960,7 +1037,7 @@ public class LauncherModel extends BroadcastReceiver {
// Lock on mBgLock *after* the db operation
synchronized (sBgLock) {
- checkItemInfoLocked(item.id, item, null);
+ checkItemInfoLocked(item.id, item, stackTrace);
sBgItemsIdMap.put(item.id, item);
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -999,45 +1076,77 @@ public class LauncherModel extends BroadcastReceiver {
| ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF);
}
+ private static ArrayList<ItemInfo> getItemsByPackageName(
+ final String pn, final UserHandleCompat user) {
+ ItemInfoFilter filter = new ItemInfoFilter() {
+ @Override
+ public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
+ return cn.getPackageName().equals(pn) && info.user.equals(user);
+ }
+ };
+ return filterItemInfos(sBgItemsIdMap.values(), filter);
+ }
+
+ /**
+ * Removes all the items from the database corresponding to the specified package.
+ */
+ static void deletePackageFromDatabase(Context context, final String pn,
+ final UserHandleCompat user) {
+ deleteItemsFromDatabase(context, getItemsByPackageName(pn, user));
+ }
+
/**
* Removes the specified item from the database
* @param context
* @param item
*/
static void deleteItemFromDatabase(Context context, final ItemInfo item) {
+ ArrayList<ItemInfo> items = new ArrayList<ItemInfo>();
+ items.add(item);
+ deleteItemsFromDatabase(context, items);
+ }
+
+ /**
+ * Removes the specified items from the database
+ * @param context
+ * @param item
+ */
+ static void deleteItemsFromDatabase(Context context, final ArrayList<ItemInfo> items) {
final ContentResolver cr = context.getContentResolver();
- final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false);
Runnable r = new Runnable() {
public void run() {
- cr.delete(uriToDelete, null, null);
+ for (ItemInfo item : items) {
+ final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false);
+ cr.delete(uri, null, null);
- // Lock on mBgLock *after* the db operation
- synchronized (sBgLock) {
- switch (item.itemType) {
- case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
- sBgFolders.remove(item.id);
- for (ItemInfo info: sBgItemsIdMap.values()) {
- if (info.container == item.id) {
- // We are deleting a folder which still contains items that
- // think they are contained by that folder.
- String msg = "deleting a folder (" + item + ") which still " +
- "contains items (" + info + ")";
- Log.e(TAG, msg);
+ // Lock on mBgLock *after* the db operation
+ synchronized (sBgLock) {
+ switch (item.itemType) {
+ case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
+ sBgFolders.remove(item.id);
+ for (ItemInfo info: sBgItemsIdMap.values()) {
+ if (info.container == item.id) {
+ // We are deleting a folder which still contains items that
+ // think they are contained by that folder.
+ String msg = "deleting a folder (" + item + ") which still " +
+ "contains items (" + info + ")";
+ Log.e(TAG, msg);
+ }
}
- }
- sBgWorkspaceItems.remove(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
- case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
- sBgWorkspaceItems.remove(item);
- break;
- case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
- sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
- break;
+ sBgWorkspaceItems.remove(item);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
+ case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
+ sBgWorkspaceItems.remove(item);
+ break;
+ case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
+ sBgAppWidgets.remove((LauncherAppWidgetInfo) item);
+ break;
+ }
+ sBgItemsIdMap.remove(item.id);
+ sBgDbIconCache.remove(item);
}
- sBgItemsIdMap.remove(item.id);
- sBgDbIconCache.remove(item);
}
}
};
@@ -1136,74 +1245,67 @@ public class LauncherModel extends BroadcastReceiver {
}
}
- /**
- * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
- * ACTION_PACKAGE_CHANGED.
- */
@Override
- public void onReceive(Context context, Intent intent) {
- if (DEBUG_LOADERS) Log.d(TAG, "onReceive intent=" + intent);
-
- final String action = intent.getAction();
+ public void onPackageChanged(String packageName, UserHandleCompat user) {
+ int op = PackageUpdatedTask.OP_UPDATE;
+ enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+ user));
+ }
- if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
- || Intent.ACTION_PACKAGE_REMOVED.equals(action)
- || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- final String packageName = intent.getData().getSchemeSpecificPart();
- final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+ @Override
+ public void onPackageRemoved(String packageName, UserHandleCompat user) {
+ int op = PackageUpdatedTask.OP_REMOVE;
+ enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+ user));
+ }
- int op = PackageUpdatedTask.OP_NONE;
+ @Override
+ public void onPackageAdded(String packageName, UserHandleCompat user) {
+ int op = PackageUpdatedTask.OP_ADD;
+ enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName },
+ user));
+ }
- if (packageName == null || packageName.length() == 0) {
- // they sent us a bad intent
- return;
+ @Override
+ public void onPackagesAvailable(String[] packageNames, UserHandleCompat user,
+ boolean replacing) {
+ if (!replacing) {
+ enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packageNames,
+ user));
+ if (mAppsCanBeOnRemoveableStorage) {
+ // Only rebind if we support removable storage. It catches the
+ // case where
+ // apps on the external sd card need to be reloaded
+ startLoaderFromBackground();
}
+ } else {
+ // If we are replacing then just update the packages in the list
+ enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
+ packageNames, user));
+ }
+ }
- if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
- op = PackageUpdatedTask.OP_UPDATE;
- } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
- if (!replacing) {
- op = PackageUpdatedTask.OP_REMOVE;
- }
- // else, we are replacing the package, so a PACKAGE_ADDED will be sent
- // later, we will update the package at this time
- } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
- if (!replacing) {
- op = PackageUpdatedTask.OP_ADD;
- } else {
- op = PackageUpdatedTask.OP_UPDATE;
- }
- }
+ @Override
+ public void onPackagesUnavailable(String[] packageNames, UserHandleCompat user,
+ boolean replacing) {
+ if (!replacing) {
+ enqueuePackageUpdated(new PackageUpdatedTask(
+ PackageUpdatedTask.OP_UNAVAILABLE, packageNames,
+ user));
+ }
- if (op != PackageUpdatedTask.OP_NONE) {
- enqueuePackageUpdated(new PackageUpdatedTask(op, new String[] { packageName }));
- }
+ }
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
- final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- if (!replacing) {
- enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_ADD, packages));
- if (mAppsCanBeOnRemoveableStorage) {
- // Only rebind if we support removable storage. It catches the case where
- // apps on the external sd card need to be reloaded
- startLoaderFromBackground();
- }
- } else {
- // If we are replacing then just update the packages in the list
- enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UPDATE,
- packages));
- }
- } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
- final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
- if (!replacing) {
- String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
- enqueuePackageUpdated(new PackageUpdatedTask(
- PackageUpdatedTask.OP_UNAVAILABLE, packages));
- }
- // else, we are replacing the packages, so ignore this event and wait for
- // EXTERNAL_APPLICATIONS_AVAILABLE to update the packages at that time
- } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
+ /**
+ * Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
+ * ACTION_PACKAGE_CHANGED.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG_RECEIVER) Log.d(TAG, "onReceive intent=" + intent);
+
+ final String action = intent.getAction();
+ if (Intent.ACTION_LOCALE_CHANGED.equals(action)) {
// If we have changed locale we need to clear out the labels in all apps/workspace.
forceReload();
} else if (Intent.ACTION_CONFIGURATION_CHANGED.equals(action)) {
@@ -1229,7 +1331,7 @@ public class LauncherModel extends BroadcastReceiver {
}
}
- private void forceReload() {
+ void forceReload() {
resetLoadedState(true, true);
// Do this here because if the launcher activity is running it will be restarted.
@@ -1284,6 +1386,10 @@ public class LauncherModel extends BroadcastReceiver {
return isLaunching;
}
+ public boolean isCurrentCallbacks(Callbacks callbacks) {
+ return (mCallbacks != null && mCallbacks.get() == callbacks);
+ }
+
public void startLoader(boolean isLaunching, int synchronousBindPage) {
startLoader(isLaunching, synchronousBindPage, LOADER_FLAG_NONE);
}
@@ -1296,7 +1402,9 @@ public class LauncherModel extends BroadcastReceiver {
// Clear any deferred bind-runnables from the synchronized load process
// We must do this before any loading/binding is scheduled below.
- mDeferredBindRunnables.clear();
+ synchronized (mDeferredBindRunnables) {
+ mDeferredBindRunnables.clear();
+ }
// Don't bother to start the thread if we know it's not going to do anything
if (mCallbacks != null && mCallbacks.get() != null) {
@@ -1318,10 +1426,15 @@ public class LauncherModel extends BroadcastReceiver {
void bindRemainingSynchronousPages() {
// Post the remaining side pages to be loaded
if (!mDeferredBindRunnables.isEmpty()) {
- for (final Runnable r : mDeferredBindRunnables) {
+ Runnable[] deferredBindRunnables = null;
+ synchronized (mDeferredBindRunnables) {
+ deferredBindRunnables = mDeferredBindRunnables.toArray(
+ new Runnable[mDeferredBindRunnables.size()]);
+ mDeferredBindRunnables.clear();
+ }
+ for (final Runnable r : deferredBindRunnables) {
mHandler.post(r, MAIN_THREAD_BINDING_RUNNABLE);
}
- mDeferredBindRunnables.clear();
}
}
@@ -1630,7 +1743,7 @@ public class LauncherModel extends BroadcastReceiver {
ArrayList<ItemInfo> added = new ArrayList<ItemInfo>();
synchronized (sBgLock) {
for (AppInfo app : mBgAllAppsList.data) {
- tmpInfos = getItemInfoForComponentName(app.componentName);
+ tmpInfos = getItemInfoForComponentName(app.componentName, app.user);
if (tmpInfos.isEmpty()) {
// We are missing an application icon, so add this to the workspace
added.add(app);
@@ -1760,6 +1873,9 @@ public class LauncherModel extends BroadcastReceiver {
final PackageManager manager = context.getPackageManager();
final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
final boolean isSafeMode = manager.isSafeMode();
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ final boolean isSdCardReady = context.registerReceiver(null,
+ new IntentFilter(StartupReceiver.SYSTEM_READY)) != null;
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
@@ -1778,22 +1894,23 @@ public class LauncherModel extends BroadcastReceiver {
} else {
// Make sure the default workspace is loaded
Launcher.addDumpLog(TAG, "loadWorkspace: loading default favorites", false);
- LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary(0);
+ LauncherAppState.getLauncherProvider().loadDefaultFavoritesIfNecessary();
}
- // Check if we need to do any upgrade-path logic
- // (Includes having just imported default favorites)
- boolean loadedOldDb = LauncherAppState.getLauncherProvider().justLoadedOldDb();
+ // This code path is for our old migration code and should no longer be exercised
+ boolean loadedOldDb = false;
// Log to disk
Launcher.addDumpLog(TAG, "11683562 - loadedOldDb: " + loadedOldDb, true);
synchronized (sBgLock) {
clearSBgDataStructures();
+ final HashSet<String> installingPkgs = PackageInstallerCompat
+ .getInstance(mContext).updateAndGetActiveSessionCache();
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final ArrayList<Long> restoredRows = new ArrayList<Long>();
- final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
+ final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION;
if (DEBUG_LOADERS) Log.d(TAG, "loading model from " + contentUri);
final Cursor c = contentResolver.query(contentUri, null, null, null, null);
@@ -1835,6 +1952,8 @@ public class LauncherModel extends BroadcastReceiver {
LauncherSettings.Favorites.SPANY);
final int restoredIndex = c.getColumnIndexOrThrow(
LauncherSettings.Favorites.RESTORED);
+ final int profileIdIndex = c.getColumnIndexOrThrow(
+ LauncherSettings.Favorites.PROFILE_ID);
//final int uriIndex = c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
//final int displayModeIndex = c.getColumnIndexOrThrow(
// LauncherSettings.Favorites.DISPLAY_MODE);
@@ -1845,43 +1964,119 @@ public class LauncherModel extends BroadcastReceiver {
int container;
long id;
Intent intent;
+ UserHandleCompat user;
while (!mStopped && c.moveToNext()) {
AtomicBoolean deleteOnInvalidPlacement = new AtomicBoolean(false);
try {
int itemType = c.getInt(itemTypeIndex);
boolean restored = 0 != c.getInt(restoredIndex);
+ boolean allowMissingTarget = false;
switch (itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
id = c.getLong(idIndex);
intentDescription = c.getString(intentIndex);
+ long serialNumber = c.getInt(profileIdIndex);
+ user = mUserManager.getUserForSerialNumber(serialNumber);
+ int promiseType = c.getInt(restoredIndex);
+ if (user == null) {
+ // User has been deleted remove the item.
+ itemsToRemove.add(id);
+ continue;
+ }
try {
intent = Intent.parseUri(intentDescription, 0);
ComponentName cn = intent.getComponent();
- if (cn != null && !isValidPackageComponent(manager, cn)) {
- if (restored) {
- // might be installed later
- Launcher.addDumpLog(TAG,
- "package not yet restored: " + cn, true);
- } else {
- if (!mAppsCanBeOnRemoveableStorage) {
- // Log the invalid package, and remove it
+ if (cn != null && cn.getPackageName() != null) {
+ boolean validPkg = launcherApps.isPackageEnabledForProfile(
+ cn.getPackageName(), user);
+ boolean validComponent = validPkg &&
+ launcherApps.isActivityEnabledForProfile(cn, user);
+
+ if (validComponent) {
+ if (restored) {
+ // no special handling necessary for this item
+ restoredRows.add(id);
+ restored = false;
+ }
+ } else if (validPkg) {
+ intent = null;
+ if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
+ // We allow auto install apps to have their intent
+ // updated after an install.
+ intent = manager.getLaunchIntentForPackage(
+ cn.getPackageName());
+ if (intent != null) {
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.INTENT,
+ intent.toUri(0));
+ String where = BaseColumns._ID + "= ?";
+ String[] args = {Long.toString(id)};
+ contentResolver.update(contentUri, values, where, args);
+ }
+ }
+
+ if (intent == null) {
+ // The app is installed but the component is no
+ // longer available.
Launcher.addDumpLog(TAG,
- "Invalid package removed: " + cn, true);
+ "Invalid component removed: " + cn, true);
itemsToRemove.add(id);
+ continue;
} else {
- // If apps can be on external storage, then we just
- // leave them for the user to remove (maybe add
- // visual treatment to it)
+ // no special handling necessary for this item
+ restoredRows.add(id);
+ restored = false;
+ }
+ } else if (restored) {
+ // Package is not yet available but might be
+ // installed later.
+ Launcher.addDumpLog(TAG,
+ "package not yet restored: " + cn, true);
+
+ if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) {
+ // Restore has started once.
+ } else if (installingPkgs.contains(cn.getPackageName())) {
+ // App restore has started. Update the flag
+ promiseType |= ShortcutInfo.FLAG_RESTORE_STARTED;
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.RESTORED,
+ promiseType);
+ String where = BaseColumns._ID + "= ?";
+ String[] args = {Long.toString(id)};
+ contentResolver.update(contentUri, values, where, args);
+
+ } else if (REMOVE_UNRESTORED_ICONS) {
Launcher.addDumpLog(TAG,
- "Invalid package found: " + cn, true);
+ "Unrestored package removed: " + cn, true);
+ itemsToRemove.add(id);
+ continue;
}
+ } else if (isSdCardReady) {
+ // Do not wait for external media load anymore.
+ // Log the invalid package, and remove it
+ Launcher.addDumpLog(TAG,
+ "Invalid package removed: " + cn, true);
+ itemsToRemove.add(id);
continue;
+ } else {
+ // SdCard is not ready yet. Package might get available,
+ // once it is ready.
+ Launcher.addDumpLog(TAG, "Invalid package: " + cn
+ + " (check again later)", true);
+ HashSet<String> pkgs = sPendingPackages.get(user);
+ if (pkgs == null) {
+ pkgs = new HashSet<String>();
+ sPendingPackages.put(user, pkgs);
+ }
+ pkgs.add(cn.getPackageName());
+ allowMissingTarget = true;
+ // Add the icon on the workspace anyway.
}
- } else if (restored) {
- // no special handling necessary for this restored item
+ } else if (cn == null) {
+ // For shortcuts with no component, keep them as they are
restoredRows.add(id);
restored = false;
}
@@ -1892,15 +2087,21 @@ public class LauncherModel extends BroadcastReceiver {
}
if (restored) {
- Launcher.addDumpLog(TAG,
- "constructing info for partially restored package",
- true);
- info = getRestoredItemInfo(c, titleIndex, intent);
- intent = getRestoredItemIntent(c, context, intent);
+ if (user.equals(UserHandleCompat.myUserHandle())) {
+ Launcher.addDumpLog(TAG,
+ "constructing info for partially restored package",
+ true);
+ info = getRestoredItemInfo(c, titleIndex, intent, promiseType);
+ intent = getRestoredItemIntent(c, context, intent);
+ } else {
+ // Don't restore items for other profiles.
+ itemsToRemove.add(id);
+ continue;
+ }
} else if (itemType ==
LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
- info = getShortcutInfo(manager, intent, context, c, iconIndex,
- titleIndex, mLabelCache);
+ info = getShortcutInfo(manager, intent, user, context, c,
+ iconIndex, titleIndex, mLabelCache, allowMissingTarget);
} else {
info = getShortcutInfo(c, context, iconTypeIndex,
iconPackageIndex, iconResourceIndex, iconIndex,
@@ -1929,6 +2130,9 @@ public class LauncherModel extends BroadcastReceiver {
info.cellY = c.getInt(cellYIndex);
info.spanX = 1;
info.spanY = 1;
+ info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
+ info.isDisabled = isSafeMode
+ && !Utilities.isSystemApp(context, intent);
// check & update map of what's occupied
deleteOnInvalidPlacement.set(false);
@@ -2005,31 +2209,79 @@ public class LauncherModel extends BroadcastReceiver {
// Read all Launcher-specific widget details
int appWidgetId = c.getInt(appWidgetIdIndex);
String savedProvider = c.getString(appWidgetProviderIndex);
-
id = c.getLong(idIndex);
+ final ComponentName component =
+ ComponentName.unflattenFromString(savedProvider);
+
+ final int restoreStatus = c.getInt(restoredIndex);
+ final boolean isIdValid = (restoreStatus &
+ LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
- final AppWidgetProviderInfo provider =
- widgets.getAppWidgetInfo(appWidgetId);
+ final boolean wasProviderReady = (restoreStatus &
+ LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
- if (!isSafeMode && (provider == null || provider.provider == null ||
- provider.provider.getPackageName() == null)) {
- String log = "Deleting widget that isn't installed anymore: id="
- + id + " appWidgetId=" + appWidgetId;
+ final AppWidgetProviderInfo provider = isIdValid
+ ? widgets.getAppWidgetInfo(appWidgetId)
+ : findAppWidgetProviderInfoWithComponent(context, component);
+
+ final boolean isProviderReady = isValidProvider(provider);
+ if (!isSafeMode && wasProviderReady && !isProviderReady) {
+ String log = "Deleting widget that isn't installed anymore: "
+ + "id=" + id + " appWidgetId=" + appWidgetId;
Log.e(TAG, log);
Launcher.addDumpLog(TAG, log, false);
itemsToRemove.add(id);
} else {
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- provider.provider);
+ if (isProviderReady) {
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ provider.provider);
+ int[] minSpan =
+ Launcher.getMinSpanForWidget(context, provider);
+ appWidgetInfo.minSpanX = minSpan[0];
+ appWidgetInfo.minSpanY = minSpan[1];
+
+ int status = restoreStatus;
+ if (!wasProviderReady) {
+ // If provider was not previously ready, update the
+ // status and UI flag.
+
+ // Id would be valid only if the widget restore broadcast was received.
+ if (isIdValid) {
+ status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ } else {
+ status &= ~LauncherAppWidgetInfo
+ .FLAG_PROVIDER_NOT_READY;
+ }
+ }
+ appWidgetInfo.restoreStatus = status;
+ } else {
+ Log.v(TAG, "Widget restore pending id=" + id
+ + " appWidgetId=" + appWidgetId
+ + " status =" + restoreStatus);
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ component);
+ appWidgetInfo.restoreStatus = restoreStatus;
+
+ if ((restoreStatus & LauncherAppWidgetInfo.FLAG_RESTORE_STARTED) != 0) {
+ // Restore has started once.
+ } else if (installingPkgs.contains(component.getPackageName())) {
+ // App restore has started. Update the flag
+ appWidgetInfo.restoreStatus |=
+ LauncherAppWidgetInfo.FLAG_RESTORE_STARTED;
+ } else if (REMOVE_UNRESTORED_ICONS) {
+ Launcher.addDumpLog(TAG,
+ "Unrestored widget removed: " + component, true);
+ itemsToRemove.add(id);
+ continue;
+ }
+ }
+
appWidgetInfo.id = id;
appWidgetInfo.screenId = c.getInt(screenIndex);
appWidgetInfo.cellX = c.getInt(cellXIndex);
appWidgetInfo.cellY = c.getInt(cellYIndex);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
- int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
- appWidgetInfo.minSpanX = minSpan[0];
- appWidgetInfo.minSpanY = minSpan[1];
container = c.getInt(containerIndex);
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
@@ -2049,13 +2301,17 @@ public class LauncherModel extends BroadcastReceiver {
}
break;
}
- String providerName = provider.provider.flattenToString();
- if (!providerName.equals(savedProvider)) {
+
+ String providerName = appWidgetInfo.providerName.flattenToString();
+ if (!providerName.equals(savedProvider) ||
+ (appWidgetInfo.restoreStatus != restoreStatus)) {
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
providerName);
+ values.put(LauncherSettings.Favorites.RESTORED,
+ appWidgetInfo.restoreStatus);
String where = BaseColumns._ID + "= ?";
- String[] args = {Integer.toString(c.getInt(idIndex))};
+ String[] args = {Long.toString(id)};
contentResolver.update(contentUri, values, where, args);
}
sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
@@ -2081,7 +2337,7 @@ public class LauncherModel extends BroadcastReceiver {
if (itemsToRemove.size() > 0) {
ContentProviderClient client = contentResolver.acquireContentProviderClient(
- LauncherSettings.Favorites.CONTENT_URI);
+ contentUri);
// Remove dead items
for (long id : itemsToRemove) {
if (DEBUG_LOADERS) {
@@ -2099,7 +2355,7 @@ public class LauncherModel extends BroadcastReceiver {
if (restoredRows.size() > 0) {
ContentProviderClient updater = contentResolver.acquireContentProviderClient(
- LauncherSettings.Favorites.CONTENT_URI);
+ contentUri);
// Update restored items that no longer require special handling
try {
StringBuilder selectionBuilder = new StringBuilder();
@@ -2109,13 +2365,19 @@ public class LauncherModel extends BroadcastReceiver {
selectionBuilder.append(")");
ContentValues values = new ContentValues();
values.put(LauncherSettings.Favorites.RESTORED, 0);
- updater.update(LauncherSettings.Favorites.CONTENT_URI,
+ updater.update(LauncherSettings.Favorites.CONTENT_URI_NO_NOTIFICATION,
values, selectionBuilder.toString(), null);
} catch (RemoteException e) {
Log.w(TAG, "Could not update restored rows");
}
}
+ if (!isSdCardReady && !sPendingPackages.isEmpty()) {
+ context.registerReceiver(new AppsAvailabilityCheck(),
+ new IntentFilter(StartupReceiver.SYSTEM_READY),
+ null, sWorker);
+ }
+
if (loadedOldDb) {
long maxScreenId = 0;
// If we're importing we use the old screen order.
@@ -2189,7 +2451,12 @@ public class LauncherModel extends BroadcastReceiver {
line += " | ";
}
for (int x = 0; x < countX; x++) {
- line += ((occupied.get(screenId)[x][y] != null) ? "#" : ".");
+ ItemInfo[][] screen = occupied.get(screenId);
+ if (x < screen.length && y < screen[x].length) {
+ line += (screen[x][y] != null) ? "#" : ".";
+ } else {
+ line += "!";
+ }
}
}
Log.d(TAG, "[ " + line + " ]");
@@ -2343,7 +2610,9 @@ public class LauncherModel extends BroadcastReceiver {
}
};
if (postOnMainThread) {
- deferredBindRunnables.add(r);
+ synchronized (deferredBindRunnables) {
+ deferredBindRunnables.add(r);
+ }
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
@@ -2360,7 +2629,9 @@ public class LauncherModel extends BroadcastReceiver {
}
};
if (postOnMainThread) {
- deferredBindRunnables.add(r);
+ synchronized (deferredBindRunnables) {
+ deferredBindRunnables.add(r);
+ }
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
@@ -2482,7 +2753,9 @@ public class LauncherModel extends BroadcastReceiver {
// Load all the remaining pages (if we are loading synchronously, we want to defer this
// work until after the first render)
- mDeferredBindRunnables.clear();
+ synchronized (mDeferredBindRunnables) {
+ mDeferredBindRunnables.clear();
+ }
bindWorkspaceItems(oldCallbacks, otherWorkspaceItems, otherAppWidgets, otherFolders,
(isLoadingSynchronously ? mDeferredBindRunnables : null));
@@ -2504,7 +2777,9 @@ public class LauncherModel extends BroadcastReceiver {
}
};
if (isLoadingSynchronously) {
- mDeferredBindRunnables.add(r);
+ synchronized (mDeferredBindRunnables) {
+ mDeferredBindRunnables.add(r);
+ }
} else {
runOnMainThread(r, MAIN_THREAD_BINDING_RUNNABLE);
}
@@ -2570,42 +2845,42 @@ public class LauncherModel extends BroadcastReceiver {
return;
}
- final PackageManager packageManager = mContext.getPackageManager();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ final List<UserHandleCompat> profiles = mUserManager.getUserProfiles();
+
// Clear the list of apps
mBgAllAppsList.clear();
+ for (UserHandleCompat user : profiles) {
+ // Query for the set of apps
+ final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+ List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "getActivityList took "
+ + (SystemClock.uptimeMillis()-qiaTime) + "ms for user " + user);
+ Log.d(TAG, "getActivityList got " + apps.size() + " apps for user " + user);
+ }
+ // Fail if we don't have any apps
+ if (apps == null || apps.isEmpty()) {
+ return;
+ }
+ // Sort the applications by name
+ final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
+ Collections.sort(apps,
+ new LauncherModel.ShortcutNameComparator(mLabelCache));
+ if (DEBUG_LOADERS) {
+ Log.d(TAG, "sort took "
+ + (SystemClock.uptimeMillis()-sortTime) + "ms");
+ }
- // Query for the set of apps
- final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- List<ResolveInfo> apps = packageManager.queryIntentActivities(mainIntent, 0);
- if (DEBUG_LOADERS) {
- Log.d(TAG, "queryIntentActivities took "
- + (SystemClock.uptimeMillis()-qiaTime) + "ms");
- Log.d(TAG, "queryIntentActivities got " + apps.size() + " apps");
- }
- // Fail if we don't have any apps
- if (apps == null || apps.isEmpty()) {
- return;
- }
- // Sort the applications by name
- final long sortTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
- Collections.sort(apps,
- new LauncherModel.ShortcutNameComparator(packageManager, mLabelCache));
- if (DEBUG_LOADERS) {
- Log.d(TAG, "sort took "
- + (SystemClock.uptimeMillis()-sortTime) + "ms");
- }
-
- // Create the ApplicationInfos
- for (int i = 0; i < apps.size(); i++) {
- ResolveInfo app = apps.get(i);
- // This builds the icon bitmaps.
- mBgAllAppsList.add(new AppInfo(packageManager, app,
- mIconCache, mLabelCache));
+ // Create the ApplicationInfos
+ for (int i = 0; i < apps.size(); i++) {
+ LauncherActivityInfoCompat app = apps.get(i);
+ // This builds the icon bitmaps.
+ mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache, mLabelCache));
+ }
}
-
// Huh? Shouldn't this be inside the Runnable below?
final ArrayList<AppInfo> added = mBgAllAppsList.added;
mBgAllAppsList.added = new ArrayList<AppInfo>();
@@ -2648,9 +2923,95 @@ public class LauncherModel extends BroadcastReceiver {
sWorker.post(task);
}
+ private class AppsAvailabilityCheck extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (sBgLock) {
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat
+ .getInstance(mApp.getContext());
+ ArrayList<String> packagesRemoved;
+ for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
+ UserHandleCompat user = entry.getKey();
+ packagesRemoved = new ArrayList<String>();
+ for (String pkg : entry.getValue()) {
+ if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
+ Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
+ packagesRemoved.add(pkg);
+ }
+ }
+ if (!packagesRemoved.isEmpty()) {
+ enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
+ packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
+ }
+ }
+ sPendingPackages.clear();
+ }
+ }
+ }
+
+ /**
+ * Workaround to re-check unrestored items, in-case they were installed but the Package-ADD
+ * runnable was missed by the launcher.
+ */
+ public void recheckRestoredItems(final Context context) {
+ Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ HashSet<String> installedPackages = new HashSet<String>();
+ UserHandleCompat user = UserHandleCompat.myUserHandle();
+ synchronized(sBgLock) {
+ for (ItemInfo info : sBgItemsIdMap.values()) {
+ if (info instanceof ShortcutInfo) {
+ ShortcutInfo si = (ShortcutInfo) info;
+ if (si.isPromise() && si.getTargetComponent() != null
+ && launcherApps.isPackageEnabledForProfile(
+ si.getTargetComponent().getPackageName(), user)) {
+ installedPackages.add(si.getTargetComponent().getPackageName());
+ }
+ } else if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo widget = (LauncherAppWidgetInfo) info;
+ if (widget.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+ && launcherApps.isPackageEnabledForProfile(
+ widget.providerName.getPackageName(), user)) {
+ installedPackages.add(widget.providerName.getPackageName());
+ }
+ }
+ }
+ }
+
+ if (!installedPackages.isEmpty()) {
+ final ArrayList<AppInfo> restoredApps = new ArrayList<AppInfo>();
+ for (String pkg : installedPackages) {
+ for (LauncherActivityInfoCompat info : launcherApps.getActivityList(pkg, user)) {
+ restoredApps.add(new AppInfo(context, info, user, mIconCache, null));
+ }
+ }
+
+ final Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
+ if (!restoredApps.isEmpty()) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
+ if (callbacks == cb && cb != null) {
+ callbacks.bindAppsRestored(restoredApps);
+ }
+ }
+ });
+ }
+
+ }
+ }
+ };
+ sWorker.post(r);
+ }
+
private class PackageUpdatedTask implements Runnable {
int mOp;
String[] mPackages;
+ UserHandleCompat mUser;
public static final int OP_NONE = 0;
public static final int OP_ADD = 1;
@@ -2659,9 +3020,10 @@ public class LauncherModel extends BroadcastReceiver {
public static final int OP_UNAVAILABLE = 4; // external media unmounted
- public PackageUpdatedTask(int op, String[] packages) {
+ public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
mOp = op;
mPackages = packages;
+ mUser = user;
}
public void run() {
@@ -2673,14 +3035,14 @@ public class LauncherModel extends BroadcastReceiver {
case OP_ADD:
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.addPackage " + packages[i]);
- mIconCache.remove(packages[i]);
- mBgAllAppsList.addPackage(context, packages[i]);
+ mIconCache.remove(packages[i], mUser);
+ mBgAllAppsList.addPackage(context, packages[i], mUser);
}
break;
case OP_UPDATE:
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.updatePackage " + packages[i]);
- mBgAllAppsList.updatePackage(context, packages[i]);
+ mBgAllAppsList.updatePackage(context, packages[i], mUser);
WidgetPreviewLoader.removePackageFromDb(
mApp.getWidgetPreviewCacheDb(), packages[i]);
}
@@ -2689,7 +3051,7 @@ public class LauncherModel extends BroadcastReceiver {
case OP_UNAVAILABLE:
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
- mBgAllAppsList.removePackage(packages[i]);
+ mBgAllAppsList.removePackage(packages[i], mUser);
WidgetPreviewLoader.removePackageFromDb(
mApp.getWidgetPreviewCacheDb(), packages[i]);
}
@@ -2735,11 +3097,12 @@ public class LauncherModel extends BroadcastReceiver {
// Update the launcher db to reflect the changes
for (AppInfo a : modifiedFinal) {
ArrayList<ItemInfo> infos =
- getItemInfoForComponentName(a.componentName);
+ getItemInfoForComponentName(a.componentName, mUser);
for (ItemInfo i : infos) {
if (isShortcutInfoUpdateable(i)) {
ShortcutInfo info = (ShortcutInfo) i;
info.title = a.title.toString();
+ info.contentDescription = a.contentDescription;
updateItemInDatabase(context, info);
}
}
@@ -2764,24 +3127,19 @@ public class LauncherModel extends BroadcastReceiver {
// Mark disabled packages in the broadcast to be removed
final PackageManager pm = context.getPackageManager();
for (int i=0; i<N; i++) {
- if (isPackageDisabled(pm, packages[i])) {
+ if (isPackageDisabled(context, packages[i], mUser)) {
removedPackageNames.add(packages[i]);
}
}
}
// Remove all the components associated with this package
for (String pn : removedPackageNames) {
- ArrayList<ItemInfo> infos = getItemInfoForPackageName(pn);
- for (ItemInfo i : infos) {
- deleteItemFromDatabase(context, i);
- }
+ deletePackageFromDatabase(context, pn, mUser);
}
// Remove all the specific components
for (AppInfo a : removedApps) {
- ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName);
- for (ItemInfo i : infos) {
- deleteItemFromDatabase(context, i);
- }
+ ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
+ deleteItemsFromDatabase(context, infos);
}
if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
// Remove any queued items from the install queue
@@ -2794,14 +3152,14 @@ public class LauncherModel extends BroadcastReceiver {
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
if (callbacks == cb && cb != null) {
- callbacks.bindComponentsRemoved(removedPackageNames, removedApps);
+ callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
}
}
});
}
final ArrayList<Object> widgetsAndShortcuts =
- getSortedWidgetsAndShortcuts(context);
+ getSortedWidgetsAndShortcuts(context);
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -2828,55 +3186,70 @@ public class LauncherModel extends BroadcastReceiver {
public static ArrayList<Object> getSortedWidgetsAndShortcuts(Context context) {
PackageManager packageManager = context.getPackageManager();
final ArrayList<Object> widgetsAndShortcuts = new ArrayList<Object>();
- widgetsAndShortcuts.addAll(AppWidgetManager.getInstance(context).getInstalledProviders());
+ widgetsAndShortcuts.addAll(AppWidgetManagerCompat.getInstance(context).getAllProviders());
+
Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT);
widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0));
- Collections.sort(widgetsAndShortcuts,
- new LauncherModel.WidgetAndShortcutNameComparator(packageManager));
+ Collections.sort(widgetsAndShortcuts, new WidgetAndShortcutNameComparator(context));
return widgetsAndShortcuts;
}
- private static boolean isPackageDisabled(PackageManager pm, String packageName) {
- try {
- PackageInfo pi = pm.getPackageInfo(packageName, 0);
- return !pi.applicationInfo.enabled;
- } catch (NameNotFoundException e) {
- // Fall through
- }
- return false;
+ private static boolean isPackageDisabled(Context context, String packageName,
+ UserHandleCompat user) {
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ return !launcherApps.isPackageEnabledForProfile(packageName, user);
}
- public static boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
+ public static boolean isValidPackageActivity(Context context, ComponentName cn,
+ UserHandleCompat user) {
if (cn == null) {
return false;
}
- if (isPackageDisabled(pm, cn.getPackageName())) {
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ if (!launcherApps.isPackageEnabledForProfile(cn.getPackageName(), user)) {
return false;
}
+ return launcherApps.isActivityEnabledForProfile(cn, user);
+ }
- try {
- // Check the activity
- PackageInfo pi = pm.getPackageInfo(cn.getPackageName(), 0);
- return (pm.getActivityInfo(cn, 0) != null);
- } catch (NameNotFoundException e) {
+ public static boolean isValidPackage(Context context, String packageName,
+ UserHandleCompat user) {
+ if (packageName == null) {
return false;
}
+ final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(context);
+ return launcherApps.isPackageEnabledForProfile(packageName, user);
}
/**
* Make an ShortcutInfo object for a restored application or shortcut item that points
* to a package that is not yet installed on the system.
*/
- public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent) {
+ public ShortcutInfo getRestoredItemInfo(Cursor cursor, int titleIndex, Intent intent,
+ int promiseType) {
final ShortcutInfo info = new ShortcutInfo();
- if (cursor != null) {
- info.title = cursor.getString(titleIndex);
+ info.user = UserHandleCompat.myUserHandle();
+ mIconCache.getTitleAndIcon(info, intent, info.user, true);
+
+ if ((promiseType & ShortcutInfo.FLAG_RESTORED_ICON) != 0) {
+ String title = (cursor != null) ? cursor.getString(titleIndex) : null;
+ if (!TextUtils.isEmpty(title)) {
+ info.title = title;
+ }
+ info.status = ShortcutInfo.FLAG_RESTORED_ICON;
+ } else if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
+ if (TextUtils.isEmpty(info.title)) {
+ info.title = (cursor != null) ? cursor.getString(titleIndex) : "";
+ }
+ info.status = ShortcutInfo.FLAG_AUTOINTALL_ICON;
} else {
- info.title = "";
+ throw new InvalidParameterException("Invalid restoreType " + promiseType);
}
- info.setIcon(mIconCache.getIcon(intent, info.title.toString()));
+
+ info.contentDescription = mUserManager.getBadgedLabelForUser(
+ info.title.toString(), info.user);
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
- info.restoredIntent = intent;
+ info.promisedIntent = intent;
return info;
}
@@ -2885,25 +3258,26 @@ public class LauncherModel extends BroadcastReceiver {
* to the market page for the item.
*/
private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) {
- final boolean debug = false;
ComponentName componentName = intent.getComponent();
- Intent marketIntent = new Intent(Intent.ACTION_VIEW);
- Uri marketUri = new Uri.Builder()
+ return getMarketIntent(componentName.getPackageName());
+ }
+
+ static Intent getMarketIntent(String packageName) {
+ return new Intent(Intent.ACTION_VIEW)
+ .setData(new Uri.Builder()
.scheme("market")
.authority("details")
- .appendQueryParameter("id", componentName.getPackageName())
- .build();
- if (debug) Log.d(TAG, "manufactured intent uri: " + marketUri.toString());
- marketIntent.setData(marketUri);
- return marketIntent;
+ .appendQueryParameter("id", packageName)
+ .build());
}
/**
* This is called from the code that adds shortcuts from the intent receiver. This
* doesn't have a Cursor, but
*/
- public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context) {
- return getShortcutInfo(manager, intent, context, null, -1, -1, null);
+ public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
+ UserHandleCompat user, Context context) {
+ return getShortcutInfo(manager, intent, user, context, null, -1, -1, null, false);
}
/**
@@ -2911,54 +3285,37 @@ public class LauncherModel extends BroadcastReceiver {
*
* If c is not null, then it will be used to fill in missing data like the title and icon.
*/
- public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
- Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
+ public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent,
+ UserHandleCompat user, Context context, Cursor c, int iconIndex, int titleIndex,
+ HashMap<Object, CharSequence> labelCache, boolean allowMissingTarget) {
+ if (user == null) {
+ Log.d(TAG, "Null user found in getShortcutInfo");
+ return null;
+ }
+
ComponentName componentName = intent.getComponent();
- final ShortcutInfo info = new ShortcutInfo();
- if (componentName != null && !isValidPackageComponent(manager, componentName)) {
- Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
+ if (componentName == null) {
+ Log.d(TAG, "Missing component found in getShortcutInfo: " + componentName);
+ return null;
+ }
+
+ Intent newIntent = new Intent(intent.getAction(), null);
+ newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ newIntent.setComponent(componentName);
+ LauncherActivityInfoCompat lai = mLauncherApps.resolveActivity(newIntent, user);
+ if ((lai == null) && !allowMissingTarget) {
+ Log.d(TAG, "Missing activity found in getShortcutInfo: " + componentName);
return null;
- } else {
- try {
- PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
- info.initFlagsAndFirstInstallTime(pi);
- } catch (NameNotFoundException e) {
- Log.d(TAG, "getPackInfo failed for package " +
- componentName.getPackageName());
- }
}
- // TODO: See if the PackageManager knows about this case. If it doesn't
- // then return null & delete this.
+ final ShortcutInfo info = new ShortcutInfo();
// the resource -- This may implicitly give us back the fallback icon,
// but don't worry about that. All we're doing with usingFallbackIcon is
// to avoid saving lots of copies of that in the database, and most apps
// have icons anyway.
+ Bitmap icon = mIconCache.getIcon(componentName, lai, labelCache);
- // Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
- // if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
- // via resolveActivity().
- Bitmap icon = null;
- ResolveInfo resolveInfo = null;
- ComponentName oldComponent = intent.getComponent();
- Intent newIntent = new Intent(intent.getAction(), null);
- newIntent.addCategory(Intent.CATEGORY_LAUNCHER);
- newIntent.setPackage(oldComponent.getPackageName());
- List<ResolveInfo> infos = manager.queryIntentActivities(newIntent, 0);
- for (ResolveInfo i : infos) {
- ComponentName cn = new ComponentName(i.activityInfo.packageName,
- i.activityInfo.name);
- if (cn.equals(oldComponent)) {
- resolveInfo = i;
- }
- }
- if (resolveInfo == null) {
- resolveInfo = manager.resolveActivity(intent, 0);
- }
- if (resolveInfo != null) {
- icon = mIconCache.getIcon(componentName, resolveInfo, labelCache);
- }
// the db
if (icon == null) {
if (c != null) {
@@ -2967,21 +3324,21 @@ public class LauncherModel extends BroadcastReceiver {
}
// the fallback icon
if (icon == null) {
- icon = getFallbackIcon();
+ icon = mIconCache.getDefaultIcon(user);
info.usingFallbackIcon = true;
}
info.setIcon(icon);
+ // From the cache.
+ if (labelCache != null) {
+ info.title = labelCache.get(componentName);
+ }
+
// from the resource
- if (resolveInfo != null) {
- ComponentName key = LauncherModel.getComponentNameFromResolveInfo(resolveInfo);
- if (labelCache != null && labelCache.containsKey(key)) {
- info.title = labelCache.get(key);
- } else {
- info.title = resolveInfo.activityInfo.loadLabel(manager);
- if (labelCache != null) {
- labelCache.put(key, info.title);
- }
+ if (info.title == null && lai != null) {
+ info.title = lai.getLabel();
+ if (labelCache != null) {
+ labelCache.put(componentName, info.title);
}
}
// from the db
@@ -2995,6 +3352,9 @@ public class LauncherModel extends BroadcastReceiver {
info.title = componentName.getClassName();
}
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
+ info.user = user;
+ info.contentDescription = mUserManager.getBadgedLabelForUser(
+ info.title.toString(), info.user);
return info;
}
@@ -3004,14 +3364,14 @@ public class LauncherModel extends BroadcastReceiver {
for (ItemInfo i : infos) {
if (i instanceof ShortcutInfo) {
ShortcutInfo info = (ShortcutInfo) i;
- ComponentName cn = info.intent.getComponent();
+ ComponentName cn = info.getTargetComponent();
if (cn != null && f.filterItem(null, info, cn)) {
filtered.add(info);
}
} else if (i instanceof FolderInfo) {
FolderInfo info = (FolderInfo) i;
for (ShortcutInfo s : info.contents) {
- ComponentName cn = s.intent.getComponent();
+ ComponentName cn = s.getTargetComponent();
if (cn != null && f.filterItem(info, s, cn)) {
filtered.add(s);
}
@@ -3027,21 +3387,16 @@ public class LauncherModel extends BroadcastReceiver {
return new ArrayList<ItemInfo>(filtered);
}
- private ArrayList<ItemInfo> getItemInfoForPackageName(final String pn) {
+ private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname,
+ final UserHandleCompat user) {
ItemInfoFilter filter = new ItemInfoFilter() {
@Override
public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
- return cn.getPackageName().equals(pn);
- }
- };
- return filterItemInfos(sBgItemsIdMap.values(), filter);
- }
-
- private ArrayList<ItemInfo> getItemInfoForComponentName(final ComponentName cname) {
- ItemInfoFilter filter = new ItemInfoFilter() {
- @Override
- public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) {
- return cn.equals(cname);
+ if (info.user == null) {
+ return cn.equals(cname);
+ } else {
+ return cn.equals(cname) && info.user.equals(user);
+ }
}
};
return filterItemInfos(sBgItemsIdMap.values(), filter);
@@ -3060,7 +3415,7 @@ public class LauncherModel extends BroadcastReceiver {
return true;
}
// placeholder shortcuts get special treatment, let them through too.
- if (info.getRestoredIntent() != null) {
+ if (info.isPromise()) {
return true;
}
}
@@ -3076,6 +3431,8 @@ public class LauncherModel extends BroadcastReceiver {
Bitmap icon = null;
final ShortcutInfo info = new ShortcutInfo();
+ // Non-app shortcuts are only supported for current user.
+ info.user = UserHandleCompat.myUserHandle();
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
// TODO: If there's an explicit component and we can't install that, delete it.
@@ -3106,14 +3463,14 @@ public class LauncherModel extends BroadcastReceiver {
}
// the fallback icon
if (icon == null) {
- icon = getFallbackIcon();
+ icon = mIconCache.getDefaultIcon(info.user);
info.usingFallbackIcon = true;
}
break;
case LauncherSettings.Favorites.ICON_TYPE_BITMAP:
icon = getIconFromCursor(c, iconIndex, context);
if (icon == null) {
- icon = getFallbackIcon();
+ icon = mIconCache.getDefaultIcon(info.user);
info.customIcon = false;
info.usingFallbackIcon = true;
} else {
@@ -3121,7 +3478,7 @@ public class LauncherModel extends BroadcastReceiver {
}
break;
default:
- icon = getFallbackIcon();
+ icon = mIconCache.getDefaultIcon(info.user);
info.usingFallbackIcon = true;
info.customIcon = false;
break;
@@ -3160,7 +3517,7 @@ public class LauncherModel extends BroadcastReceiver {
/**
* Attempts to find an AppWidgetProviderInfo that matches the given component.
*/
- AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
+ static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
ComponentName component) {
List<AppWidgetProviderInfo> widgets =
AppWidgetManager.getInstance(context).getInstalledProviders();
@@ -3172,44 +3529,6 @@ public class LauncherModel extends BroadcastReceiver {
return null;
}
- /**
- * Returns a list of all the widgets that can handle configuration with a particular mimeType.
- */
- List<WidgetMimeTypeHandlerData> resolveWidgetsForMimeType(Context context, String mimeType) {
- final PackageManager packageManager = context.getPackageManager();
- final List<WidgetMimeTypeHandlerData> supportedConfigurationActivities =
- new ArrayList<WidgetMimeTypeHandlerData>();
-
- final Intent supportsIntent =
- new Intent(InstallWidgetReceiver.ACTION_SUPPORTS_CLIPDATA_MIMETYPE);
- supportsIntent.setType(mimeType);
-
- // Create a set of widget configuration components that we can test against
- final List<AppWidgetProviderInfo> widgets =
- AppWidgetManager.getInstance(context).getInstalledProviders();
- final HashMap<ComponentName, AppWidgetProviderInfo> configurationComponentToWidget =
- new HashMap<ComponentName, AppWidgetProviderInfo>();
- for (AppWidgetProviderInfo info : widgets) {
- configurationComponentToWidget.put(info.configure, info);
- }
-
- // Run through each of the intents that can handle this type of clip data, and cross
- // reference them with the components that are actual configuration components
- final List<ResolveInfo> activities = packageManager.queryIntentActivities(supportsIntent,
- PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo info : activities) {
- final ActivityInfo activityInfo = info.activityInfo;
- final ComponentName infoComponent = new ComponentName(activityInfo.packageName,
- activityInfo.name);
- if (configurationComponentToWidget.containsKey(infoComponent)) {
- supportedConfigurationActivities.add(
- new InstallWidgetReceiver.WidgetMimeTypeHandlerData(info,
- configurationComponentToWidget.get(infoComponent)));
- }
- }
- return supportedConfigurationActivities;
- }
-
ShortcutInfo infoFromShortcutIntent(Context context, Intent data, Bitmap fallbackIcon) {
Intent intent = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_INTENT);
String name = data.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
@@ -3238,7 +3557,8 @@ public class LauncherModel extends BroadcastReceiver {
iconResource.packageName);
final int id = resources.getIdentifier(iconResource.resourceName, null, null);
icon = Utilities.createIconBitmap(
- mIconCache.getFullResIcon(resources, id), context);
+ mIconCache.getFullResIcon(resources, id),
+ context);
} catch (Exception e) {
Log.w(TAG, "Could not load shortcut icon: " + extra);
}
@@ -3247,17 +3567,22 @@ public class LauncherModel extends BroadcastReceiver {
final ShortcutInfo info = new ShortcutInfo();
+ // Only support intents for current user for now. Intents sent from other
+ // users wouldn't get here without intent forwarding anyway.
+ info.user = UserHandleCompat.myUserHandle();
if (icon == null) {
if (fallbackIcon != null) {
icon = fallbackIcon;
} else {
- icon = getFallbackIcon();
+ icon = mIconCache.getDefaultIcon(info.user);
info.usingFallbackIcon = true;
}
}
info.setIcon(icon);
info.title = name;
+ info.contentDescription = mUserManager.getBadgedLabelForUser(
+ info.title.toString(), info.user);
info.intent = intent;
info.customIcon = customIcon;
info.iconResource = iconResource;
@@ -3323,12 +3648,18 @@ public class LauncherModel extends BroadcastReceiver {
final Collator collator = Collator.getInstance();
return new Comparator<AppInfo>() {
public final int compare(AppInfo a, AppInfo b) {
- int result = collator.compare(a.title.toString().trim(),
- b.title.toString().trim());
- if (result == 0) {
- result = a.componentName.compareTo(b.componentName);
+ if (a.user.equals(b.user)) {
+ int result = collator.compare(a.title.toString().trim(),
+ b.title.toString().trim());
+ if (result == 0) {
+ result = a.componentName.compareTo(b.componentName);
+ }
+ return result;
+ } else {
+ // TODO Need to figure out rules for sorting
+ // profiles, this puts work second.
+ return a.user.toString().compareTo(b.user.toString());
}
- return result;
}
};
}
@@ -3340,14 +3671,6 @@ public class LauncherModel extends BroadcastReceiver {
return 0;
}
};
- public static final Comparator<AppWidgetProviderInfo> getWidgetNameComparator() {
- final Collator collator = Collator.getInstance();
- return new Comparator<AppWidgetProviderInfo>() {
- public final int compare(AppWidgetProviderInfo a, AppWidgetProviderInfo b) {
- return collator.compare(a.label.toString().trim(), b.label.toString().trim());
- }
- };
- }
static ComponentName getComponentNameFromResolveInfo(ResolveInfo info) {
if (info.activityInfo != null) {
return new ComponentName(info.activityInfo.packageName, info.activityInfo.name);
@@ -3355,35 +3678,32 @@ public class LauncherModel extends BroadcastReceiver {
return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
}
}
- public static class ShortcutNameComparator implements Comparator<ResolveInfo> {
+ public static class ShortcutNameComparator implements Comparator<LauncherActivityInfoCompat> {
private Collator mCollator;
- private PackageManager mPackageManager;
private HashMap<Object, CharSequence> mLabelCache;
ShortcutNameComparator(PackageManager pm) {
- mPackageManager = pm;
mLabelCache = new HashMap<Object, CharSequence>();
mCollator = Collator.getInstance();
}
- ShortcutNameComparator(PackageManager pm, HashMap<Object, CharSequence> labelCache) {
- mPackageManager = pm;
+ ShortcutNameComparator(HashMap<Object, CharSequence> labelCache) {
mLabelCache = labelCache;
mCollator = Collator.getInstance();
}
- public final int compare(ResolveInfo a, ResolveInfo b) {
- CharSequence labelA, labelB;
- ComponentName keyA = LauncherModel.getComponentNameFromResolveInfo(a);
- ComponentName keyB = LauncherModel.getComponentNameFromResolveInfo(b);
+ public final int compare(LauncherActivityInfoCompat a, LauncherActivityInfoCompat b) {
+ String labelA, labelB;
+ ComponentName keyA = a.getComponentName();
+ ComponentName keyB = b.getComponentName();
if (mLabelCache.containsKey(keyA)) {
- labelA = mLabelCache.get(keyA);
+ labelA = mLabelCache.get(keyA).toString();
} else {
- labelA = a.loadLabel(mPackageManager).toString().trim();
+ labelA = a.getLabel().toString().trim();
mLabelCache.put(keyA, labelA);
}
if (mLabelCache.containsKey(keyB)) {
- labelB = mLabelCache.get(keyB);
+ labelB = mLabelCache.get(keyB).toString();
} else {
- labelB = b.loadLabel(mPackageManager).toString().trim();
+ labelB = b.getLabel().toString().trim();
mLabelCache.put(keyB, labelB);
}
@@ -3391,11 +3711,14 @@ public class LauncherModel extends BroadcastReceiver {
}
};
public static class WidgetAndShortcutNameComparator implements Comparator<Object> {
- private Collator mCollator;
- private PackageManager mPackageManager;
- private HashMap<Object, String> mLabelCache;
- WidgetAndShortcutNameComparator(PackageManager pm) {
- mPackageManager = pm;
+ private final AppWidgetManagerCompat mManager;
+ private final PackageManager mPackageManager;
+ private final HashMap<Object, String> mLabelCache;
+ private final Collator mCollator;
+
+ WidgetAndShortcutNameComparator(Context context) {
+ mManager = AppWidgetManagerCompat.getInstance(context);
+ mPackageManager = context.getPackageManager();
mLabelCache = new HashMap<Object, String>();
mCollator = Collator.getInstance();
}
@@ -3404,23 +3727,28 @@ public class LauncherModel extends BroadcastReceiver {
if (mLabelCache.containsKey(a)) {
labelA = mLabelCache.get(a);
} else {
- labelA = (a instanceof AppWidgetProviderInfo) ?
- ((AppWidgetProviderInfo) a).label :
- ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
+ labelA = (a instanceof AppWidgetProviderInfo)
+ ? mManager.loadLabel((AppWidgetProviderInfo) a)
+ : ((ResolveInfo) a).loadLabel(mPackageManager).toString().trim();
mLabelCache.put(a, labelA);
}
if (mLabelCache.containsKey(b)) {
labelB = mLabelCache.get(b);
} else {
- labelB = (b instanceof AppWidgetProviderInfo) ?
- ((AppWidgetProviderInfo) b).label :
- ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
+ labelB = (b instanceof AppWidgetProviderInfo)
+ ? mManager.loadLabel((AppWidgetProviderInfo) b)
+ : ((ResolveInfo) b).loadLabel(mPackageManager).toString().trim();
mLabelCache.put(b, labelB);
}
return mCollator.compare(labelA, labelB);
}
};
+ static boolean isValidProvider(AppWidgetProviderInfo provider) {
+ return (provider != null) && (provider.provider != null)
+ && (provider.provider.getPackageName() != null);
+ }
+
public void dumpState() {
Log.d(TAG, "mCallbacks=" + mCallbacks);
AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index a080dd8ca..6cc1688de 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -32,9 +32,10 @@ import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
-import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.database.SQLException;
@@ -48,12 +49,13 @@ import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
-import android.util.Xml;
+import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.UserHandleCompat;
+import com.android.launcher3.compat.UserManagerCompat;
import com.android.launcher3.config.ProviderConfig;
import org.xmlpull.v1.XmlPullParser;
@@ -63,6 +65,7 @@ import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@@ -72,7 +75,7 @@ public class LauncherProvider extends ContentProvider {
private static final String DATABASE_NAME = "launcher.db";
- private static final int DATABASE_VERSION = 17;
+ private static final int DATABASE_VERSION = 20;
static final String OLD_AUTHORITY = "com.android.launcher2.settings";
static final String AUTHORITY = ProviderConfig.AUTHORITY;
@@ -87,12 +90,14 @@ public class LauncherProvider extends ContentProvider {
"UPGRADED_FROM_OLD_DATABASE";
static final String EMPTY_DATABASE_CREATED =
"EMPTY_DATABASE_CREATED";
- static final String DEFAULT_WORKSPACE_RESOURCE_ID =
- "DEFAULT_WORKSPACE_RESOURCE_ID";
private static final String ACTION_APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE =
"com.android.launcher.action.APPWIDGET_DEFAULT_WORKSPACE_CONFIGURE";
+ private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd";
+
+ private LauncherProviderChangeListener mListener;
+
/**
* {@link Uri} triggered at any registered {@link android.database.ContentObserver} when
* {@link AppWidgetHost#deleteHost()} is called during database creation.
@@ -116,6 +121,10 @@ public class LauncherProvider extends ContentProvider {
return mOpenHelper.wasNewDbCreated();
}
+ public void setLauncherProviderChangeListener(LauncherProviderChangeListener listener) {
+ mListener = listener;
+ }
+
@Override
public String getType(Uri uri) {
SqlArguments args = new SqlArguments(uri, null, null);
@@ -146,7 +155,7 @@ public class LauncherProvider extends ContentProvider {
if (values == null) {
throw new RuntimeException("Error: attempting to insert null values");
}
- if (!values.containsKey(LauncherSettings.BaseLauncherColumns._ID)) {
+ if (!values.containsKey(LauncherSettings.ChangeLogColumns._ID)) {
throw new RuntimeException("Error: attempting to add item without specifying an id");
}
helper.checkId(table, values);
@@ -163,6 +172,14 @@ public class LauncherProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues initialValues) {
SqlArguments args = new SqlArguments(uri);
+ // In very limited cases, we support system|signature permission apps to add to the db
+ String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD);
+ if (externalAdd != null && "true".equals(externalAdd)) {
+ if (!mOpenHelper.initializeExternalAdd(initialValues)) {
+ return null;
+ }
+ }
+
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
addModifiedTime(initialValues);
final long rowId = dbInsertAndCheck(mOpenHelper, db, args.table, null, initialValues);
@@ -174,6 +191,7 @@ public class LauncherProvider extends ContentProvider {
return uri;
}
+
@Override
public int bulkInsert(Uri uri, ContentValues[] values) {
SqlArguments args = new SqlArguments(uri);
@@ -242,6 +260,9 @@ public class LauncherProvider extends ContentProvider {
// always notify the backup agent
LauncherBackupAgentHelper.dataChanged(getContext());
+ if (mListener != null) {
+ mListener.onLauncherProviderChange();
+ }
}
private void addModifiedTime(ContentValues values) {
@@ -287,45 +308,64 @@ public class LauncherProvider extends ContentProvider {
}
/**
- * @param workspaceResId that can be 0 to use default or non-zero for specific resource
+ * Clears all the data for a fresh start.
*/
- synchronized public void loadDefaultFavoritesIfNecessary(int origWorkspaceResId) {
+ synchronized public void createEmptyDB() {
+ mOpenHelper.createEmptyDB(mOpenHelper.getWritableDatabase());
+ }
+
+ /**
+ * Loads the default workspace based on the following priority scheme:
+ * 1) From a package provided by play store
+ * 2) From a partner configuration APK, already in the system image
+ * 3) The default configuration for the particular device
+ */
+ synchronized public void loadDefaultFavoritesIfNecessary() {
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = getContext().getSharedPreferences(spKey, Context.MODE_PRIVATE);
if (sp.getBoolean(EMPTY_DATABASE_CREATED, false)) {
Log.d(TAG, "loading default workspace");
- int workspaceResId = origWorkspaceResId;
- // Use default workspace resource if none provided
- if (workspaceResId == 0) {
- workspaceResId =
- sp.getInt(DEFAULT_WORKSPACE_RESOURCE_ID, getDefaultWorkspaceResourceId());
+ WorkspaceLoader loader = AutoInstallsLayout.get(getContext(),
+ mOpenHelper.mAppWidgetHost, mOpenHelper);
+
+ if (loader == null) {
+ final Partner partner = Partner.get(getContext().getPackageManager());
+ if (partner != null && partner.hasDefaultLayout()) {
+ final Resources partnerRes = partner.getResources();
+ int workspaceResId = partnerRes.getIdentifier(Partner.RES_DEFAULT_LAYOUT,
+ "xml", partner.getPackageName());
+ if (workspaceResId != 0) {
+ loader = new SimpleWorkspaceLoader(mOpenHelper, partnerRes, workspaceResId);
+ }
+ }
}
- // Populate favorites table with initial favorites
- SharedPreferences.Editor editor = sp.edit();
- editor.remove(EMPTY_DATABASE_CREATED);
- if (origWorkspaceResId != 0) {
- editor.putInt(DEFAULT_WORKSPACE_RESOURCE_ID, origWorkspaceResId);
+ if (loader == null) {
+ loader = new SimpleWorkspaceLoader(mOpenHelper, getContext().getResources(),
+ getDefaultWorkspaceResourceId());
}
- mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), workspaceResId);
- mOpenHelper.setFlagJustLoadedOldDb();
+ // Populate favorites table with initial favorites
+ SharedPreferences.Editor editor = sp.edit().remove(EMPTY_DATABASE_CREATED);
+ mOpenHelper.loadFavorites(mOpenHelper.getWritableDatabase(), loader);
editor.commit();
}
}
public void migrateLauncher2Shortcuts() {
mOpenHelper.migrateLauncher2Shortcuts(mOpenHelper.getWritableDatabase(),
- LauncherSettings.Favorites.OLD_CONTENT_URI);
+ Uri.parse(getContext().getString(R.string.old_launcher_provider_uri)));
}
private static int getDefaultWorkspaceResourceId() {
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
if (LauncherAppState.isDisableAllApps()) {
- return R.xml.default_workspace_no_all_apps;
+ return grid.defaultNoAllAppsLayoutId;
} else {
- return R.xml.default_workspace;
+ return grid.defaultLayoutId;
}
}
@@ -351,18 +391,39 @@ public class LauncherProvider extends ContentProvider {
mOpenHelper = new DatabaseHelper(getContext());
}
- private static class DatabaseHelper extends SQLiteOpenHelper {
+ private static class DatabaseHelper extends SQLiteOpenHelper implements LayoutParserCallback {
+ private static final String TAG_RESOLVE = "resolve";
private static final String TAG_FAVORITES = "favorites";
private static final String TAG_FAVORITE = "favorite";
- private static final String TAG_CLOCK = "clock";
- private static final String TAG_SEARCH = "search";
private static final String TAG_APPWIDGET = "appwidget";
private static final String TAG_SHORTCUT = "shortcut";
private static final String TAG_FOLDER = "folder";
+ private static final String TAG_PARTNER_FOLDER = "partner-folder";
private static final String TAG_EXTRA = "extra";
private static final String TAG_INCLUDE = "include";
+ // Style attrs -- "Favorite"
+ private static final String ATTR_CLASS_NAME = "className";
+ private static final String ATTR_PACKAGE_NAME = "packageName";
+ private static final String ATTR_CONTAINER = "container";
+ private static final String ATTR_SCREEN = "screen";
+ private static final String ATTR_X = "x";
+ private static final String ATTR_Y = "y";
+ private static final String ATTR_SPAN_X = "spanX";
+ private static final String ATTR_SPAN_Y = "spanY";
+ private static final String ATTR_ICON = "icon";
+ private static final String ATTR_TITLE = "title";
+ private static final String ATTR_URI = "uri";
+
+ // Style attrs -- "Include"
+ private static final String ATTR_WORKSPACE = "workspace";
+
+ // Style attrs -- "Extra"
+ private static final String ATTR_KEY = "key";
+ private static final String ATTR_VALUE = "value";
+
private final Context mContext;
+ private final PackageManager mPackageManager;
private final AppWidgetHost mAppWidgetHost;
private long mMaxItemId = -1;
private long mMaxScreenId = -1;
@@ -372,6 +433,7 @@ public class LauncherProvider extends ContentProvider {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
mContext = context;
+ mPackageManager = context.getPackageManager();
mAppWidgetHost = new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
// In the case where neither onCreate nor onUpgrade gets called, we read the maxId from
@@ -407,6 +469,10 @@ public class LauncherProvider extends ContentProvider {
mMaxScreenId = 0;
mNewDbCreated = true;
+ UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+ long userSerialNumber = userManager.getSerialNumberForUser(
+ UserHandleCompat.myUserHandle());
+
db.execSQL("CREATE TABLE favorites (" +
"_id INTEGER PRIMARY KEY," +
"title TEXT," +
@@ -428,7 +494,8 @@ public class LauncherProvider extends ContentProvider {
"displayMode INTEGER," +
"appWidgetProvider TEXT," +
"modified INTEGER NOT NULL DEFAULT 0," +
- "restored INTEGER NOT NULL DEFAULT 0" +
+ "restored INTEGER NOT NULL DEFAULT 0," +
+ "profileId INTEGER DEFAULT " + userSerialNumber +
");");
addWorkspacesTable(db);
@@ -454,7 +521,7 @@ public class LauncherProvider extends ContentProvider {
"/old_favorites?notify=true");
if (!convertDatabase(db, uri, permuteScreensCb, true)) {
// Try and upgrade from the Launcher2 db
- uri = LauncherSettings.Favorites.OLD_CONTENT_URI;
+ uri = Uri.parse(mContext.getString(R.string.old_launcher_provider_uri));
if (!convertDatabase(db, uri, permuteScreensCb, false)) {
// If we fail, then set a flag to load the default workspace
setFlagEmptyDbCreated();
@@ -480,6 +547,37 @@ public class LauncherProvider extends ContentProvider {
");");
}
+ private void removeOrphanedItems(SQLiteDatabase db) {
+ // Delete items directly on the workspace who's screen id doesn't exist
+ // "DELETE FROM favorites WHERE screen NOT IN (SELECT _id FROM workspaceScreens)
+ // AND container = -100"
+ String removeOrphanedDesktopItems = "DELETE FROM " + TABLE_FAVORITES +
+ " WHERE " +
+ LauncherSettings.Favorites.SCREEN + " NOT IN (SELECT " +
+ LauncherSettings.WorkspaceScreens._ID + " FROM " + TABLE_WORKSPACE_SCREENS + ")" +
+ " AND " +
+ LauncherSettings.Favorites.CONTAINER + " = " +
+ LauncherSettings.Favorites.CONTAINER_DESKTOP;
+ db.execSQL(removeOrphanedDesktopItems);
+
+ // Delete items contained in folders which no longer exist (after above statement)
+ // "DELETE FROM favorites WHERE container <> -100 AND container <> -101 AND container
+ // NOT IN (SELECT _id FROM favorites WHERE itemType = 2)"
+ String removeOrphanedFolderItems = "DELETE FROM " + TABLE_FAVORITES +
+ " WHERE " +
+ LauncherSettings.Favorites.CONTAINER + " <> " +
+ LauncherSettings.Favorites.CONTAINER_DESKTOP +
+ " AND "
+ + LauncherSettings.Favorites.CONTAINER + " <> " +
+ LauncherSettings.Favorites.CONTAINER_HOTSEAT +
+ " AND "
+ + LauncherSettings.Favorites.CONTAINER + " NOT IN (SELECT " +
+ LauncherSettings.Favorites._ID + " FROM " + TABLE_FAVORITES +
+ " WHERE " + LauncherSettings.Favorites.ITEM_TYPE + " = " +
+ LauncherSettings.Favorites.ITEM_TYPE_FOLDER + ")";
+ db.execSQL(removeOrphanedFolderItems);
+ }
+
private void setFlagJustLoadedOldDb() {
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp = mContext.getSharedPreferences(spKey, Context.MODE_PRIVATE);
@@ -691,7 +789,8 @@ public class LauncherProvider extends ContentProvider {
}
// Add default hotseat icons
- loadFavorites(db, R.xml.update_workspace);
+ loadFavorites(db, new SimpleWorkspaceLoader(this, mContext.getResources(),
+ R.xml.update_workspace));
version = 9;
}
@@ -780,6 +879,28 @@ public class LauncherProvider extends ContentProvider {
version = 17;
}
+ if (version < 18) {
+ // No-op
+ version = 18;
+ }
+
+ if (version < 19) {
+ // Due to a data loss bug, some users may have items associated with screen ids
+ // which no longer exist. Since this can cause other problems, and since the user
+ // will never see these items anyway, we use database upgrade as an opportunity to
+ // clean things up.
+ removeOrphanedItems(db);
+ version = 19;
+ }
+
+ if (version < 20) {
+ // Add userId column
+ if (addProfileColumn(db)) {
+ version = 20;
+ }
+ // else old version remains, which means we wipe old data
+ }
+
if (version != DATABASE_VERSION) {
Log.w(TAG, "Destroying all old data.");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
@@ -789,6 +910,47 @@ public class LauncherProvider extends ContentProvider {
}
}
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ // This shouldn't happen -- throw our hands up in the air and start over.
+ Log.w(TAG, "Database version downgrade from: " + oldVersion + " to " + newVersion +
+ ". Wiping databse.");
+ createEmptyDB(db);
+ }
+
+
+ /**
+ * Clears all the data for a fresh start.
+ */
+ public void createEmptyDB(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_FAVORITES);
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_WORKSPACE_SCREENS);
+ onCreate(db);
+ }
+
+ private boolean addProfileColumn(SQLiteDatabase db) {
+ db.beginTransaction();
+ try {
+ UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+ // Default to the serial number of this user, for older
+ // shortcuts.
+ long userSerialNumber = userManager.getSerialNumberForUser(
+ UserHandleCompat.myUserHandle());
+ // Insert new column for holding user serial number
+ db.execSQL("ALTER TABLE favorites " +
+ "ADD COLUMN profileId INTEGER DEFAULT "
+ + userSerialNumber + ";");
+ db.setTransactionSuccessful();
+ } catch (SQLException ex) {
+ // Old version remains, which means we wipe old data
+ Log.e(TAG, ex.getMessage(), ex);
+ return false;
+ } finally {
+ db.endTransaction();
+ }
+ return true;
+ }
+
private boolean updateContactsShortcuts(SQLiteDatabase db) {
final String selectWhere = buildOrWhereString(Favorites.ITEM_TYPE,
new int[] { Favorites.ITEM_TYPE_SHORTCUT });
@@ -930,6 +1092,7 @@ public class LauncherProvider extends ContentProvider {
// constructor from the worker thread; however, this doesn't extend until after the
// constructor is called, and we only pass a reference to LauncherProvider to LauncherApp
// after that point
+ @Override
public long generateNewItemId() {
if (mMaxItemId < 0) {
throw new RuntimeException("Error: max item id was not initialized");
@@ -938,6 +1101,11 @@ public class LauncherProvider extends ContentProvider {
return mMaxItemId;
}
+ @Override
+ public long insertAndCheck(SQLiteDatabase db, ContentValues values) {
+ return dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values);
+ }
+
public void updateMaxItemId(long id) {
mMaxItemId = id + 1;
}
@@ -1102,6 +1270,93 @@ public class LauncherProvider extends ContentProvider {
if (LOGD) Log.d(TAG, "mMaxItemId: " + mMaxItemId);
}
+ private boolean initializeExternalAdd(ContentValues values) {
+ // 1. Ensure that externally added items have a valid item id
+ long id = generateNewItemId();
+ values.put(LauncherSettings.Favorites._ID, id);
+
+ // 2. In the case of an app widget, and if no app widget id is specified, we
+ // attempt allocate and bind the widget.
+ Integer itemType = values.getAsInteger(LauncherSettings.Favorites.ITEM_TYPE);
+ if (itemType != null &&
+ itemType.intValue() == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET &&
+ !values.containsKey(LauncherSettings.Favorites.APPWIDGET_ID)) {
+
+ final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
+ ComponentName cn = ComponentName.unflattenFromString(
+ values.getAsString(Favorites.APPWIDGET_PROVIDER));
+
+ if (cn != null) {
+ try {
+ int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
+ if (!appWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,cn)) {
+ return false;
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Failed to initialize external widget", e);
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ // Add screen id if not present
+ long screenId = values.getAsLong(LauncherSettings.Favorites.SCREEN);
+ if (!addScreenIdIfNecessary(screenId)) {
+ return false;
+ }
+ return true;
+ }
+
+ // Returns true of screen id exists, or if successfully added
+ private boolean addScreenIdIfNecessary(long screenId) {
+ if (!hasScreenId(screenId)) {
+ int rank = getMaxScreenRank() + 1;
+
+ ContentValues v = new ContentValues();
+ v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
+ v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
+ if (dbInsertAndCheck(this, getWritableDatabase(),
+ TABLE_WORKSPACE_SCREENS, null, v) < 0) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean hasScreenId(long screenId) {
+ SQLiteDatabase db = getWritableDatabase();
+ Cursor c = db.rawQuery("SELECT * FROM " + TABLE_WORKSPACE_SCREENS + " WHERE "
+ + LauncherSettings.WorkspaceScreens._ID + " = " + screenId, null);
+ if (c != null) {
+ int count = c.getCount();
+ c.close();
+ return count > 0;
+ } else {
+ return false;
+ }
+ }
+
+ private int getMaxScreenRank() {
+ SQLiteDatabase db = getWritableDatabase();
+ Cursor c = db.rawQuery("SELECT MAX(" + LauncherSettings.WorkspaceScreens.SCREEN_RANK
+ + ") FROM " + TABLE_WORKSPACE_SCREENS, null);
+
+ // get the result
+ final int maxRankIndex = 0;
+ int rank = -1;
+ if (c != null && c.moveToNext()) {
+ rank = c.getInt(maxRankIndex);
+ }
+ if (c != null) {
+ c.close();
+ }
+
+ return rank;
+ }
+
private static final void beginDocument(XmlPullParser parser, String firstElementName)
throws XmlPullParserException, IOException {
int type;
@@ -1120,24 +1375,55 @@ public class LauncherProvider extends ContentProvider {
}
}
+ private static Intent buildMainIntent() {
+ Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ intent.addCategory(Intent.CATEGORY_LAUNCHER);
+ return intent;
+ }
+
+ private int loadFavorites(SQLiteDatabase db, WorkspaceLoader loader) {
+ ArrayList<Long> screenIds = new ArrayList<Long>();
+ // TODO: Use multiple loaders with fall-back and transaction.
+ int count = loader.loadLayout(db, screenIds);
+
+ // Add the screens specified by the items above
+ Collections.sort(screenIds);
+ int rank = 0;
+ ContentValues values = new ContentValues();
+ for (Long id : screenIds) {
+ values.clear();
+ values.put(LauncherSettings.WorkspaceScreens._ID, id);
+ values.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, rank);
+ if (dbInsertAndCheck(this, db, TABLE_WORKSPACE_SCREENS, null, values) < 0) {
+ throw new RuntimeException("Failed initialize screen table"
+ + "from default layout");
+ }
+ rank++;
+ }
+
+ // Ensure that the max ids are initialized
+ mMaxItemId = initializeMaxItemId(db);
+ mMaxScreenId = initializeMaxScreenId(db);
+
+ return count;
+ }
+
/**
* Loads the default set of favorite packages from an xml file.
*
* @param db The database to write the values into
* @param filterContainerId The specific container id of items to load
+ * @param the set of screenIds which are used by the favorites
*/
- private int loadFavorites(SQLiteDatabase db, int workspaceResourceId) {
- Intent intent = new Intent(Intent.ACTION_MAIN, null);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- ContentValues values = new ContentValues();
+ private int loadFavoritesRecursive(SQLiteDatabase db, Resources res, int workspaceResourceId,
+ ArrayList<Long> screenIds) {
+ ContentValues values = new ContentValues();
if (LOGD) Log.v(TAG, String.format("Loading favorites from resid=0x%08x", workspaceResourceId));
- PackageManager packageManager = mContext.getPackageManager();
- int i = 0;
+ int count = 0;
try {
- XmlResourceParser parser = mContext.getResources().getXml(workspaceResourceId);
- AttributeSet attrs = Xml.asAttributeSet(parser);
+ XmlResourceParser parser = res.getXml(workspaceResourceId);
beginDocument(parser, TAG_FAVORITES);
final int depth = parser.getDepth();
@@ -1154,38 +1440,34 @@ public class LauncherProvider extends ContentProvider {
final String name = parser.getName();
if (TAG_INCLUDE.equals(name)) {
- final TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Include);
- final int resId = a.getResourceId(R.styleable.Include_workspace, 0);
+ final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s<include workspace=%08x>"),
"", resId));
if (resId != 0 && resId != workspaceResourceId) {
// recursively load some more favorites, why not?
- i += loadFavorites(db, resId);
+ count += loadFavoritesRecursive(db, res, resId, screenIds);
added = false;
} else {
Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
}
- a.recycle();
-
if (LOGD) Log.v(TAG, String.format(("%" + (2*(depth+1)) + "s</include>"), ""));
continue;
}
// Assuming it's a <favorite> at this point
- TypedArray a = mContext.obtainStyledAttributes(attrs, R.styleable.Favorite);
-
long container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
- if (a.hasValue(R.styleable.Favorite_container)) {
- container = Long.valueOf(a.getString(R.styleable.Favorite_container));
+ String strContainer = getAttributeValue(parser, ATTR_CONTAINER);
+ if (strContainer != null) {
+ container = Long.valueOf(strContainer);
}
- String screen = a.getString(R.styleable.Favorite_screen);
- String x = a.getString(R.styleable.Favorite_x);
- String y = a.getString(R.styleable.Favorite_y);
+ String screen = getAttributeValue(parser, ATTR_SCREEN);
+ String x = getAttributeValue(parser, ATTR_X);
+ String y = getAttributeValue(parser, ATTR_Y);
values.clear();
values.put(LauncherSettings.Favorites.CONTAINER, container);
@@ -1194,8 +1476,8 @@ public class LauncherProvider extends ContentProvider {
values.put(LauncherSettings.Favorites.CELLY, y);
if (LOGD) {
- final String title = a.getString(R.styleable.Favorite_title);
- final String pkg = a.getString(R.styleable.Favorite_packageName);
+ final String title = getAttributeValue(parser, ATTR_TITLE);
+ final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
final String something = title != null ? title : pkg;
Log.v(TAG, String.format(
("%" + (2*(depth+1)) + "s<%s%s c=%d s=%s x=%s y=%s>"),
@@ -1205,82 +1487,62 @@ public class LauncherProvider extends ContentProvider {
}
if (TAG_FAVORITE.equals(name)) {
- long id = addAppShortcut(db, values, a, packageManager, intent);
+ long id = addAppShortcut(db, values, parser);
added = id >= 0;
- } else if (TAG_SEARCH.equals(name)) {
- added = addSearchWidget(db, values);
- } else if (TAG_CLOCK.equals(name)) {
- added = addClockWidget(db, values);
} else if (TAG_APPWIDGET.equals(name)) {
- added = addAppWidget(parser, attrs, type, db, values, a, packageManager);
+ added = addAppWidget(parser, type, db, values);
} else if (TAG_SHORTCUT.equals(name)) {
- long id = addUriShortcut(db, values, a);
+ long id = addUriShortcut(db, values, res, parser);
added = id >= 0;
- } else if (TAG_FOLDER.equals(name)) {
- String title;
- int titleResId = a.getResourceId(R.styleable.Favorite_title, -1);
- if (titleResId != -1) {
- title = mContext.getResources().getString(titleResId);
- } else {
- title = mContext.getResources().getString(R.string.folder_name);
- }
- values.put(LauncherSettings.Favorites.TITLE, title);
- long folderId = addFolder(db, values);
- added = folderId >= 0;
-
- ArrayList<Long> folderItems = new ArrayList<Long>();
-
- int folderDepth = parser.getDepth();
+ } else if (TAG_RESOLVE.equals(name)) {
+ // This looks through the contained favorites (or meta-favorites) and
+ // attempts to add them as shortcuts in the fallback group's location
+ // until one is added successfully.
+ added = false;
+ final int groupDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > folderDepth) {
+ parser.getDepth() > groupDepth) {
if (type != XmlPullParser.START_TAG) {
continue;
}
- final String folder_item_name = parser.getName();
-
- TypedArray ar = mContext.obtainStyledAttributes(attrs,
- R.styleable.Favorite);
- values.clear();
- values.put(LauncherSettings.Favorites.CONTAINER, folderId);
-
- if (LOGD) {
- final String pkg = ar.getString(R.styleable.Favorite_packageName);
- final String uri = ar.getString(R.styleable.Favorite_uri);
- Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
- folder_item_name, uri != null ? uri : pkg));
- }
-
- if (TAG_FAVORITE.equals(folder_item_name) && folderId >= 0) {
- long id =
- addAppShortcut(db, values, ar, packageManager, intent);
- if (id >= 0) {
- folderItems.add(id);
+ final String fallback_item_name = parser.getName();
+ if (!added) {
+ if (TAG_FAVORITE.equals(fallback_item_name)) {
+ final long id = addAppShortcut(db, values, parser);
+ added = id >= 0;
+ } else {
+ Log.e(TAG, "Fallback groups can contain only favorites, found "
+ + fallback_item_name);
}
- } else if (TAG_SHORTCUT.equals(folder_item_name) && folderId >= 0) {
- long id = addUriShortcut(db, values, ar);
- if (id >= 0) {
- folderItems.add(id);
- }
- } else {
- throw new RuntimeException("Folders can " +
- "contain only shortcuts");
}
- ar.recycle();
}
- // We can only have folders with >= 2 items, so we need to remove the
- // folder and clean up if less than 2 items were included, or some
- // failed to add, and less than 2 were actually added
- if (folderItems.size() < 2 && folderId >= 0) {
- // We just delete the folder and any items that made it
- deleteId(db, folderId);
- if (folderItems.size() > 0) {
- deleteId(db, folderItems.get(0));
+ } else if (TAG_FOLDER.equals(name)) {
+ // Folder contents are nested in this XML file
+ added = loadFolder(db, values, res, parser);
+
+ } else if (TAG_PARTNER_FOLDER.equals(name)) {
+ // Folder contents come from an external XML resource
+ final Partner partner = Partner.get(mPackageManager);
+ if (partner != null) {
+ final Resources partnerRes = partner.getResources();
+ final int resId = partnerRes.getIdentifier(Partner.RES_FOLDER,
+ "xml", partner.getPackageName());
+ if (resId != 0) {
+ final XmlResourceParser partnerParser = partnerRes.getXml(resId);
+ beginDocument(partnerParser, TAG_FOLDER);
+ added = loadFolder(db, values, partnerRes, partnerParser);
}
- added = false;
}
}
- if (added) i++;
- a.recycle();
+ if (added) {
+ long screenId = Long.parseLong(screen);
+ // Keep track of the set of screens which need to be added to the db.
+ if (!screenIds.contains(screenId) &&
+ container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ screenIds.add(screenId);
+ }
+ count++;
+ }
}
} catch (XmlPullParserException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
@@ -1289,50 +1551,231 @@ public class LauncherProvider extends ContentProvider {
} catch (RuntimeException e) {
Log.w(TAG, "Got exception parsing favorites.", e);
}
+ return count;
+ }
- // Update the max item id after we have loaded the database
- if (mMaxItemId == -1) {
- mMaxItemId = initializeMaxItemId(db);
+ /**
+ * Parse folder items starting at {@link XmlPullParser} location. Allow recursive
+ * includes of items.
+ */
+ private void addToFolder(SQLiteDatabase db, Resources res, XmlResourceParser parser,
+ ArrayList<Long> folderItems, long folderId) throws IOException, XmlPullParserException {
+ int type;
+ int folderDepth = parser.getDepth();
+ while ((type = parser.next()) != XmlPullParser.END_TAG ||
+ parser.getDepth() > folderDepth) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final String tag = parser.getName();
+
+ final ContentValues childValues = new ContentValues();
+ childValues.put(LauncherSettings.Favorites.CONTAINER, folderId);
+
+ if (LOGD) {
+ final String pkg = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ final String uri = getAttributeValue(parser, ATTR_URI);
+ Log.v(TAG, String.format(("%" + (2*(folderDepth+1)) + "s<%s \"%s\">"), "",
+ tag, uri != null ? uri : pkg));
+ }
+
+ if (TAG_FAVORITE.equals(tag) && folderId >= 0) {
+ final long id = addAppShortcut(db, childValues, parser);
+ if (id >= 0) {
+ folderItems.add(id);
+ }
+ } else if (TAG_SHORTCUT.equals(tag) && folderId >= 0) {
+ final long id = addUriShortcut(db, childValues, res, parser);
+ if (id >= 0) {
+ folderItems.add(id);
+ }
+ } else if (TAG_INCLUDE.equals(tag) && folderId >= 0) {
+ addToFolder(db, res, parser, folderItems, folderId);
+ } else {
+ throw new RuntimeException("Folders can contain only shortcuts");
+ }
}
+ }
- return i;
+ /**
+ * Parse folder starting at current {@link XmlPullParser} location.
+ */
+ private boolean loadFolder(SQLiteDatabase db, ContentValues values, Resources res,
+ XmlResourceParser parser) throws IOException, XmlPullParserException {
+ final String title;
+ final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
+ if (titleResId != 0) {
+ title = res.getString(titleResId);
+ } else {
+ title = mContext.getResources().getString(R.string.folder_name);
+ }
+
+ values.put(LauncherSettings.Favorites.TITLE, title);
+ long folderId = addFolder(db, values);
+ boolean added = folderId >= 0;
+
+ ArrayList<Long> folderItems = new ArrayList<Long>();
+ addToFolder(db, res, parser, folderItems, folderId);
+
+ // We can only have folders with >= 2 items, so we need to remove the
+ // folder and clean up if less than 2 items were included, or some
+ // failed to add, and less than 2 were actually added
+ if (folderItems.size() < 2 && folderId >= 0) {
+ // Delete the folder
+ deleteId(db, folderId);
+
+ // If we have a single item, promote it to where the folder
+ // would have been.
+ if (folderItems.size() == 1) {
+ final ContentValues childValues = new ContentValues();
+ copyInteger(values, childValues, LauncherSettings.Favorites.CONTAINER);
+ copyInteger(values, childValues, LauncherSettings.Favorites.SCREEN);
+ copyInteger(values, childValues, LauncherSettings.Favorites.CELLX);
+ copyInteger(values, childValues, LauncherSettings.Favorites.CELLY);
+
+ final long id = folderItems.get(0);
+ db.update(TABLE_FAVORITES, childValues,
+ LauncherSettings.Favorites._ID + "=" + id, null);
+ } else {
+ added = false;
+ }
+ }
+ return added;
}
- private long addAppShortcut(SQLiteDatabase db, ContentValues values, TypedArray a,
- PackageManager packageManager, Intent intent) {
- long id = -1;
- ActivityInfo info;
- String packageName = a.getString(R.styleable.Favorite_packageName);
- String className = a.getString(R.styleable.Favorite_className);
+ // A meta shortcut attempts to resolve an intent specified as a URI in the XML, if a
+ // logical choice for what shortcut should be used for that intent exists, then it is
+ // added. Otherwise add nothing.
+ private long addAppShortcutByUri(SQLiteDatabase db, ContentValues values,
+ String intentUri) {
+ Intent metaIntent;
try {
- ComponentName cn;
+ metaIntent = Intent.parseUri(intentUri, 0);
+ } catch (URISyntaxException e) {
+ Log.e(TAG, "Unable to add meta-favorite: " + intentUri, e);
+ return -1;
+ }
+
+ ResolveInfo resolved = mPackageManager.resolveActivity(metaIntent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ final List<ResolveInfo> appList = mPackageManager.queryIntentActivities(
+ metaIntent, PackageManager.MATCH_DEFAULT_ONLY);
+
+ // Verify that the result is an app and not just the resolver dialog asking which
+ // app to use.
+ if (wouldLaunchResolverActivity(resolved, appList)) {
+ // If only one of the results is a system app then choose that as the default.
+ final ResolveInfo systemApp = getSingleSystemActivity(appList);
+ if (systemApp == null) {
+ // There is no logical choice for this meta-favorite, so rather than making
+ // a bad choice just add nothing.
+ Log.w(TAG, "No preference or single system activity found for "
+ + metaIntent.toString());
+ return -1;
+ }
+ resolved = systemApp;
+ }
+ final ActivityInfo info = resolved.activityInfo;
+ final Intent intent = mPackageManager.getLaunchIntentForPackage(info.packageName);
+ if (intent == null) {
+ return -1;
+ }
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ return addAppShortcut(db, values, info.loadLabel(mPackageManager).toString(), intent);
+ }
+
+ private ResolveInfo getSingleSystemActivity(List<ResolveInfo> appList) {
+ ResolveInfo systemResolve = null;
+ final int N = appList.size();
+ for (int i = 0; i < N; ++i) {
try {
- cn = new ComponentName(packageName, className);
- info = packageManager.getActivityInfo(cn, 0);
- } catch (PackageManager.NameNotFoundException nnfe) {
- String[] packages = packageManager.currentToCanonicalPackageNames(
- new String[] { packageName });
- cn = new ComponentName(packages[0], className);
- info = packageManager.getActivityInfo(cn, 0);
+ ApplicationInfo info = mPackageManager.getApplicationInfo(
+ appList.get(i).activityInfo.packageName, 0);
+ if ((info.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ if (systemResolve != null) {
+ return null;
+ } else {
+ systemResolve = appList.get(i);
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to get info about resolve results", e);
+ return null;
}
- id = generateNewItemId();
- intent.setComponent(cn);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- values.put(Favorites.INTENT, intent.toUri(0));
- values.put(Favorites.TITLE, info.loadLabel(packageManager).toString());
- values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
- values.put(Favorites.SPANX, 1);
- values.put(Favorites.SPANY, 1);
- values.put(Favorites._ID, generateNewItemId());
- if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
- return -1;
+ }
+ return systemResolve;
+ }
+
+ private boolean wouldLaunchResolverActivity(ResolveInfo resolved,
+ List<ResolveInfo> appList) {
+ // If the list contains the above resolved activity, then it can't be
+ // ResolverActivity itself.
+ for (int i = 0; i < appList.size(); ++i) {
+ ResolveInfo tmp = appList.get(i);
+ if (tmp.activityInfo.name.equals(resolved.activityInfo.name)
+ && tmp.activityInfo.packageName.equals(resolved.activityInfo.packageName)) {
+ return false;
}
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to add favorite: " + packageName +
- "/" + className, e);
}
- return id;
+ return true;
+ }
+
+ private long addAppShortcut(SQLiteDatabase db, ContentValues values,
+ XmlResourceParser parser) {
+ final String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ final String className = getAttributeValue(parser, ATTR_CLASS_NAME);
+ final String uri = getAttributeValue(parser, ATTR_URI);
+
+ if (!TextUtils.isEmpty(packageName) && !TextUtils.isEmpty(className)) {
+ ActivityInfo info;
+ try {
+ ComponentName cn;
+ try {
+ cn = new ComponentName(packageName, className);
+ info = mPackageManager.getActivityInfo(cn, 0);
+ } catch (PackageManager.NameNotFoundException nnfe) {
+ String[] packages = mPackageManager.currentToCanonicalPackageNames(
+ new String[] { packageName });
+ cn = new ComponentName(packages[0], className);
+ info = mPackageManager.getActivityInfo(cn, 0);
+ }
+ final Intent intent = buildMainIntent();
+ intent.setComponent(cn);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+ return addAppShortcut(db, values, info.loadLabel(mPackageManager).toString(),
+ intent);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Unable to add favorite: " + packageName +
+ "/" + className, e);
+ }
+ return -1;
+ } else if (!TextUtils.isEmpty(uri)) {
+ // If no component specified try to find a shortcut to add from the URI.
+ return addAppShortcutByUri(db, values, uri);
+ } else {
+ Log.e(TAG, "Skipping invalid <favorite> with no component or uri");
+ return -1;
+ }
+ }
+
+ private long addAppShortcut(SQLiteDatabase db, ContentValues values, String title,
+ Intent intent) {
+ long id = generateNewItemId();
+ values.put(Favorites.INTENT, intent.toUri(0));
+ values.put(Favorites.TITLE, title);
+ values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPLICATION);
+ values.put(Favorites.SPANX, 1);
+ values.put(Favorites.SPANY, 1);
+ values.put(Favorites._ID, id);
+ if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
+ return -1;
+ } else {
+ return id;
+ }
}
private long addFolder(SQLiteDatabase db, ContentValues values) {
@@ -1374,23 +1817,12 @@ public class LauncherProvider extends ContentProvider {
return null;
}
- private boolean addSearchWidget(SQLiteDatabase db, ContentValues values) {
- ComponentName cn = getSearchWidgetProvider();
- return addAppWidget(db, values, cn, 4, 1, null);
- }
-
- private boolean addClockWidget(SQLiteDatabase db, ContentValues values) {
- ComponentName cn = new ComponentName("com.android.alarmclock",
- "com.android.alarmclock.AnalogAppWidgetProvider");
- return addAppWidget(db, values, cn, 2, 2, null);
- }
-
- private boolean addAppWidget(XmlResourceParser parser, AttributeSet attrs, int type,
- SQLiteDatabase db, ContentValues values, TypedArray a,
- PackageManager packageManager) throws XmlPullParserException, IOException {
+ private boolean addAppWidget(XmlResourceParser parser, int type,
+ SQLiteDatabase db, ContentValues values)
+ throws XmlPullParserException, IOException {
- String packageName = a.getString(R.styleable.Favorite_packageName);
- String className = a.getString(R.styleable.Favorite_className);
+ String packageName = getAttributeValue(parser, ATTR_PACKAGE_NAME);
+ String className = getAttributeValue(parser, ATTR_CLASS_NAME);
if (packageName == null || className == null) {
return false;
@@ -1399,21 +1831,25 @@ public class LauncherProvider extends ContentProvider {
boolean hasPackage = true;
ComponentName cn = new ComponentName(packageName, className);
try {
- packageManager.getReceiverInfo(cn, 0);
+ mPackageManager.getReceiverInfo(cn, 0);
} catch (Exception e) {
- String[] packages = packageManager.currentToCanonicalPackageNames(
+ String[] packages = mPackageManager.currentToCanonicalPackageNames(
new String[] { packageName });
cn = new ComponentName(packages[0], className);
try {
- packageManager.getReceiverInfo(cn, 0);
+ mPackageManager.getReceiverInfo(cn, 0);
} catch (Exception e1) {
+ System.out.println("Can't find widget provider: " + className);
hasPackage = false;
}
}
if (hasPackage) {
- int spanX = a.getInt(R.styleable.Favorite_spanX, 0);
- int spanY = a.getInt(R.styleable.Favorite_spanY, 0);
+ String spanX = getAttributeValue(parser, ATTR_SPAN_X);
+ String spanY = getAttributeValue(parser, ATTR_SPAN_Y);
+
+ values.put(Favorites.SPANX, spanX);
+ values.put(Favorites.SPANY, spanY);
// Read the extras
Bundle extras = new Bundle();
@@ -1424,10 +1860,9 @@ public class LauncherProvider extends ContentProvider {
continue;
}
- TypedArray ar = mContext.obtainStyledAttributes(attrs, R.styleable.Extra);
if (TAG_EXTRA.equals(parser.getName())) {
- String key = ar.getString(R.styleable.Extra_key);
- String value = ar.getString(R.styleable.Extra_value);
+ String key = getAttributeValue(parser, ATTR_KEY);
+ String value = getAttributeValue(parser, ATTR_VALUE);
if (key != null && value != null) {
extras.putString(key, value);
} else {
@@ -1436,16 +1871,16 @@ public class LauncherProvider extends ContentProvider {
} else {
throw new RuntimeException("Widgets can contain only extras");
}
- ar.recycle();
}
- return addAppWidget(db, values, cn, spanX, spanY, extras);
+ return addAppWidget(db, values, cn, extras);
}
return false;
}
+
private boolean addAppWidget(SQLiteDatabase db, ContentValues values, ComponentName cn,
- int spanX, int spanY, Bundle extras) {
+ Bundle extras) {
boolean allocatedAppWidgets = false;
final AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
@@ -1453,8 +1888,6 @@ public class LauncherProvider extends ContentProvider {
int appWidgetId = mAppWidgetHost.allocateAppWidgetId();
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_APPWIDGET);
- values.put(Favorites.SPANX, spanX);
- values.put(Favorites.SPANY, spanY);
values.put(Favorites.APPWIDGET_ID, appWidgetId);
values.put(Favorites.APPWIDGET_PROVIDER, cn.flattenToString());
values.put(Favorites._ID, generateNewItemId());
@@ -1480,17 +1913,15 @@ public class LauncherProvider extends ContentProvider {
return allocatedAppWidgets;
}
- private long addUriShortcut(SQLiteDatabase db, ContentValues values,
- TypedArray a) {
- Resources r = mContext.getResources();
-
- final int iconResId = a.getResourceId(R.styleable.Favorite_icon, 0);
- final int titleResId = a.getResourceId(R.styleable.Favorite_title, 0);
+ private long addUriShortcut(SQLiteDatabase db, ContentValues values, Resources res,
+ XmlResourceParser parser) {
+ final int iconResId = getAttributeResourceValue(parser, ATTR_ICON, 0);
+ final int titleResId = getAttributeResourceValue(parser, ATTR_TITLE, 0);
Intent intent;
String uri = null;
try {
- uri = a.getString(R.styleable.Favorite_uri);
+ uri = getAttributeValue(parser, ATTR_URI);
intent = Intent.parseUri(uri, 0);
} catch (URISyntaxException e) {
Log.w(TAG, "Shortcut has malformed uri: " + uri);
@@ -1505,13 +1936,13 @@ public class LauncherProvider extends ContentProvider {
long id = generateNewItemId();
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
values.put(Favorites.INTENT, intent.toUri(0));
- values.put(Favorites.TITLE, r.getString(titleResId));
+ values.put(Favorites.TITLE, res.getString(titleResId));
values.put(Favorites.ITEM_TYPE, Favorites.ITEM_TYPE_SHORTCUT);
values.put(Favorites.SPANX, 1);
values.put(Favorites.SPANY, 1);
values.put(Favorites.ICON_TYPE, Favorites.ICON_TYPE_RESOURCE);
- values.put(Favorites.ICON_PACKAGE, mContext.getPackageName());
- values.put(Favorites.ICON_RESOURCE, r.getResourceName(iconResId));
+ values.put(Favorites.ICON_PACKAGE, res.getResourcePackageName(iconResId));
+ values.put(Favorites.ICON_RESOURCE, res.getResourceName(iconResId));
values.put(Favorites._ID, id);
if (dbInsertAndCheck(this, db, TABLE_FAVORITES, null, values) < 0) {
@@ -1520,7 +1951,7 @@ public class LauncherProvider extends ContentProvider {
return id;
}
- public void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
+ private void migrateLauncher2Shortcuts(SQLiteDatabase db, Uri uri) {
final ContentResolver resolver = mContext.getContentResolver();
Cursor c = null;
int count = 0;
@@ -1563,6 +1994,8 @@ public class LauncherProvider extends ContentProvider {
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.URI);
final int displayModeIndex
= c.getColumnIndexOrThrow(LauncherSettings.Favorites.DISPLAY_MODE);
+ final int profileIndex
+ = c.getColumnIndex(LauncherSettings.Favorites.PROFILE_ID);
int i = 0;
int curX = 0;
@@ -1573,7 +2006,6 @@ public class LauncherProvider extends ContentProvider {
final int width = (int) grid.numColumns;
final int height = (int) grid.numRows;
final int hotseatWidth = (int) grid.numHotseatIcons;
- PackageManager pm = mContext.getPackageManager();
final HashSet<String> seenIntents = new HashSet<String>(c.getCount());
@@ -1594,6 +2026,19 @@ public class LauncherProvider extends ContentProvider {
final int screen = c.getInt(screenIndex);
int container = c.getInt(containerIndex);
final String intentStr = c.getString(intentIndex);
+
+ UserManagerCompat userManager = UserManagerCompat.getInstance(mContext);
+ UserHandleCompat userHandle;
+ final long userSerialNumber;
+ if (profileIndex != -1 && !c.isNull(profileIndex)) {
+ userSerialNumber = c.getInt(profileIndex);
+ userHandle = userManager.getUserForSerialNumber(userSerialNumber);
+ } else {
+ // Default to the serial number of this user, for older
+ // shortcuts.
+ userHandle = UserHandleCompat.myUserHandle();
+ userSerialNumber = userManager.getSerialNumberForUser(userHandle);
+ }
Launcher.addDumpLog(TAG, "migrating \""
+ c.getString(titleIndex) + "\" ("
+ cellX + "," + cellY + "@"
@@ -1620,7 +2065,8 @@ public class LauncherProvider extends ContentProvider {
Launcher.addDumpLog(TAG, "skipping empty intent", true);
continue;
} else if (cn != null &&
- !LauncherModel.isValidPackageComponent(pm, cn)) {
+ !LauncherModel.isValidPackageActivity(mContext, cn,
+ userHandle)) {
// component no longer exists.
Launcher.addDumpLog(TAG, "skipping item whose component " +
"no longer exists.", true);
@@ -1659,6 +2105,7 @@ public class LauncherProvider extends ContentProvider {
values.put(LauncherSettings.Favorites.URI, c.getString(uriIndex));
values.put(LauncherSettings.Favorites.DISPLAY_MODE,
c.getInt(displayModeIndex));
+ values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.put(screen, values);
@@ -1792,7 +2239,7 @@ public class LauncherProvider extends ContentProvider {
* Build a query string that will match any row where the column matches
* anything in the values list.
*/
- static String buildOrWhereString(String column, int[] values) {
+ 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]);
@@ -1803,6 +2250,38 @@ public class LauncherProvider extends ContentProvider {
return selectWhere.toString();
}
+ /**
+ * Return attribute value, attempting launcher-specific namespace first
+ * before falling back to anonymous attribute.
+ */
+ private static String getAttributeValue(XmlResourceParser parser, String attribute) {
+ String value = parser.getAttributeValue(
+ "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute);
+ if (value == null) {
+ value = parser.getAttributeValue(null, attribute);
+ }
+ return value;
+ }
+
+ /**
+ * Return attribute resource value, attempting launcher-specific namespace
+ * first before falling back to anonymous attribute.
+ */
+ private static int getAttributeResourceValue(XmlResourceParser parser, String attribute,
+ int defaultValue) {
+ int value = parser.getAttributeResourceValue(
+ "http://schemas.android.com/apk/res-auto/com.android.launcher3", attribute,
+ defaultValue);
+ if (value == defaultValue) {
+ value = parser.getAttributeResourceValue(null, attribute, defaultValue);
+ }
+ return value;
+ }
+
+ private static void copyInteger(ContentValues from, ContentValues to, String key) {
+ to.put(key, from.getAsInteger(key));
+ }
+
static class SqlArguments {
public final String table;
public final String where;
@@ -1834,4 +2313,29 @@ public class LauncherProvider extends ContentProvider {
}
}
}
+
+ static interface WorkspaceLoader {
+ /**
+ * @param screenIds A mutable list of screen its
+ * @return the number of workspace items added.
+ */
+ int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds);
+ }
+
+ private static class SimpleWorkspaceLoader implements WorkspaceLoader {
+ private final Resources mRes;
+ private final int mWorkspaceId;
+ private final DatabaseHelper mHelper;
+
+ SimpleWorkspaceLoader(DatabaseHelper helper, Resources res, int workspaceId) {
+ mHelper = helper;
+ mRes = res;
+ mWorkspaceId = workspaceId;
+ }
+
+ @Override
+ public int loadLayout(SQLiteDatabase db, ArrayList<Long> screenIds) {
+ return mHelper.loadFavoritesRecursive(db, mRes, mWorkspaceId, screenIds);
+ }
+ }
}
diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java
new file mode 100644
index 000000000..0de96fbc4
--- /dev/null
+++ b/src/com/android/launcher3/LauncherProviderChangeListener.java
@@ -0,0 +1,11 @@
+package com.android.launcher3;
+
+/**
+ * This class is a listener for {@link LauncherProvider} changes. It gets notified in the
+ * sendNotify method. This listener is needed because by default the Launcher suppresses
+ * standard data change callbacks.
+ */
+public interface LauncherProviderChangeListener {
+
+ public void onLauncherProviderChange();
+}
diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java
index 2a768a278..355370283 100644
--- a/src/com/android/launcher3/LauncherSettings.java
+++ b/src/com/android/launcher3/LauncherSettings.java
@@ -212,6 +212,14 @@ class LauncherSettings {
static final String SPANY = "spanY";
/**
+ * The profile id of the item in the cell.
+ * <P>
+ * Type: INTEGER
+ * </P>
+ */
+ static final String PROFILE_ID = "profileId";
+
+ /**
* The favorite is a user created folder
*/
static final int ITEM_TYPE_FOLDER = 2;
diff --git a/src/com/android/launcher3/LogAccelerateInterpolator.java b/src/com/android/launcher3/LogAccelerateInterpolator.java
new file mode 100644
index 000000000..c3bbfa536
--- /dev/null
+++ b/src/com/android/launcher3/LogAccelerateInterpolator.java
@@ -0,0 +1,25 @@
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+
+public class LogAccelerateInterpolator implements TimeInterpolator {
+
+ int mBase;
+ int mDrift;
+ final float mLogScale;
+
+ public LogAccelerateInterpolator(int base, int drift) {
+ mBase = base;
+ mDrift = drift;
+ mLogScale = 1f / computeLog(1, mBase, mDrift);
+ }
+
+ static float computeLog(float t, int base, int drift) {
+ return (float) -Math.pow(base, -t) + 1 + (drift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return 1 - computeLog(1 - t, mBase, mDrift) * mLogScale;
+ }
+}
diff --git a/src/com/android/launcher3/LogDecelerateInterpolator.java b/src/com/android/launcher3/LogDecelerateInterpolator.java
new file mode 100644
index 000000000..4c5f6f08c
--- /dev/null
+++ b/src/com/android/launcher3/LogDecelerateInterpolator.java
@@ -0,0 +1,26 @@
+package com.android.launcher3;
+
+import android.animation.TimeInterpolator;
+
+public class LogDecelerateInterpolator implements TimeInterpolator {
+
+ int mBase;
+ int mDrift;
+ final float mLogScale;
+
+ public LogDecelerateInterpolator(int base, int drift) {
+ mBase = base;
+ mDrift = drift;
+
+ mLogScale = 1f / computeLog(1, mBase, mDrift);
+ }
+
+ static float computeLog(float t, int base, int drift) {
+ return (float) -Math.pow(base, -t) + 1 + (drift * t);
+ }
+
+ @Override
+ public float getInterpolation(float t) {
+ return computeLog(t, mBase, mDrift) * mLogScale;
+ }
+}
diff --git a/src/com/android/launcher3/MainThreadExecutor.java b/src/com/android/launcher3/MainThreadExecutor.java
new file mode 100644
index 000000000..866b17c71
--- /dev/null
+++ b/src/com/android/launcher3/MainThreadExecutor.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.launcher3;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.util.List;
+import java.util.concurrent.AbstractExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An executor service that executes its tasks on the main thread.
+ *
+ * Shutting down this executor is not supported.
+ */
+public class MainThreadExecutor extends AbstractExecutorService {
+
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+
+ @Override
+ public void execute(Runnable runnable) {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ runnable.run();
+ } else {
+ mHandler.post(runnable);
+ }
+ }
+
+ /**
+ * Not supported and throws an exception when used.
+ */
+ @Override
+ @Deprecated
+ public void shutdown() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Not supported and throws an exception when used.
+ */
+ @Override
+ @Deprecated
+ public List<Runnable> shutdownNow() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean isShutdown() {
+ return false;
+ }
+
+ @Override
+ public boolean isTerminated() {
+ return false;
+ }
+
+ /**
+ * Not supported and throws an exception when used.
+ */
+ @Override
+ @Deprecated
+ public boolean awaitTermination(long l, TimeUnit timeUnit) throws InterruptedException {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 8d5d8dd4d..48fc0c98f 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -74,11 +74,12 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private static final int MIN_LENGTH_FOR_FLING = 25;
protected static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+ protected static final int OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION = 350;
protected static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
protected static final float NANOTIME_DIV = 1000000000.0f;
private static final float OVERSCROLL_ACCELERATE_FACTOR = 2;
- private static final float OVERSCROLL_DAMP_FACTOR = 0.14f;
+ private static final float OVERSCROLL_DAMP_FACTOR = 0.07f;
private static final float RETURN_TO_ORIGINAL_PAGE_THRESHOLD = 0.33f;
// The page is moved more than halfway, automatically move to the next page on touch up.
@@ -152,16 +153,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected int mTouchState = TOUCH_STATE_REST;
protected boolean mForceScreenScrolled = false;
-
protected OnLongClickListener mLongClickListener;
protected int mTouchSlop;
private int mPagingTouchSlop;
private int mMaximumVelocity;
- protected int mPageLayoutPaddingTop;
- protected int mPageLayoutPaddingBottom;
- protected int mPageLayoutPaddingLeft;
- protected int mPageLayoutPaddingRight;
protected int mPageLayoutWidthGap;
protected int mPageLayoutHeightGap;
protected int mCellCountX = 0;
@@ -171,6 +167,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected int mUnboundedScrollX;
protected int[] mTempVisiblePagesRange = new int[2];
protected boolean mForceDrawAllChildrenNextFrame;
+ private boolean mSpacePagesAutomatically = false;
// mOverScrollX is equal to getScrollX() when we're within the normal scroll range. Otherwise
// it is equal to the scaled overscroll position. We use a separate value so as to prevent
@@ -283,14 +280,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PagedView, defStyle, 0);
- mPageLayoutPaddingTop = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingTop, 0);
- mPageLayoutPaddingBottom = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingBottom, 0);
- mPageLayoutPaddingLeft = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingLeft, 0);
- mPageLayoutPaddingRight = a.getDimensionPixelSize(
- R.styleable.PagedView_pageLayoutPaddingRight, 0);
mPageLayoutWidthGap = a.getDimensionPixelSize(
R.styleable.PagedView_pageLayoutWidthGap, 0);
mPageLayoutHeightGap = a.getDimensionPixelSize(
@@ -339,8 +328,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Hook up the page indicator
ViewGroup parent = (ViewGroup) getParent();
+ ViewGroup grandParent = (ViewGroup) parent.getParent();
if (mPageIndicator == null && mPageIndicatorViewId > -1) {
- mPageIndicator = (PageIndicator) parent.findViewById(mPageIndicatorViewId);
+ mPageIndicator = (PageIndicator) grandParent.findViewById(mPageIndicatorViewId);
mPageIndicator.removeAllMarkers(mAllowPagedViewAnimations);
ArrayList<PageIndicator.PageMarkerResources> markers =
@@ -547,6 +537,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
mNextPage = INVALID_PAGE;
}
+ private int validateNewPage(int newPage) {
+ int validatedPage = newPage;
+ // When in free scroll mode, we need to clamp to the free scroll page range.
+ if (mFreeScroll) {
+ getFreeScrollPageRange(mTempVisiblePagesRange);
+ validatedPage = Math.max(mTempVisiblePagesRange[0],
+ Math.min(newPage, mTempVisiblePagesRange[1]));
+ }
+ // Ensure that it is clamped by the actual set of children in all cases
+ validatedPage = Math.max(0, Math.min(validatedPage, getPageCount() - 1));
+ return validatedPage;
+ }
+
/**
* Sets the current page.
*/
@@ -560,7 +563,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return;
}
mForceScreenScrolled = true;
- mCurrentPage = Math.max(0, Math.min(currentPage, getPageCount() - 1));
+ mCurrentPage = validateNewPage(currentPage);
updateCurrentPageScroll();
notifyPageSwitchListener();
invalidate();
@@ -591,8 +594,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
private void updatePageIndicator() {
// Update the page indicator (when we aren't reordering)
- if (mPageIndicator != null && !isReordering(false)) {
- mPageIndicator.setActiveMarker(getNextPage());
+ if (mPageIndicator != null) {
+ mPageIndicator.setContentDescription(getPageIndicatorDescription());
+ if (!isReordering(false)) {
+ mPageIndicator.setActiveMarker(getNextPage());
+ }
}
}
protected void pageBeginMoving() {
@@ -727,7 +733,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
} else if (mNextPage != INVALID_PAGE) {
sendScrollAccessibilityEvent();
- mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
+ mCurrentPage = validateNewPage(mNextPage);
mNextPage = INVALID_PAGE;
notifyPageSwitchListener();
@@ -843,6 +849,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
final int verticalPadding = getPaddingTop() + getPaddingBottom();
final int horizontalPadding = getPaddingLeft() + getPaddingRight();
+ int referenceChildWidth = 0;
// The children are given the same width and height as the workspace
// unless they were set to WRAP_CONTENT
if (DEBUG) Log.d(TAG, "PagedView.onMeasure(): " + widthSize + ", " + heightSize);
@@ -887,6 +894,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
childWidth = getViewportWidth() - mInsets.left - mInsets.right;
childHeight = getViewportHeight();
}
+ if (referenceChildWidth == 0) {
+ referenceChildWidth = childWidth;
+ }
final int childWidthMeasureSpec =
MeasureSpec.makeMeasureSpec(childWidth, childWidthMode);
@@ -895,9 +905,24 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
+ if (mSpacePagesAutomatically) {
+ int spacing = (getViewportWidth() - mInsets.left - mInsets.right
+ - referenceChildWidth) / 2;
+ if (spacing >= 0) {
+ setPageSpacing(spacing);
+ }
+ mSpacePagesAutomatically = false;
+ }
setMeasuredDimension(scaledWidthSize, scaledHeightSize);
}
+ /**
+ * This method should be called once before first layout / measure pass.
+ */
+ protected void setSinglePageInViewport() {
+ mSpacePagesAutomatically = true;
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!mIsDataReady || getChildCount() == 0) {
@@ -974,9 +999,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
- setHorizontalScrollBarEnabled(false);
updateCurrentPageScroll();
- setHorizontalScrollBarEnabled(true);
mFirstLayout = false;
}
@@ -1105,7 +1128,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return offset;
}
- protected void getOverviewModePages(int[] range) {
+ protected void getFreeScrollPageRange(int[] range) {
range[0] = 0;
range[1] = Math.max(0, getChildCount() - 1);
}
@@ -1158,7 +1181,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected boolean shouldDrawChild(View child) {
- return child.getAlpha() > 0 && child.getVisibility() == VISIBLE;
+ return child.getVisibility() == VISIBLE;
}
@Override
@@ -1580,29 +1603,20 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return f * f * f + 1.0f;
}
- protected void acceleratedOverScroll(float amount) {
+ protected float acceleratedOverFactor(float amount) {
int screenSize = getViewportWidth();
// We want to reach the max over scroll effect when the user has
// over scrolled half the size of the screen
float f = OVERSCROLL_ACCELERATE_FACTOR * (amount / screenSize);
- if (f == 0) return;
+ if (f == 0) return 0;
// Clamp this factor, f, to -1 < f < 1
if (Math.abs(f) >= 1) {
f /= Math.abs(f);
}
-
- int overScrollAmount = (int) Math.round(f * screenSize);
- if (amount < 0) {
- mOverScrollX = overScrollAmount;
- super.scrollTo(0, getScrollY());
- } else {
- mOverScrollX = mMaxScrollX + overScrollAmount;
- super.scrollTo(mMaxScrollX, getScrollY());
- }
- invalidate();
+ return f;
}
protected void dampedOverScroll(float amount) {
@@ -1621,10 +1635,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
int overScrollAmount = (int) Math.round(OVERSCROLL_DAMP_FACTOR * f * screenSize);
if (amount < 0) {
mOverScrollX = overScrollAmount;
- super.scrollTo(0, getScrollY());
+ super.scrollTo(mOverScrollX, getScrollY());
} else {
mOverScrollX = mMaxScrollX + overScrollAmount;
- super.scrollTo(mMaxScrollX, getScrollY());
+ super.scrollTo(mOverScrollX, getScrollY());
}
invalidate();
}
@@ -1650,7 +1664,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
void updateFreescrollBounds() {
- getOverviewModePages(mTempVisiblePagesRange);
+ getFreeScrollPageRange(mTempVisiblePagesRange);
if (isLayoutRtl()) {
mFreeScrollMinScrollX = getScrollForPage(mTempVisiblePagesRange[1]);
mFreeScrollMaxScrollX = getScrollForPage(mTempVisiblePagesRange[0]);
@@ -1665,7 +1679,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (mFreeScroll) {
updateFreescrollBounds();
- getOverviewModePages(mTempVisiblePagesRange);
+ getFreeScrollPageRange(mTempVisiblePagesRange);
if (getCurrentPage() < mTempVisiblePagesRange[0]) {
setCurrentPage(mTempVisiblePagesRange[0]);
} else if (getCurrentPage() > mTempVisiblePagesRange[1]) {
@@ -1684,7 +1698,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (mDragView != null) {
int dragX = (int) (mDragView.getLeft() + (mDragView.getMeasuredWidth() / 2)
+ mDragView.getTranslationX());
- getOverviewModePages(mTempVisiblePagesRange);
+ getFreeScrollPageRange(mTempVisiblePagesRange);
int minDistance = Integer.MAX_VALUE;
int minIndex = indexOfChild(mDragView);
for (int i = mTempVisiblePagesRange[0]; i <= mTempVisiblePagesRange[1]; i++) {
@@ -1801,7 +1815,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
!isHoveringOverDelete) {
mTempVisiblePagesRange[0] = 0;
mTempVisiblePagesRange[1] = getPageCount() - 1;
- getOverviewModePages(mTempVisiblePagesRange);
+ getFreeScrollPageRange(mTempVisiblePagesRange);
if (mTempVisiblePagesRange[0] <= pageUnderPointIndex &&
pageUnderPointIndex <= mTempVisiblePagesRange[1] &&
pageUnderPointIndex != mSidePageHoverIndex && mScroller.isFinished()) {
@@ -2124,8 +2138,20 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
return minDistanceFromScreenCenterIndex;
}
+ protected boolean isInOverScroll() {
+ return (mOverScrollX > mMaxScrollX || mOverScrollX < 0);
+ }
+
+ protected int getPageSnapDuration() {
+ if (isInOverScroll()) {
+ return OVER_SCROLL_PAGE_SNAP_ANIMATION_DURATION;
+ }
+ return PAGE_SNAP_ANIMATION_DURATION;
+
+ }
+
protected void snapToDestination() {
- snapToPage(getPageNearestToCenterOfScreen(), PAGE_SNAP_ANIMATION_DURATION);
+ snapToPage(getPageNearestToCenterOfScreen(), getPageSnapDuration());
}
private static class ScrollInterpolator implements Interpolator {
@@ -2149,17 +2175,17 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected void snapToPageWithVelocity(int whichPage, int velocity) {
- whichPage = Math.max(0, Math.min(whichPage, getChildCount() - 1));
+ whichPage = validateNewPage(whichPage);
int halfScreenSize = getViewportWidth() / 2;
final int newX = getScrollForPage(whichPage);
int delta = newX - mUnboundedScrollX;
int duration = 0;
- if (Math.abs(velocity) < mMinFlingVelocity) {
+ if (Math.abs(velocity) < mMinFlingVelocity || isInOverScroll()) {
// If the velocity is low enough, then treat this more as an automatic page advance
// as opposed to an apparent physical response to flinging
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ snapToPage(whichPage, getPageSnapDuration());
return;
}
@@ -2183,11 +2209,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected void snapToPage(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION);
+ snapToPage(whichPage, getPageSnapDuration());
}
protected void snapToPageImmediately(int whichPage) {
- snapToPage(whichPage, PAGE_SNAP_ANIMATION_DURATION, true, null);
+ snapToPage(whichPage, getPageSnapDuration(), true, null);
}
protected void snapToPage(int whichPage, int duration) {
@@ -2200,7 +2226,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected void snapToPage(int whichPage, int duration, boolean immediate,
TimeInterpolator interpolator) {
- whichPage = Math.max(0, Math.min(whichPage, getPageCount() - 1));
+ whichPage = validateNewPage(whichPage);
int newX = getScrollForPage(whichPage);
final int sX = mUnboundedScrollX;
@@ -2214,6 +2240,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected void snapToPage(int whichPage, int delta, int duration, boolean immediate,
TimeInterpolator interpolator) {
+ whichPage = validateNewPage(whichPage);
+
mNextPage = whichPage;
View focusedChild = getFocusedChild();
if (focusedChild != null && whichPage != mCurrentPage &&
@@ -2482,11 +2510,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
public boolean startReordering(View v) {
int dragViewIndex = indexOfChild(v);
- if (mTouchState != TOUCH_STATE_REST) return false;
+ if (mTouchState != TOUCH_STATE_REST || dragViewIndex == -1) return false;
mTempVisiblePagesRange[0] = 0;
mTempVisiblePagesRange[1] = getPageCount() - 1;
- getOverviewModePages(mTempVisiblePagesRange);
+ getFreeScrollPageRange(mTempVisiblePagesRange);
mReorderingStarted = true;
// Check if we are within the reordering range
@@ -2619,7 +2647,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// in the layout)
// NOTE: We can make an assumption here because we have side-bound pages that we
// will always have pages to animate in from the left
- getOverviewModePages(mTempVisiblePagesRange);
+ getFreeScrollPageRange(mTempVisiblePagesRange);
boolean isLastWidgetPage = (mTempVisiblePagesRange[0] == mTempVisiblePagesRange[1]);
boolean slideFromLeft = (isLastWidgetPage ||
dragViewIndex > mTempVisiblePagesRange[0]);
diff --git a/src/com/android/launcher3/PagedViewGridLayout.java b/src/com/android/launcher3/PagedViewGridLayout.java
index b28686113..f69fa562d 100644
--- a/src/com/android/launcher3/PagedViewGridLayout.java
+++ b/src/com/android/launcher3/PagedViewGridLayout.java
@@ -56,18 +56,6 @@ public class PagedViewGridLayout extends GridLayout implements Page {
}
}
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- // PagedView currently has issues with different-sized pages since it calculates the
- // offset of each page to scroll to before it updates the actual size of each page
- // (which can change depending on the content if the contents aren't a fixed size).
- // We work around this by having a minimum size on each widget page).
- int widthSpecSize = Math.min(getSuggestedMinimumWidth(),
- MeasureSpec.getSize(widthMeasureSpec));
- int widthSpecMode = MeasureSpec.EXACTLY;
- super.onMeasure(MeasureSpec.makeMeasureSpec(widthSpecSize, widthSpecMode),
- heightMeasureSpec);
- }
-
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java
deleted file mode 100644
index f7cb997cd..000000000
--- a/src/com/android/launcher3/PagedViewIcon.java
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * 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;
-
-import android.content.Context;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Region;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.util.TypedValue;
-import android.widget.TextView;
-
-/**
- * An icon on a PagedView, specifically for items in the launcher's paged view (with compound
- * drawables on the top).
- */
-public class PagedViewIcon extends TextView {
- /** A simple callback interface to allow a PagedViewIcon to notify when it has been pressed */
- public static interface PressedCallback {
- void iconPressed(PagedViewIcon icon);
- }
-
- @SuppressWarnings("unused")
- private static final String TAG = "PagedViewIcon";
- private static final float PRESS_ALPHA = 0.4f;
-
- private PagedViewIcon.PressedCallback mPressedCallback;
- private boolean mLockDrawableState = false;
-
- private Bitmap mIcon;
-
- public PagedViewIcon(Context context) {
- this(context, null);
- }
-
- public PagedViewIcon(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public PagedViewIcon(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- public void onFinishInflate() {
- super.onFinishInflate();
-
- // Ensure we are using the right text size
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
- }
-
- public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
- PagedViewIcon.PressedCallback cb) {
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
-
- mIcon = info.iconBitmap;
- mPressedCallback = cb;
- Drawable icon = Utilities.createIconDrawable(mIcon);
- icon.setBounds(0, 0, grid.allAppsIconSizePx, grid.allAppsIconSizePx);
- setCompoundDrawables(null, icon, null, null);
- setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
- setText(info.title);
- setTag(info);
- }
-
- public void lockDrawableState() {
- mLockDrawableState = true;
- }
-
- public void resetDrawableState() {
- mLockDrawableState = false;
- post(new Runnable() {
- @Override
- public void run() {
- refreshDrawableState();
- }
- });
- }
-
- protected void drawableStateChanged() {
- super.drawableStateChanged();
-
- // We keep in the pressed state until resetDrawableState() is called to reset the press
- // feedback
- if (isPressed()) {
- setAlpha(PRESS_ALPHA);
- if (mPressedCallback != null) {
- mPressedCallback.iconPressed(this);
- }
- } else if (!mLockDrawableState) {
- setAlpha(1f);
- }
- }
-
- @Override
- public void draw(Canvas canvas) {
- // If text is transparent, don't draw any shadow
- if (getCurrentTextColor() == getResources().getColor(android.R.color.transparent)) {
- getPaint().clearShadowLayer();
- super.draw(canvas);
- return;
- }
-
- // We enhance the shadow by drawing the shadow twice
- getPaint().setShadowLayer(BubbleTextView.SHADOW_LARGE_RADIUS, 0.0f,
- BubbleTextView.SHADOW_Y_OFFSET, BubbleTextView.SHADOW_LARGE_COLOUR);
- super.draw(canvas);
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
- getScrollX() + getWidth(),
- getScrollY() + getHeight(), Region.Op.INTERSECT);
- getPaint().setShadowLayer(BubbleTextView.SHADOW_SMALL_RADIUS, 0.0f, 0.0f,
- BubbleTextView.SHADOW_SMALL_COLOUR);
- super.draw(canvas);
- canvas.restore();
- }
-}
diff --git a/src/com/android/launcher3/PagedViewIconCache.java b/src/com/android/launcher3/PagedViewIconCache.java
deleted file mode 100644
index 93887ea23..000000000
--- a/src/com/android/launcher3/PagedViewIconCache.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.launcher3;
-
-import android.appwidget.AppWidgetProviderInfo;
-import android.content.ComponentName;
-import android.content.pm.ComponentInfo;
-import android.content.pm.ResolveInfo;
-import android.graphics.Bitmap;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-
-/**
- * Simple cache mechanism for PagedView outlines.
- */
-public class PagedViewIconCache {
- public static class Key {
- public enum Type {
- ApplicationInfoKey,
- AppWidgetProviderInfoKey,
- ResolveInfoKey
- }
- private final ComponentName mComponentName;
- private final Type mType;
-
- public Key(AppInfo info) {
- mComponentName = info.componentName;
- mType = Type.ApplicationInfoKey;
- }
- public Key(ResolveInfo info) {
- final ComponentInfo ci = info.activityInfo != null ? info.activityInfo :
- info.serviceInfo;
- mComponentName = new ComponentName(ci.packageName, ci.name);
- mType = Type.ResolveInfoKey;
- }
- public Key(AppWidgetProviderInfo info) {
- mComponentName = info.provider;
- mType = Type.AppWidgetProviderInfoKey;
- }
-
- private ComponentName getComponentName() {
- return mComponentName;
- }
- public boolean isKeyType(Type t) {
- return (mType == t);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o instanceof Key) {
- Key k = (Key) o;
- return mComponentName.equals(k.mComponentName);
- }
- return super.equals(o);
- }
- @Override
- public int hashCode() {
- return getComponentName().hashCode();
- }
- }
-
- private final HashMap<Key, Bitmap> mIconOutlineCache = new HashMap<Key, Bitmap>();
-
- public void clear() {
- for (Key key : mIconOutlineCache.keySet()) {
- mIconOutlineCache.get(key).recycle();
- }
- mIconOutlineCache.clear();
- }
- private void retainAll(HashSet<Key> keysToKeep, Key.Type t) {
- HashSet<Key> keysToRemove = new HashSet<Key>(mIconOutlineCache.keySet());
- keysToRemove.removeAll(keysToKeep);
- for (Key key : keysToRemove) {
- if (key.isKeyType(t)) {
- mIconOutlineCache.get(key).recycle();
- mIconOutlineCache.remove(key);
- }
- }
- }
- /** Removes all the keys to applications that aren't in the passed in collection */
- public void retainAllApps(ArrayList<AppInfo> keys) {
- HashSet<Key> keysSet = new HashSet<Key>();
- for (AppInfo info : keys) {
- keysSet.add(new Key(info));
- }
- retainAll(keysSet, Key.Type.ApplicationInfoKey);
- }
- /** Removes all the keys to shortcuts that aren't in the passed in collection */
- public void retainAllShortcuts(List<ResolveInfo> keys) {
- HashSet<Key> keysSet = new HashSet<Key>();
- for (ResolveInfo info : keys) {
- keysSet.add(new Key(info));
- }
- retainAll(keysSet, Key.Type.ResolveInfoKey);
- }
- /** Removes all the keys to widgets that aren't in the passed in collection */
- public void retainAllAppWidgets(List<AppWidgetProviderInfo> keys) {
- HashSet<Key> keysSet = new HashSet<Key>();
- for (AppWidgetProviderInfo info : keys) {
- keysSet.add(new Key(info));
- }
- retainAll(keysSet, Key.Type.AppWidgetProviderInfoKey);
- }
- public void addOutline(Key key, Bitmap b) {
- mIconOutlineCache.put(key, b);
- }
- public void removeOutline(Key key) {
- if (mIconOutlineCache.containsKey(key)) {
- mIconOutlineCache.get(key).recycle();
- mIconOutlineCache.remove(key);
- }
- }
- public Bitmap getOutline(Key key) {
- return mIconOutlineCache.get(key);
- }
-}
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java
index db4aeb940..e6e11a312 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/PagedViewWidget.java
@@ -30,6 +30,8 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
/**
* The linear layout used strictly for the widget/wallpaper tab of the customization tray
*/
@@ -127,7 +129,7 @@ public class PagedViewWidget extends LinearLayout {
image.setMaxWidth(maxWidth);
}
final TextView name = (TextView) findViewById(R.id.widget_name);
- name.setText(info.label);
+ name.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info));
final TextView dims = (TextView) findViewById(R.id.widget_dims);
if (dims != null) {
int hSpan = Math.min(cellSpan[0], (int) grid.numColumns);
diff --git a/src/com/android/launcher3/Partner.java b/src/com/android/launcher3/Partner.java
new file mode 100644
index 000000000..e1913193b
--- /dev/null
+++ b/src/com/android/launcher3/Partner.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.File;
+
+/**
+ * Utilities to discover and interact with partner customizations. There can
+ * only be one set of customizations on a device, and it must be bundled with
+ * the system.
+ */
+public class Partner {
+
+ static final String TAG = "Launcher.Partner";
+
+ /** Marker action used to discover partner */
+ private static final String
+ ACTION_PARTNER_CUSTOMIZATION = "com.android.launcher3.action.PARTNER_CUSTOMIZATION";
+
+ public static final String RES_FOLDER = "partner_folder";
+ public static final String RES_WALLPAPERS = "partner_wallpapers";
+ public static final String RES_DEFAULT_LAYOUT = "partner_default_layout";
+
+ public static final String RES_DEFAULT_WALLPAPER_HIDDEN = "default_wallpapper_hidden";
+ public static final String RES_SYSTEM_WALLPAPER_DIR = "system_wallpaper_directory";
+
+ public static final String RES_REQUIRE_FIRST_RUN_FLOW = "requires_first_run_flow";
+
+ /** These resources are used to override the device profile */
+ public static final String RES_GRID_AA_SHORT_EDGE_COUNT = "grid_aa_short_edge_count";
+ public static final String RES_GRID_AA_LONG_EDGE_COUNT = "grid_aa_long_edge_count";
+ public static final String RES_GRID_NUM_ROWS = "grid_num_rows";
+ public static final String RES_GRID_NUM_COLUMNS = "grid_num_columns";
+ public static final String RES_GRID_ICON_SIZE_DP = "grid_icon_size_dp";
+
+ private static boolean sSearched = false;
+ private static Partner sPartner;
+
+ /**
+ * Find and return partner details, or {@code null} if none exists.
+ */
+ public static synchronized Partner get(PackageManager pm) {
+ if (!sSearched) {
+ Pair<String, Resources> apkInfo = Utilities.findSystemApk(ACTION_PARTNER_CUSTOMIZATION, pm);
+ if (apkInfo != null) {
+ sPartner = new Partner(apkInfo.first, apkInfo.second);
+ }
+ sSearched = true;
+ }
+ return sPartner;
+ }
+
+ private final String mPackageName;
+ private final Resources mResources;
+
+ private Partner(String packageName, Resources res) {
+ mPackageName = packageName;
+ mResources = res;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public Resources getResources() {
+ return mResources;
+ }
+
+ public boolean hasDefaultLayout() {
+ int defaultLayout = getResources().getIdentifier(Partner.RES_DEFAULT_LAYOUT,
+ "xml", getPackageName());
+ return defaultLayout != 0;
+ }
+
+ public boolean hasFolder() {
+ int folder = getResources().getIdentifier(Partner.RES_FOLDER,
+ "xml", getPackageName());
+ return folder != 0;
+ }
+
+ public boolean hideDefaultWallpaper() {
+ int resId = getResources().getIdentifier(RES_DEFAULT_WALLPAPER_HIDDEN, "bool",
+ getPackageName());
+ return resId != 0 && getResources().getBoolean(resId);
+ }
+
+ public File getWallpaperDirectory() {
+ int resId = getResources().getIdentifier(RES_SYSTEM_WALLPAPER_DIR, "string",
+ getPackageName());
+ return (resId != 0) ? new File(getResources().getString(resId)) : null;
+ }
+
+ public boolean requiresFirstRunFlow() {
+ int resId = getResources().getIdentifier(RES_REQUIRE_FIRST_RUN_FLOW, "bool",
+ getPackageName());
+ return resId != 0 && getResources().getBoolean(resId);
+ }
+
+ public DeviceProfile getDeviceProfileOverride(DisplayMetrics dm) {
+ boolean containsProfileOverrides = false;
+
+ DeviceProfile dp = new DeviceProfile();
+
+ // We initialize customizable fields to be invalid
+ dp.numRows = -1;
+ dp.numColumns = -1;
+ dp.allAppsShortEdgeCount = -1;
+ dp.allAppsLongEdgeCount = -1;
+
+ try {
+ int resId = getResources().getIdentifier(RES_GRID_NUM_ROWS,
+ "integer", getPackageName());
+ if (resId > 0) {
+ containsProfileOverrides = true;
+ dp.numRows = getResources().getInteger(resId);
+ }
+
+ resId = getResources().getIdentifier(RES_GRID_NUM_COLUMNS,
+ "integer", getPackageName());
+ if (resId > 0) {
+ containsProfileOverrides = true;
+ dp.numColumns = getResources().getInteger(resId);
+ }
+
+ resId = getResources().getIdentifier(RES_GRID_AA_SHORT_EDGE_COUNT,
+ "integer", getPackageName());
+ if (resId > 0) {
+ containsProfileOverrides = true;
+ dp.allAppsShortEdgeCount = getResources().getInteger(resId);
+ }
+
+ resId = getResources().getIdentifier(RES_GRID_AA_LONG_EDGE_COUNT,
+ "integer", getPackageName());
+ if (resId > 0) {
+ containsProfileOverrides = true;
+ dp.allAppsLongEdgeCount = getResources().getInteger(resId);
+ }
+
+ resId = getResources().getIdentifier(RES_GRID_ICON_SIZE_DP,
+ "dimen", getPackageName());
+ if (resId > 0) {
+ containsProfileOverrides = true;
+ int px = getResources().getDimensionPixelSize(resId);
+ dp.iconSize = DynamicGrid.dpiFromPx(px, dm);
+ }
+ } catch (Resources.NotFoundException ex) {
+ Log.e(TAG, "Invalid Partner grid resource!", ex);
+ }
+ return containsProfileOverrides ? dp : null;
+ }
+}
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
new file mode 100644
index 000000000..d23a33033
--- /dev/null
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources.Theme;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.text.Layout;
+import android.text.StaticLayout;
+import android.text.TextPaint;
+import android.util.TypedValue;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
+
+ private static Theme sPreloaderTheme;
+
+ private final Rect mRect = new Rect();
+ private View mDefaultView;
+ private OnClickListener mClickListener;
+ private final LauncherAppWidgetInfo mInfo;
+ private final int mStartState;
+ private final Intent mIconLookupIntent;
+
+ private Bitmap mIcon;
+ private PreloadIconDrawable mDrawable;
+
+ private Drawable mCenterDrawable;
+ private Drawable mTopCornerDrawable;
+
+ private boolean mDrawableSizeChanged;
+
+ private final TextPaint mPaint;
+ private Layout mSetupTextLayout;
+
+ public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info) {
+ super(context);
+ mInfo = info;
+ mStartState = info.restoreStatus;
+ mIconLookupIntent = new Intent().setComponent(info.providerName);
+
+ mPaint = new TextPaint();
+ mPaint.setColor(0xFFFFFFFF);
+ mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
+ getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics()));
+ setBackgroundResource(R.drawable.quantum_panel_dark);
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
+ int maxHeight) {
+ // No-op
+ }
+
+ @Override
+ protected View getDefaultView() {
+ if (mDefaultView == null) {
+ mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
+ mDefaultView.setOnClickListener(this);
+ applyState();
+ }
+ return mDefaultView;
+ }
+
+ @Override
+ public void setOnClickListener(OnClickListener l) {
+ mClickListener = l;
+ }
+
+ @Override
+ public boolean isReinflateRequired() {
+ // Re inflate is required any time the widget restore status changes
+ return mStartState != mInfo.restoreStatus;
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mDrawableSizeChanged = true;
+ }
+
+ public void updateIcon(IconCache cache) {
+ Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user);
+ if (mIcon == icon) {
+ return;
+ }
+ mIcon = icon;
+ if (mDrawable != null) {
+ mDrawable.setCallback(null);
+ mDrawable = null;
+ }
+ if (mIcon != null) {
+ // The view displays two modes, one with a setup icon and another with a preload icon
+ // in the center.
+ if (isReadyForClickSetup()) {
+ mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting);
+ mTopCornerDrawable = new FastBitmapDrawable(mIcon);
+ } else {
+ if (sPreloaderTheme == null) {
+ sPreloaderTheme = getResources().newTheme();
+ sPreloaderTheme.applyStyle(R.style.PreloadIcon, true);
+ }
+
+ FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon);
+ mDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
+ mDrawable.setCallback(this);
+ applyState();
+ }
+ mDrawableSizeChanged = true;
+ }
+ }
+
+ @Override
+ protected boolean verifyDrawable(Drawable who) {
+ return (who == mDrawable) || super.verifyDrawable(who);
+ }
+
+ public void applyState() {
+ if (mDrawable != null) {
+ mDrawable.setLevel(Math.max(mInfo.installProgress, 0));
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ // AppWidgetHostView blocks all click events on the root view. Instead handle click events
+ // on the content and pass it along.
+ if (mClickListener != null) {
+ mClickListener.onClick(this);
+ }
+ }
+
+ public boolean isReadyForClickSetup() {
+ return (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
+ && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ if (mDrawable != null) {
+ if (mDrawableSizeChanged) {
+ int maxSize = LauncherAppState.getInstance().getDynamicGrid()
+ .getDeviceProfile().iconSizePx + 2 * mDrawable.getOutset();
+ int size = Math.min(maxSize, Math.min(
+ getWidth() - getPaddingLeft() - getPaddingRight(),
+ getHeight() - getPaddingTop() - getPaddingBottom()));
+
+ mRect.set(0, 0, size, size);
+ mRect.inset(mDrawable.getOutset(), mDrawable.getOutset());
+ mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
+ mDrawable.setBounds(mRect);
+ mDrawableSizeChanged = false;
+ }
+
+ mDrawable.draw(canvas);
+ } else if ((mCenterDrawable != null) && (mTopCornerDrawable != null)) {
+ if (mDrawableSizeChanged) {
+ DeviceProfile grid = getDeviceProfile();
+ int iconSize = grid.iconSizePx;
+ int paddingTop = getPaddingTop();
+ int paddingBottom = getPaddingBottom();
+ int paddingLeft = getPaddingLeft();
+ int paddingRight = getPaddingRight();
+
+ int availableWidth = getWidth() - paddingLeft - paddingRight;
+ int availableHeight = getHeight() - paddingTop - paddingBottom;
+
+ // Recreate the setup text.
+ mSetupTextLayout = new StaticLayout(
+ getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth,
+ Layout.Alignment.ALIGN_CENTER, 1, 0, true);
+ if (mSetupTextLayout.getLineCount() == 1) {
+ // The text fits in a single line. No need to draw the setup icon.
+ int size = Math.min(iconSize, Math.min(availableWidth,
+ availableHeight - mSetupTextLayout.getHeight()));
+ mRect.set(0, 0, size, size);
+ mRect.offsetTo((getWidth() - mRect.width()) / 2,
+ (getHeight() - mRect.height() - mSetupTextLayout.getHeight()
+ - grid.iconDrawablePaddingPx) / 2);
+
+ mTopCornerDrawable.setBounds(mRect);
+
+ // Update left and top to indicate the position where the text will be drawn.
+ mRect.left = paddingLeft;
+ mRect.top = mRect.bottom + grid.iconDrawablePaddingPx;
+ } else {
+ // The text can't be drawn in a single line. Draw a setup icon instead.
+ mSetupTextLayout = null;
+ int size = Math.min(iconSize, Math.min(
+ getWidth() - paddingLeft - paddingRight,
+ getHeight() - paddingTop - paddingBottom));
+ mRect.set(0, 0, size, size);
+ mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
+ mCenterDrawable.setBounds(mRect);
+
+ size = Math.min(size / 2,
+ Math.max(mRect.top - paddingTop, mRect.left - paddingLeft));
+ mTopCornerDrawable.setBounds(paddingLeft, paddingTop,
+ paddingLeft + size, paddingTop + size);
+ }
+ mDrawableSizeChanged = false;
+ }
+
+ if (mSetupTextLayout == null) {
+ mCenterDrawable.draw(canvas);
+ mTopCornerDrawable.draw(canvas);
+ } else {
+ canvas.save();
+ canvas.translate(mRect.left, mRect.top);
+ mSetupTextLayout.draw(canvas);
+ canvas.restore();
+ mTopCornerDrawable.draw(canvas);
+ }
+ }
+ }
+
+ private DeviceProfile getDeviceProfile() {
+ return LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile();
+ }
+}
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
new file mode 100644
index 000000000..2972c4f9b
--- /dev/null
+++ b/src/com/android/launcher3/PreloadIconDrawable.java
@@ -0,0 +1,249 @@
+package com.android.launcher3;
+
+import android.animation.ObjectAnimator;
+import android.content.res.Resources.Theme;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+class PreloadIconDrawable extends Drawable {
+
+ private static final float ANIMATION_PROGRESS_STOPPED = -1.0f;
+ private static final float ANIMATION_PROGRESS_STARTED = 0f;
+ private static final float ANIMATION_PROGRESS_COMPLETED = 1.0f;
+
+ private static final float MIN_SATUNATION = 0.2f;
+ private static final float MIN_LIGHTNESS = 0.6f;
+
+ private static final float ICON_SCALE_FACTOR = 0.5f;
+ private static final int DEFAULT_COLOR = 0xFF009688;
+
+ private static final Rect sTempRect = new Rect();
+
+ private final RectF mIndicatorRect = new RectF();
+ private boolean mIndicatorRectDirty;
+
+ private final Paint mPaint;
+ final Drawable mIcon;
+
+ private Drawable mBgDrawable;
+ private int mRingOutset;
+
+ private int mIndicatorColor = 0;
+
+ /**
+ * Indicates the progress of the preloader [0-100]. If it goes above 100, only the icon
+ * is shown with no progress bar.
+ */
+ private int mProgress = 0;
+
+ private float mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
+ private ObjectAnimator mAnimator;
+
+ public PreloadIconDrawable(Drawable icon, Theme theme) {
+ mIcon = icon;
+
+ mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mPaint.setStrokeCap(Paint.Cap.ROUND);
+
+ setBounds(icon.getBounds());
+ applyTheme(theme);
+ onLevelChange(0);
+ }
+
+ @Override
+ public void applyTheme(Theme t) {
+ TypedArray ta = t.obtainStyledAttributes(R.styleable.PreloadIconDrawable);
+ mBgDrawable = ta.getDrawable(R.styleable.PreloadIconDrawable_background);
+ mBgDrawable.setFilterBitmap(true);
+ mPaint.setStrokeWidth(ta.getDimension(R.styleable.PreloadIconDrawable_indicatorSize, 0));
+ mRingOutset = ta.getDimensionPixelSize(R.styleable.PreloadIconDrawable_ringOutset, 0);
+ ta.recycle();
+ onBoundsChange(getBounds());
+ invalidateSelf();
+ }
+
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ mIcon.setBounds(bounds);
+ if (mBgDrawable != null) {
+ sTempRect.set(bounds);
+ sTempRect.inset(-mRingOutset, -mRingOutset);
+ mBgDrawable.setBounds(sTempRect);
+ }
+ mIndicatorRectDirty = true;
+ }
+
+ public int getOutset() {
+ return mRingOutset;
+ }
+
+ /**
+ * The size of the indicator is same as the content region of the {@link #mBgDrawable} minus
+ * half the stroke size to accommodate the indicator.
+ */
+ private void initIndicatorRect() {
+ Drawable d = mBgDrawable;
+ Rect bounds = d.getBounds();
+
+ d.getPadding(sTempRect);
+ // Amount by which padding has to be scaled
+ float paddingScaleX = ((float) bounds.width()) / d.getIntrinsicWidth();
+ float paddingScaleY = ((float) bounds.height()) / d.getIntrinsicHeight();
+ mIndicatorRect.set(
+ bounds.left + sTempRect.left * paddingScaleX,
+ bounds.top + sTempRect.top * paddingScaleY,
+ bounds.right - sTempRect.right * paddingScaleX,
+ bounds.bottom - sTempRect.bottom * paddingScaleY);
+
+ float inset = mPaint.getStrokeWidth() / 2;
+ mIndicatorRect.inset(inset, inset);
+ mIndicatorRectDirty = false;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ final Rect r = new Rect(getBounds());
+ if (canvas.getClipBounds(sTempRect) && !Rect.intersects(sTempRect, r)) {
+ // The draw region has been clipped.
+ return;
+ }
+ if (mIndicatorRectDirty) {
+ initIndicatorRect();
+ }
+ final float iconScale;
+
+ if ((mAnimationProgress >= ANIMATION_PROGRESS_STARTED)
+ && (mAnimationProgress < ANIMATION_PROGRESS_COMPLETED)) {
+ mPaint.setAlpha((int) ((1 - mAnimationProgress) * 255));
+ mBgDrawable.setAlpha(mPaint.getAlpha());
+ mBgDrawable.draw(canvas);
+ canvas.drawOval(mIndicatorRect, mPaint);
+
+ iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress;
+ } else if (mAnimationProgress == ANIMATION_PROGRESS_STOPPED) {
+ mPaint.setAlpha(255);
+ iconScale = ICON_SCALE_FACTOR;
+ mBgDrawable.setAlpha(255);
+ mBgDrawable.draw(canvas);
+
+ if (mProgress >= 100) {
+ canvas.drawOval(mIndicatorRect, mPaint);
+ } else if (mProgress > 0) {
+ canvas.drawArc(mIndicatorRect, -90, mProgress * 3.6f, false, mPaint);
+ }
+ } else {
+ iconScale = 1;
+ }
+
+ canvas.save();
+ canvas.scale(iconScale, iconScale, r.exactCenterX(), r.exactCenterY());
+ mIcon.draw(canvas);
+ canvas.restore();
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ mIcon.setAlpha(alpha);
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ mIcon.setColorFilter(cf);
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ mProgress = level;
+
+ // Stop Animation
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ mAnimator = null;
+ }
+ mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
+ if (level > 0) {
+ // Set the paint color only when the level changes, so that the dominant color
+ // is only calculated when needed.
+ mPaint.setColor(getIndicatorColor());
+ }
+ if (mIcon instanceof FastBitmapDrawable) {
+ ((FastBitmapDrawable) mIcon).setGhostModeEnabled(level <= 0);
+ }
+
+ invalidateSelf();
+ return true;
+ }
+
+ /**
+ * Runs the finish animation if it is has not been run after last level change.
+ */
+ public void maybePerformFinishedAnimation() {
+ if (mAnimationProgress > ANIMATION_PROGRESS_STOPPED) {
+ return;
+ }
+ if (mAnimator != null) {
+ mAnimator.cancel();
+ }
+ setAnimationProgress(ANIMATION_PROGRESS_STARTED);
+ mAnimator = ObjectAnimator.ofFloat(this, "animationProgress",
+ ANIMATION_PROGRESS_STARTED, ANIMATION_PROGRESS_COMPLETED);
+ mAnimator.start();
+ }
+
+ public void setAnimationProgress(float progress) {
+ if (progress != mAnimationProgress) {
+ mAnimationProgress = progress;
+ invalidateSelf();
+ }
+ }
+
+ public float getAnimationProgress() {
+ return mAnimationProgress;
+ }
+
+ @Override
+ public int getIntrinsicHeight() {
+ return mIcon.getIntrinsicHeight();
+ }
+
+ @Override
+ public int getIntrinsicWidth() {
+ return mIcon.getIntrinsicWidth();
+ }
+
+ private int getIndicatorColor() {
+ if (mIndicatorColor != 0) {
+ return mIndicatorColor;
+ }
+ if (!(mIcon instanceof FastBitmapDrawable)) {
+ mIndicatorColor = DEFAULT_COLOR;
+ return mIndicatorColor;
+ }
+ mIndicatorColor = Utilities.findDominantColorByHue(
+ ((FastBitmapDrawable) mIcon).getBitmap(), 20);
+
+ // Make sure that the dominant color has enough saturation to be visible properly.
+ float[] hsv = new float[3];
+ Color.colorToHSV(mIndicatorColor, hsv);
+ if (hsv[1] < MIN_SATUNATION) {
+ mIndicatorColor = DEFAULT_COLOR;
+ return mIndicatorColor;
+ }
+ hsv[2] = Math.max(MIN_LIGHTNESS, hsv[2]);
+ mIndicatorColor = Color.HSVToColor(hsv);
+ return mIndicatorColor;
+ }
+}
diff --git a/src/com/android/launcher3/PreloadReceiver.java b/src/com/android/launcher3/PreloadReceiver.java
deleted file mode 100644
index ca25746eb..000000000
--- a/src/com/android/launcher3/PreloadReceiver.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.os.AsyncTask;
-import android.text.TextUtils;
-import android.util.Log;
-
-public class PreloadReceiver extends BroadcastReceiver {
- private static final String TAG = "Launcher.PreloadReceiver";
- private static final boolean LOGD = false;
-
- public static final String EXTRA_WORKSPACE_NAME =
- "com.android.launcher3.action.EXTRA_WORKSPACE_NAME";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- final LauncherProvider provider = LauncherAppState.getLauncherProvider();
- if (provider != null) {
- String name = intent.getStringExtra(EXTRA_WORKSPACE_NAME);
- final int workspaceResId = !TextUtils.isEmpty(name)
- ? context.getResources().getIdentifier(name, "xml", "com.android.launcher3") : 0;
- if (LOGD) {
- Log.d(TAG, "workspace name: " + name + " id: " + workspaceResId);
- }
- new AsyncTask<Void, Void, Void>() {
- public Void doInBackground(Void ... args) {
- provider.loadDefaultFavoritesIfNecessary(workspaceResId);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
- }
- }
-}
diff --git a/src/com/android/launcher3/ScrimView.java b/src/com/android/launcher3/ScrimView.java
deleted file mode 100644
index 68200fe64..000000000
--- a/src/com/android/launcher3/ScrimView.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.
- */
-
-package com.android.launcher3;
-
-import android.content.Context;
-import android.graphics.Rect;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-
-public class ScrimView extends FrameLayout implements Insettable {
-
- public ScrimView(Context context) {
- this(context, null, 0);
- }
-
- public ScrimView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public ScrimView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- }
-
- @Override
- public void setInsets(Rect insets) {
- // Do nothing
- }
-}
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index 79d114c06..daf343460 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -20,18 +20,44 @@ import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.util.Log;
+import com.android.launcher3.compat.UserHandleCompat;
+
import java.util.ArrayList;
+import java.util.Arrays;
/**
* Represents a launchable icon on the workspaces and in folders.
*/
-class ShortcutInfo extends ItemInfo {
+public class ShortcutInfo extends ItemInfo {
+
+ public static final int DEFAULT = 0;
+
+ /**
+ * The shortcut was restored from a backup and it not ready to be used. This is automatically
+ * set during backup/restore
+ */
+ public static final int FLAG_RESTORED_ICON = 1;
+
+ /**
+ * The icon was added as an auto-install app, and is not ready to be used. This flag can't
+ * be present along with {@link #FLAG_RESTORED_ICON}, and is set during default layout
+ * parsing.
+ */
+ public static final int FLAG_AUTOINTALL_ICON = 2;
+
+ /**
+ * The icon is being installed. If {@link FLAG_RESTORED_ICON} or {@link FLAG_AUTOINTALL_ICON}
+ * is set, then the icon is either being installed or is in a broken state.
+ */
+ public static final int FLAG_INSTALL_SESSION_ACTIVE = 4;
+
+ /**
+ * Indicates that the widget restore has started.
+ */
+ public static final int FLAG_RESTORE_STARTED = 8;
/**
* The intent used to start the application.
@@ -61,43 +87,52 @@ class ShortcutInfo extends ItemInfo {
*/
private Bitmap mIcon;
+ /**
+ * Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
+ * sd-card is not available).
+ */
+ boolean isDisabled = false;
+
+ int status;
+
+ /**
+ * The installation progress [0-100] of the package that this shortcut represents.
+ */
+ private int mInstallProgress;
+
+ /**
+ * Refer {@link AppInfo#firstInstallTime}.
+ */
long firstInstallTime;
+
+ /**
+ * TODO move this to {@link status}
+ */
int flags = 0;
/**
* If this shortcut is a placeholder, then intent will be a market intent for the package, and
* this will hold the original intent from the database. Otherwise, null.
+ * Refer {@link #FLAG_RESTORE_PENDING}, {@link #FLAG_INSTALL_PENDING}
*/
- Intent restoredIntent;
+ Intent promisedIntent;
ShortcutInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
- protected Intent getIntent() {
+ public Intent getIntent() {
return intent;
}
- protected Intent getRestoredIntent() {
- return restoredIntent;
- }
-
- /**
- * Overwrite placeholder data with restored data, or do nothing if this is not a placeholder.
- */
- public void restore() {
- if (restoredIntent != null) {
- intent = restoredIntent;
- restoredIntent = null;
- }
- }
-
-
- ShortcutInfo(Intent intent, CharSequence title, Bitmap icon) {
+ ShortcutInfo(Intent intent, CharSequence title, CharSequence contentDescription,
+ Bitmap icon, UserHandleCompat user) {
this();
this.intent = intent;
this.title = title;
+ this.contentDescription = contentDescription;
mIcon = icon;
+ this.user = user;
}
public ShortcutInfo(Context context, ShortcutInfo info) {
@@ -111,8 +146,10 @@ class ShortcutInfo extends ItemInfo {
}
mIcon = info.mIcon; // TODO: should make a copy here. maybe we don't need this ctor at all
customIcon = info.customIcon;
- initFlagsAndFirstInstallTime(
- getPackageInfo(context, intent.getComponent().getPackageName()));
+ flags = info.flags;
+ firstInstallTime = info.firstInstallTime;
+ user = info.user;
+ status = info.status;
}
/** TODO: Remove this. It's only called by ApplicationInfo.makeShortcut. */
@@ -125,22 +162,6 @@ class ShortcutInfo extends ItemInfo {
firstInstallTime = info.firstInstallTime;
}
- public static PackageInfo getPackageInfo(Context context, String packageName) {
- PackageInfo pi = null;
- try {
- PackageManager pm = context.getPackageManager();
- pi = pm.getPackageInfo(packageName, 0);
- } catch (NameNotFoundException e) {
- Log.d("ShortcutInfo", "PackageManager.getPackageInfo failed for " + packageName);
- }
- return pi;
- }
-
- void initFlagsAndFirstInstallTime(PackageInfo pi) {
- flags = AppInfo.initFlags(pi);
- firstInstallTime = AppInfo.initFirstInstallTime(pi);
- }
-
public void setIcon(Bitmap b) {
mIcon = b;
}
@@ -153,35 +174,19 @@ class ShortcutInfo extends ItemInfo {
}
public void updateIcon(IconCache iconCache) {
- mIcon = iconCache.getIcon(intent);
- usingFallbackIcon = iconCache.isDefaultIcon(mIcon);
- }
-
- /**
- * Creates the application intent based on a component name and various launch flags.
- * Sets {@link #itemType} to {@link LauncherSettings.BaseLauncherColumns#ITEM_TYPE_APPLICATION}.
- *
- * @param className the class name of the component representing the intent
- * @param launchFlags the launch flags
- */
- final void setActivity(Context context, ComponentName className, int launchFlags) {
- intent = new Intent(Intent.ACTION_MAIN);
- intent.addCategory(Intent.CATEGORY_LAUNCHER);
- intent.setComponent(className);
- intent.setFlags(launchFlags);
- itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_APPLICATION;
- initFlagsAndFirstInstallTime(
- getPackageInfo(context, intent.getComponent().getPackageName()));
+ mIcon = iconCache.getIcon(promisedIntent != null ? promisedIntent : intent, user);
+ usingFallbackIcon = iconCache.isDefaultIcon(mIcon, user);
}
@Override
- void onAddToDatabase(ContentValues values) {
- super.onAddToDatabase(values);
+ void onAddToDatabase(Context context, ContentValues values) {
+ super.onAddToDatabase(context, values);
String titleStr = title != null ? title.toString() : null;
values.put(LauncherSettings.BaseLauncherColumns.TITLE, titleStr);
- String uri = intent != null ? intent.toUri(0) : null;
+ String uri = promisedIntent != null ? promisedIntent.toUri(0)
+ : (intent != null ? intent.toUri(0) : null);
values.put(LauncherSettings.BaseLauncherColumns.INTENT, uri);
if (customIcon) {
@@ -205,10 +210,10 @@ class ShortcutInfo extends ItemInfo {
@Override
public String toString() {
- return "ShortcutInfo(title=" + title.toString() + "intent=" + intent + "id=" + this.id
+ return "ShortcutInfo(title=" + title + "intent=" + intent + "id=" + this.id
+ " type=" + this.itemType + " container=" + this.container + " screen=" + screenId
+ " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX + " spanY=" + spanY
- + " dropPos=" + dropPos + ")";
+ + " dropPos=" + Arrays.toString(dropPos) + " user=" + user + ")";
}
public static void dumpShortcutInfoList(String tag, String label,
@@ -219,5 +224,27 @@ class ShortcutInfo extends ItemInfo {
+ " customIcon=" + info.customIcon);
}
}
+
+ public ComponentName getTargetComponent() {
+ return promisedIntent != null ? promisedIntent.getComponent() : intent.getComponent();
+ }
+
+ public boolean hasStatusFlag(int flag) {
+ return (status & flag) != 0;
+ }
+
+
+ public final boolean isPromise() {
+ return hasStatusFlag(FLAG_RESTORED_ICON | FLAG_AUTOINTALL_ICON);
+ }
+
+ public int getInstallProgress() {
+ return mInstallProgress;
+ }
+
+ public void setInstallProgress(int progress) {
+ mInstallProgress = progress;
+ status |= FLAG_INSTALL_SESSION_ACTIVE;
+ }
}
diff --git a/src/com/android/launcher3/StartupReceiver.java b/src/com/android/launcher3/StartupReceiver.java
new file mode 100644
index 000000000..65f913fdf
--- /dev/null
+++ b/src/com/android/launcher3/StartupReceiver.java
@@ -0,0 +1,15 @@
+package com.android.launcher3;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class StartupReceiver extends BroadcastReceiver {
+
+ static final String SYSTEM_READY = "com.android.launcher3.SYSTEM_READY";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ context.sendStickyBroadcast(new Intent(SYSTEM_READY));
+ }
+}
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
index 882fb04a3..f3977e456 100644
--- a/src/com/android/launcher3/Stats.java
+++ b/src/com/android/launcher3/Stats.java
@@ -32,7 +32,6 @@ public class Stats {
private static final boolean LOCAL_LAUNCH_LOG = true;
public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
- public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS";
public static final String EXTRA_INTENT = "intent";
public static final String EXTRA_CONTAINER = "container";
public static final String EXTRA_SCREEN = "screen";
@@ -53,6 +52,8 @@ public class Stats {
private final Launcher mLauncher;
+ private final String mLaunchBroadcastPermission;
+
DataOutputStream mLog;
ArrayList<String> mIntents;
@@ -61,6 +62,9 @@ public class Stats {
public Stats(Launcher launcher) {
mLauncher = launcher;
+ mLaunchBroadcastPermission =
+ launcher.getResources().getString(R.string.receive_launch_broadcasts_permission);
+
loadStats();
if (LOCAL_LAUNCH_LOG) {
@@ -87,7 +91,7 @@ public class Stats {
}
},
new IntentFilter(ACTION_LAUNCH),
- PERM_LAUNCH,
+ mLaunchBroadcastPermission,
null
);
}
@@ -120,7 +124,7 @@ public class Stats {
.putExtra(EXTRA_CELLX, shortcut.cellX)
.putExtra(EXTRA_CELLY, shortcut.cellY);
}
- mLauncher.sendBroadcast(broadcastIntent, PERM_LAUNCH);
+ mLauncher.sendBroadcast(broadcastIntent, mLaunchBroadcastPermission);
incrementLaunch(flat);
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cbc978585..80d4b22ce 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,14 +18,18 @@ package com.android.launcher3;
import android.app.Activity;
import android.content.ActivityNotFoundException;
+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.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.graphics.Bitmap;
-import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
-import android.graphics.ColorMatrix;
-import android.graphics.ColorMatrixColorFilter;
+import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
@@ -33,8 +37,10 @@ import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.PaintDrawable;
-import android.util.DisplayMetrics;
+import android.os.Build;
import android.util.Log;
+import android.util.Pair;
+import android.util.SparseArray;
import android.view.View;
import android.widget.Toast;
@@ -51,10 +57,6 @@ public final class Utilities {
public static int sIconTextureWidth = -1;
public static int sIconTextureHeight = -1;
- private static final Paint sBlurPaint = new Paint();
- private static final Paint sGlowColorPressedPaint = new Paint();
- private static final Paint sGlowColorFocusedPaint = new Paint();
- private static final Paint sDisabledPaint = new Paint();
private static final Rect sOldBounds = new Rect();
private static final Canvas sCanvas = new Canvas();
@@ -65,6 +67,8 @@ public final class Utilities {
static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
static int sColorIndex = 0;
+ static int[] sLoc0 = new int[2];
+ static int[] sLoc1 = new int[2];
// To turn on these properties, type
// adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS]
@@ -74,7 +78,7 @@ public final class Utilities {
/**
* Returns a FastBitmapDrawable with the icon, accurately sized.
*/
- static Drawable createIconDrawable(Bitmap icon) {
+ public static FastBitmapDrawable createIconDrawable(Bitmap icon) {
FastBitmapDrawable d = new FastBitmapDrawable(icon);
d.setFilterBitmap(true);
resizeIconDrawable(d);
@@ -99,6 +103,13 @@ public final class Utilities {
}
/**
+ * Indicates if the device is running LMP or higher.
+ */
+ public static boolean isLmpOrAbove() {
+ return Build.VERSION.SDK_INT >= Build.VERSION_CODES.L;
+ }
+
+ /**
* Returns a bitmap suitable for the all apps view. Used to convert pre-ICS
* icon bitmaps that are stored in the database (which were 74x74 pixels at hdpi size)
* to the proper size (48dp)
@@ -305,22 +316,21 @@ public final class Utilities {
return scale;
}
+ /**
+ * Utility method to determine whether the given point, in local coordinates,
+ * is inside the view, where the area of the view is expanded by the slop factor.
+ * This method is called while processing touch-move events to determine if the event
+ * is still within the view.
+ */
+ public static boolean pointInView(View v, float localX, float localY, float slop) {
+ return localX >= -slop && localY >= -slop && localX < (v.getWidth() + slop) &&
+ localY < (v.getHeight() + slop);
+ }
+
private static void initStatics(Context context) {
final Resources resources = context.getResources();
- final DisplayMetrics metrics = resources.getDisplayMetrics();
- final float density = metrics.density;
-
sIconWidth = sIconHeight = (int) resources.getDimension(R.dimen.app_icon_size);
sIconTextureWidth = sIconTextureHeight = sIconWidth;
-
- sBlurPaint.setMaskFilter(new BlurMaskFilter(5 * density, BlurMaskFilter.Blur.NORMAL));
- sGlowColorPressedPaint.setColor(0xffffc300);
- sGlowColorFocusedPaint.setColor(0xffff8e00);
-
- ColorMatrix cm = new ColorMatrix();
- cm.setSaturation(0.2f);
- sDisabledPaint.setColorFilter(new ColorMatrixColorFilter(cm));
- sDisabledPaint.setAlpha(0x88);
}
public static void setIconSize(int widthPx) {
@@ -337,6 +347,25 @@ public final class Utilities {
}
}
+ public static int[] getCenterDeltaInScreenSpace(View v0, View v1, int[] delta) {
+ v0.getLocationInWindow(sLoc0);
+ v1.getLocationInWindow(sLoc1);
+
+ sLoc0[0] += (v0.getMeasuredWidth() * v0.getScaleX()) / 2;
+ sLoc0[1] += (v0.getMeasuredHeight() * v0.getScaleY()) / 2;
+ sLoc1[0] += (v1.getMeasuredWidth() * v1.getScaleX()) / 2;
+ sLoc1[1] += (v1.getMeasuredHeight() * v1.getScaleY()) / 2;
+
+ if (delta == null) {
+ delta = new int[2];
+ }
+
+ delta[0] = sLoc1[0] - sLoc0[0];
+ delta[1] = sLoc1[1] - sLoc0[1];
+
+ return delta;
+ }
+
public static void scaleRectAboutCenter(Rect r, float scale) {
int cx = r.centerX();
int cy = r.centerY();
@@ -358,4 +387,130 @@ public final class Utilities {
"or use the exported attribute for this activity.", e);
}
}
+
+ static boolean isSystemApp(Context context, Intent intent) {
+ PackageManager pm = context.getPackageManager();
+ ComponentName cn = intent.getComponent();
+ String packageName = null;
+ if (cn == null) {
+ ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ if ((info != null) && (info.activityInfo != null)) {
+ packageName = info.activityInfo.packageName;
+ }
+ } else {
+ packageName = cn.getPackageName();
+ }
+ if (packageName != null) {
+ try {
+ PackageInfo info = pm.getPackageInfo(packageName, 0);
+ return (info != null) && (info.applicationInfo != null) &&
+ ((info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This picks a dominant color, looking for high-saturation, high-value, repeated hues.
+ * @param bitmap The bitmap to scan
+ * @param samples The approximate max number of samples to use.
+ */
+ static int findDominantColorByHue(Bitmap bitmap, int samples) {
+ final int height = bitmap.getHeight();
+ final int width = bitmap.getWidth();
+ int sampleStride = (int) Math.sqrt((height * width) / samples);
+ if (sampleStride < 1) {
+ sampleStride = 1;
+ }
+
+ // This is an out-param, for getting the hsv values for an rgb
+ float[] hsv = new float[3];
+
+ // First get the best hue, by creating a histogram over 360 hue buckets,
+ // where each pixel contributes a score weighted by saturation, value, and alpha.
+ float[] hueScoreHistogram = new float[360];
+ float highScore = -1;
+ int bestHue = -1;
+
+ for (int y = 0; y < height; y += sampleStride) {
+ for (int x = 0; x < width; x += sampleStride) {
+ int argb = bitmap.getPixel(x, y);
+ int alpha = 0xFF & (argb >> 24);
+ if (alpha < 0x80) {
+ // Drop mostly-transparent pixels.
+ continue;
+ }
+ // Remove the alpha channel.
+ int rgb = argb | 0xFF000000;
+ Color.colorToHSV(rgb, hsv);
+ // Bucket colors by the 360 integer hues.
+ int hue = (int) hsv[0];
+ if (hue < 0 || hue >= hueScoreHistogram.length) {
+ // Defensively avoid array bounds violations.
+ continue;
+ }
+ float score = hsv[1] * hsv[2];
+ hueScoreHistogram[hue] += score;
+ if (hueScoreHistogram[hue] > highScore) {
+ highScore = hueScoreHistogram[hue];
+ bestHue = hue;
+ }
+ }
+ }
+
+ SparseArray<Float> rgbScores = new SparseArray<Float>();
+ int bestColor = 0xff000000;
+ highScore = -1;
+ // Go back over the RGB colors that match the winning hue,
+ // creating a histogram of weighted s*v scores, for up to 100*100 [s,v] buckets.
+ // The highest-scoring RGB color wins.
+ for (int y = 0; y < height; y += sampleStride) {
+ for (int x = 0; x < width; x += sampleStride) {
+ int rgb = bitmap.getPixel(x, y) | 0xff000000;
+ Color.colorToHSV(rgb, hsv);
+ int hue = (int) hsv[0];
+ if (hue == bestHue) {
+ float s = hsv[1];
+ float v = hsv[2];
+ int bucket = (int) (s * 100) + (int) (v * 10000);
+ // Score by cumulative saturation * value.
+ float score = s * v;
+ Float oldTotal = rgbScores.get(bucket);
+ float newTotal = oldTotal == null ? score : oldTotal + score;
+ rgbScores.put(bucket, newTotal);
+ if (newTotal > highScore) {
+ highScore = newTotal;
+ // All the colors in the winning bucket are very similar. Last in wins.
+ bestColor = rgb;
+ }
+ }
+ }
+ }
+ return bestColor;
+ }
+
+ /*
+ * Finds a system apk which had a broadcast receiver listening to a particular action.
+ * @param action intent action used to find the apk
+ * @return a pair of apk package name and the resources.
+ */
+ static Pair<String, Resources> findSystemApk(String action, PackageManager pm) {
+ final Intent intent = new Intent(action);
+ for (ResolveInfo info : pm.queryBroadcastReceivers(intent, 0)) {
+ if (info.activityInfo != null &&
+ (info.activityInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ final String packageName = info.activityInfo.packageName;
+ try {
+ final Resources res = pm.getResourcesForApplication(packageName);
+ return Pair.create(packageName, res);
+ } catch (NameNotFoundException e) {
+ Log.w(TAG, "Failed to find resources for " + packageName);
+ }
+ }
+ }
+ return null;
+ }
}
diff --git a/src/com/android/launcher3/WidgetAdder.java b/src/com/android/launcher3/WidgetAdder.java
deleted file mode 100644
index 79ac50492..000000000
--- a/src/com/android/launcher3/WidgetAdder.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.android.launcher3;
-
-import android.app.Activity;
-
-public class WidgetAdder extends Activity {
-
-}
diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java
index 3db0b51ad..5aa719027 100644
--- a/src/com/android/launcher3/WidgetPreviewLoader.java
+++ b/src/com/android/launcher3/WidgetPreviewLoader.java
@@ -5,16 +5,17 @@ import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.database.Cursor;
+import android.database.sqlite.SQLiteCantOpenDatabaseException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDiskIOException;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
+import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
@@ -27,129 +28,138 @@ import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.util.Log;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
-abstract class SoftReferenceThreadLocal<T> {
- private ThreadLocal<SoftReference<T>> mThreadLocal;
- public SoftReferenceThreadLocal() {
- mThreadLocal = new ThreadLocal<SoftReference<T>>();
- }
+public class WidgetPreviewLoader {
- abstract T initialValue();
+ private static abstract class SoftReferenceThreadLocal<T> {
+ private ThreadLocal<SoftReference<T>> mThreadLocal;
+ public SoftReferenceThreadLocal() {
+ mThreadLocal = new ThreadLocal<SoftReference<T>>();
+ }
- public void set(T t) {
- mThreadLocal.set(new SoftReference<T>(t));
- }
+ abstract T initialValue();
- public T get() {
- SoftReference<T> reference = mThreadLocal.get();
- T obj;
- if (reference == null) {
- obj = initialValue();
- mThreadLocal.set(new SoftReference<T>(obj));
- return obj;
- } else {
- obj = reference.get();
- if (obj == null) {
+ public void set(T t) {
+ mThreadLocal.set(new SoftReference<T>(t));
+ }
+
+ public T get() {
+ SoftReference<T> reference = mThreadLocal.get();
+ T obj;
+ if (reference == null) {
obj = initialValue();
mThreadLocal.set(new SoftReference<T>(obj));
+ return obj;
+ } else {
+ obj = reference.get();
+ if (obj == null) {
+ obj = initialValue();
+ mThreadLocal.set(new SoftReference<T>(obj));
+ }
+ return obj;
}
- return obj;
}
}
-}
-class CanvasCache extends SoftReferenceThreadLocal<Canvas> {
- @Override
- protected Canvas initialValue() {
- return new Canvas();
+ private static class CanvasCache extends SoftReferenceThreadLocal<Canvas> {
+ @Override
+ protected Canvas initialValue() {
+ return new Canvas();
+ }
}
-}
-class PaintCache extends SoftReferenceThreadLocal<Paint> {
- @Override
- protected Paint initialValue() {
- return null;
+ private static class PaintCache extends SoftReferenceThreadLocal<Paint> {
+ @Override
+ protected Paint initialValue() {
+ return null;
+ }
}
-}
-class BitmapCache extends SoftReferenceThreadLocal<Bitmap> {
- @Override
- protected Bitmap initialValue() {
- return null;
+ private static class BitmapCache extends SoftReferenceThreadLocal<Bitmap> {
+ @Override
+ protected Bitmap initialValue() {
+ return null;
+ }
}
-}
-class RectCache extends SoftReferenceThreadLocal<Rect> {
- @Override
- protected Rect initialValue() {
- return new Rect();
+ private static class RectCache extends SoftReferenceThreadLocal<Rect> {
+ @Override
+ protected Rect initialValue() {
+ return new Rect();
+ }
}
-}
-class BitmapFactoryOptionsCache extends SoftReferenceThreadLocal<BitmapFactory.Options> {
- @Override
- protected BitmapFactory.Options initialValue() {
- return new BitmapFactory.Options();
+ private static class BitmapFactoryOptionsCache extends
+ SoftReferenceThreadLocal<BitmapFactory.Options> {
+ @Override
+ protected BitmapFactory.Options initialValue() {
+ return new BitmapFactory.Options();
+ }
}
-}
-public class WidgetPreviewLoader {
- static final String TAG = "WidgetPreviewLoader";
- static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version";
+ private static final String TAG = "WidgetPreviewLoader";
+ private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version";
+
+ private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
+ private static final HashSet<String> sInvalidPackages = new HashSet<String>();
+
+ // Used for drawing shortcut previews
+ private final BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
+ private final PaintCache mCachedShortcutPreviewPaint = new PaintCache();
+ private final CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
+
+ // Used for drawing widget previews
+ private final CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
+ private final RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
+ private final RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
+ private final PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
+ private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache();
+ private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
+
+ private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>();
+ private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<>();
+
+ private final Context mContext;
+ private final int mAppIconSize;
+ private final IconCache mIconCache;
+ private final AppWidgetManagerCompat mManager;
private int mPreviewBitmapWidth;
private int mPreviewBitmapHeight;
private String mSize;
- private Context mContext;
- private PackageManager mPackageManager;
private PagedViewCellLayout mWidgetSpacingLayout;
- // Used for drawing shortcut previews
- private BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
- private PaintCache mCachedShortcutPreviewPaint = new PaintCache();
- private CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
-
- // Used for drawing widget previews
- private CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
- private RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
- private RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
- private PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
private String mCachedSelectQuery;
- private BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
-
- private int mAppIconSize;
- private IconCache mIconCache;
- private final float sWidgetPreviewIconPaddingPercentage = 0.25f;
private CacheDb mDb;
- private HashMap<String, WeakReference<Bitmap>> mLoadedPreviews;
- private ArrayList<SoftReference<Bitmap>> mUnusedBitmaps;
- private static HashSet<String> sInvalidPackages;
-
- static {
- sInvalidPackages = new HashSet<String>();
- }
+ private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
public WidgetPreviewLoader(Context context) {
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
mContext = context;
- mPackageManager = mContext.getPackageManager();
mAppIconSize = grid.iconSizePx;
mIconCache = app.getIconCache();
+ mManager = AppWidgetManagerCompat.getInstance(context);
+
mDb = app.getWidgetPreviewCacheDb();
- mLoadedPreviews = new HashMap<String, WeakReference<Bitmap>>();
- mUnusedBitmaps = new ArrayList<SoftReference<Bitmap>>();
SharedPreferences sp = context.getSharedPreferences(
LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE);
@@ -165,7 +175,7 @@ public class WidgetPreviewLoader {
editor.commit();
}
}
-
+
public void recreateDb() {
LauncherAppState app = LauncherAppState.getInstance();
app.recreateWidgetPreviewDb();
@@ -184,18 +194,19 @@ public class WidgetPreviewLoader {
final String name = getObjectName(o);
final String packageName = getObjectPackage(o);
// check if the package is valid
- boolean packageValid = true;
synchronized(sInvalidPackages) {
- packageValid = !sInvalidPackages.contains(packageName);
- }
- if (!packageValid) {
- return null;
+ boolean packageValid = !sInvalidPackages.contains(packageName);
+ if (!packageValid) {
+ return null;
+ }
}
- if (packageValid) {
- synchronized(mLoadedPreviews) {
- // check if it exists in our existing cache
- if (mLoadedPreviews.containsKey(name) && mLoadedPreviews.get(name).get() != null) {
- return mLoadedPreviews.get(name).get();
+ synchronized(mLoadedPreviews) {
+ // check if it exists in our existing cache
+ if (mLoadedPreviews.containsKey(name)) {
+ WeakReference<Bitmap> bitmapReference = mLoadedPreviews.get(name);
+ Bitmap bitmap = bitmapReference.get();
+ if (bitmap != null) {
+ return bitmap;
}
}
}
@@ -203,11 +214,13 @@ public class WidgetPreviewLoader {
Bitmap unusedBitmap = null;
synchronized(mUnusedBitmaps) {
// not in cache; we need to load it from the db
- while ((unusedBitmap == null || !unusedBitmap.isMutable() ||
- unusedBitmap.getWidth() != mPreviewBitmapWidth ||
- unusedBitmap.getHeight() != mPreviewBitmapHeight)
- && mUnusedBitmaps.size() > 0) {
- unusedBitmap = mUnusedBitmaps.remove(0).get();
+ while (unusedBitmap == null && mUnusedBitmaps.size() > 0) {
+ Bitmap candidate = mUnusedBitmaps.remove(0).get();
+ if (candidate != null && candidate.isMutable() &&
+ candidate.getWidth() == mPreviewBitmapWidth &&
+ candidate.getHeight() == mPreviewBitmapHeight) {
+ unusedBitmap = candidate;
+ }
}
if (unusedBitmap != null) {
final Canvas c = mCachedAppWidgetPreviewCanvas.get();
@@ -221,12 +234,7 @@ public class WidgetPreviewLoader {
unusedBitmap = Bitmap.createBitmap(mPreviewBitmapWidth, mPreviewBitmapHeight,
Bitmap.Config.ARGB_8888);
}
-
- Bitmap preview = null;
-
- if (packageValid) {
- preview = readFromDb(name, unusedBitmap);
- }
+ Bitmap preview = readFromDb(name, unusedBitmap);
if (preview != null) {
synchronized(mLoadedPreviews) {
@@ -320,7 +328,7 @@ public class WidgetPreviewLoader {
String output;
if (o instanceof AppWidgetProviderInfo) {
sb.append(WIDGET_PREFIX);
- sb.append(((AppWidgetProviderInfo) o).provider.flattenToString());
+ sb.append(((AppWidgetProviderInfo) o).toString());
output = sb.toString();
sb.setLength(0);
} else {
@@ -358,6 +366,9 @@ public class WidgetPreviewLoader {
db.insert(CacheDb.TABLE_NAME, null, values);
} catch (SQLiteDiskIOException e) {
recreateDb();
+ } catch (SQLiteCantOpenDatabaseException e) {
+ dumpOpenFiles();
+ throw e;
}
}
@@ -367,6 +378,9 @@ public class WidgetPreviewLoader {
try {
db.delete(CacheDb.TABLE_NAME, null, null);
} catch (SQLiteDiskIOException e) {
+ } catch (SQLiteCantOpenDatabaseException e) {
+ dumpOpenFiles();
+ throw e;
}
}
@@ -387,6 +401,9 @@ public class WidgetPreviewLoader {
} // args to SELECT query
);
} catch (SQLiteDiskIOException e) {
+ } catch (SQLiteCantOpenDatabaseException e) {
+ dumpOpenFiles();
+ throw e;
}
synchronized(sInvalidPackages) {
sInvalidPackages.remove(packageName);
@@ -396,7 +413,7 @@ public class WidgetPreviewLoader {
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
}
- public static void removeItemFromDb(final CacheDb cacheDb, final String objectName) {
+ private static void removeItemFromDb(final CacheDb cacheDb, final String objectName) {
new AsyncTask<Void, Void, Void>() {
public Void doInBackground(Void ... args) {
SQLiteDatabase db = cacheDb.getWritableDatabase();
@@ -405,6 +422,9 @@ public class WidgetPreviewLoader {
CacheDb.COLUMN_NAME + " = ? ", // SELECT query
new String[] { objectName }); // args to SELECT query
} catch (SQLiteDiskIOException e) {
+ } catch (SQLiteCantOpenDatabaseException e) {
+ dumpOpenFiles();
+ throw e;
}
return null;
}
@@ -430,6 +450,9 @@ public class WidgetPreviewLoader {
} catch (SQLiteDiskIOException e) {
recreateDb();
return null;
+ } catch (SQLiteCantOpenDatabaseException e) {
+ dumpOpenFiles();
+ throw e;
}
if (result.getCount() > 0) {
result.moveToFirst();
@@ -450,7 +473,7 @@ public class WidgetPreviewLoader {
}
}
- public Bitmap generatePreview(Object info, Bitmap preview) {
+ private Bitmap generatePreview(Object info, Bitmap preview) {
if (preview != null &&
(preview.getWidth() != mPreviewBitmapWidth ||
preview.getHeight() != mPreviewBitmapHeight)) {
@@ -468,8 +491,8 @@ public class WidgetPreviewLoader {
int[] cellSpans = Launcher.getSpanForWidget(mContext, info);
int maxWidth = maxWidthForWidgetPreview(cellSpans[0]);
int maxHeight = maxHeightForWidgetPreview(cellSpans[1]);
- return generateWidgetPreview(info.provider, info.previewImage, info.icon,
- cellSpans[0], cellSpans[1], maxWidth, maxHeight, preview, null);
+ return generateWidgetPreview(info, cellSpans[0], cellSpans[1],
+ maxWidth, maxHeight, preview, null);
}
public int maxWidthForWidgetPreview(int spanX) {
@@ -482,20 +505,20 @@ public class WidgetPreviewLoader {
mWidgetSpacingLayout.estimateCellHeight(spanY));
}
- public Bitmap generateWidgetPreview(ComponentName provider, int previewImage,
- int iconId, int cellHSpan, int cellVSpan, int maxPreviewWidth, int maxPreviewHeight,
- Bitmap preview, int[] preScaledWidthOut) {
+ public Bitmap generateWidgetPreview(AppWidgetProviderInfo info, int cellHSpan, int cellVSpan,
+ int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) {
// Load the preview image if possible
- String packageName = provider.getPackageName();
if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE;
Drawable drawable = null;
- if (previewImage != 0) {
- drawable = mPackageManager.getDrawable(packageName, previewImage, null);
- if (drawable == null) {
+ if (info.previewImage != 0) {
+ drawable = mManager.loadPreview(info);
+ if (drawable != null) {
+ drawable = mutateOnMainThread(drawable);
+ } else {
Log.w(TAG, "Can't load widget preview drawable 0x" +
- Integer.toHexString(previewImage) + " for provider: " + provider);
+ Integer.toHexString(info.previewImage) + " for provider: " + info.provider);
}
}
@@ -511,6 +534,7 @@ public class WidgetPreviewLoader {
if (cellHSpan < 1) cellHSpan = 1;
if (cellVSpan < 1) cellVSpan = 1;
+ // This Drawable is not directly drawn, so there's no need to mutate it.
BitmapDrawable previewDrawable = (BitmapDrawable) mContext.getResources()
.getDrawable(R.drawable.widget_tile);
final int previewDrawableWidth = previewDrawable
@@ -520,31 +544,33 @@ public class WidgetPreviewLoader {
previewWidth = previewDrawableWidth * cellHSpan;
previewHeight = previewDrawableHeight * cellVSpan;
- defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight,
- Config.ARGB_8888);
+ defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
final Canvas c = mCachedAppWidgetPreviewCanvas.get();
c.setBitmap(defaultPreview);
- previewDrawable.setBounds(0, 0, previewWidth, previewHeight);
- previewDrawable.setTileModeXY(Shader.TileMode.REPEAT,
- Shader.TileMode.REPEAT);
- previewDrawable.draw(c);
+ Paint p = mDefaultAppWidgetPreviewPaint.get();
+ if (p == null) {
+ p = new Paint();
+ p.setShader(new BitmapShader(previewDrawable.getBitmap(),
+ Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+ mDefaultAppWidgetPreviewPaint.set(p);
+ }
+ final Rect dest = mCachedAppWidgetPreviewDestRect.get();
+ dest.set(0, 0, previewWidth, previewHeight);
+ c.drawRect(dest, p);
c.setBitmap(null);
// Draw the icon in the top left corner
- int minOffset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage);
+ int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE);
int smallestSide = Math.min(previewWidth, previewHeight);
float iconScale = Math.min((float) smallestSide
/ (mAppIconSize + 2 * minOffset), 1f);
try {
- Drawable icon = null;
- int hoffset =
- (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2);
- int yoffset =
- (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
- if (iconId > 0)
- icon = mIconCache.getFullResIcon(packageName, iconId);
+ Drawable icon = mManager.loadIcon(info, mIconCache);
if (icon != null) {
+ int hoffset = (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2);
+ int yoffset = (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
+ icon = mutateOnMainThread(icon);
renderDrawableToBitmap(icon, defaultPreview, hoffset,
yoffset, (int) (mAppIconSize * iconScale),
(int) (mAppIconSize * iconScale));
@@ -594,7 +620,7 @@ public class WidgetPreviewLoader {
c.drawBitmap(defaultPreview, src, dest, p);
c.setBitmap(null);
}
- return preview;
+ return mManager.getBadgeBitmap(info, preview);
}
private Bitmap generateShortcutPreview(
@@ -612,7 +638,7 @@ public class WidgetPreviewLoader {
c.setBitmap(null);
}
// Render the icon
- Drawable icon = mIconCache.getFullResIcon(info);
+ Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info));
int paddingTop = mContext.
getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top);
@@ -652,18 +678,10 @@ public class WidgetPreviewLoader {
return preview;
}
-
- public static void renderDrawableToBitmap(
- Drawable d, Bitmap bitmap, int x, int y, int w, int h) {
- renderDrawableToBitmap(d, bitmap, x, y, w, h, 1f);
- }
-
private static void renderDrawableToBitmap(
- Drawable d, Bitmap bitmap, int x, int y, int w, int h,
- float scale) {
+ Drawable d, Bitmap bitmap, int x, int y, int w, int h) {
if (bitmap != null) {
Canvas c = new Canvas(bitmap);
- c.scale(scale, scale);
Rect oldBounds = d.copyBounds();
d.setBounds(x, y, x + w, y + h);
d.draw(c);
@@ -672,4 +690,98 @@ public class WidgetPreviewLoader {
}
}
+ private Drawable mutateOnMainThread(final Drawable drawable) {
+ try {
+ return mMainThreadExecutor.submit(new Callable<Drawable>() {
+ @Override
+ public Drawable call() throws Exception {
+ return drawable.mutate();
+ }
+ }).get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static final int MAX_OPEN_FILES = 1024;
+ private static final int SAMPLE_RATE = 23;
+ /**
+ * Dumps all files that are open in this process without allocating a file descriptor.
+ */
+ private static void dumpOpenFiles() {
+ try {
+ Log.i(TAG, "DUMP OF OPEN FILES (sample rate: 1 every " + SAMPLE_RATE + "):");
+ final String TYPE_APK = "apk";
+ final String TYPE_JAR = "jar";
+ final String TYPE_PIPE = "pipe";
+ final String TYPE_SOCKET = "socket";
+ final String TYPE_DB = "db";
+ final String TYPE_ANON_INODE = "anon_inode";
+ final String TYPE_DEV = "dev";
+ final String TYPE_NON_FS = "non-fs";
+ final String TYPE_OTHER = "other";
+ List<String> types = Arrays.asList(TYPE_APK, TYPE_JAR, TYPE_PIPE, TYPE_SOCKET, TYPE_DB,
+ TYPE_ANON_INODE, TYPE_DEV, TYPE_NON_FS, TYPE_OTHER);
+ int[] count = new int[types.size()];
+ int[] duplicates = new int[types.size()];
+ HashSet<String> files = new HashSet<String>();
+ int total = 0;
+ for (int i = 0; i < MAX_OPEN_FILES; i++) {
+ // This is a gigantic hack but unfortunately the only way to resolve an fd
+ // to a file name. Note that we have to loop over all possible fds because
+ // reading the directory would require allocating a new fd. The kernel is
+ // currently implemented such that no fd is larger then the current rlimit,
+ // which is why it's safe to loop over them in such a way.
+ String fd = "/proc/self/fd/" + i;
+ try {
+ // getCanonicalPath() uses readlink behind the scene which doesn't require
+ // a file descriptor.
+ String resolved = new File(fd).getCanonicalPath();
+ int type = types.indexOf(TYPE_OTHER);
+ if (resolved.startsWith("/dev/")) {
+ type = types.indexOf(TYPE_DEV);
+ } else if (resolved.endsWith(".apk")) {
+ type = types.indexOf(TYPE_APK);
+ } else if (resolved.endsWith(".jar")) {
+ type = types.indexOf(TYPE_JAR);
+ } else if (resolved.contains("/fd/pipe:")) {
+ type = types.indexOf(TYPE_PIPE);
+ } else if (resolved.contains("/fd/socket:")) {
+ type = types.indexOf(TYPE_SOCKET);
+ } else if (resolved.contains("/fd/anon_inode:")) {
+ type = types.indexOf(TYPE_ANON_INODE);
+ } else if (resolved.endsWith(".db") || resolved.contains("/databases/")) {
+ type = types.indexOf(TYPE_DB);
+ } else if (resolved.startsWith("/proc/") && resolved.contains("/fd/")) {
+ // Those are the files that don't point anywhere on the file system.
+ // getCanonicalPath() wrongly interprets these as relative symlinks and
+ // resolves them within /proc/<pid>/fd/.
+ type = types.indexOf(TYPE_NON_FS);
+ }
+ count[type]++;
+ total++;
+ if (files.contains(resolved)) {
+ duplicates[type]++;
+ }
+ files.add(resolved);
+ if (total % SAMPLE_RATE == 0) {
+ Log.i(TAG, " fd " + i + ": " + resolved
+ + " (" + types.get(type) + ")");
+ }
+ } catch (IOException e) {
+ // Ignoring exceptions for non-existing file descriptors.
+ }
+ }
+ for (int i = 0; i < types.size(); i++) {
+ Log.i(TAG, String.format("Open %10s files: %4d total, %4d duplicates",
+ types.get(i), count[i], duplicates[i]));
+ }
+ } catch (Throwable t) {
+ // Catch everything. This is called from an exception handler that we shouldn't upset.
+ Log.e(TAG, "Unable to log open files.", t);
+ }
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 567abfa47..774996e56 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -31,12 +31,16 @@ import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
+import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -44,6 +48,7 @@ import android.graphics.Region.Op;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Handler;
import android.os.IBinder;
import android.os.Parcelable;
import android.support.v4.view.ViewCompat;
@@ -63,11 +68,17 @@ import android.widget.TextView;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
+import com.android.launcher3.compat.UserHandleCompat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -96,6 +107,9 @@ public class Workspace extends SmoothPagedView
private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f;
+ static final boolean MAP_NO_RECURSE = false;
+ static final boolean MAP_RECURSE = true;
+
// These animators are used to fade the children's outlines
private ObjectAnimator mChildrenOutlineFadeInAnimation;
private ObjectAnimator mChildrenOutlineFadeOutAnimation;
@@ -104,9 +118,6 @@ public class Workspace extends SmoothPagedView
// These properties refer to the background protection gradient used for AllApps and Customize
private ValueAnimator mBackgroundFadeInAnimation;
private ValueAnimator mBackgroundFadeOutAnimation;
- private Drawable mBackground;
- boolean mDrawBackground = true;
- private float mBackgroundAlpha = 0;
private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200;
private long mTouchDownTime = -1;
@@ -123,13 +134,14 @@ public class Workspace extends SmoothPagedView
private static boolean sAccessibilityEnabled;
// The screen id used for the empty screen always present to the right.
- private final static long EXTRA_EMPTY_SCREEN_ID = -201;
+ final static long EXTRA_EMPTY_SCREEN_ID = -201;
private final static long CUSTOM_CONTENT_SCREEN_ID = -301;
private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
private Runnable mRemoveEmptyScreenRunnable;
+ private boolean mDeferRemoveExtraEmptyScreen = false;
/**
* CellInfo for the cell that is currently being dragged
@@ -185,7 +197,7 @@ public class Workspace extends SmoothPagedView
// State variable that indicates whether the pages are small (ie when you're
// in all apps or customize mode)
- enum State { NORMAL, SPRING_LOADED, SMALL, OVERVIEW};
+ enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN};
private State mState = State.NORMAL;
private boolean mIsSwitchingState = false;
@@ -200,11 +212,10 @@ public class Workspace extends SmoothPagedView
private HolographicOutlineHelper mOutlineHelper;
private Bitmap mDragOutline = null;
- private final Rect mTempRect = new Rect();
+ private static final Rect sTempRect = new Rect();
private final int[] mTempXY = new int[2];
private int[] mTempVisiblePagesRange = new int[2];
- private boolean mOverscrollTransformsSet;
- private float mLastOverscrollPivotX;
+ private boolean mOverscrollEffectSet;
public static final int DRAG_BITMAP_PADDING = 2;
private boolean mWorkspaceFadeInAdjacentScreens;
@@ -230,6 +241,8 @@ public class Workspace extends SmoothPagedView
private DropTarget.DragEnforcer mDragEnforcer;
private float mMaxDistanceForFolderCreation;
+ private final Canvas mCanvas = new Canvas();
+
// Variables relating to touch disambiguation (scrolling workspace vs. scrolling a widget)
private float mXDown;
private float mYDown;
@@ -270,6 +283,8 @@ public class Workspace extends SmoothPagedView
private int mLastChildCount = -1;
private float mTransitionProgress;
+ float mOverScrollEffect = 0f;
+
private Runnable mDeferredAction;
private boolean mDeferDropAfterUninstall;
private boolean mUninstallSuccessful;
@@ -392,13 +407,23 @@ public class Workspace extends SmoothPagedView
@Override
public void run() {
if (mIsDragOccuring) {
+ mDeferRemoveExtraEmptyScreen = false;
addExtraEmptyScreenOnDrag();
}
}
});
}
+
+ public void deferRemoveExtraEmptyScreen() {
+ mDeferRemoveExtraEmptyScreen = true;
+ }
+
public void onDragEnd() {
+ if (!mDeferRemoveExtraEmptyScreen) {
+ removeExtraEmptyScreen(true, mDragSourceInternal != null);
+ }
+
mIsDragOccuring = false;
updateChildrenLayersEnabled(false);
mLauncher.unlockScreenOrientation(false);
@@ -415,7 +440,6 @@ public class Workspace extends SmoothPagedView
* Initializes various states for this workspace.
*/
protected void initWorkspace() {
- Context context = getContext();
mCurrentPage = mDefaultPage;
Launcher.setScreen(mCurrentPage);
LauncherAppState app = LauncherAppState.getInstance();
@@ -429,13 +453,6 @@ public class Workspace extends SmoothPagedView
setMinScale(mOverviewModeShrinkFactor);
setupLayoutTransition();
- final Resources res = getResources();
- try {
- mBackground = res.getDrawable(R.drawable.apps_customize_bg);
- } catch (Resources.NotFoundException e) {
- // In this case, we will skip drawing background protection
- }
-
mWallpaperOffset = new WallpaperOffsetInterpolator();
Display display = mLauncher.getWindowManager().getDefaultDisplay();
display.getSize(mDisplaySize);
@@ -570,6 +587,7 @@ public class Workspace extends SmoothPagedView
CellLayout customScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
customScreen.disableBackground();
+ customScreen.disableDragTarget();
mWorkspaceScreens.put(CUSTOM_CONTENT_SCREEN_ID, customScreen);
mScreenOrder.add(0, CUSTOM_CONTENT_SCREEN_ID);
@@ -583,7 +601,6 @@ public class Workspace extends SmoothPagedView
mDefaultPage = mOriginalDefaultPage + 1;
// Update the custom content hint
- mLauncher.getLauncherClings().updateCustomContentHintVisibility();
if (mRestorePage != INVALID_RESTORE_PAGE) {
mRestorePage = mRestorePage + 1;
} else {
@@ -612,7 +629,6 @@ public class Workspace extends SmoothPagedView
mDefaultPage = mOriginalDefaultPage - 1;
// Update the custom content hint
- mLauncher.getLauncherClings().updateCustomContentHintVisibility();
if (mRestorePage != INVALID_RESTORE_PAGE) {
mRestorePage = mRestorePage - 1;
} else {
@@ -693,6 +709,12 @@ public class Workspace extends SmoothPagedView
// Log to disk
Launcher.addDumpLog(TAG, "11683562 - convertFinalScreenToEmptyScreenIfNecessary()", true);
+ if (mLauncher.isWorkspaceLoading()) {
+ // Invalid and dangerous operation if workspace is loading
+ Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
+ return;
+ }
+
if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
@@ -715,21 +737,26 @@ public class Workspace extends SmoothPagedView
}
}
- public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete) {
- removeExtraEmptyScreen(animate, onComplete, 0, false);
+ public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
+ removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
}
- public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete,
+ public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
final int delay, final boolean stripEmptyScreens) {
// Log to disk
Launcher.addDumpLog(TAG, "11683562 - removeExtraEmptyScreen()", true);
+ if (mLauncher.isWorkspaceLoading()) {
+ // Don't strip empty screens if the workspace is still loading
+ Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
+ return;
+ }
+
if (delay > 0) {
postDelayed(new Runnable() {
@Override
public void run() {
- removeExtraEmptyScreen(animate, onComplete, 0, stripEmptyScreens);
+ removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
}
-
}, delay);
return;
}
@@ -807,6 +834,11 @@ public class Workspace extends SmoothPagedView
public long commitExtraEmptyScreen() {
// Log to disk
Launcher.addDumpLog(TAG, "11683562 - commitExtraEmptyScreen()", true);
+ if (mLauncher.isWorkspaceLoading()) {
+ // Invalid and dangerous operation if workspace is loading
+ Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
+ return -1;
+ }
int index = getPageIndexForScreenId(EXTRA_EMPTY_SCREEN_ID);
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
@@ -864,7 +896,8 @@ public class Workspace extends SmoothPagedView
Launcher.addDumpLog(TAG, "11683562 - stripEmptyScreens()", true);
if (mLauncher.isWorkspaceLoading()) {
- // Don't strip empty screens if the workspace is still loading
+ // Don't strip empty screens if the workspace is still loading.
+ // This is dangerous and can result in data loss.
Launcher.addDumpLog(TAG, " - workspace loading, skip", true);
return;
}
@@ -969,7 +1002,7 @@ public class Workspace extends SmoothPagedView
final CellLayout layout;
if (container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
layout = mLauncher.getHotseat().getLayout();
- child.setOnKeyListener(null);
+ child.setOnKeyListener(new HotseatIconKeyEventListener());
// Hide folder title in the hotseat
if (child instanceof FolderIcon) {
@@ -1035,8 +1068,8 @@ public class Workspace extends SmoothPagedView
*/
@Override
public boolean onTouch(View v, MotionEvent event) {
- return (isSmall() || !isFinishedSwitchingState())
- || (!isSmall() && indexOfChild(v) != mCurrentPage);
+ return (workspaceInModalState() || !isFinishedSwitchingState())
+ || (!workspaceInModalState() && indexOfChild(v) != mCurrentPage);
}
public boolean isSwitchingState() {
@@ -1055,7 +1088,7 @@ public class Workspace extends SmoothPagedView
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
- if (isSmall() || !isFinishedSwitchingState()) {
+ if (workspaceInModalState() || !isFinishedSwitchingState()) {
// when the home screens are shrunken, shouldn't allow side-scrolling
return false;
}
@@ -1074,7 +1107,7 @@ public class Workspace extends SmoothPagedView
case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_REST) {
final CellLayout currentPage = (CellLayout) getChildAt(mCurrentPage);
- if (!currentPage.lastDownOnOccupiedCell()) {
+ if (currentPage != null && !currentPage.lastDownOnOccupiedCell()) {
onWallpaperTap(ev);
}
}
@@ -1082,6 +1115,17 @@ public class Workspace extends SmoothPagedView
return super.onInterceptTouchEvent(ev);
}
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ // Ignore pointer scroll events if the custom content doesn't allow scrolling.
+ if ((getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID)
+ && (mCustomContentCallbacks != null)
+ && !mCustomContentCallbacks.isScrollingAllowed()) {
+ return false;
+ }
+ return super.onGenericMotionEvent(event);
+ }
+
protected void reinflateWidgetsIfNecessary() {
final int clCount = getChildCount();
for (int i = 0; i < clCount; i++) {
@@ -1091,10 +1135,10 @@ public class Workspace extends SmoothPagedView
for (int j = 0; j < itemCount; j++) {
View v = swc.getChildAt(j);
- if (v.getTag() instanceof LauncherAppWidgetInfo) {
+ if (v != null && v.getTag() instanceof LauncherAppWidgetInfo) {
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
- if (lahv != null && lahv.orientationChangedSincedInflation()) {
+ if (lahv != null && lahv.isReinflateRequired()) {
mLauncher.removeAppWidget(info);
// Remove the current widget which is inflated with the wrong orientation
cl.removeView(lahv);
@@ -1126,12 +1170,20 @@ public class Workspace extends SmoothPagedView
(mTouchDownTime - mCustomContentShowTime) > CUSTOM_CONTENT_GESTURE_DELAY;
boolean swipeInIgnoreDirection = isLayoutRtl() ? deltaX < 0 : deltaX > 0;
- if (swipeInIgnoreDirection && getScreenIdForPageIndex(getCurrentPage()) ==
- CUSTOM_CONTENT_SCREEN_ID && passRightSwipesToCustomContent) {
+ boolean onCustomContentScreen =
+ getScreenIdForPageIndex(getCurrentPage()) == CUSTOM_CONTENT_SCREEN_ID;
+ if (swipeInIgnoreDirection && onCustomContentScreen && passRightSwipesToCustomContent) {
// Pass swipes to the right to the custom content page.
return;
}
+ if (onCustomContentScreen && (mCustomContentCallbacks != null)
+ && !mCustomContentCallbacks.isScrollingAllowed()) {
+ // Don't allow workspace scrolling if the current custom content screen doesn't allow
+ // scrolling.
+ return;
+ }
+
if (theta > MAX_SWIPE_ANGLE) {
// Above MAX_SWIPE_ANGLE, we don't want to ever start scrolling the workspace
return;
@@ -1165,14 +1217,6 @@ public class Workspace extends SmoothPagedView
enableChildrenCache(mCurrentPage - 1, mCurrentPage + 1);
}
}
-
- // If we are not fading in adjacent screens, we still need to restore the alpha in case the
- // user scrolls while we are transitioning (should not affect dispatchDraw optimizations)
- if (!mWorkspaceFadeInAdjacentScreens) {
- for (int i = 0; i < getChildCount(); ++i) {
- ((CellLayout) getPageAt(i)).setShortcutAndWidgetAlpha(1f);
- }
- }
}
protected void onPageEndMoving() {
@@ -1185,7 +1229,7 @@ public class Workspace extends SmoothPagedView
}
if (mDragController.isDragging()) {
- if (isSmall()) {
+ if (workspaceInModalState()) {
// If we are in springloaded mode, then force an event to check if the current touch
// is under a new page (to scroll to)
mDragController.forceTouchMove();
@@ -1215,7 +1259,7 @@ public class Workspace extends SmoothPagedView
if (hasCustomContent() && getNextPage() == 0 && !mCustomContentShowing) {
mCustomContentShowing = true;
if (mCustomContentCallbacks != null) {
- mCustomContentCallbacks.onShow();
+ mCustomContentCallbacks.onShow(false);
mCustomContentShowTime = System.currentTimeMillis();
mLauncher.updateVoiceButtonProxyVisible(false);
}
@@ -1227,9 +1271,6 @@ public class Workspace extends SmoothPagedView
mLauncher.updateVoiceButtonProxyVisible(false);
}
}
- if (getPageIndicator() != null) {
- getPageIndicator().setContentDescription(getPageIndicatorDescription());
- }
}
protected CustomContentCallbacks getCustomContentCallbacks() {
@@ -1243,7 +1284,8 @@ public class Workspace extends SmoothPagedView
SharedPreferences sp =
mLauncher.getSharedPreferences(spKey, Context.MODE_MULTI_PROCESS);
LauncherWallpaperPickerActivity.suggestWallpaperDimension(mLauncher.getResources(),
- sp, mLauncher.getWindowManager(), mWallpaperManager);
+ sp, mLauncher.getWindowManager(), mWallpaperManager,
+ mLauncher.overrideWallpaperDimensions());
return null;
}
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
@@ -1261,6 +1303,10 @@ public class Workspace extends SmoothPagedView
snapToPage(whichPage, duration);
}
+ public void snapToScreenId(long screenId) {
+ snapToScreenId(screenId, null);
+ }
+
protected void snapToScreenId(long screenId, Runnable r) {
snapToPage(getPageIndexForScreenId(screenId), r);
}
@@ -1445,8 +1491,16 @@ public class Workspace extends SmoothPagedView
mWallpaperOffset.syncWithScroll();
}
+ @Override
+ public void announceForAccessibility(CharSequence text) {
+ // Don't announce if apps is on top of us.
+ if (!mLauncher.isAllAppsVisible()) {
+ super.announceForAccessibility(text);
+ }
+ }
+
void showOutlines() {
- if (!isSmall() && !mIsSwitchingState) {
+ if (!workspaceInModalState() && !mIsSwitchingState) {
if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
mChildrenOutlineFadeInAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 1.0f);
@@ -1456,7 +1510,7 @@ public class Workspace extends SmoothPagedView
}
void hideOutlines() {
- if (!isSmall() && !mIsSwitchingState) {
+ if (!workspaceInModalState() && !mIsSwitchingState) {
if (mChildrenOutlineFadeInAnimation != null) mChildrenOutlineFadeInAnimation.cancel();
if (mChildrenOutlineFadeOutAnimation != null) mChildrenOutlineFadeOutAnimation.cancel();
mChildrenOutlineFadeOutAnimation = LauncherAnimUtils.ofFloat(this, "childrenOutlineAlpha", 0.0f);
@@ -1484,15 +1538,9 @@ public class Workspace extends SmoothPagedView
return mChildrenOutlineAlpha;
}
- void disableBackground() {
- mDrawBackground = false;
- }
- void enableBackground() {
- mDrawBackground = true;
- }
-
private void animateBackgroundGradient(float finalAlpha, boolean animated) {
- if (mBackground == null) return;
+ final DragLayer dragLayer = mLauncher.getDragLayer();
+
if (mBackgroundFadeInAnimation != null) {
mBackgroundFadeInAnimation.cancel();
mBackgroundFadeInAnimation = null;
@@ -1501,36 +1549,26 @@ public class Workspace extends SmoothPagedView
mBackgroundFadeOutAnimation.cancel();
mBackgroundFadeOutAnimation = null;
}
- float startAlpha = getBackgroundAlpha();
+ float startAlpha = dragLayer.getBackgroundAlpha();
if (finalAlpha != startAlpha) {
if (animated) {
mBackgroundFadeOutAnimation =
LauncherAnimUtils.ofFloat(this, startAlpha, finalAlpha);
mBackgroundFadeOutAnimation.addUpdateListener(new AnimatorUpdateListener() {
public void onAnimationUpdate(ValueAnimator animation) {
- setBackgroundAlpha(((Float) animation.getAnimatedValue()).floatValue());
+ dragLayer.setBackgroundAlpha(
+ ((Float)animation.getAnimatedValue()).floatValue());
}
});
mBackgroundFadeOutAnimation.setInterpolator(new DecelerateInterpolator(1.5f));
mBackgroundFadeOutAnimation.setDuration(BACKGROUND_FADE_OUT_DURATION);
mBackgroundFadeOutAnimation.start();
} else {
- setBackgroundAlpha(finalAlpha);
+ dragLayer.setBackgroundAlpha(finalAlpha);
}
}
}
- public void setBackgroundAlpha(float alpha) {
- if (alpha != mBackgroundAlpha) {
- mBackgroundAlpha = alpha;
- invalidate();
- }
- }
-
- public float getBackgroundAlpha() {
- return mBackgroundAlpha;
- }
-
float backgroundAlphaInterpolator(float r) {
float pivotA = 0.1f;
float pivotB = 0.4f;
@@ -1546,7 +1584,7 @@ public class Workspace extends SmoothPagedView
private void updatePageAlphaValues(int screenCenter) {
boolean isInOverscroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
if (mWorkspaceFadeInAdjacentScreens &&
- mState == State.NORMAL &&
+ !workspaceInModalState() &&
!mIsSwitchingState &&
!isInOverscroll) {
for (int i = numCustomPages(); i < getChildCount(); i++) {
@@ -1555,6 +1593,7 @@ public class Workspace extends SmoothPagedView
float scrollProgress = getScrollProgress(screenCenter, child, i);
float alpha = 1 - Math.abs(scrollProgress);
child.getShortcutsAndWidgets().setAlpha(alpha);
+ //child.setBackgroundAlphaMultiplier(1 - alpha);
}
}
}
@@ -1602,13 +1641,13 @@ public class Workspace extends SmoothPagedView
if (Float.compare(progress, mLastCustomContentScrollProgress) == 0) return;
CellLayout cc = mWorkspaceScreens.get(CUSTOM_CONTENT_SCREEN_ID);
- if (progress > 0 && cc.getVisibility() != VISIBLE && !isSmall()) {
+ if (progress > 0 && cc.getVisibility() != VISIBLE && !workspaceInModalState()) {
cc.setVisibility(VISIBLE);
}
mLastCustomContentScrollProgress = progress;
- setBackgroundAlpha(progress * 0.8f);
+ mLauncher.getDragLayer().setBackgroundAlpha(progress * 0.8f);
if (mLauncher.getHotseat() != null) {
mLauncher.getHotseat().setTranslationX(translationX);
@@ -1648,47 +1687,40 @@ public class Workspace extends SmoothPagedView
updateStateForCustomContent(screenCenter);
enableHwLayersOnVisiblePages();
- boolean shouldOverScroll = (mOverScrollX < 0 && (!hasCustomContent() || isLayoutRtl())) ||
- (mOverScrollX > mMaxScrollX && (!hasCustomContent() || !isLayoutRtl()));
+ boolean shouldOverScroll = mOverScrollX < 0 || mOverScrollX > mMaxScrollX;
if (shouldOverScroll) {
int index = 0;
- float pivotX = 0f;
- final float leftBiasedPivot = 0.25f;
- final float rightBiasedPivot = 0.75f;
final int lowerIndex = 0;
final int upperIndex = getChildCount() - 1;
final boolean isLeftPage = mOverScrollX < 0;
index = (!isRtl && isLeftPage) || (isRtl && !isLeftPage) ? lowerIndex : upperIndex;
- pivotX = isLeftPage ? rightBiasedPivot : leftBiasedPivot;
CellLayout cl = (CellLayout) getChildAt(index);
- float scrollProgress = getScrollProgress(screenCenter, cl, index);
- cl.setOverScrollAmount(Math.abs(scrollProgress), isLeftPage);
- float rotation = -WORKSPACE_OVERSCROLL_ROTATION * scrollProgress;
- cl.setRotationY(rotation);
-
- if (!mOverscrollTransformsSet || Float.compare(mLastOverscrollPivotX, pivotX) != 0) {
- mOverscrollTransformsSet = true;
- mLastOverscrollPivotX = pivotX;
- cl.setCameraDistance(mDensity * mCameraDistance);
- cl.setPivotX(cl.getMeasuredWidth() * pivotX);
- cl.setPivotY(cl.getMeasuredHeight() * 0.5f);
- cl.setOverscrollTransformsDirty(true);
- }
+ float effect = Math.abs(mOverScrollEffect);
+ cl.setOverScrollAmount(Math.abs(effect), isLeftPage);
+
+ mOverscrollEffectSet = true;
} else {
- if (mOverscrollTransformsSet && getChildCount() > 0) {
- mOverscrollTransformsSet = false;
- ((CellLayout) getChildAt(0)).resetOverscrollTransforms();
- ((CellLayout) getChildAt(getChildCount() - 1)).resetOverscrollTransforms();
+ if (mOverscrollEffectSet && getChildCount() > 0) {
+ mOverscrollEffectSet = false;
+ ((CellLayout) getChildAt(0)).setOverScrollAmount(0, false);
+ ((CellLayout) getChildAt(getChildCount() - 1)).setOverScrollAmount(0, false);
}
}
}
@Override
protected void overScroll(float amount) {
- acceleratedOverScroll(amount);
+ boolean shouldOverScroll = (amount < 0 && (!hasCustomContent() || isLayoutRtl())) ||
+ (amount > 0 && (!hasCustomContent() || !isLayoutRtl()));
+ if (shouldOverScroll) {
+ dampedOverScroll(amount);
+ mOverScrollEffect = acceleratedOverFactor(amount);
+ } else {
+ mOverScrollEffect = 0;
+ }
}
protected void onAttachedToWindow() {
@@ -1738,25 +1770,12 @@ public class Workspace extends SmoothPagedView
@Override
protected void onDraw(Canvas canvas) {
- // Draw the background gradient if necessary
- if (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground) {
- int alpha = (int) (mBackgroundAlpha * 255);
- mBackground.setAlpha(alpha);
- mBackground.setBounds(getScrollX(), 0, getScrollX() + getMeasuredWidth(),
- getMeasuredHeight());
- mBackground.draw(canvas);
- }
-
super.onDraw(canvas);
// Call back to LauncherModel to finish binding after the first draw
post(mBindPages);
}
- boolean isDrawingBackgroundGradient() {
- return (mBackground != null && mBackgroundAlpha > 0.0f && mDrawBackground);
- }
-
@Override
protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) {
if (!mLauncher.isAllAppsVisible()) {
@@ -1772,7 +1791,7 @@ public class Workspace extends SmoothPagedView
@Override
public int getDescendantFocusability() {
- if (isSmall()) {
+ if (workspaceInModalState()) {
return ViewGroup.FOCUS_BLOCK_DESCENDANTS;
}
return super.getDescendantFocusability();
@@ -1790,8 +1809,8 @@ public class Workspace extends SmoothPagedView
}
}
- public boolean isSmall() {
- return mState == State.SMALL || mState == State.SPRING_LOADED || mState == State.OVERVIEW;
+ public boolean workspaceInModalState() {
+ return mState != State.NORMAL;
}
void enableChildrenCache(int fromPage, int toPage) {
@@ -1826,7 +1845,7 @@ public class Workspace extends SmoothPagedView
}
private void updateChildrenLayersEnabled(boolean force) {
- boolean small = mState == State.SMALL || mState == State.OVERVIEW || mIsSwitchingState;
+ boolean small = mState == State.OVERVIEW || mIsSwitchingState;
boolean enableChildrenLayers = force || small || mAnimatingViewIntoPlace || isPageMoving();
if (enableChildrenLayers != mChildrenLayersEnabled) {
@@ -1964,21 +1983,54 @@ public class Workspace extends SmoothPagedView
* appearance).
*
*/
- public void onDragStartedWithItem(View v) {
- final Canvas canvas = new Canvas();
+ private static Rect getDrawableBounds(Drawable d) {
+ Rect bounds = new Rect();
+ d.copyBounds(bounds);
+ if (bounds.width() == 0 || bounds.height() == 0) {
+ bounds.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
+ } else {
+ bounds.offsetTo(0, 0);
+ }
+ if (d instanceof PreloadIconDrawable) {
+ int inset = -((PreloadIconDrawable) d).getOutset();
+ bounds.inset(inset, inset);
+ }
+ return bounds;
+ }
+
+ public void onExternalDragStartedWithItem(View v) {
+ // Compose a drag bitmap with the view scaled to the icon size
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ int iconSize = grid.iconSizePx;
+ int bmpWidth = v.getMeasuredWidth();
+ int bmpHeight = v.getMeasuredHeight();
+
+ // If this is a text view, use its drawable instead
+ if (v instanceof TextView) {
+ TextView tv = (TextView) v;
+ Drawable d = tv.getCompoundDrawables()[1];
+ Rect bounds = getDrawableBounds(d);
+ bmpWidth = bounds.width();
+ bmpHeight = bounds.height();
+ }
+
+ // Compose the bitmap to create the icon from
+ Bitmap b = Bitmap.createBitmap(bmpWidth, bmpHeight,
+ Bitmap.Config.ARGB_8888);
+ mCanvas.setBitmap(b);
+ drawDragView(v, mCanvas, 0);
+ mCanvas.setBitmap(null);
// The outline is used to visualize where the item will land if dropped
- mDragOutline = createDragOutline(v, canvas, DRAG_BITMAP_PADDING);
+ mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, iconSize, iconSize, true);
}
public void onDragStartedWithItem(PendingAddItemInfo info, Bitmap b, boolean clipAlpha) {
- final Canvas canvas = new Canvas();
-
int[] size = estimateItemSize(info.spanX, info.spanY, info, false);
// The outline is used to visualize where the item will land if dropped
- mDragOutline = createDragOutline(b, canvas, DRAG_BITMAP_PADDING, size[0],
- size[1], clipAlpha);
+ mDragOutline = createDragOutline(b, DRAG_BITMAP_PADDING, size[0], size[1], clipAlpha);
}
public void exitWidgetResizeMode() {
@@ -1996,18 +2048,23 @@ public class Workspace extends SmoothPagedView
mNewAlphas = new float[childCount];
}
- Animator getChangeStateAnimation(final State state, boolean animated) {
- return getChangeStateAnimation(state, animated, 0, -1);
+ Animator getChangeStateAnimation(final State state, boolean animated,
+ ArrayList<View> layerViews) {
+ return getChangeStateAnimation(state, animated, 0, -1, layerViews);
}
@Override
- protected void getOverviewModePages(int[] range) {
+ protected void getFreeScrollPageRange(int[] range) {
+ getOverviewModePages(range);
+ }
+
+ private void getOverviewModePages(int[] range) {
int start = numCustomPages();
int end = getChildCount() - 1;
range[0] = Math.max(0, Math.min(start, getChildCount() - 1));
range[1] = Math.max(0, end);
- }
+ }
protected void onStartReordering() {
super.onStartReordering();
@@ -2019,6 +2076,11 @@ public class Workspace extends SmoothPagedView
protected void onEndReordering() {
super.onEndReordering();
+ if (mLauncher.isWorkspaceLoading()) {
+ // Invalid and dangerous operation if workspace is loading
+ return;
+ }
+
hideOutlines();
mScreenOrder.clear();
int count = getChildCount();
@@ -2110,6 +2172,10 @@ public class Workspace extends SmoothPagedView
updateAccessibilityFlags();
}
+ State getState() {
+ return mState;
+ }
+
private void updateAccessibilityFlags() {
int accessible = mState == State.NORMAL ?
ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES :
@@ -2117,7 +2183,14 @@ public class Workspace extends SmoothPagedView
setImportantForAccessibility(accessible);
}
+ private static final int HIDE_WORKSPACE_DURATION = 100;
+
Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
+ return getChangeStateAnimation(state, animated, delay, snapPage, null);
+ }
+
+ Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage,
+ ArrayList<View> layerViews) {
if (mState == state) {
return null;
}
@@ -2130,21 +2203,25 @@ public class Workspace extends SmoothPagedView
final State oldState = mState;
final boolean oldStateIsNormal = (oldState == State.NORMAL);
final boolean oldStateIsSpringLoaded = (oldState == State.SPRING_LOADED);
- final boolean oldStateIsSmall = (oldState == State.SMALL);
+ final boolean oldStateIsNormalHidden = (oldState == State.NORMAL_HIDDEN);
+ final boolean oldStateIsOverviewHidden = (oldState == State.OVERVIEW_HIDDEN);
final boolean oldStateIsOverview = (oldState == State.OVERVIEW);
setState(state);
final boolean stateIsNormal = (state == State.NORMAL);
final boolean stateIsSpringLoaded = (state == State.SPRING_LOADED);
- final boolean stateIsSmall = (state == State.SMALL);
+ final boolean stateIsNormalHidden = (state == State.NORMAL_HIDDEN);
+ final boolean stateIsOverviewHidden = (state == State.OVERVIEW_HIDDEN);
final boolean stateIsOverview = (state == State.OVERVIEW);
float finalBackgroundAlpha = (stateIsSpringLoaded || stateIsOverview) ? 1.0f : 0f;
- float finalHotseatAndPageIndicatorAlpha = (stateIsOverview || stateIsSmall) ? 0f : 1f;
+ float finalHotseatAndPageIndicatorAlpha = (stateIsNormal || stateIsSpringLoaded) ? 1f : 0f;
float finalOverviewPanelAlpha = stateIsOverview ? 1f : 0f;
float finalSearchBarAlpha = !stateIsNormal ? 0f : 1f;
- float finalWorkspaceTranslationY = stateIsOverview ? getOverviewModeTranslationY() : 0;
+ float finalWorkspaceTranslationY = stateIsOverview || stateIsOverviewHidden ?
+ getOverviewModeTranslationY() : 0;
- boolean workspaceToAllApps = (oldStateIsNormal && stateIsSmall);
- boolean allAppsToWorkspace = (oldStateIsSmall && stateIsNormal);
+ boolean workspaceToAllApps = (oldStateIsNormal && stateIsNormalHidden);
+ boolean overviewToAllApps = (oldStateIsOverview && stateIsOverviewHidden);
+ boolean allAppsToWorkspace = (stateIsNormalHidden && stateIsNormal);
boolean workspaceToOverview = (oldStateIsNormal && stateIsOverview);
boolean overviewToWorkspace = (oldStateIsOverview && stateIsNormal);
@@ -2159,19 +2236,14 @@ public class Workspace extends SmoothPagedView
if (state != State.NORMAL) {
if (stateIsSpringLoaded) {
mNewScale = mSpringLoadedShrinkFactor;
- } else if (stateIsOverview) {
+ } else if (stateIsOverview || stateIsOverviewHidden) {
mNewScale = mOverviewModeShrinkFactor;
- } else if (stateIsSmall){
- mNewScale = mOverviewModeShrinkFactor - 0.3f;
- }
- if (workspaceToAllApps) {
- updateChildrenLayersEnabled(false);
}
}
final int duration;
- if (workspaceToAllApps) {
- duration = getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
+ if (workspaceToAllApps || overviewToAllApps) {
+ duration = HIDE_WORKSPACE_DURATION; //getResources().getInteger(R.integer.config_workspaceUnshrinkTime);
} else if (workspaceToOverview || overviewToWorkspace) {
duration = getResources().getInteger(R.integer.config_overviewTransitionTime);
} else {
@@ -2188,7 +2260,7 @@ public class Workspace extends SmoothPagedView
boolean isCurrentPage = (i == snapPage);
float initialAlpha = cl.getShortcutsAndWidgets().getAlpha();
float finalAlpha;
- if (stateIsSmall) {
+ if (stateIsNormalHidden || stateIsOverviewHidden) {
finalAlpha = 0f;
} else if (stateIsNormal && mWorkspaceFadeInAdjacentScreens) {
finalAlpha = (i == snapPage || i < numCustomPages()) ? 1f : 0f;
@@ -2225,11 +2297,11 @@ public class Workspace extends SmoothPagedView
final View hotseat = mLauncher.getHotseat();
final View pageIndicator = getPageIndicator();
if (animated) {
- anim.setDuration(duration);
LauncherViewPropertyAnimator scale = new LauncherViewPropertyAnimator(this);
scale.scaleX(mNewScale)
.scaleY(mNewScale)
.translationY(finalWorkspaceTranslationY)
+ .setDuration(duration)
.setInterpolator(mZoomInInterpolator);
anim.play(scale);
for (int index = 0; index < getChildCount(); index++) {
@@ -2240,10 +2312,14 @@ public class Workspace extends SmoothPagedView
cl.setBackgroundAlpha(mNewBackgroundAlphas[i]);
cl.setShortcutAndWidgetAlpha(mNewAlphas[i]);
} else {
+ if (layerViews != null) {
+ layerViews.add(cl);
+ }
if (mOldAlphas[i] != mNewAlphas[i] || currentAlpha != mNewAlphas[i]) {
LauncherViewPropertyAnimator alphaAnim =
new LauncherViewPropertyAnimator(cl.getShortcutsAndWidgets());
alphaAnim.alpha(mNewAlphas[i])
+ .setDuration(duration)
.setInterpolator(mZoomInInterpolator);
anim.play(alphaAnim);
}
@@ -2252,6 +2328,7 @@ public class Workspace extends SmoothPagedView
ValueAnimator bgAnim =
LauncherAnimUtils.ofFloat(cl, 0f, 1f);
bgAnim.setInterpolator(mZoomInInterpolator);
+ bgAnim.setDuration(duration);
bgAnim.addUpdateListener(new LauncherAnimatorUpdateListener() {
public void onAnimationUpdate(float a, float b) {
cl.setBackgroundAlpha(
@@ -2285,6 +2362,17 @@ public class Workspace extends SmoothPagedView
.alpha(finalOverviewPanelAlpha).withLayer();
overviewPanelAlpha.addListener(new AlphaUpdateListener(overviewPanel));
+ // For animation optimations, we may need to provide the Launcher transition
+ // with a set of views on which to force build layers in certain scenarios.
+ hotseat.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ overviewPanel.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ if (layerViews != null) {
+ layerViews.add(hotseat);
+ layerViews.add(searchBar);
+ layerViews.add(overviewPanel);
+ }
+
if (workspaceToOverview) {
pageIndicatorAlpha.setInterpolator(new DecelerateInterpolator(2));
hotseatAlpha.setInterpolator(new DecelerateInterpolator(2));
@@ -2294,7 +2382,11 @@ public class Workspace extends SmoothPagedView
hotseatAlpha.setInterpolator(null);
overviewPanelAlpha.setInterpolator(new DecelerateInterpolator(2));
}
- searchBarAlpha.setInterpolator(null);
+
+ overviewPanelAlpha.setDuration(duration);
+ pageIndicatorAlpha.setDuration(duration);
+ hotseatAlpha.setDuration(duration);
+ searchBarAlpha.setDuration(duration);
anim.play(overviewPanelAlpha);
anim.play(hotseatAlpha);
@@ -2319,18 +2411,11 @@ public class Workspace extends SmoothPagedView
}
mLauncher.updateVoiceButtonProxyVisible(false);
- if (stateIsSpringLoaded) {
- // Right now we're covered by Apps Customize
- // Show the background gradient immediately, so the gradient will
- // be showing once AppsCustomize disappears
- animateBackgroundGradient(getResources().getInteger(
- R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, false);
- } else if (stateIsOverview) {
- animateBackgroundGradient(getResources().getInteger(
- R.integer.config_appsCustomizeSpringLoadedBgAlpha) / 100f, true);
- } else {
- // Fade the background gradient away
+ if (stateIsNormal) {
animateBackgroundGradient(0f, animated);
+ } else {
+ animateBackgroundGradient(getResources().getInteger(
+ R.integer.config_workspaceScrimAlpha) / 100f, animated);
}
return anim;
}
@@ -2433,21 +2518,6 @@ public class Workspace extends SmoothPagedView
private void onTransitionEnd() {
mIsSwitchingState = false;
updateChildrenLayersEnabled(false);
- // The code in getChangeStateAnimation to determine initialAlpha and finalAlpha will ensure
- // ensure that only the current page is visible during (and subsequently, after) the
- // transition animation. If fade adjacent pages is disabled, then re-enable the page
- // visibility after the transition animation.
- if (!mWorkspaceFadeInAdjacentScreens) {
- for (int i = 0; i < getChildCount(); i++) {
- final CellLayout cl = (CellLayout) getChildAt(i);
- cl.setShortcutAndWidgetAlpha(1f);
- }
- } else {
- for (int i = 0; i < numCustomPages(); i++) {
- final CellLayout cl = (CellLayout) getChildAt(i);
- cl.setShortcutAndWidgetAlpha(1f);
- }
- }
showCustomContentIfNecessary();
}
@@ -2463,17 +2533,18 @@ public class Workspace extends SmoothPagedView
* @param destCanvas the canvas to draw on
* @param padding the horizontal and vertical padding to use when drawing
*/
- private void drawDragView(View v, Canvas destCanvas, int padding, boolean pruneToDrawable) {
- final Rect clipRect = mTempRect;
+ private static void drawDragView(View v, Canvas destCanvas, int padding) {
+ final Rect clipRect = sTempRect;
v.getDrawingRect(clipRect);
boolean textVisible = false;
destCanvas.save();
- if (v instanceof TextView && pruneToDrawable) {
+ if (v instanceof TextView) {
Drawable d = ((TextView) v).getCompoundDrawables()[1];
- clipRect.set(0, 0, d.getIntrinsicWidth() + padding, d.getIntrinsicHeight() + padding);
- destCanvas.translate(padding / 2, padding / 2);
+ Rect bounds = getDrawableBounds(d);
+ clipRect.set(0, 0, bounds.width() + padding, bounds.height() + padding);
+ destCanvas.translate(padding / 2 - bounds.left, padding / 2 - bounds.top);
d.draw(destCanvas);
} else {
if (v instanceof FolderIcon) {
@@ -2483,14 +2554,6 @@ public class Workspace extends SmoothPagedView
((FolderIcon) v).setTextVisible(false);
textVisible = true;
}
- } else if (v instanceof BubbleTextView) {
- final BubbleTextView tv = (BubbleTextView) v;
- clipRect.bottom = tv.getExtendedPaddingTop() - (int) BubbleTextView.PADDING_V +
- tv.getLayout().getLineTop(0);
- } else if (v instanceof TextView) {
- final TextView tv = (TextView) v;
- clipRect.bottom = tv.getExtendedPaddingTop() - tv.getCompoundDrawablePadding() +
- tv.getLayout().getLineTop(0);
}
destCanvas.translate(-v.getScrollX() + padding / 2, -v.getScrollY() + padding / 2);
destCanvas.clipRect(clipRect, Op.REPLACE);
@@ -2507,22 +2570,27 @@ public class Workspace extends SmoothPagedView
/**
* Returns a new bitmap to show when the given View is being dragged around.
* Responsibility for the bitmap is transferred to the caller.
+ * @param expectedPadding padding to add to the drag view. If a different padding was used
+ * its value will be changed
*/
- public Bitmap createDragBitmap(View v, Canvas canvas, int padding) {
+ public Bitmap createDragBitmap(View v, AtomicInteger expectedPadding) {
Bitmap b;
+ int padding = expectedPadding.get();
if (v instanceof TextView) {
Drawable d = ((TextView) v).getCompoundDrawables()[1];
- b = Bitmap.createBitmap(d.getIntrinsicWidth() + padding,
- d.getIntrinsicHeight() + padding, Bitmap.Config.ARGB_8888);
+ Rect bounds = getDrawableBounds(d);
+ b = Bitmap.createBitmap(bounds.width() + padding,
+ bounds.height() + padding, Bitmap.Config.ARGB_8888);
+ expectedPadding.set(padding - bounds.left - bounds.top);
} else {
b = Bitmap.createBitmap(
v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
}
- canvas.setBitmap(b);
- drawDragView(v, canvas, padding, true);
- canvas.setBitmap(null);
+ mCanvas.setBitmap(b);
+ drawDragView(v, mCanvas, padding);
+ mCanvas.setBitmap(null);
return b;
}
@@ -2531,15 +2599,15 @@ public class Workspace extends SmoothPagedView
* Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
* Responsibility for the bitmap is transferred to the caller.
*/
- private Bitmap createDragOutline(View v, Canvas canvas, int padding) {
+ private Bitmap createDragOutline(View v, int padding) {
final int outlineColor = getResources().getColor(R.color.outline_color);
final Bitmap b = Bitmap.createBitmap(
v.getWidth() + padding, v.getHeight() + padding, Bitmap.Config.ARGB_8888);
- canvas.setBitmap(b);
- drawDragView(v, canvas, padding, true);
- mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor);
- canvas.setBitmap(null);
+ mCanvas.setBitmap(b);
+ drawDragView(v, mCanvas, padding);
+ mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor);
+ mCanvas.setBitmap(null);
return b;
}
@@ -2547,11 +2615,11 @@ public class Workspace extends SmoothPagedView
* Returns a new bitmap to be used as the object outline, e.g. to visualize the drop location.
* Responsibility for the bitmap is transferred to the caller.
*/
- private Bitmap createDragOutline(Bitmap orig, Canvas canvas, int padding, int w, int h,
+ private Bitmap createDragOutline(Bitmap orig, int padding, int w, int h,
boolean clipAlpha) {
final int outlineColor = getResources().getColor(R.color.outline_color);
final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
- canvas.setBitmap(b);
+ mCanvas.setBitmap(b);
Rect src = new Rect(0, 0, orig.getWidth(), orig.getHeight());
float scaleFactor = Math.min((w - padding) / (float) orig.getWidth(),
@@ -2563,10 +2631,10 @@ public class Workspace extends SmoothPagedView
// center the image
dst.offset((w - scaledWidth) / 2, (h - scaledHeight) / 2);
- canvas.drawBitmap(orig, src, dst, null);
- mOutlineHelper.applyMediumExpensiveOutlineWithBlur(b, canvas, outlineColor, outlineColor,
+ mCanvas.drawBitmap(orig, src, dst, null);
+ mOutlineHelper.applyExpensiveOutlineWithBlur(b, mCanvas, outlineColor, outlineColor,
clipAlpha);
- canvas.setBitmap(null);
+ mCanvas.setBitmap(null);
return b;
}
@@ -2584,35 +2652,34 @@ public class Workspace extends SmoothPagedView
CellLayout layout = (CellLayout) child.getParent().getParent();
layout.prepareChildForDrag(child);
+ beginDragShared(child, this);
+ }
+
+ public void beginDragShared(View child, DragSource source) {
child.clearFocus();
child.setPressed(false);
- final Canvas canvas = new Canvas();
-
// The outline is used to visualize where the item will land if dropped
- mDragOutline = createDragOutline(child, canvas, DRAG_BITMAP_PADDING);
- beginDragShared(child, this);
- }
+ mDragOutline = createDragOutline(child, DRAG_BITMAP_PADDING);
- public void beginDragShared(View child, DragSource source) {
+ mLauncher.onDragStarted(child);
// The drag bitmap follows the touch point around on the screen
- final Bitmap b = createDragBitmap(child, new Canvas(), DRAG_BITMAP_PADDING);
+ AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
+ final Bitmap b = createDragBitmap(child, padding);
final int bmpWidth = b.getWidth();
final int bmpHeight = b.getHeight();
float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY);
- int dragLayerX =
- Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
- int dragLayerY =
- Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
- - DRAG_BITMAP_PADDING / 2);
+ int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
+ int dragLayerY = Math.round(mTempXY[1] - (bmpHeight - scale * bmpHeight) / 2
+ - padding.get() / 2);
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
Point dragVisualizeOffset = null;
Rect dragRect = null;
- if (child instanceof BubbleTextView || child instanceof PagedViewIcon) {
+ if (child instanceof BubbleTextView) {
int iconSize = grid.iconSizePx;
int top = child.getPaddingTop();
int left = (bmpWidth - iconSize) / 2;
@@ -2621,7 +2688,7 @@ public class Workspace extends SmoothPagedView
dragLayerY += top;
// Note: The drag region is used to calculate drag layer offsets, but the
// dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
- dragVisualizeOffset = new Point(-DRAG_BITMAP_PADDING / 2, DRAG_BITMAP_PADDING / 2);
+ dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
dragRect = new Rect(left, top, right, bottom);
} else if (child instanceof FolderIcon) {
int previewSize = grid.folderIconSizePx;
@@ -2631,10 +2698,7 @@ public class Workspace extends SmoothPagedView
// Clear the pressed state if necessary
if (child instanceof BubbleTextView) {
BubbleTextView icon = (BubbleTextView) child;
- icon.clearPressedOrFocusedBackground();
- } else if (child instanceof FolderIcon) {
- // Dismiss the folder cling if we haven't already
- mLauncher.getLauncherClings().markFolderClingDismissed();
+ icon.clearPressedBackground();
}
if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
@@ -2655,6 +2719,53 @@ public class Workspace extends SmoothPagedView
b.recycle();
}
+ public void beginExternalDragShared(View child, DragSource source) {
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ int iconSize = grid.iconSizePx;
+
+ // Notify launcher of drag start
+ mLauncher.onDragStarted(child);
+
+ // Compose a new drag bitmap that is of the icon size
+ AtomicInteger padding = new AtomicInteger(DRAG_BITMAP_PADDING);
+ final Bitmap tmpB = createDragBitmap(child, padding);
+ Bitmap b = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
+ Paint p = new Paint();
+ p.setFilterBitmap(true);
+ mCanvas.setBitmap(b);
+ mCanvas.drawBitmap(tmpB, new Rect(0, 0, tmpB.getWidth(), tmpB.getHeight()),
+ new Rect(0, 0, iconSize, iconSize), p);
+ mCanvas.setBitmap(null);
+
+ // Find the child's location on the screen
+ int bmpWidth = tmpB.getWidth();
+ float iconScale = (float) bmpWidth / iconSize;
+ float scale = mLauncher.getDragLayer().getLocationInDragLayer(child, mTempXY) * iconScale;
+ int dragLayerX = Math.round(mTempXY[0] - (bmpWidth - scale * child.getWidth()) / 2);
+ int dragLayerY = Math.round(mTempXY[1]);
+
+ // Note: The drag region is used to calculate drag layer offsets, but the
+ // dragVisualizeOffset in addition to the dragRect (the size) to position the outline.
+ Point dragVisualizeOffset = new Point(-padding.get() / 2, padding.get() / 2);
+ Rect dragRect = new Rect(0, 0, iconSize, iconSize);
+
+ if (child.getTag() == null || !(child.getTag() instanceof ItemInfo)) {
+ String msg = "Drag started with a view that has no tag set. This "
+ + "will cause a crash (issue 11627249) down the line. "
+ + "View: " + child + " tag: " + child.getTag();
+ throw new IllegalStateException(msg);
+ }
+
+ // Start the drag
+ DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(),
+ DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale);
+ dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor());
+
+ // Recycle temporary bitmaps
+ tmpB.recycle();
+ }
+
void addApplicationShortcut(ShortcutInfo info, CellLayout target, long container, long screenId,
int cellX, int cellY, boolean insertAtFirst, int intersectX, int intersectY) {
View view = mLauncher.createShortcut(R.layout.application, target, (ShortcutInfo) info);
@@ -2668,7 +2779,8 @@ public class Workspace extends SmoothPagedView
}
public boolean transitionStateShouldAllowDrop() {
- return ((!isSwitchingState() || mTransitionProgress > 0.5f) && mState != State.SMALL);
+ return ((!isSwitchingState() || mTransitionProgress > 0.5f) &&
+ (mState == State.NORMAL || mState == State.SPRING_LOADED));
}
/**
@@ -2934,13 +3046,11 @@ public class Workspace extends SmoothPagedView
// cell also contains a shortcut, then create a folder with the two shortcuts.
if (!mInScrollArea && createUserFolderIfNecessary(cell, container,
dropTargetLayout, mTargetCell, distance, false, d.dragView, null)) {
- removeExtraEmptyScreen(true, null, 0, true);
return;
}
if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
distance, d, false)) {
- removeExtraEmptyScreen(true, null, 0, true);
return;
}
@@ -2981,7 +3091,12 @@ public class Workspace extends SmoothPagedView
final ItemInfo info = (ItemInfo) cell.getTag();
if (hasMovedLayouts) {
// Reparent the view
- getParentCellLayoutForView(cell).removeView(cell);
+ CellLayout parentCell = getParentCellLayoutForView(cell);
+ if (parentCell != null) {
+ parentCell.removeView(cell);
+ } else if (LauncherAppState.isDogfoodBuild()) {
+ throw new NullPointerException("mDragInfo.cell has null parent");
+ }
addInScreen(cell, container, screenId, mTargetCell[0], mTargetCell[1],
info.spanX, info.spanY);
}
@@ -3046,7 +3161,6 @@ public class Workspace extends SmoothPagedView
if (finalResizeRunnable != null) {
finalResizeRunnable.run();
}
- removeExtraEmptyScreen(true, null, 0, true);
}
};
mAnimatingViewIntoPlace = true;
@@ -3115,10 +3229,8 @@ public class Workspace extends SmoothPagedView
setCurrentDropLayout(layout);
setCurrentDragOverlappingLayout(layout);
- // Because we don't have space in the Phone UI (the CellLayouts run to the edge) we
- // don't need to show the outlines
- if (LauncherAppState.getInstance().isScreenLarge()) {
- showOutlines();
+ if (!workspaceInModalState()) {
+ mLauncher.getDragLayer().showPageHints();
}
}
@@ -3128,7 +3240,6 @@ public class Workspace extends SmoothPagedView
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- Resources res = launcher.getResources();
Display display = launcher.getWindowManager().getDefaultDisplay();
Point smallestSize = new Point();
Point largestSize = new Point();
@@ -3194,6 +3305,7 @@ public class Workspace extends SmoothPagedView
if (!mIsPageMoving) {
hideOutlines();
}
+ mLauncher.getDragLayer().hidePageHints();
}
void setCurrentDropLayout(CellLayout layout) {
@@ -3436,11 +3548,17 @@ public class Workspace extends SmoothPagedView
public void onDragOver(DragObject d) {
// Skip drag over events while we are dragging over side pages
- if (mInScrollArea || mIsSwitchingState || mState == State.SMALL) return;
+ if (mInScrollArea || !transitionStateShouldAllowDrop()) return;
Rect r = new Rect();
CellLayout layout = null;
ItemInfo item = (ItemInfo) d.dragInfo;
+ if (item == null) {
+ if (LauncherAppState.isDogfoodBuild()) {
+ throw new NullPointerException("DragObject has null info");
+ }
+ return;
+ }
// Ensure that we have proper spans for the item that we are dropping
if (item.spanX < 0 || item.spanY < 0) throw new RuntimeException("Improper spans found");
@@ -3449,7 +3567,7 @@ public class Workspace extends SmoothPagedView
final View child = (mDragInfo == null) ? null : mDragInfo.cell;
// Identify whether we have dragged over a side page
- if (isSmall()) {
+ if (workspaceInModalState()) {
if (mLauncher.getHotseat() != null && !isExternalDragWidget(d)) {
if (isPointInSelfOverHotseat(d.x, d.y, r)) {
layout = mLauncher.getHotseat().getLayout();
@@ -3700,13 +3818,8 @@ public class Workspace extends SmoothPagedView
final Runnable exitSpringLoadedRunnable = new Runnable() {
@Override
public void run() {
- removeExtraEmptyScreen(false, new Runnable() {
- @Override
- public void run() {
- mLauncher.exitSpringLoadedDragModeDelayed(true,
- Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
- }
- });
+ mLauncher.exitSpringLoadedDragModeDelayed(true,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
}
};
@@ -3768,6 +3881,11 @@ public class Workspace extends SmoothPagedView
Runnable onAnimationCompleteRunnable = new Runnable() {
@Override
public void run() {
+ // Normally removeExtraEmptyScreen is called in Workspace#onDragEnd, but when
+ // adding an item that may not be dropped right away (due to a config activity)
+ // we defer the removal until the activity returns.
+ deferRemoveExtraEmptyScreen();
+
// When dragging and dropping from customization tray, we deal with creating
// widgets/shortcuts/folders in a slightly different way
switch (pendingInfo.itemType) {
@@ -3852,15 +3970,16 @@ public class Workspace extends SmoothPagedView
} else {
cellLayout.findCellForSpan(mTargetCell, 1, 1);
}
+ // Add the item to DB before adding to screen ensures that the container and other
+ // values of the info is properly updated.
+ LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
+ mTargetCell[0], mTargetCell[1]);
+
addInScreen(view, container, screenId, mTargetCell[0], mTargetCell[1], info.spanX,
info.spanY, insertAtFirst);
cellLayout.onDropChild(view);
- CellLayout.LayoutParams lp = (CellLayout.LayoutParams) view.getLayoutParams();
cellLayout.getShortcutsAndWidgets().measureChild(view);
- LauncherModel.addOrMoveItemInDatabase(mLauncher, info, container, screenId,
- lp.cellX, lp.cellY);
-
if (d.dragView != null) {
// We wrap the animation call in the temporary set and reset of the current
// cellLayout to its final transform -- this means we animate the drag view to
@@ -3883,12 +4002,12 @@ public class Workspace extends SmoothPagedView
int height = MeasureSpec.makeMeasureSpec(unScaledSize[1], MeasureSpec.EXACTLY);
Bitmap b = Bitmap.createBitmap(unScaledSize[0], unScaledSize[1],
Bitmap.Config.ARGB_8888);
- Canvas c = new Canvas(b);
+ mCanvas.setBitmap(b);
layout.measure(width, height);
layout.layout(0, 0, unScaledSize[0], unScaledSize[1]);
- layout.draw(c);
- c.setBitmap(null);
+ layout.draw(mCanvas);
+ mCanvas.setBitmap(null);
layout.setVisibility(visibility);
return b;
}
@@ -4064,14 +4183,12 @@ public class Workspace extends SmoothPagedView
CellLayout parentCell = getParentCellLayoutForView(mDragInfo.cell);
if (parentCell != null) {
parentCell.removeView(mDragInfo.cell);
+ } else if (LauncherAppState.isDogfoodBuild()) {
+ throw new NullPointerException("mDragInfo.cell has null parent");
}
if (mDragInfo.cell instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) mDragInfo.cell);
}
- // If we move the item to anything not on the Workspace, check if any empty
- // screens need to be removed. If we dropped back on the workspace, this will
- // be done post drop animation.
- removeExtraEmptyScreen(true, null, 0, true);
}
} else if (mDragInfo != null) {
CellLayout cellLayout;
@@ -4327,7 +4444,7 @@ public class Workspace extends SmoothPagedView
@Override
public void scrollLeft() {
- if (!isSmall() && !mIsSwitchingState) {
+ if (!workspaceInModalState() && !mIsSwitchingState) {
super.scrollLeft();
}
Folder openFolder = getOpenFolder();
@@ -4338,7 +4455,7 @@ public class Workspace extends SmoothPagedView
@Override
public void scrollRight() {
- if (!isSmall() && !mIsSwitchingState) {
+ if (!workspaceInModalState() && !mIsSwitchingState) {
super.scrollRight();
}
Folder openFolder = getOpenFolder();
@@ -4360,7 +4477,7 @@ public class Workspace extends SmoothPagedView
}
boolean result = false;
- if (!isSmall() && !mIsSwitchingState && getOpenFolder() == null) {
+ if (!workspaceInModalState() && !mIsSwitchingState && getOpenFolder() == null) {
mInScrollArea = true;
final int page = getNextPage() +
@@ -4452,57 +4569,70 @@ public class Workspace extends SmoothPagedView
return childrenLayouts;
}
- public Folder getFolderForTag(Object tag) {
- ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
- getAllShortcutAndWidgetContainers();
- for (ShortcutAndWidgetContainer layout: childrenLayouts) {
- int count = layout.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = layout.getChildAt(i);
- if (child instanceof Folder) {
- Folder f = (Folder) child;
- if (f.getInfo() == tag && f.getInfo().opened) {
- return f;
- }
- }
+ public Folder getFolderForTag(final Object tag) {
+ return (Folder) getFirstMatch(new ItemOperator() {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ return (v instanceof Folder) && (((Folder) v).getInfo() == tag)
+ && ((Folder) v).getInfo().opened;
}
- }
- return null;
+ });
}
- public View getViewForTag(Object tag) {
- ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
- getAllShortcutAndWidgetContainers();
- for (ShortcutAndWidgetContainer layout: childrenLayouts) {
- int count = layout.getChildCount();
- for (int i = 0; i < count; i++) {
- View child = layout.getChildAt(i);
- if (child.getTag() == tag) {
- return child;
+ public View getViewForTag(final Object tag) {
+ return getFirstMatch(new ItemOperator() {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ return info == tag;
+ }
+ });
+ }
+
+ public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
+ return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ return (info instanceof LauncherAppWidgetInfo) &&
+ ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
+ }
+ });
+ }
+
+ private View getFirstMatch(final ItemOperator operator) {
+ final View[] value = new View[1];
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (operator.evaluate(info, v, parent)) {
+ value[0] = v;
+ return true;
}
+ return false;
}
- }
- return null;
+ });
+ return value[0];
}
void clearDropTargets() {
- ArrayList<ShortcutAndWidgetContainer> childrenLayouts =
- getAllShortcutAndWidgetContainers();
- for (ShortcutAndWidgetContainer layout: childrenLayouts) {
- int childCount = layout.getChildCount();
- for (int j = 0; j < childCount; j++) {
- View v = layout.getChildAt(j);
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
if (v instanceof DropTarget) {
mDragController.removeDropTarget((DropTarget) v);
}
+ // not done, process all the shortcuts
+ return false;
}
- }
+ });
}
// Removes ALL items that match a given package name, this is usually called when a package
// has been removed and we want to remove all components (widgets, shortcuts, apps) that
// belong to that package.
- void removeItemsByPackageName(final ArrayList<String> packages) {
+ void removeItemsByPackageName(final ArrayList<String> packages, final UserHandleCompat user) {
final HashSet<String> packageNames = new HashSet<String>();
packageNames.addAll(packages);
@@ -4522,7 +4652,8 @@ public class Workspace extends SmoothPagedView
@Override
public boolean filterItem(ItemInfo parent, ItemInfo info,
ComponentName cn) {
- if (packageNames.contains(cn.getPackageName())) {
+ if (packageNames.contains(cn.getPackageName())
+ && info.user.equals(user)) {
cns.add(cn);
return true;
}
@@ -4532,13 +4663,13 @@ public class Workspace extends SmoothPagedView
LauncherModel.filterItemInfos(infos, filter);
// Remove the affected components
- removeItemsByComponentName(cns);
+ removeItemsByComponentName(cns, user);
}
// Removes items that match the application info specified, when applications are removed
// as a part of an update, this is called to ensure that other widgets and application
// shortcuts are not removed.
- void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos) {
+ void removeItemsByApplicationInfo(final ArrayList<AppInfo> appInfos, UserHandleCompat user) {
// Just create a hash table of all the specific components that this will affect
HashSet<ComponentName> cns = new HashSet<ComponentName>();
for (AppInfo info : appInfos) {
@@ -4546,10 +4677,11 @@ public class Workspace extends SmoothPagedView
}
// Remove all the things
- removeItemsByComponentName(cns);
+ removeItemsByComponentName(cns, user);
}
- void removeItemsByComponentName(final HashSet<ComponentName> componentNames) {
+ void removeItemsByComponentName(final HashSet<ComponentName> componentNames,
+ final UserHandleCompat user) {
ArrayList<CellLayout> cellLayouts = getWorkspaceAndHotseatCellLayouts();
for (final CellLayout layoutParent: cellLayouts) {
final ViewGroup layout = layoutParent.getShortcutsAndWidgets();
@@ -4568,7 +4700,7 @@ public class Workspace extends SmoothPagedView
public boolean filterItem(ItemInfo parent, ItemInfo info,
ComponentName cn) {
if (parent instanceof FolderInfo) {
- if (componentNames.contains(cn)) {
+ if (componentNames.contains(cn) && info.user.equals(user)) {
FolderInfo folder = (FolderInfo) parent;
ArrayList<ShortcutInfo> appsToRemove;
if (folderAppsToRemove.containsKey(folder)) {
@@ -4581,7 +4713,7 @@ public class Workspace extends SmoothPagedView
return true;
}
} else {
- if (componentNames.contains(cn)) {
+ if (componentNames.contains(cn) && info.user.equals(user)) {
childrenToRemove.add(children.get(info));
return true;
}
@@ -4619,56 +4751,290 @@ public class Workspace extends SmoothPagedView
stripEmptyScreens();
}
- private void updateShortcut(HashMap<ComponentName, AppInfo> appsMap, ItemInfo info,
- View child) {
- ComponentName cn = info.getIntent().getComponent();
- if (info.getRestoredIntent() != null) {
- cn = info.getRestoredIntent().getComponent();
+ interface ItemOperator {
+ /**
+ * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
+ *
+ * @param info info for the shortcut
+ * @param view view for the shortcut
+ * @param parent containing folder, or null
+ * @return true if done, false to continue the map
+ */
+ public boolean evaluate(ItemInfo info, View view, View parent);
+ }
+
+ /**
+ * Map the operator over the shortcuts and widgets, return the first-non-null value.
+ *
+ * @param recurse true: iterate over folder children. false: op get the folders themselves.
+ * @param op the operator to map over the shortcuts
+ */
+ void mapOverItems(boolean recurse, ItemOperator op) {
+ ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
+ final int containerCount = containers.size();
+ for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
+ ShortcutAndWidgetContainer container = containers.get(containerIdx);
+ // map over all the shortcuts on the workspace
+ final int itemCount = container.getChildCount();
+ for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) {
+ View item = container.getChildAt(itemIdx);
+ ItemInfo info = (ItemInfo) item.getTag();
+ if (recurse && info instanceof FolderInfo && item instanceof FolderIcon) {
+ FolderIcon folder = (FolderIcon) item;
+ ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
+ // map over all the children in the folder
+ final int childCount = folderChildren.size();
+ for (int childIdx = 0; childIdx < childCount; childIdx++) {
+ View child = folderChildren.get(childIdx);
+ info = (ItemInfo) child.getTag();
+ if (op.evaluate(info, child, folder)) {
+ return;
+ }
+ }
+ } else {
+ if (op.evaluate(info, item, null)) {
+ return;
+ }
+ }
+ }
}
- if (cn != null) {
- AppInfo appInfo = appsMap.get(cn);
- if ((appInfo != null) && LauncherModel.isShortcutInfoUpdateable(info)) {
- ShortcutInfo shortcutInfo = (ShortcutInfo) info;
- BubbleTextView shortcut = (BubbleTextView) child;
- shortcutInfo.restore();
- shortcutInfo.updateIcon(mIconCache);
- shortcutInfo.title = appInfo.title.toString();
- shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache);
+ }
+
+ void updateShortcutsAndWidgets(ArrayList<AppInfo> apps) {
+ // Break the appinfo list per user
+ final HashMap<UserHandleCompat, ArrayList<AppInfo>> appsPerUser =
+ new HashMap<UserHandleCompat, ArrayList<AppInfo>>();
+ for (AppInfo info : apps) {
+ ArrayList<AppInfo> filtered = appsPerUser.get(info.user);
+ if (filtered == null) {
+ filtered = new ArrayList<AppInfo>();
+ appsPerUser.put(info.user, filtered);
}
+ filtered.add(info);
+ }
+
+ for (Map.Entry<UserHandleCompat, ArrayList<AppInfo>> entry : appsPerUser.entrySet()) {
+ updateShortcutsAndWidgetsPerUser(entry.getValue(), entry.getKey());
}
}
- void updateShortcuts(ArrayList<AppInfo> apps) {
+ private void updateShortcutsAndWidgetsPerUser(ArrayList<AppInfo> apps,
+ final UserHandleCompat user) {
// Create a map of the apps to test against
final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
+ final HashSet<String> pkgNames = new HashSet<String>();
for (AppInfo ai : apps) {
appsMap.put(ai.componentName, ai);
+ pkgNames.add(ai.componentName.getPackageName());
}
+ final HashSet<ComponentName> iconsToRemove = new HashSet<ComponentName>();
- ArrayList<ShortcutAndWidgetContainer> childrenLayouts = getAllShortcutAndWidgetContainers();
- for (ShortcutAndWidgetContainer layout: childrenLayouts) {
- // Update all the children shortcuts
- final HashMap<ItemInfo, View> children = new HashMap<ItemInfo, View>();
- for (int j = 0; j < layout.getChildCount(); j++) {
- View v = layout.getChildAt(j);
- ItemInfo info = (ItemInfo) v.getTag();
- if (info instanceof FolderInfo && v instanceof FolderIcon) {
- FolderIcon folder = (FolderIcon) v;
- ArrayList<View> folderChildren = folder.getFolder().getItemsInReadingOrder();
- for (View fv : folderChildren) {
- info = (ItemInfo) fv.getTag();
- updateShortcut(appsMap, info, fv);
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+ ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+ ComponentName cn = shortcutInfo.getTargetComponent();
+ AppInfo appInfo = appsMap.get(cn);
+ if (user.equals(shortcutInfo.user) && cn != null
+ && LauncherModel.isShortcutInfoUpdateable(info)
+ && pkgNames.contains(cn.getPackageName())) {
+ boolean promiseStateChanged = false;
+ boolean infoUpdated = false;
+ if (shortcutInfo.isPromise()) {
+ if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+ // Auto install icon
+ PackageManager pm = getContext().getPackageManager();
+ ResolveInfo matched = pm.resolveActivity(
+ new Intent(Intent.ACTION_MAIN)
+ .setComponent(cn).addCategory(Intent.CATEGORY_LAUNCHER),
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (matched == null) {
+ // Try to find the best match activity.
+ Intent intent = pm.getLaunchIntentForPackage(
+ cn.getPackageName());
+ if (intent != null) {
+ cn = intent.getComponent();
+ appInfo = appsMap.get(cn);
+ }
+
+ if ((intent == null) || (appsMap == null)) {
+ // Could not find a default activity. Remove this item.
+ iconsToRemove.add(shortcutInfo.getTargetComponent());
+
+ // process next shortcut.
+ return false;
+ }
+ shortcutInfo.promisedIntent = intent;
+ }
+ }
+
+ // Restore the shortcut.
+ shortcutInfo.intent = shortcutInfo.promisedIntent;
+ shortcutInfo.promisedIntent = null;
+ shortcutInfo.status &= ~ShortcutInfo.FLAG_RESTORED_ICON
+ & ~ShortcutInfo.FLAG_AUTOINTALL_ICON
+ & ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+
+ promiseStateChanged = true;
+ infoUpdated = true;
+ shortcutInfo.updateIcon(mIconCache);
+ LauncherModel.updateItemInDatabase(getContext(), shortcutInfo);
+ }
+
+
+ if (appInfo != null) {
+ shortcutInfo.updateIcon(mIconCache);
+ shortcutInfo.title = appInfo.title.toString();
+ shortcutInfo.contentDescription = appInfo.contentDescription;
+ infoUpdated = true;
+ }
+
+ if (infoUpdated) {
+ BubbleTextView shortcut = (BubbleTextView) v;
+ shortcut.applyFromShortcutInfo(shortcutInfo,
+ mIconCache, true, promiseStateChanged);
+
+ if (parent != null) {
+ parent.invalidate();
+ }
+ }
+ }
+ }
+ // process all the shortcuts
+ return false;
+ }
+ });
+
+ if (!iconsToRemove.isEmpty()) {
+ removeItemsByComponentName(iconsToRemove, user);
+ }
+ if (user.equals(UserHandleCompat.myUserHandle())) {
+ restorePendingWidgets(pkgNames);
+ }
+ }
+
+ public void removeAbandonedPromise(String packageName, UserHandleCompat user) {
+ ArrayList<String> packages = new ArrayList<String>(1);
+ packages.add(packageName);
+ LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user);
+ removeItemsByPackageName(packages, user);
+ }
+
+ public void updatePackageBadge(final String packageName, final UserHandleCompat user) {
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+ ShortcutInfo shortcutInfo = (ShortcutInfo) info;
+ ComponentName cn = shortcutInfo.getTargetComponent();
+ if (user.equals(shortcutInfo.user) && cn != null
+ && shortcutInfo.isPromise()
+ && packageName.equals(cn.getPackageName())) {
+ if (shortcutInfo.hasStatusFlag(ShortcutInfo.FLAG_AUTOINTALL_ICON)) {
+ // For auto install apps update the icon as well as label.
+ mIconCache.getTitleAndIcon(shortcutInfo,
+ shortcutInfo.promisedIntent, user, true);
+ } else {
+ // Only update the icon for restored apps.
+ shortcutInfo.updateIcon(mIconCache);
+ }
+ BubbleTextView shortcut = (BubbleTextView) v;
+ shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
+
+ if (parent != null) {
+ parent.invalidate();
+ }
+ }
+ }
+ // process all the shortcuts
+ return false;
+ }
+ });
+ }
+
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfos) {
+ HashSet<String> completedPackages = new HashSet<String>();
+
+ for (final PackageInstallInfo installInfo : installInfos) {
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
+ ShortcutInfo si = (ShortcutInfo) info;
+ ComponentName cn = si.getTargetComponent();
+ if (si.isPromise() && (cn != null)
+ && installInfo.packageName.equals(cn.getPackageName())) {
+ si.setInstallProgress(installInfo.progress);
+ if (installInfo.state == PackageInstallerCompat.STATUS_FAILED) {
+ // Mark this info as broken.
+ si.status &= ~ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE;
+ }
+ ((BubbleTextView)v).applyState(false);
+ }
+ } else if (v instanceof PendingAppWidgetHostView
+ && info instanceof LauncherAppWidgetInfo
+ && ((LauncherAppWidgetInfo) info).providerName.getPackageName()
+ .equals(installInfo.packageName)) {
+ ((LauncherAppWidgetInfo) info).installProgress = installInfo.progress;
+ ((PendingAppWidgetHostView) v).applyState();
+ }
+
+ // process all the shortcuts
+ return false;
+ }
+ });
+
+ if (installInfo.state == PackageInstallerCompat.STATUS_INSTALLED) {
+ completedPackages.add(installInfo.packageName);
+ }
+ }
+
+ // Note that package states are sent only for myUser
+ if (!completedPackages.isEmpty()) {
+ restorePendingWidgets(completedPackages);
+ }
+ }
+
+ private void restorePendingWidgets(final Set<String> installedPackaged) {
+ final ArrayList<LauncherAppWidgetInfo> changedInfo = new ArrayList<LauncherAppWidgetInfo>();
+
+ // Iterate non recursively as widgets can't be inside a folder.
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+ if (widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
+ && installedPackaged.contains(widgetInfo.providerName.getPackageName())) {
+
+ changedInfo.add(widgetInfo);
+
+ // Remove the provider not ready flag
+ widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
+ LauncherModel.updateItemInDatabase(getContext(), widgetInfo);
}
- folder.invalidate();
- } else if (info instanceof ShortcutInfo) {
- updateShortcut(appsMap, info, v);
}
+ // process all the widget
+ return false;
+ }
+ });
+ if (!changedInfo.isEmpty()) {
+ DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
+ mLauncher.getAppWidgetHost());
+ if (LauncherModel.findAppWidgetProviderInfoWithComponent(getContext(),
+ changedInfo.get(0).providerName) != null) {
+ // Re-inflate the widgets which have changed status
+ widgetRefresh.run();
+ } else {
+ // widgetRefresh will automatically run when the packages are updated.
}
}
}
private void moveToScreen(int page, boolean animate) {
- if (!isSmall()) {
+ if (!workspaceInModalState()) {
if (animate) {
snapToPage(page);
} else {
@@ -4741,4 +5107,53 @@ public class Workspace extends SmoothPagedView
public void getLocationInDragLayer(int[] loc) {
mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
}
+
+ /**
+ * Used as a workaround to ensure that the AppWidgetService receives the
+ * PACKAGE_ADDED broadcast before updating widgets.
+ */
+ private class DeferredWidgetRefresh implements Runnable {
+ private final ArrayList<LauncherAppWidgetInfo> mInfos;
+ private final LauncherAppWidgetHost mHost;
+ private final Handler mHandler;
+
+ private boolean mRefreshPending;
+
+ public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
+ LauncherAppWidgetHost host) {
+ mInfos = infos;
+ mHost = host;
+ mHandler = new Handler();
+ mRefreshPending = true;
+
+ mHost.addProviderChangeListener(this);
+ // Force refresh after 10 seconds, if we don't get the provider changed event.
+ // This could happen when the provider is no longer available in the app.
+ mHandler.postDelayed(this, 10000);
+ }
+
+ @Override
+ public void run() {
+ mHost.removeProviderChangeListener(this);
+ mHandler.removeCallbacks(this);
+
+ if (!mRefreshPending) {
+ return;
+ }
+
+ mRefreshPending = false;
+
+ for (LauncherAppWidgetInfo info : mInfos) {
+ if (info.hostView instanceof PendingAppWidgetHostView) {
+ PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
+ mLauncher.removeAppWidget(info);
+
+ CellLayout cl = (CellLayout) view.getParent().getParent();
+ // Remove the current widget
+ cl.removeView(view);
+ mLauncher.bindAppWidget(info);
+ }
+ }
+ }
+ }
}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
new file mode 100644
index 000000000..6512d427e
--- /dev/null
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+public abstract class AppWidgetManagerCompat {
+
+ private static final Object sInstanceLock = new Object();
+ private static AppWidgetManagerCompat sInstance;
+
+
+ public static AppWidgetManagerCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ if (Utilities.isLmpOrAbove()) {
+ sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext());
+ } else {
+ sInstance = new AppWidgetManagerCompatV16(context.getApplicationContext());
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ final AppWidgetManager mAppWidgetManager;
+ final Context mContext;
+
+ AppWidgetManagerCompat(Context context) {
+ mContext = context;
+ mAppWidgetManager = AppWidgetManager.getInstance(context);
+ }
+
+ public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
+ return mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ }
+
+ public abstract List<AppWidgetProviderInfo> getAllProviders();
+
+ public abstract String loadLabel(AppWidgetProviderInfo info);
+
+ public abstract boolean bindAppWidgetIdIfAllowed(
+ int appWidgetId, AppWidgetProviderInfo info, Bundle options);
+
+ public abstract UserHandleCompat getUser(AppWidgetProviderInfo info);
+
+ public abstract void startConfigActivity(AppWidgetProviderInfo info, int widgetId,
+ Activity activity, AppWidgetHost host, int requestCode);
+
+ public abstract Drawable loadPreview(AppWidgetProviderInfo info);
+
+ public abstract Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache);
+
+ public abstract Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap);
+
+}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
new file mode 100644
index 000000000..f599f4303
--- /dev/null
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat {
+
+ AppWidgetManagerCompatV16(Context context) {
+ super(context);
+ }
+
+ @Override
+ public List<AppWidgetProviderInfo> getAllProviders() {
+ return mAppWidgetManager.getInstalledProviders();
+ }
+
+ @Override
+ public String loadLabel(AppWidgetProviderInfo info) {
+ return info.label.trim();
+ }
+
+ @Override
+ public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
+ Bundle options) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider);
+ } else {
+ return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider, options);
+ }
+ }
+
+ @Override
+ public UserHandleCompat getUser(AppWidgetProviderInfo info) {
+ return UserHandleCompat.myUserHandle();
+ }
+
+ @Override
+ public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
+ AppWidgetHost host, int requestCode) {
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
+ intent.setComponent(info.configure);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
+ Utilities.startActivityForResultSafely(activity, intent, requestCode);
+ }
+
+ @Override
+ public Drawable loadPreview(AppWidgetProviderInfo info) {
+ return mContext.getPackageManager().getDrawable(
+ info.provider.getPackageName(), info.previewImage, null);
+ }
+
+ @Override
+ public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) {
+ return cache.getFullResIcon(info.provider.getPackageName(), info.icon);
+ }
+
+ @Override
+ public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) {
+ return bitmap;
+ }
+}
diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
new file mode 100644
index 000000000..c3853ab62
--- /dev/null
+++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatVL.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@TargetApi(Build.VERSION_CODES.L)
+class AppWidgetManagerCompatVL extends AppWidgetManagerCompat {
+
+ private final UserManager mUserManager;
+ private final PackageManager mPm;
+
+ AppWidgetManagerCompatVL(Context context) {
+ super(context);
+ mPm = context.getPackageManager();
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ @Override
+ public List<AppWidgetProviderInfo> getAllProviders() {
+ ArrayList<AppWidgetProviderInfo> providers = new ArrayList<AppWidgetProviderInfo>();
+ for (UserHandle user : mUserManager.getUserProfiles()) {
+ providers.addAll(mAppWidgetManager.getInstalledProvidersForProfile(user));
+ }
+ return providers;
+ }
+
+ @Override
+ public String loadLabel(AppWidgetProviderInfo info) {
+ return info.loadLabel(mPm);
+ }
+
+ @Override
+ public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info,
+ Bundle options) {
+ return mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ appWidgetId, info.getProfile(), info.provider, options);
+ }
+
+ @Override
+ public UserHandleCompat getUser(AppWidgetProviderInfo info) {
+ return UserHandleCompat.fromUser(info.getProfile());
+ }
+
+ @Override
+ public void startConfigActivity(AppWidgetProviderInfo info, int widgetId, Activity activity,
+ AppWidgetHost host, int requestCode) {
+ try {
+ host.startAppWidgetConfigureActivityForResult(activity, widgetId, 0, requestCode, null);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ } catch (SecurityException e) {
+ Toast.makeText(activity, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ }
+ }
+
+ @Override
+ public Drawable loadPreview(AppWidgetProviderInfo info) {
+ return info.loadPreviewImage(mContext, 0);
+ }
+
+ @Override
+ public Drawable loadIcon(AppWidgetProviderInfo info, IconCache cache) {
+ return info.loadIcon(mContext, cache.getFullResIconDpi());
+ }
+
+ @Override
+ public Bitmap getBadgeBitmap(AppWidgetProviderInfo info, Bitmap bitmap) {
+ if (info.getProfile().equals(android.os.Process.myUserHandle())) {
+ return bitmap;
+ }
+
+ // Add a user badge in the bottom right of the image.
+ final Resources res = mContext.getResources();
+ final int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size);
+ final int badgeMargin = res.getDimensionPixelSize(R.dimen.profile_badge_margin);
+ final Rect badgeLocation = new Rect(0, 0, badgeSize, badgeSize);
+
+ final int top = bitmap.getHeight() - badgeSize - badgeMargin;
+ if (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+ badgeLocation.offset(badgeMargin, top);
+ } else {
+ badgeLocation.offset(bitmap.getWidth() - badgeSize - badgeMargin, top);
+ }
+
+ Drawable drawable = mPm.getUserBadgedDrawableForDensity(
+ new BitmapDrawable(res, bitmap), info.getProfile(), badgeLocation, 0);
+
+ if (drawable instanceof BitmapDrawable) {
+ return ((BitmapDrawable) drawable).getBitmap();
+ }
+
+ bitmap.eraseColor(Color.TRANSPARENT);
+ Canvas c = new Canvas(bitmap);
+ drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+ drawable.draw(c);
+ c.setBitmap(null);
+ return bitmap;
+ }
+}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
new file mode 100644
index 000000000..90a4d1a1f
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompat.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.graphics.drawable.Drawable;
+
+public abstract class LauncherActivityInfoCompat {
+
+ LauncherActivityInfoCompat() {
+ }
+
+ public abstract ComponentName getComponentName();
+ public abstract UserHandleCompat getUser();
+ public abstract CharSequence getLabel();
+ public abstract Drawable getIcon(int density);
+ public abstract ApplicationInfo getApplicationInfo();
+ public abstract long getFirstInstallTime();
+ public abstract Drawable getBadgedIcon(int density);
+}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
new file mode 100644
index 000000000..1d41a6ff6
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatV16.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+
+
+public class LauncherActivityInfoCompatV16 extends LauncherActivityInfoCompat {
+ private ActivityInfo mActivityInfo;
+ private ComponentName mComponentName;
+ private PackageManager mPm;
+
+ LauncherActivityInfoCompatV16(Context context, ResolveInfo info) {
+ super();
+ this.mActivityInfo = info.activityInfo;
+ mComponentName = new ComponentName(mActivityInfo.packageName, mActivityInfo.name);
+ mPm = context.getPackageManager();
+ }
+
+ public ComponentName getComponentName() {
+ return mComponentName;
+ }
+
+ public UserHandleCompat getUser() {
+ return UserHandleCompat.myUserHandle();
+ }
+
+ public CharSequence getLabel() {
+ return mActivityInfo.loadLabel(mPm);
+ }
+
+ public Drawable getIcon(int density) {
+ Drawable d = null;
+ if (mActivityInfo.getIconResource() != 0) {
+ Resources resources;
+ try {
+ resources = mPm.getResourcesForApplication(mActivityInfo.packageName);
+ } catch (PackageManager.NameNotFoundException e) {
+ resources = null;
+ }
+ if (resources != null) {
+ try {
+ d = resources.getDrawableForDensity(mActivityInfo.getIconResource(), density);
+ } catch (Resources.NotFoundException e) {
+ // Return default icon below.
+ }
+ }
+ }
+ if (d == null) {
+ Resources resources = Resources.getSystem();
+ d = resources.getDrawableForDensity(android.R.mipmap.sym_def_app_icon, density);
+ }
+ return d;
+ }
+
+ public ApplicationInfo getApplicationInfo() {
+ return mActivityInfo.applicationInfo;
+ }
+
+ public long getFirstInstallTime() {
+ try {
+ PackageInfo info = mPm.getPackageInfo(mActivityInfo.packageName, 0);
+ return info != null ? info.firstInstallTime : 0;
+ } catch (NameNotFoundException e) {
+ return 0;
+ }
+ }
+
+ public String getName() {
+ return mActivityInfo.name;
+ }
+
+ public Drawable getBadgedIcon(int density) {
+ return getIcon(density);
+ }
+}
diff --git a/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
new file mode 100644
index 000000000..b52cf1de2
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherActivityInfoCompatVL.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.LauncherActivityInfo;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+
+public class LauncherActivityInfoCompatVL extends LauncherActivityInfoCompat {
+ private LauncherActivityInfo mLauncherActivityInfo;
+
+ LauncherActivityInfoCompatVL(LauncherActivityInfo launcherActivityInfo) {
+ super();
+ mLauncherActivityInfo = launcherActivityInfo;
+ }
+
+ public ComponentName getComponentName() {
+ return mLauncherActivityInfo.getComponentName();
+ }
+
+ public UserHandleCompat getUser() {
+ return UserHandleCompat.fromUser(mLauncherActivityInfo.getUser());
+ }
+
+ public CharSequence getLabel() {
+ return mLauncherActivityInfo.getLabel();
+ }
+
+ public Drawable getIcon(int density) {
+ return mLauncherActivityInfo.getIcon(density);
+ }
+
+ public ApplicationInfo getApplicationInfo() {
+ return mLauncherActivityInfo.getApplicationInfo();
+ }
+
+ public long getFirstInstallTime() {
+ return mLauncherActivityInfo.getFirstInstallTime();
+ }
+
+ public Drawable getBadgedIcon(int density) {
+ return mLauncherActivityInfo.getBadgedIcon(density);
+ }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
new file mode 100644
index 000000000..6efcc00fd
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+public abstract class LauncherAppsCompat {
+
+ public static final String ACTION_MANAGED_PROFILE_ADDED =
+ "android.intent.action.MANAGED_PROFILE_ADDED";
+ public static final String ACTION_MANAGED_PROFILE_REMOVED =
+ "android.intent.action.MANAGED_PROFILE_REMOVED";
+
+ public interface OnAppsChangedCallbackCompat {
+ void onPackageRemoved(String packageName, UserHandleCompat user);
+ void onPackageAdded(String packageName, UserHandleCompat user);
+ void onPackageChanged(String packageName, UserHandleCompat user);
+ void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing);
+ void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
+ }
+
+ protected LauncherAppsCompat() {
+ }
+
+ private static LauncherAppsCompat sInstance;
+ private static Object sInstanceLock = new Object();
+
+ public static LauncherAppsCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ if (Utilities.isLmpOrAbove()) {
+ sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
+ } else {
+ sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ public abstract List<LauncherActivityInfoCompat> getActivityList(String packageName,
+ UserHandleCompat user);
+ public abstract LauncherActivityInfoCompat resolveActivity(Intent intent,
+ UserHandleCompat user);
+ public abstract void startActivityForProfile(ComponentName component, UserHandleCompat user,
+ Rect sourceBounds, Bundle opts);
+ public abstract void showAppDetailsForProfile(ComponentName component, UserHandleCompat user);
+ public abstract void addOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
+ public abstract void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat listener);
+ public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user);
+ public abstract boolean isActivityEnabledForProfile(ComponentName component,
+ UserHandleCompat user);
+} \ No newline at end of file
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
new file mode 100644
index 000000000..7e5e6bf2c
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Version of {@link LauncherAppsCompat} for devices with API level 16.
+ * Devices Pre-L don't support multiple profiles in one launcher so
+ * user parameters are ignored and all methods operate on the current user.
+ */
+public class LauncherAppsCompatV16 extends LauncherAppsCompat {
+
+ private PackageManager mPm;
+ private Context mContext;
+ private List<OnAppsChangedCallbackCompat> mCallbacks
+ = new ArrayList<OnAppsChangedCallbackCompat>();
+ private PackageMonitor mPackageMonitor;
+
+ LauncherAppsCompatV16(Context context) {
+ mPm = context.getPackageManager();
+ mContext = context;
+ mPackageMonitor = new PackageMonitor();
+ }
+
+ public List<LauncherActivityInfoCompat> getActivityList(String packageName,
+ UserHandleCompat user) {
+ final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
+ mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ mainIntent.setPackage(packageName);
+ List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0);
+ List<LauncherActivityInfoCompat> list =
+ new ArrayList<LauncherActivityInfoCompat>(infos.size());
+ for (ResolveInfo info : infos) {
+ list.add(new LauncherActivityInfoCompatV16(mContext, info));
+ }
+ return list;
+ }
+
+ public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) {
+ ResolveInfo info = mPm.resolveActivity(intent, 0);
+ if (info != null) {
+ return new LauncherActivityInfoCompatV16(mContext, info);
+ }
+ return null;
+ }
+
+ public void startActivityForProfile(ComponentName component, UserHandleCompat user,
+ Rect sourceBounds, Bundle opts) {
+ Intent launchIntent = new Intent(Intent.ACTION_MAIN);
+ launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+ launchIntent.setComponent(component);
+ launchIntent.setSourceBounds(sourceBounds);
+ launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(launchIntent, opts);
+ }
+
+ public void showAppDetailsForProfile(ComponentName component, UserHandleCompat user) {
+ String packageName = component.getPackageName();
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", packageName, null));
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
+ Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ mContext.startActivity(intent, null);
+ }
+
+ public synchronized void addOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
+ if (callback != null && !mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ if (mCallbacks.size() == 1) {
+ registerForPackageIntents();
+ }
+ }
+ }
+
+ public synchronized void removeOnAppsChangedCallback(OnAppsChangedCallbackCompat callback) {
+ mCallbacks.remove(callback);
+ if (mCallbacks.size() == 0) {
+ unregisterForPackageIntents();
+ }
+ }
+
+ public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
+ try {
+ PackageInfo info = mPm.getPackageInfo(packageName, 0);
+ return info != null && info.applicationInfo.enabled;
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
+ try {
+ ActivityInfo info = mPm.getActivityInfo(component, 0);
+ return info != null && info.isEnabled();
+ } catch (NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ private void unregisterForPackageIntents() {
+ mContext.unregisterReceiver(mPackageMonitor);
+ }
+
+ private void registerForPackageIntents() {
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiver(mPackageMonitor, filter);
+ filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+ mContext.registerReceiver(mPackageMonitor, filter);
+ }
+
+ private synchronized List<OnAppsChangedCallbackCompat> getCallbacks() {
+ return new ArrayList<OnAppsChangedCallbackCompat>(mCallbacks);
+ }
+
+ private class PackageMonitor extends BroadcastReceiver {
+ public void onReceive(Context context, Intent intent) {
+ final String action = intent.getAction();
+ final UserHandleCompat user = UserHandleCompat.myUserHandle();
+
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(action)
+ || Intent.ACTION_PACKAGE_REMOVED.equals(action)
+ || Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ final String packageName = intent.getData().getSchemeSpecificPart();
+ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+ if (packageName == null || packageName.length() == 0) {
+ // they sent us a bad intent
+ return;
+ }
+ if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
+ callback.onPackageChanged(packageName, user);
+ }
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ if (!replacing) {
+ for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
+ callback.onPackageRemoved(packageName, user);
+ }
+ }
+ // else, we are replacing the package, so a PACKAGE_ADDED will be sent
+ // later, we will update the package at this time
+ } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ if (!replacing) {
+ for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
+ callback.onPackageAdded(packageName, user);
+ }
+ } else {
+ for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
+ callback.onPackageChanged(packageName, user);
+ }
+ }
+ }
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ // EXTRA_REPLACING is available Kitkat onwards. For lower devices, it is broadcasted
+ // when moving a package or mounting/un-mounting external storage. Assume that
+ // it is a replacing operation.
+ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT);
+ String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
+ callback.onPackagesAvailable(packages, user, replacing);
+ }
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
+ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
+ Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT);
+ String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
+ for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
+ callback.onPackagesUnavailable(packages, user, replacing);
+ }
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatVL.java b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
new file mode 100644
index 000000000..e0d28b566
--- /dev/null
+++ b/src/com/android/launcher3/compat/LauncherAppsCompatVL.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.LauncherActivityInfo;
+import android.content.pm.LauncherApps;
+import android.graphics.Rect;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class LauncherAppsCompatVL extends LauncherAppsCompat {
+
+ private LauncherApps mLauncherApps;
+
+ private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks
+ = new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
+
+ LauncherAppsCompatVL(Context context) {
+ super();
+ mLauncherApps = (LauncherApps) context.getSystemService("launcherapps");
+ }
+
+ public List<LauncherActivityInfoCompat> getActivityList(String packageName,
+ UserHandleCompat user) {
+ List<LauncherActivityInfo> list = mLauncherApps.getActivityList(packageName,
+ user.getUser());
+ if (list.size() == 0) {
+ return Collections.EMPTY_LIST;
+ }
+ ArrayList<LauncherActivityInfoCompat> compatList =
+ new ArrayList<LauncherActivityInfoCompat>(list.size());
+ for (LauncherActivityInfo info : list) {
+ compatList.add(new LauncherActivityInfoCompatVL(info));
+ }
+ return compatList;
+ }
+
+ public LauncherActivityInfoCompat resolveActivity(Intent intent, UserHandleCompat user) {
+ LauncherActivityInfo activity = mLauncherApps.resolveActivity(intent, user.getUser());
+ if (activity != null) {
+ return new LauncherActivityInfoCompatVL(activity);
+ } else {
+ return null;
+ }
+ }
+
+ public void startActivityForProfile(ComponentName component, UserHandleCompat user,
+ Rect sourceBounds, Bundle opts) {
+ mLauncherApps.startMainActivity(component, user.getUser(), sourceBounds, opts);
+ }
+
+ public void showAppDetailsForProfile(ComponentName component, UserHandleCompat user) {
+ mLauncherApps.startAppDetailsActivity(component, user.getUser(), null, null);
+ }
+
+ public void addOnAppsChangedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
+ WrappedCallback wrappedCallback = new WrappedCallback(callback);
+ synchronized (mCallbacks) {
+ mCallbacks.put(callback, wrappedCallback);
+ }
+ mLauncherApps.registerCallback(wrappedCallback);
+ }
+
+ public void removeOnAppsChangedCallback(
+ LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
+ WrappedCallback wrappedCallback = null;
+ synchronized (mCallbacks) {
+ wrappedCallback = mCallbacks.remove(callback);
+ }
+ if (wrappedCallback != null) {
+ mLauncherApps.unregisterCallback(wrappedCallback);
+ }
+ }
+
+ public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
+ return mLauncherApps.isPackageEnabled(packageName, user.getUser());
+ }
+
+ public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
+ return mLauncherApps.isActivityEnabled(component, user.getUser());
+ }
+
+ private static class WrappedCallback extends LauncherApps.Callback {
+ private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
+
+ public WrappedCallback(LauncherAppsCompat.OnAppsChangedCallbackCompat callback) {
+ mCallback = callback;
+ }
+
+ public void onPackageRemoved(String packageName, UserHandle user) {
+ mCallback.onPackageRemoved(packageName, UserHandleCompat.fromUser(user));
+ }
+
+ public void onPackageAdded(String packageName, UserHandle user) {
+ mCallback.onPackageAdded(packageName, UserHandleCompat.fromUser(user));
+ }
+
+ public void onPackageChanged(String packageName, UserHandle user) {
+ mCallback.onPackageChanged(packageName, UserHandleCompat.fromUser(user));
+ }
+
+ public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) {
+ mCallback.onPackagesAvailable(packageNames, UserHandleCompat.fromUser(user), replacing);
+ }
+
+ public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+ boolean replacing) {
+ mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user),
+ replacing);
+ }
+ }
+}
+
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
new file mode 100644
index 000000000..0eb8754e8
--- /dev/null
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Context;
+
+import com.android.launcher3.Utilities;
+
+import java.util.HashSet;
+
+public abstract class PackageInstallerCompat {
+
+ public static final int STATUS_INSTALLED = 0;
+ public static final int STATUS_INSTALLING = 1;
+ public static final int STATUS_FAILED = 2;
+
+ private static final Object sInstanceLock = new Object();
+ private static PackageInstallerCompat sInstance;
+
+ public static PackageInstallerCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ if (Utilities.isLmpOrAbove()) {
+ sInstance = new PackageInstallerCompatVL(context);
+ } else {
+ sInstance = new PackageInstallerCompatV16(context) { };
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ public abstract HashSet<String> updateAndGetActiveSessionCache();
+
+ public abstract void onPause();
+
+ public abstract void onResume();
+
+ public abstract void onFinishBind();
+
+ public abstract void onStop();
+
+ public abstract void recordPackageUpdate(String packageName, int state, int progress);
+
+ public static final class PackageInstallInfo {
+ public final String packageName;
+
+ public int state;
+ public int progress;
+
+ public PackageInstallInfo(String packageName) {
+ this.packageName = packageName;
+ }
+
+ public PackageInstallInfo(String packageName, int state, int progress) {
+ this.packageName = packageName;
+ this.state = state;
+ this.progress = progress;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
new file mode 100644
index 000000000..1910d22ae
--- /dev/null
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.LauncherAppState;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONStringer;
+import org.json.JSONTokener;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+public class PackageInstallerCompatV16 extends PackageInstallerCompat {
+
+ private static final String TAG = "PackageInstallerCompatV16";
+ private static final boolean DEBUG = false;
+
+ private static final String KEY_PROGRESS = "progress";
+ private static final String KEY_STATE = "state";
+
+ private static final String PREFS =
+ "com.android.launcher3.compat.PackageInstallerCompatV16.queue";
+
+ protected final SharedPreferences mPrefs;
+
+ boolean mUseQueue;
+ boolean mFinishedBind;
+ boolean mReplayPending;
+
+ PackageInstallerCompatV16(Context context) {
+ mPrefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+ }
+
+ @Override
+ public void onPause() {
+ mUseQueue = true;
+ if (DEBUG) Log.d(TAG, "updates paused");
+ }
+
+ @Override
+ public void onResume() {
+ mUseQueue = false;
+ if (mFinishedBind) {
+ replayUpdates();
+ }
+ }
+
+ @Override
+ public void onFinishBind() {
+ mFinishedBind = true;
+ if (!mUseQueue) {
+ replayUpdates();
+ }
+ }
+
+ @Override
+ public void onStop() { }
+
+ private void replayUpdates() {
+ if (DEBUG) Log.d(TAG, "updates resumed");
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ mReplayPending = true; // try again later
+ if (DEBUG) Log.d(TAG, "app is null, delaying send");
+ return;
+ }
+ mReplayPending = false;
+ ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
+ for (String packageName: mPrefs.getAll().keySet()) {
+ final String json = mPrefs.getString(packageName, null);
+ if (!TextUtils.isEmpty(json)) {
+ updates.add(infoFromJson(packageName, json));
+ }
+ }
+ if (!updates.isEmpty()) {
+ sendUpdate(app, updates);
+ }
+ }
+
+ /**
+ * This should be called by the implementations to register a package update.
+ */
+ @Override
+ public synchronized void recordPackageUpdate(String packageName, int state, int progress) {
+ SharedPreferences.Editor editor = mPrefs.edit();
+ PackageInstallInfo installInfo = new PackageInstallInfo(packageName);
+ installInfo.progress = progress;
+ installInfo.state = state;
+ if (state == STATUS_INSTALLED) {
+ // no longer necessary to track this package
+ editor.remove(packageName);
+ if (DEBUG) Log.d(TAG, "no longer tracking " + packageName);
+ } else {
+ editor.putString(packageName, infoToJson(installInfo));
+ if (DEBUG)
+ Log.d(TAG, "saved state: " + infoToJson(installInfo)
+ + " for package: " + packageName);
+
+ }
+ editor.commit();
+
+ if (!mUseQueue) {
+ if (mReplayPending) {
+ replayUpdates();
+ } else if (state != STATUS_INSTALLED) {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ ArrayList<PackageInstallInfo> update = new ArrayList<PackageInstallInfo>();
+ update.add(installInfo);
+ sendUpdate(app, update);
+ }
+ }
+ }
+
+ private void sendUpdate(LauncherAppState app, ArrayList<PackageInstallInfo> updates) {
+ if (app == null) {
+ mReplayPending = true; // try again later
+ if (DEBUG) Log.d(TAG, "app is null, delaying send");
+ } else {
+ app.setPackageState(updates);
+ }
+ }
+
+ private static PackageInstallInfo infoFromJson(String packageName, String json) {
+ PackageInstallInfo info = new PackageInstallInfo(packageName);
+ try {
+ JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
+ info.state = object.getInt(KEY_STATE);
+ info.progress = object.getInt(KEY_PROGRESS);
+ } catch (JSONException e) {
+ Log.e(TAG, "failed to deserialize app state update", e);
+ }
+ return info;
+ }
+
+ private static String infoToJson(PackageInstallInfo info) {
+ String value = null;
+ try {
+ JSONStringer json = new JSONStringer()
+ .object()
+ .key(KEY_STATE).value(info.state)
+ .key(KEY_PROGRESS).value(info.progress)
+ .endObject();
+ value = json.toString();
+ } catch (JSONException e) {
+ Log.e(TAG, "failed to serialize app state update", e);
+ }
+ return value;
+ }
+
+ @Override
+ public HashSet<String> updateAndGetActiveSessionCache() {
+ return new HashSet<String>();
+ }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
new file mode 100644
index 000000000..16ad3792a
--- /dev/null
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Context;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionCallback;
+import android.content.pm.PackageInstaller.SessionInfo;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.IconCache;
+import com.android.launcher3.LauncherAppState;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+public class PackageInstallerCompatVL extends PackageInstallerCompat {
+
+ private static final String TAG = "PackageInstallerCompatVL";
+ private static final boolean DEBUG = false;
+
+ private final SparseArray<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
+ private final HashSet<String> mPendingBadgeUpdates = new HashSet<String>();
+ private final PackageInstaller mInstaller;
+ private final IconCache mCache;
+
+ private boolean mResumed;
+ private boolean mBound;
+
+ PackageInstallerCompatVL(Context context) {
+ mInstaller = context.getPackageManager().getPackageInstaller();
+ LauncherAppState.setApplicationContext(context.getApplicationContext());
+ mCache = LauncherAppState.getInstance().getIconCache();
+
+ mResumed = false;
+ mBound = false;
+
+ mInstaller.registerSessionCallback(mCallback);
+
+ // On start, send updates for all active sessions
+ for (SessionInfo info : mInstaller.getAllSessions()) {
+ mPendingReplays.append(info.getSessionId(), info);
+ }
+ }
+
+ @Override
+ public HashSet<String> updateAndGetActiveSessionCache() {
+ HashSet<String> activePackages = new HashSet<String>();
+ UserHandleCompat user = UserHandleCompat.myUserHandle();
+ for (SessionInfo info : mInstaller.getAllSessions()) {
+ addSessionInfoToCahce(info, user);
+ if (info.getAppPackageName() != null) {
+ activePackages.add(info.getAppPackageName());
+ }
+ }
+ return activePackages;
+ }
+
+ private void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) {
+ String packageName = info.getAppPackageName();
+ if (packageName != null) {
+ mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
+ info.getAppLabel());
+ }
+ }
+
+ @Override
+ public void onStop() {
+ }
+
+ @Override
+ public void onFinishBind() {
+ mBound = true;
+ replayUpdates(null);
+ }
+
+ @Override
+ public void onPause() {
+ mResumed = false;
+ }
+
+ @Override
+ public void onResume() {
+ mResumed = true;
+ replayUpdates(null);
+ }
+
+ @Override
+ public void recordPackageUpdate(String packageName, int state, int progress) {
+ // No op
+ }
+
+ private void replayUpdates(PackageInstallInfo newInfo) {
+ if (DEBUG) Log.d(TAG, "updates resumed");
+ if (!mResumed || !mBound) {
+ // Not yet ready
+ return;
+ }
+ if ((mPendingReplays.size() == 0) && (newInfo == null) && mPendingBadgeUpdates.isEmpty()) {
+ // Nothing to update
+ return;
+ }
+
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ // Try again later
+ if (DEBUG) Log.d(TAG, "app is null, delaying send");
+ return;
+ }
+
+ ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
+ if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) {
+ updates.add(newInfo);
+ }
+ for (int i = mPendingReplays.size() - 1; i >= 0; i--) {
+ SessionInfo session = mPendingReplays.valueAt(i);
+ if (session.getAppPackageName() != null) {
+ updates.add(new PackageInstallInfo(session.getAppPackageName(),
+ STATUS_INSTALLING,
+ (int) (session.getProgress() * 100)));
+ }
+ }
+ mPendingReplays.clear();
+ if (!updates.isEmpty()) {
+ app.setPackageState(updates);
+ }
+
+ if (!mPendingBadgeUpdates.isEmpty()) {
+ for (String pkg : mPendingBadgeUpdates) {
+ app.updatePackageBadge(pkg);
+ }
+ mPendingBadgeUpdates.clear();
+ }
+ }
+
+ private final SessionCallback mCallback = new SessionCallback() {
+
+ @Override
+ public void onCreated(int sessionId) {
+ pushSessionBadgeToLauncher(sessionId);
+ }
+
+ @Override
+ public void onFinished(int sessionId, boolean success) {
+ mPendingReplays.remove(sessionId);
+ SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ if ((session != null) && (session.getAppPackageName() != null)) {
+ mPendingBadgeUpdates.remove(session.getAppPackageName());
+ // Replay all updates with a one time update for this installed package. No
+ // need to store this record for future updates, as the app list will get
+ // refreshed on resume.
+ replayUpdates(new PackageInstallInfo(session.getAppPackageName(),
+ success ? STATUS_INSTALLED : STATUS_FAILED, 0));
+ }
+ }
+
+ @Override
+ public void onProgressChanged(int sessionId, float progress) {
+ SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ if (session != null) {
+ mPendingReplays.put(sessionId, session);
+ replayUpdates(null);
+ }
+ }
+
+ @Override
+ public void onActiveChanged(int sessionId, boolean active) { }
+
+ @Override
+ public void onBadgingChanged(int sessionId) {
+ pushSessionBadgeToLauncher(sessionId);
+ }
+
+ private void pushSessionBadgeToLauncher(int sessionId) {
+ SessionInfo session = mInstaller.getSessionInfo(sessionId);
+ if (session != null) {
+ addSessionInfoToCahce(session, UserHandleCompat.myUserHandle());
+ if (session.getAppPackageName() != null) {
+ mPendingBadgeUpdates.add(session.getAppPackageName());
+ }
+ mPendingReplays.put(sessionId, session);
+ replayUpdates(null);
+ }
+ }
+ };
+}
diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java
new file mode 100644
index 000000000..2ae673171
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserHandleCompat.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Intent;
+import android.os.Build;
+import android.os.UserHandle;
+
+import com.android.launcher3.Utilities;
+
+public class UserHandleCompat {
+ private UserHandle mUser;
+
+ private UserHandleCompat(UserHandle user) {
+ mUser = user;
+ }
+
+ private UserHandleCompat() {
+ }
+
+ public static UserHandleCompat myUserHandle() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return new UserHandleCompat(android.os.Process.myUserHandle());
+ } else {
+ return new UserHandleCompat();
+ }
+ }
+
+ static UserHandleCompat fromUser(UserHandle user) {
+ if (user == null) {
+ return null;
+ } else {
+ return new UserHandleCompat(user);
+ }
+ }
+
+ UserHandle getUser() {
+ return mUser;
+ }
+
+ @Override
+ public String toString() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return mUser.toString();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof UserHandleCompat)) {
+ return false;
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return mUser.equals(((UserHandleCompat) other).mUser);
+ } else {
+ return true;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return mUser.hashCode();
+ } else {
+ return 0;
+ }
+ }
+
+ /**
+ * Adds {@link UserHandle} to the intent in for L or above.
+ * Pre-L the launcher doesn't support showing apps for multiple
+ * profiles so this is a no-op.
+ */
+ public void addToIntent(Intent intent, String name) {
+ if (Utilities.isLmpOrAbove() && mUser != null) {
+ intent.putExtra(name, mUser);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java
new file mode 100644
index 000000000..1374b4e49
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompat.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+
+import com.android.launcher3.Utilities;
+
+import java.util.List;
+
+public abstract class UserManagerCompat {
+ protected UserManagerCompat() {
+ }
+
+ public static UserManagerCompat getInstance(Context context) {
+ if (Utilities.isLmpOrAbove()) {
+ return new UserManagerCompatVL(context);
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ return new UserManagerCompatV17(context);
+ } else {
+ return new UserManagerCompatV16();
+ }
+ }
+
+ public abstract List<UserHandleCompat> getUserProfiles();
+ public abstract long getSerialNumberForUser(UserHandleCompat user);
+ public abstract UserHandleCompat getUserForSerialNumber(long serialNumber);
+ public abstract Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user);
+ public abstract CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user);
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java
new file mode 100644
index 000000000..32f972e85
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.graphics.drawable.Drawable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserManagerCompatV16 extends UserManagerCompat {
+
+ UserManagerCompatV16() {
+ }
+
+ public List<UserHandleCompat> getUserProfiles() {
+ List<UserHandleCompat> profiles = new ArrayList<UserHandleCompat>(1);
+ profiles.add(UserHandleCompat.myUserHandle());
+ return profiles;
+ }
+
+ public UserHandleCompat getUserForSerialNumber(long serialNumber) {
+ return UserHandleCompat.myUserHandle();
+ }
+
+ public Drawable getBadgedDrawableForUser(Drawable unbadged,
+ UserHandleCompat user) {
+ return unbadged;
+ }
+
+ public long getSerialNumberForUser(UserHandleCompat user) {
+ return 0;
+ }
+
+ public CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user) {
+ return label;
+ }
+}
diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java
new file mode 100644
index 000000000..055359afe
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UserManagerCompatV17 extends UserManagerCompatV16 {
+ protected UserManager mUserManager;
+
+ UserManagerCompatV17(Context context) {
+ mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+ }
+
+ public long getSerialNumberForUser(UserHandleCompat user) {
+ return mUserManager.getSerialNumberForUser(user.getUser());
+ }
+
+ public UserHandleCompat getUserForSerialNumber(long serialNumber) {
+ return UserHandleCompat.fromUser(mUserManager.getUserForSerialNumber(serialNumber));
+ }
+}
+
diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java
new file mode 100644
index 000000000..19eeabdcf
--- /dev/null
+++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java
@@ -0,0 +1,65 @@
+
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.compat;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import android.os.UserManager;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class UserManagerCompatVL extends UserManagerCompatV17 {
+ private final PackageManager mPm;
+
+ UserManagerCompatVL(Context context) {
+ super(context);
+ mPm = context.getPackageManager();
+ }
+
+ @Override
+ public List<UserHandleCompat> getUserProfiles() {
+ List<UserHandle> users = mUserManager.getUserProfiles();
+ if (users == null) {
+ return Collections.EMPTY_LIST;
+ }
+ ArrayList<UserHandleCompat> compatUsers = new ArrayList<UserHandleCompat>(
+ users.size());
+ for (UserHandle user : users) {
+ compatUsers.add(UserHandleCompat.fromUser(user));
+ }
+ return compatUsers;
+ }
+
+ @Override
+ public Drawable getBadgedDrawableForUser(Drawable unbadged, UserHandleCompat user) {
+ return mPm.getUserBadgedIcon(unbadged, user.getUser());
+ }
+
+ @Override
+ public CharSequence getBadgedLabelForUser(CharSequence label, UserHandleCompat user) {
+ if (user == null) {
+ return label;
+ }
+ return mPm.getUserBadgedLabel(label, user.getUser());
+ }
+}
+