summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.pngbin395 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.pngbin405 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/ic_allapps.pngbin8354 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/ic_allapps_pressed.pngbin9434 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/overscroll_glow_left.9.pngbin1309 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/overscroll_glow_right.9.pngbin1183 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/portal_ring_inner_holo.pngbin12123 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.pngbin3967 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-hdpi/portal_ring_outer_holo.pngbin4565 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.pngbin310 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.pngbin318 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/ic_allapps.pngbin5408 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/ic_allapps_pressed.pngbin6523 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/overscroll_glow_left.9.pngbin910 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/overscroll_glow_right.9.pngbin862 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/portal_ring_inner_holo.pngbin6424 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.pngbin2308 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-mdpi/portal_ring_outer_holo.pngbin2823 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.pngbin497 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.pngbin505 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/ic_allapps.pngbin13262 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/ic_allapps_pressed.pngbin14475 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.pngbin1857 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.pngbin1653 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.pngbin18653 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.pngbin5874 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.pngbin6545 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.pngbin1403 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.pngbin1452 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/ic_allapps.pngbin18116 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.pngbin18068 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.pngbin3390 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.pngbin3024 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.pngbin25799 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.pngbin10455 -> 0 bytes
-rw-r--r--res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.pngbin13911 -> 0 bytes
-rw-r--r--res/drawable-sw720dp-hdpi/workspace_bg.9.pngbin226 -> 0 bytes
-rw-r--r--res/drawable-sw720dp-mdpi/workspace_bg.9.pngbin213 -> 0 bytes
-rw-r--r--res/drawable-sw720dp-xhdpi/workspace_bg.9.pngbin255 -> 0 bytes
-rw-r--r--res/layout/folder_icon.xml5
-rw-r--r--res/layout/wallpaper_cropper.xml2
-rw-r--r--res/layout/wallpaper_picker.xml7
-rw-r--r--res/values/colors.xml1
-rw-r--r--res/values/dimens.xml1
-rw-r--r--res/values/strings.xml8
-rw-r--r--res/values/styles.xml2
-rw-r--r--src/com/android/launcher3/AppsCustomizePagedView.java34
-rw-r--r--src/com/android/launcher3/AppsCustomizeTabHost.java7
-rw-r--r--src/com/android/launcher3/BubbleTextView.java8
-rw-r--r--src/com/android/launcher3/CellLayout.java2
-rw-r--r--src/com/android/launcher3/Cling.java2
-rw-r--r--src/com/android/launcher3/DrawableTileSource.java102
-rw-r--r--src/com/android/launcher3/DynamicGrid.java181
-rw-r--r--src/com/android/launcher3/FastBitmapDrawable.java1
-rw-r--r--src/com/android/launcher3/Folder.java3
-rw-r--r--src/com/android/launcher3/FolderIcon.java19
-rw-r--r--src/com/android/launcher3/IconCache.java2
-rw-r--r--src/com/android/launcher3/Launcher.java112
-rw-r--r--src/com/android/launcher3/LauncherAnimUtils.java10
-rw-r--r--src/com/android/launcher3/LauncherAppState.java11
-rw-r--r--src/com/android/launcher3/LauncherProvider.java1
-rw-r--r--src/com/android/launcher3/PagedView.java89
-rw-r--r--src/com/android/launcher3/PagedViewIcon.java6
-rw-r--r--src/com/android/launcher3/PagedViewWidget.java4
-rw-r--r--src/com/android/launcher3/SavedWallpaperImages.java10
-rw-r--r--src/com/android/launcher3/SmoothPagedView.java2
-rw-r--r--src/com/android/launcher3/WallpaperCropActivity.java143
-rw-r--r--src/com/android/launcher3/WallpaperPickerActivity.java142
-rw-r--r--src/com/android/launcher3/Workspace.java138
-rw-r--r--src/com/android/photos/BitmapRegionTileSource.java409
-rw-r--r--update_system_wallpaper_cropper.py55
71 files changed, 1138 insertions, 381 deletions
diff --git a/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index 98f7ac8a7..000000000
--- a/res/drawable-sw600dp-hdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index 0a511f9df..000000000
--- a/res/drawable-sw600dp-hdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/ic_allapps.png b/res/drawable-sw600dp-hdpi/ic_allapps.png
deleted file mode 100644
index 8bda43569..000000000
--- a/res/drawable-sw600dp-hdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png
deleted file mode 100644
index 07ff33183..000000000
--- a/res/drawable-sw600dp-hdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png
deleted file mode 100644
index 3928e2c59..000000000
--- a/res/drawable-sw600dp-hdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png
deleted file mode 100644
index e34de34ac..000000000
--- a/res/drawable-sw600dp-hdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png
deleted file mode 100644
index b3be4720a..000000000
--- a/res/drawable-sw600dp-hdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 96b981c50..000000000
--- a/res/drawable-sw600dp-hdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png
deleted file mode 100644
index bc13a26cd..000000000
--- a/res/drawable-sw600dp-hdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index 3ae4affb0..000000000
--- a/res/drawable-sw600dp-mdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index 31148dd52..000000000
--- a/res/drawable-sw600dp-mdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/ic_allapps.png b/res/drawable-sw600dp-mdpi/ic_allapps.png
deleted file mode 100644
index e2afea5f3..000000000
--- a/res/drawable-sw600dp-mdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png
deleted file mode 100644
index d409c7ed3..000000000
--- a/res/drawable-sw600dp-mdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png
deleted file mode 100644
index 58ec10c43..000000000
--- a/res/drawable-sw600dp-mdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png
deleted file mode 100644
index a986fd301..000000000
--- a/res/drawable-sw600dp-mdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png
deleted file mode 100644
index 319c07427..000000000
--- a/res/drawable-sw600dp-mdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 8537714aa..000000000
--- a/res/drawable-sw600dp-mdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png
deleted file mode 100644
index 365dcfce9..000000000
--- a/res/drawable-sw600dp-mdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index c25b590a9..000000000
--- a/res/drawable-sw600dp-xhdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index 24ac88013..000000000
--- a/res/drawable-sw600dp-xhdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/ic_allapps.png b/res/drawable-sw600dp-xhdpi/ic_allapps.png
deleted file mode 100644
index 8fed2903a..000000000
--- a/res/drawable-sw600dp-xhdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png
deleted file mode 100644
index 786b676ff..000000000
--- a/res/drawable-sw600dp-xhdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png
deleted file mode 100644
index b66dd2fe6..000000000
--- a/res/drawable-sw600dp-xhdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png
deleted file mode 100644
index 3ccce3302..000000000
--- a/res/drawable-sw600dp-xhdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png
deleted file mode 100644
index d4ce45f12..000000000
--- a/res/drawable-sw600dp-xhdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 9aa13c987..000000000
--- a/res/drawable-sw600dp-xhdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png
deleted file mode 100644
index 0106cd6e0..000000000
--- a/res/drawable-sw600dp-xhdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png
deleted file mode 100644
index c661f68fb..000000000
--- a/res/drawable-sw600dp-xxhdpi/homescreen_blue_normal_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png b/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png
deleted file mode 100644
index bf6ab97c8..000000000
--- a/res/drawable-sw600dp-xxhdpi/homescreen_blue_strong_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps.png b/res/drawable-sw600dp-xxhdpi/ic_allapps.png
deleted file mode 100644
index 242965605..000000000
--- a/res/drawable-sw600dp-xxhdpi/ic_allapps.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png b/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png
deleted file mode 100644
index b93a51bea..000000000
--- a/res/drawable-sw600dp-xxhdpi/ic_allapps_pressed.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png
deleted file mode 100644
index 472c3f921..000000000
--- a/res/drawable-sw600dp-xxhdpi/overscroll_glow_left.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png b/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png
deleted file mode 100644
index e30cc9705..000000000
--- a/res/drawable-sw600dp-xxhdpi/overscroll_glow_right.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png
deleted file mode 100644
index 65b254197..000000000
--- a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png
deleted file mode 100644
index 506864643..000000000
--- a/res/drawable-sw600dp-xxhdpi/portal_ring_inner_nolip_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png b/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png
deleted file mode 100644
index 662842560..000000000
--- a/res/drawable-sw600dp-xxhdpi/portal_ring_outer_holo.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-hdpi/workspace_bg.9.png b/res/drawable-sw720dp-hdpi/workspace_bg.9.png
deleted file mode 100644
index 5bbfa4fff..000000000
--- a/res/drawable-sw720dp-hdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-mdpi/workspace_bg.9.png b/res/drawable-sw720dp-mdpi/workspace_bg.9.png
deleted file mode 100644
index 2856e0985..000000000
--- a/res/drawable-sw720dp-mdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png b/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
deleted file mode 100644
index 72269f207..000000000
--- a/res/drawable-sw720dp-xhdpi/workspace_bg.9.png
+++ /dev/null
Binary files differ
diff --git a/res/layout/folder_icon.xml b/res/layout/folder_icon.xml
index 44056828c..5147f9960 100644
--- a/res/layout/folder_icon.xml
+++ b/res/layout/folder_icon.xml
@@ -30,5 +30,8 @@
android:src="@drawable/portal_ring_inner_holo"/>
<com.android.launcher3.BubbleTextView
style="@style/WorkspaceIcon"
- android:id="@+id/folder_icon_name" />
+ android:id="@+id/folder_icon_name"
+ android:layout_gravity="top"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
</com.android.launcher3.FolderIcon>
diff --git a/res/layout/wallpaper_cropper.xml b/res/layout/wallpaper_cropper.xml
index 3a3d98a69..abb860898 100644
--- a/res/layout/wallpaper_cropper.xml
+++ b/res/layout/wallpaper_cropper.xml
@@ -32,7 +32,7 @@
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_centerInParent="true"
android:indeterminate="true"
android:indeterminateOnly="true"
android:background="@android:color/transparent" />
diff --git a/res/layout/wallpaper_picker.xml b/res/layout/wallpaper_picker.xml
index 0492b7bd9..c36493d2f 100644
--- a/res/layout/wallpaper_picker.xml
+++ b/res/layout/wallpaper_picker.xml
@@ -27,18 +27,13 @@
android:id="@+id/cropView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
- <ImageView
- android:id="@+id/defaultWallpaperView"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:visibility="invisible" />
<ProgressBar
android:id="@+id/loading"
style="@android:style/Widget.Holo.ProgressBar.Large"
android:visibility="invisible"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_gravity="center"
+ android:layout_centerInParent="true"
android:indeterminate="true"
android:indeterminateOnly="true"
android:background="@android:color/transparent" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index dc35a3f68..c07390380 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -34,6 +34,7 @@
<color name="apps_customize_icon_text_color">#FFF</color>
<color name="wallpaper_picker_translucent_gray">#66000000</color>
<color name="folder_items_text_color">#FF333333</color>
+ <color name="folder_items_glow_color">#FFCCCCCC</color>
<color name="outline_color">#FFFFFFFF</color>
<color name="first_run_cling_circle_background_color">#64b1ea</color>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b4b2367c7..dcddc09ad 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,6 +20,7 @@
<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_icon_drawable_padding">4dp</dimen>
<!-- Wallpaper picker -->
<dimen name="wallpaperThumbnailWidth">106.5dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f916ad8a5..30f4d8fd3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -30,6 +30,14 @@
<string name="folder_name"></string>
<!-- Button label on Wallpaper picker screen; user selects this button to set a specific wallpaper -->
<string name="wallpaper_instructions">Set wallpaper</string>
+ <!-- Error message when an image is selected as a wallpaper,
+ but the wallpaper picker cannot load it -->
+ <string name="image_load_fail">Coudn\'t load image</string>
+ <!-- Error message when an image is selected as a wallpaper,
+ but the wallpaper cropper cannot load it. The user will
+ usually see this when using another app and trying to set
+ an image as the wallpaper -->
+ <string name="wallpaper_load_fail">Couldn\'t load image as wallpaper</string>
<!-- Shown when wallpapers are selected in Wallpaper picker -->
<!-- String indicating how many media item(s) is(are) selected
eg. 1 selected [CHAR LIMIT=30] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index a1d2c5ccf..39e55d894 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -98,7 +98,7 @@
<style name="WorkspaceIcon.AppsCustomize">
<item name="android:background">@null</item>
<item name="android:textColor">@color/apps_customize_icon_text_color</item>
- <item name="android:drawablePadding">4dp</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>
</style>
diff --git a/src/com/android/launcher3/AppsCustomizePagedView.java b/src/com/android/launcher3/AppsCustomizePagedView.java
index 9b35bb5ea..25cd9f2f1 100644
--- a/src/com/android/launcher3/AppsCustomizePagedView.java
+++ b/src/com/android/launcher3/AppsCustomizePagedView.java
@@ -811,13 +811,23 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
!(target instanceof DeleteDropTarget))) {
// Exit spring loaded mode if we have not successfully dropped or have not handled the
// drop in Workspace
- mLauncher.exitSpringLoadedDragMode();
+ mLauncher.getWorkspace().removeExtraEmptyScreen(true, new Runnable() {
+ @Override
+ public void run() {
+ mLauncher.exitSpringLoadedDragMode();
+ mLauncher.unlockScreenOrientation(false);
+ }
+ });
+ } else {
+ mLauncher.unlockScreenOrientation(false);
}
- mLauncher.unlockScreenOrientation(false);
}
@Override
public View getContent() {
+ if (getChildCount() > 0) {
+ return getChildAt(0);
+ }
return null;
}
@@ -841,7 +851,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) {
mInTransition = false;
for (AsyncTaskPageData d : mDeferredSyncWidgetPageItems) {
- onSyncWidgetPageItems(d);
+ onSyncWidgetPageItems(d, false);
}
mDeferredSyncWidgetPageItems.clear();
for (Runnable r : mDeferredPrepareLoadWidgetPreviewsTasks) {
@@ -935,12 +945,12 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
}
public void setContentType(ContentType type) {
- int page = getCurrentPage();
- if (mContentType != type) {
- page = 0;
+ // Widgets appear to be cleared every time you leave, always force invalidate for them
+ if (mContentType != type || type == ContentType.Widgets) {
+ int page = (mContentType != type) ? 0 : getCurrentPage();
+ mContentType = type;
+ invalidatePageData(page, true);
}
- mContentType = type;
- invalidatePageData(page, true);
}
public ContentType getContentType() {
@@ -1110,7 +1120,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
mRunningTasks.remove(task);
if (task.isCancelled()) return;
// do cleanup inside onSyncWidgetPageItems
- onSyncWidgetPageItems(data);
+ onSyncWidgetPageItems(data, false);
}
}, mWidgetPreviewLoader);
@@ -1226,7 +1236,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
AsyncTaskPageData data = new AsyncTaskPageData(page, items,
maxPreviewWidth, maxPreviewHeight, null, null, mWidgetPreviewLoader);
loadWidgetPreviewsInBackground(null, data);
- onSyncWidgetPageItems(data);
+ onSyncWidgetPageItems(data, immediate);
} else {
if (mInTransition) {
mDeferredPrepareLoadWidgetPreviewsTasks.add(this);
@@ -1265,8 +1275,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
}
}
- private void onSyncWidgetPageItems(AsyncTaskPageData data) {
- if (mInTransition) {
+ private void onSyncWidgetPageItems(AsyncTaskPageData data, boolean immediatelySyncItems) {
+ if (!immediatelySyncItems && mInTransition) {
mDeferredSyncWidgetPageItems.add(data);
return;
}
diff --git a/src/com/android/launcher3/AppsCustomizeTabHost.java b/src/com/android/launcher3/AppsCustomizeTabHost.java
index bfcf92ac2..697bd7ecf 100644
--- a/src/com/android/launcher3/AppsCustomizeTabHost.java
+++ b/src/com/android/launcher3/AppsCustomizeTabHost.java
@@ -367,6 +367,10 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
@Override
public View getContent() {
+ View appsCustomizeContent = mAppsCustomizePane.getContent();
+ if (appsCustomizeContent != null) {
+ return appsCustomizeContent;
+ }
return mContent;
}
@@ -397,6 +401,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
@Override
public void onLauncherTransitionStart(Launcher l, boolean animated, boolean toWorkspace) {
+ mAppsCustomizePane.onLauncherTransitionStart(l, animated, toWorkspace);
if (animated) {
enableAndBuildHardwareLayer();
}
@@ -407,7 +412,7 @@ public class AppsCustomizeTabHost extends TabHost implements LauncherTransitiona
@Override
public void onLauncherTransitionStep(Launcher l, float t) {
- // Do nothing
+ mAppsCustomizePane.onLauncherTransitionStep(l, t);
}
@Override
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 8dab9432a..30016e5b3 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -87,7 +87,7 @@ public class BubbleTextView extends TextView {
// Ensure we are using the right text size
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setTextColor(getResources().getColor(R.color.workspace_icon_text_color));
}
@@ -111,7 +111,7 @@ public class BubbleTextView extends TextView {
setCompoundDrawables(null,
Utilities.createIconDrawable(b), null, null);
- setCompoundDrawablePadding((int) ((grid.folderIconSizePx - grid.iconSizePx) / 2f));
+ setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
setText(info.title);
setTag(info);
}
@@ -203,6 +203,10 @@ public class BubbleTextView extends TextView {
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.
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java
index dafb79ffc..bf1e9c931 100644
--- a/src/com/android/launcher3/CellLayout.java
+++ b/src/com/android/launcher3/CellLayout.java
@@ -214,7 +214,7 @@ public class CellLayout extends ViewGroup {
setAlwaysDrawnWithCacheEnabled(false);
final Resources res = getResources();
- mHotseatScale = (float) grid.hotseatIconSize / grid.iconSize;
+ mHotseatScale = (float) grid.hotseatIconSizePx / grid.iconSizePx;
mNormalBackground = res.getDrawable(R.drawable.screenpanel);
mActiveGlowBackground = res.getDrawable(R.drawable.screenpanel_hover);
diff --git a/src/com/android/launcher3/Cling.java b/src/com/android/launcher3/Cling.java
index 338b722ab..9974acde6 100644
--- a/src/com/android/launcher3/Cling.java
+++ b/src/com/android/launcher3/Cling.java
@@ -147,7 +147,7 @@ public class Cling extends FrameLayout implements Insettable, View.OnClickListen
pos.left + Utilities.sIconTextureWidth,
pos.top + Utilities.sIconTextureHeight);
Utilities.scaleRectAboutCenter(mFocusedHotseatAppBounds,
- (grid.hotseatIconSize / grid.iconSize));
+ ((float) grid.hotseatIconSizePx / grid.iconSizePx));
// Set the title
TextView v = (TextView) findViewById(R.id.focused_hotseat_app_title);
diff --git a/src/com/android/launcher3/DrawableTileSource.java b/src/com/android/launcher3/DrawableTileSource.java
new file mode 100644
index 000000000..c1f2eff0f
--- /dev/null
+++ b/src/com/android/launcher3/DrawableTileSource.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+
+import com.android.gallery3d.glrenderer.BasicTexture;
+import com.android.gallery3d.glrenderer.BitmapTexture;
+import com.android.photos.views.TiledImageRenderer;
+
+public class DrawableTileSource implements TiledImageRenderer.TileSource {
+ private static final int GL_SIZE_LIMIT = 2048;
+ // This must be no larger than half the size of the GL_SIZE_LIMIT
+ // due to decodePreview being allowed to be up to 2x the size of the target
+ public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
+
+ private int mTileSize;
+ private int mPreviewSize;
+ private Drawable mDrawable;
+ private BitmapTexture mPreview;
+
+ public DrawableTileSource(Context context, Drawable d, int previewSize) {
+ mTileSize = TiledImageRenderer.suggestedTileSize(context);
+ mDrawable = d;
+ mPreviewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
+ }
+
+ @Override
+ public int getTileSize() {
+ return mTileSize;
+ }
+
+ @Override
+ public int getImageWidth() {
+ return mDrawable.getIntrinsicWidth();
+ }
+
+ @Override
+ public int getImageHeight() {
+ return mDrawable.getIntrinsicHeight();
+ }
+
+ @Override
+ public int getRotation() {
+ return 0;
+ }
+
+ @Override
+ public BasicTexture getPreview() {
+ if (mPreviewSize == 0) {
+ return null;
+ }
+ if (mPreview == null){
+ float width = getImageWidth();
+ float height = getImageHeight();
+ while (width > MAX_PREVIEW_SIZE || height > MAX_PREVIEW_SIZE) {
+ width /= 2;
+ height /= 2;
+ }
+ Bitmap b = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
+ Canvas c = new Canvas(b);
+ mDrawable.setBounds(new Rect(0, 0, (int) width, (int) height));
+ mDrawable.draw(c);
+ c.setBitmap(null);
+ mPreview = new BitmapTexture(b);
+ }
+ return mPreview;
+ }
+
+ @Override
+ public Bitmap getTile(int level, int x, int y, Bitmap bitmap) {
+ int tileSize = getTileSize();
+ if (bitmap == null) {
+ bitmap = Bitmap.createBitmap(tileSize, tileSize, Bitmap.Config.ARGB_8888);
+ }
+ Canvas c = new Canvas(bitmap);
+ Rect bounds = new Rect(0, 0, getImageWidth(), getImageHeight());
+ bounds.offset(-x, -y);
+ mDrawable.setBounds(bounds);
+ mDrawable.draw(c);
+ c.setBitmap(null);
+ return bitmap;
+ }
+}
diff --git a/src/com/android/launcher3/DynamicGrid.java b/src/com/android/launcher3/DynamicGrid.java
index 4776c8668..d90deca4f 100644
--- a/src/com/android/launcher3/DynamicGrid.java
+++ b/src/com/android/launcher3/DynamicGrid.java
@@ -23,13 +23,17 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
+import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.util.TypedValue;
+import android.view.Display;
import android.view.Gravity;
+import android.view.Surface;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
import android.widget.FrameLayout;
import java.util.ArrayList;
@@ -52,15 +56,20 @@ class DeviceProfileQuery {
}
class DeviceProfile {
+ public static interface DeviceProfileCallbacks {
+ public void onAvailableSizeChanged(DeviceProfile grid);
+ }
+
String name;
float minWidthDps;
float minHeightDps;
float numRows;
float numColumns;
- float iconSize;
- float iconTextSize;
float numHotseatIcons;
- float hotseatIconSize;
+ private float iconSize;
+ private float iconTextSize;
+ private int iconDrawablePaddingOriginalPx;
+ private float hotseatIconSize;
boolean isLandscape;
boolean isTablet;
@@ -75,8 +84,10 @@ class DeviceProfile {
int heightPx;
int availableWidthPx;
int availableHeightPx;
+
int iconSizePx;
int iconTextSizePx;
+ int iconDrawablePaddingPx;
int cellWidthPx;
int cellHeightPx;
int folderBackgroundOffset;
@@ -96,6 +107,8 @@ class DeviceProfile {
int searchBarHeightPx;
int pageIndicatorHeightPx;
+ 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) {
// Ensure that we have an odd number of hotseat items (since we need to place all apps)
@@ -146,13 +159,20 @@ class DeviceProfile {
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));
+ hotseatAllAppsRank = (int) (numHotseatIcons / 2);
+
// Interpolate the icon size
points.clear();
for (DeviceProfile p : profiles) {
points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconSize));
}
iconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
- iconSizePx = DynamicGrid.pxFromDp(iconSize, dm);
// Interpolate the icon text size
points.clear();
@@ -160,14 +180,8 @@ class DeviceProfile {
points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.iconTextSize));
}
iconTextSize = invDistWeightedInterpolate(minWidth, minHeight, points);
- iconTextSizePx = DynamicGrid.pxFromSp(iconTextSize, dm);
+ iconDrawablePaddingOriginalPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding);
- // Interpolate the hotseat size
- points.clear();
- for (DeviceProfile p : profiles) {
- points.add(new DeviceProfileQuery(p.minWidthDps, p.minHeightDps, p.numHotseatIcons));
- }
- numHotseatIcons = Math.round(invDistWeightedInterpolate(minWidth, minHeight, points));
// Interpolate the hotseat icon size
points.clear();
for (DeviceProfile p : profiles) {
@@ -175,11 +189,91 @@ class DeviceProfile {
}
// Hotseat
hotseatIconSize = invDistWeightedInterpolate(minWidth, minHeight, points);
- hotseatIconSizePx = DynamicGrid.pxFromDp(hotseatIconSize, dm);
- hotseatAllAppsRank = (int) (numColumns / 2);
- // Calculate other vars based on Configuration
- updateFromConfiguration(resources, wPx, hPx, awPx, ahPx);
+ // Calculate the remaining vars
+ updateFromConfiguration(context, resources, wPx, hPx, awPx, ahPx);
+ updateAvailableDimensions(context);
+ }
+
+ void addCallback(DeviceProfileCallbacks cb) {
+ mCallbacks.add(cb);
+ cb.onAvailableSizeChanged(this);
+ }
+ void removeCallback(DeviceProfileCallbacks cb) {
+ mCallbacks.remove(cb);
+ }
+
+ private int getDeviceOrientation(Context context) {
+ WindowManager windowManager = (WindowManager)
+ context.getSystemService(Context.WINDOW_SERVICE);
+ Resources resources = context.getResources();
+ DisplayMetrics dm = resources.getDisplayMetrics();
+ Configuration config = resources.getConfiguration();
+ int rotation = windowManager.getDefaultDisplay().getRotation();
+
+ boolean isLandscape = (config.orientation == Configuration.ORIENTATION_LANDSCAPE) &&
+ (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180);
+ boolean isRotatedPortrait = (config.orientation == Configuration.ORIENTATION_PORTRAIT) &&
+ (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
+ if (isLandscape || isRotatedPortrait) {
+ return CellLayout.LANDSCAPE;
+ } else {
+ return CellLayout.PORTRAIT;
+ }
+ }
+
+ private void updateAvailableDimensions(Context context) {
+ WindowManager windowManager = (WindowManager)
+ context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+ Resources resources = context.getResources();
+ DisplayMetrics dm = resources.getDisplayMetrics();
+ Configuration config = resources.getConfiguration();
+
+ // There are three possible configurations that the dynamic grid accounts for, portrait,
+ // landscape with the nav bar at the bottom, and landscape with the nav bar at the side.
+ // To prevent waiting for fitSystemWindows(), we make the observation that in landscape,
+ // the height is the smallest height (either with the nav bar at the bottom or to the
+ // side) and otherwise, the height is simply the largest possible height for a portrait
+ // device.
+ Point size = new Point();
+ Point smallestSize = new Point();
+ Point largestSize = new Point();
+ display.getSize(size);
+ display.getCurrentSizeRange(smallestSize, largestSize);
+ availableWidthPx = size.x;
+ if (config.orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ availableHeightPx = smallestSize.y;
+ } else {
+ availableHeightPx = largestSize.y;
+ }
+
+ // Check to see if the icons fit in the new available height. If not, then we need to
+ // shrink the icon size.
+ Rect workspacePadding = getWorkspacePadding();
+ float scale = 1f;
+ int drawablePadding = iconDrawablePaddingOriginalPx;
+ updateIconSize(1f, drawablePadding, resources, dm);
+ float usedHeight = (cellHeightPx * numRows);
+ int maxHeight = (availableHeightPx - workspacePadding.top - workspacePadding.bottom);
+ if (usedHeight > maxHeight) {
+ scale = maxHeight / usedHeight;
+ drawablePadding = 0;
+ }
+ updateIconSize(scale, drawablePadding, resources, dm);
+
+ // Make the callbacks
+ for (DeviceProfileCallbacks cb : mCallbacks) {
+ cb.onAvailableSizeChanged(this);
+ }
+ }
+
+ private void updateIconSize(float scale, int drawablePadding, Resources resources,
+ DisplayMetrics dm) {
+ iconSizePx = (int) (DynamicGrid.pxFromDp(iconSize, dm) * scale);
+ iconTextSizePx = (int) (DynamicGrid.pxFromSp(iconTextSize, dm) * scale);
+ iconDrawablePaddingPx = drawablePadding;
+ hotseatIconSizePx = (int) (DynamicGrid.pxFromDp(hotseatIconSize, dm) * scale);
// Search Bar
searchBarSpaceMaxWidthPx = resources.getDimensionPixelSize(R.dimen.dynamic_grid_search_bar_max_width);
@@ -192,23 +286,7 @@ class DeviceProfile {
textPaint.setTextSize(iconTextSizePx);
FontMetrics fm = textPaint.getFontMetrics();
cellWidthPx = iconSizePx;
- cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);
-
- // At this point, if the cells do not fit into the available height, then we need
- // to shrink the icon size
- /*
- Rect padding = getWorkspacePadding(isLandscape ?
- CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
- int h = (int) (numRows * cellHeightPx) + padding.top + padding.bottom;
- if (h > availableHeightPx) {
- float delta = h - availableHeightPx;
- int deltaPx = (int) Math.ceil(delta / numRows);
- iconSizePx -= deltaPx;
- iconSize = DynamicGrid.dpiFromPx(iconSizePx, dm);
- cellWidthPx = iconSizePx;
- cellHeightPx = iconSizePx + (int) Math.ceil(fm.bottom - fm.top);
- }
- */
+ cellHeightPx = iconSizePx + iconDrawablePaddingPx + (int) Math.ceil(fm.bottom - fm.top);
// Hotseat
hotseatBarHeightPx = iconSizePx + 4 * edgeMarginPx;
@@ -217,26 +295,15 @@ class DeviceProfile {
// Folder
folderCellWidthPx = cellWidthPx + 3 * edgeMarginPx;
- folderCellHeightPx = cellHeightPx + (int) ((3f/2f) * edgeMarginPx);
+ folderCellHeightPx = cellHeightPx + edgeMarginPx;
folderBackgroundOffset = -edgeMarginPx;
folderIconSizePx = iconSizePx + 2 * -folderBackgroundOffset;
- }
-
- void updateFromConfiguration(Resources resources, int wPx, int hPx,
- int awPx, int ahPx) {
- isLandscape = (resources.getConfiguration().orientation ==
- Configuration.ORIENTATION_LANDSCAPE);
- isTablet = resources.getBoolean(R.bool.is_tablet);
- isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
- widthPx = wPx;
- heightPx = hPx;
- availableWidthPx = awPx;
- availableHeightPx = ahPx;
+ // All Apps
Rect padding = getWorkspacePadding(isLandscape ?
CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
int pageIndicatorOffset =
- resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
+ resources.getDimensionPixelSize(R.dimen.apps_customize_page_indicator_offset);
if (isLandscape) {
allAppsNumRows = (availableHeightPx - pageIndicatorOffset - 4 * edgeMarginPx) /
(iconSizePx + iconTextSizePx + 2 * edgeMarginPx);
@@ -247,6 +314,20 @@ class DeviceProfile {
(iconSizePx + 2 * edgeMarginPx);
}
+ void updateFromConfiguration(Context context, Resources resources, int wPx, int hPx,
+ int awPx, int ahPx) {
+ isLandscape = (resources.getConfiguration().orientation ==
+ Configuration.ORIENTATION_LANDSCAPE);
+ isTablet = resources.getBoolean(R.bool.is_tablet);
+ isLargeTablet = resources.getBoolean(R.bool.is_large_tablet);
+ widthPx = wPx;
+ heightPx = hPx;
+ availableWidthPx = awPx;
+ availableHeightPx = ahPx;
+
+ updateAvailableDimensions(context);
+ }
+
private float dist(PointF p0, PointF p1) {
return (float) Math.sqrt((p1.x - p0.x)*(p1.x-p0.x) +
(p1.y-p0.y)*(p1.y-p0.y));
@@ -298,6 +379,9 @@ class DeviceProfile {
return sum;
}
+ Rect getWorkspacePadding() {
+ return getWorkspacePadding(isLandscape ? CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
+ }
Rect getWorkspacePadding(int orientation) {
Rect padding = new Rect();
if (orientation == CellLayout.LANDSCAPE &&
@@ -431,8 +515,7 @@ class DeviceProfile {
lp.gravity = Gravity.RIGHT;
lp.width = hotseatBarHeightPx;
lp.height = LayoutParams.MATCH_PARENT;
- hotseat.setPadding(0, 2 * edgeMarginPx,
- 2 * edgeMarginPx, 2 * edgeMarginPx);
+ hotseat.findViewById(R.id.layout).setPadding(0, 2 * edgeMarginPx, 0, 2 * edgeMarginPx);
} else if (isTablet()) {
// Pad the hotseat with the grid gap calculated above
int gridGap = (int) ((widthPx - 2 * edgeMarginPx -
@@ -553,7 +636,7 @@ public class DynamicGrid {
"Wd: " + mProfile.minWidthDps + ", Hd: " + mProfile.minHeightDps +
", W: " + mProfile.widthPx + ", H: " + mProfile.heightPx +
" [r: " + mProfile.numRows + ", c: " + mProfile.numColumns +
- ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSize +
+ ", is: " + mProfile.iconSizePx + ", its: " + mProfile.iconTextSizePx +
", cw: " + mProfile.cellWidthPx + ", ch: " + mProfile.cellHeightPx +
", hc: " + mProfile.numHotseatIcons + ", his: " + mProfile.hotseatIconSizePx + "]";
}
diff --git a/src/com/android/launcher3/FastBitmapDrawable.java b/src/com/android/launcher3/FastBitmapDrawable.java
index 14760c7b6..bce6707da 100644
--- a/src/com/android/launcher3/FastBitmapDrawable.java
+++ b/src/com/android/launcher3/FastBitmapDrawable.java
@@ -67,6 +67,7 @@ class FastBitmapDrawable extends Drawable {
public void setFilterBitmap(boolean filterBitmap) {
mPaint.setFilterBitmap(filterBitmap);
+ mPaint.setAntiAlias(filterBitmap);
}
public int getAlpha() {
diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java
index 69d9a3d4b..bd6101080 100644
--- a/src/com/android/launcher3/Folder.java
+++ b/src/com/android/launcher3/Folder.java
@@ -538,6 +538,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
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));
textView.setOnClickListener(this);
textView.setOnLongClickListener(this);
@@ -956,9 +957,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList
Rect workspacePadding = grid.getWorkspacePadding(grid.isLandscape ?
CellLayout.LANDSCAPE : CellLayout.PORTRAIT);
int maxContentAreaHeight = grid.availableHeightPx -
- 4 * grid.edgeMarginPx -
workspacePadding.top - workspacePadding.bottom -
- getPaddingTop() - getPaddingBottom() -
mFolderNameHeight;
return Math.min(maxContentAreaHeight,
mContent.getDesiredHeight());
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index cd1ff2c37..5e461a7b5 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -37,7 +37,7 @@ import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
import android.widget.TextView;
import com.android.launcher3.R;
@@ -49,7 +49,7 @@ import java.util.ArrayList;
/**
* An icon that can appear on in the workspace representing an {@link UserFolder}.
*/
-public class FolderIcon extends LinearLayout implements FolderListener {
+public class FolderIcon extends FrameLayout implements FolderListener {
private Launcher mLauncher;
private Folder mFolder;
private FolderInfo mInfo;
@@ -134,17 +134,20 @@ public class FolderIcon extends LinearLayout implements FolderListener {
"INITIAL_ITEM_ANIMATION_DURATION, as sequencing of adding first two items " +
"is dependent on this");
}
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
FolderIcon icon = (FolderIcon) LayoutInflater.from(launcher).inflate(resId, group, false);
icon.setClipToPadding(false);
icon.mFolderName = (BubbleTextView) icon.findViewById(R.id.folder_icon_name);
icon.mFolderName.setText(folderInfo.title);
- icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
- LauncherAppState app = LauncherAppState.getInstance();
- DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+ icon.mFolderName.setCompoundDrawablePadding(0);
+ FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) icon.mFolderName.getLayoutParams();
+ lp.topMargin = grid.iconSizePx + grid.iconDrawablePaddingPx;
+
// Offset the preview background to center this view accordingly
- LinearLayout.LayoutParams lp =
- (LinearLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
+ icon.mPreviewBackground = (ImageView) icon.findViewById(R.id.preview_background);
+ lp = (FrameLayout.LayoutParams) icon.mPreviewBackground.getLayoutParams();
lp.topMargin = grid.folderBackgroundOffset;
lp.width = grid.folderIconSizePx;
lp.height = grid.folderIconSizePx;
@@ -537,12 +540,10 @@ public class FolderIcon extends LinearLayout implements FolderListener {
if (d != null) {
mOldBounds.set(d.getBounds());
d.setBounds(0, 0, mIntrinsicIconSize, mIntrinsicIconSize);
- d.setFilterBitmap(true);
d.setColorFilter(Color.argb(params.overlayAlpha, 255, 255, 255),
PorterDuff.Mode.SRC_ATOP);
d.draw(canvas);
d.clearColorFilter();
- d.setFilterBitmap(false);
d.setBounds(mOldBounds);
}
canvas.restore();
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 543b8ee2d..a55fce01c 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -156,7 +156,7 @@ public class IconCache {
Iterator<Entry<ComponentName, 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.getWidth() < grid.iconSizePx || e.icon.getHeight() < grid.iconSizePx) {
it.remove();
}
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index af58f79df..f721571c2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -197,8 +197,8 @@ public class Launcher extends Activity
private AnimatorSet mStateAnimation;
static final int APPWIDGET_HOST_ID = 1024;
- private static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
- private static final int EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT = 600;
+ public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
+ private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
private static final int SHOW_CLING_DURATION = 250;
private static final int DISMISS_CLING_DURATION = 200;
@@ -305,6 +305,7 @@ public class Launcher extends Activity
private Drawable mWorkspaceBackgroundDrawable;
private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
+ private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false;
static final ArrayList<String> sDumpLogs = new ArrayList<String>();
static Date sDateStamp = new Date();
@@ -390,6 +391,7 @@ public class Launcher extends Activity
display.getRealSize(realSize);
DisplayMetrics dm = new DisplayMetrics();
display.getMetrics(dm);
+
// Lazy-initialize the dynamic grid
DeviceProfile grid = app.initDynamicGrid(this,
Math.min(smallestSize.x, smallestSize.y),
@@ -437,18 +439,12 @@ public class Launcher extends Activity
mSavedState = savedInstanceState;
restoreState(mSavedState);
- // Update customization drawer _after_ restoring the states
- if (mAppsCustomizeContent != null) {
- mAppsCustomizeContent.onPackagesUpdated(
- LauncherModel.getSortedWidgetsAndShortcuts(this));
- }
-
if (PROFILE_STARTUP) {
android.os.Debug.stopMethodTracing();
}
if (!mRestoring) {
- if (sPausedFromUserAction) {
+ if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE || sPausedFromUserAction) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, -1);
@@ -709,13 +705,24 @@ public class Launcher extends Activity
// Reset the startActivity waiting flag
mWaitingForResult = false;
+ Runnable exitSpringLoaded = new Runnable() {
+ @Override
+ public void run() {
+ exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
+ EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ }
+ };
+
if (requestCode == REQUEST_BIND_APPWIDGET) {
- int appWidgetId = data != null ?
+ final int appWidgetId = data != null ?
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
+ mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_OK) {
- addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo);
+ addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
+ mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
}
return;
} else if (requestCode == REQUEST_PICK_WALLPAPER) {
@@ -725,22 +732,37 @@ public class Launcher extends Activity
return;
}
- boolean delayExitSpringLoadedMode = false;
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
requestCode == REQUEST_CREATE_APPWIDGET);
// We have special handling for widgets
if (isWidgetDrop) {
- int appWidgetId = data != null ?
+ final int appWidgetId = data != null ?
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
+ final int result;
+ final Runnable onComplete;
if (appWidgetId < 0) {
Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" +
"widget configuration activity.");
- completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
- mWorkspace.stripEmptyScreens();
+ result = RESULT_CANCELED;
+ completeTwoStageWidgetDrop(result, appWidgetId);
+ onComplete = new Runnable() {
+ @Override
+ public void run() {
+ exitSpringLoadedDragModeDelayed(false, 0, null);
+ }
+ };
} else {
- completeTwoStageWidgetDrop(resultCode, appWidgetId);
+ result = resultCode;
+ onComplete = new Runnable() {
+ @Override
+ public void run() {
+ completeTwoStageWidgetDrop(result, appWidgetId);
+ }
+ };
}
+ mWorkspace.removeExtraEmptyScreen(true, onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY,
+ false);
return;
}
@@ -760,15 +782,15 @@ public class Launcher extends Activity
if (isWorkspaceLocked()) {
sPendingAddList.add(args);
} else {
- delayExitSpringLoadedMode = completeAdd(args);
+ completeAdd(args);
}
+ mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_CANCELED) {
- mWorkspace.stripEmptyScreens();
+ mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
mDragLayer.clearAnimatedView();
- // Exit spring loaded mode if necessary after cancelling the configuration of a widget
- exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), delayExitSpringLoadedMode,
- null);
}
private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
@@ -788,25 +810,18 @@ public class Launcher extends Activity
public void run() {
completeAddAppWidget(appWidgetId, mPendingAddInfo.container,
mPendingAddInfo.screenId, layout, null);
- exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
- null);
+ exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED),
+ EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
}
};
} else if (resultCode == RESULT_CANCELED) {
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
- onCompleteRunnable = new Runnable() {
- @Override
- public void run() {
- exitSpringLoadedDragModeDelayed((resultCode != RESULT_CANCELED), false,
- null);
- }
- };
}
if (mDragLayer.getAnimatedView() != null) {
mWorkspace.animateWidgetDrop(mPendingAddInfo, cellLayout,
(DragView) mDragLayer.getAnimatedView(), onCompleteRunnable,
animationType, boundWidget, true);
- } else {
+ } else if (onCompleteRunnable != null) {
// The animated view may be null in the case of a rotation during widget configuration
onCompleteRunnable.run();
}
@@ -1900,8 +1915,14 @@ public class Launcher extends Activity
mPendingAddInfo.dropPos = null;
}
- void addAppWidgetImpl(final int appWidgetId, ItemInfo info, AppWidgetHostView boundWidget,
- AppWidgetProviderInfo appWidgetInfo) {
+ void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
+ final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo) {
+ addAppWidgetImpl(appWidgetId, info, boundWidget, appWidgetInfo, 0);
+ }
+
+ void addAppWidgetImpl(final int appWidgetId, final ItemInfo info,
+ final AppWidgetHostView boundWidget, final AppWidgetProviderInfo appWidgetInfo, int
+ delay) {
if (appWidgetInfo.configure != null) {
mPendingAddWidgetInfo = appWidgetInfo;
@@ -1912,10 +1933,17 @@ public class Launcher extends Activity
Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET);
} else {
// Otherwise just add it
+ Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ // Exit spring loaded mode if necessary after adding the widget
+ exitSpringLoadedDragModeDelayed(true, EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT,
+ null);
+ }
+ };
completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
appWidgetInfo);
- // Exit spring loaded mode if necessary after adding the widget
- exitSpringLoadedDragModeDelayed(true, false, null);
+ mWorkspace.removeExtraEmptyScreen(true, onComplete, delay, false);
}
}
@@ -2979,7 +3007,7 @@ public class Launcher extends Activity
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
dispatchOnLauncherTransitionPrepare(toView, animated, true);
- mAppsCustomizeContent.pauseScrolling();
+ mAppsCustomizeContent.stopScrolling();
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -2991,7 +3019,6 @@ public class Launcher extends Activity
onCompleteRunnable.run();
}
mAppsCustomizeContent.updateCurrentPageScroll();
- mAppsCustomizeContent.resumeScrolling();
}
});
@@ -3104,7 +3131,7 @@ public class Launcher extends Activity
}
}
- void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, boolean extendedDelay,
+ void exitSpringLoadedDragModeDelayed(final boolean successfulDrop, int delay,
final Runnable onCompleteRunnable) {
if (mState != State.APPS_CUSTOMIZE_SPRING_LOADED) return;
@@ -3121,9 +3148,8 @@ public class Launcher extends Activity
exitSpringLoadedDragMode();
}
}
- }, (extendedDelay ?
- EXIT_SPRINGLOADED_MODE_LONG_TIMEOUT :
- EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT));
+ }, delay);
+
}
void exitSpringLoadedDragMode() {
@@ -3662,7 +3688,7 @@ public class Launcher extends Activity
}
// Remove the extra empty screen
- mWorkspace.removeExtraEmptyScreen();
+ mWorkspace.removeExtraEmptyScreen(false, null);
if (!AppsCustomizePagedView.DISABLE_ALL_APPS &&
addedApps != null && mAppsCustomizeContent != null) {
@@ -3932,6 +3958,8 @@ public class Launcher extends Activity
} else {
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.setApps(apps);
+ mAppsCustomizeContent.onPackagesUpdated(
+ LauncherModel.getSortedWidgetsAndShortcuts(this));
}
}
}
diff --git a/src/com/android/launcher3/LauncherAnimUtils.java b/src/com/android/launcher3/LauncherAnimUtils.java
index 01f72a7ce..e6c220b2a 100644
--- a/src/com/android/launcher3/LauncherAnimUtils.java
+++ b/src/com/android/launcher3/LauncherAnimUtils.java
@@ -25,11 +25,13 @@ import android.view.View;
import android.view.ViewTreeObserver;
import java.util.HashSet;
+import java.util.WeakHashMap;
public class LauncherAnimUtils {
- static HashSet<Animator> sAnimators = new HashSet<Animator>();
+ static WeakHashMap<Animator, Object> sAnimators = new WeakHashMap<Animator, Object>();
static Animator.AnimatorListener sEndAnimListener = new Animator.AnimatorListener() {
public void onAnimationStart(Animator animation) {
+ sAnimators.put(animation, null);
}
public void onAnimationRepeat(Animator animation) {
@@ -45,7 +47,6 @@ public class LauncherAnimUtils {
};
public static void cancelOnDestroyActivity(Animator a) {
- sAnimators.add(a);
a.addListener(sEndAnimListener);
}
@@ -74,13 +75,12 @@ public class LauncherAnimUtils {
}
public static void onDestroyActivity() {
- HashSet<Animator> animators = new HashSet<Animator>(sAnimators);
+ HashSet<Animator> animators = new HashSet<Animator>(sAnimators.keySet());
for (Animator a : animators) {
if (a.isRunning()) {
a.cancel();
- } else {
- sAnimators.remove(a);
}
+ sAnimators.remove(a);
}
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index a255b89a2..fe2b43fe4 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -28,7 +28,7 @@ import android.view.Display;
import java.lang.ref.WeakReference;
-public class LauncherAppState {
+public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
private static final String TAG = "LauncherAppState";
private static final String SHARED_PREFERENCES_KEY = "com.android.launcher3.prefs";
@@ -182,12 +182,12 @@ public class LauncherAppState {
context.getResources(),
minWidth, minHeight, width, height,
availableWidth, availableHeight);
+ mDynamicGrid.getDeviceProfile().addCallback(this);
}
// Update the icon size
DeviceProfile grid = mDynamicGrid.getDeviceProfile();
- Utilities.setIconSize(grid.iconSizePx);
- grid.updateFromConfiguration(context.getResources(), width, height,
+ grid.updateFromConfiguration(context, context.getResources(), width, height,
availableWidth, availableHeight);
return grid;
}
@@ -216,4 +216,9 @@ public class LauncherAppState {
public int getLongPressTimeout() {
return mLongPressTimeout;
}
+
+ @Override
+ public void onAvailableSizeChanged(DeviceProfile grid) {
+ Utilities.setIconSize(grid.iconSizePx);
+ }
}
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index d0f6770d3..e992706de 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -1046,7 +1046,6 @@ public class LauncherProvider extends ContentProvider {
// recursively load some more favorites, why not?
i += loadFavorites(db, resId);
added = false;
- mMaxItemId = -1;
} else {
Log.w(TAG, String.format("Skipping <include workspace=0x%08x>", resId));
}
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index e724063df..9b891e45a 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -157,7 +157,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected int mTouchSlop;
private int mPagingTouchSlop;
private int mMaximumVelocity;
- protected int mPageSpacing;
protected int mPageLayoutPaddingTop;
protected int mPageLayoutPaddingBottom;
protected int mPageLayoutPaddingLeft;
@@ -260,9 +259,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// Drop to delete
private View mDeleteDropTarget;
- private boolean mAutoComputePageSpacing = false;
- private boolean mRecomputePageSpacing = false;
-
// Bouncer
private boolean mTopAlignPageWhenShrinkingForBouncer = false;
@@ -285,10 +281,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.PagedView, defStyle, 0);
- setPageSpacing(a.getDimensionPixelSize(R.styleable.PagedView_pageSpacing, 0));
- if (mPageSpacing < 0) {
- mAutoComputePageSpacing = mRecomputePageSpacing = true;
- }
+
mPageLayoutPaddingTop = a.getDimensionPixelSize(
R.styleable.PagedView_pageLayoutPaddingTop, 0);
mPageLayoutPaddingBottom = a.getDimensionPixelSize(
@@ -513,33 +506,41 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
scrollTo(newX, 0);
mScroller.setFinalX(newX);
- mScroller.forceFinished(true);
+ forceFinishScroller();
}
/**
* Called during AllApps/Home transitions to avoid unnecessary work. When that other animation
- * ends, {@link #resumeScrolling()} should be called, along with
- * {@link #updateCurrentPageScroll()} to correctly set the final state and re-enable scrolling.
+ * {@link #updateCurrentPageScroll()} should be called, to correctly set the final state and
+ * re-enable scrolling.
*/
- void pauseScrolling() {
- mScroller.forceFinished(true);
+ void stopScrolling() {
+ mCurrentPage = getNextPage();
+ forceFinishScroller();
}
- /**
- * Enables scrolling again.
- * @see #pauseScrolling()
- */
- void resumeScrolling() {
+ private void abortScrollerAnimation(boolean resetNextPage) {
+ mScroller.abortAnimation();
+ // We need to clean up the next page here to avoid computeScrollHelper from
+ // updating current page on the pass.
+ if (resetNextPage) {
+ mNextPage = INVALID_PAGE;
+ }
+ }
+
+ private void forceFinishScroller() {
+ mScroller.forceFinished(true);
+ // We need to clean up the next page here to avoid computeScrollHelper from
+ // updating current page on the pass.
+ mNextPage = INVALID_PAGE;
}
+
/**
* Sets the current page.
*/
void setCurrentPage(int currentPage) {
if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- // We need to clean up the next page here to avoid computeScrollHelper from
- // updating current page on the pass.
- mNextPage = INVALID_PAGE;
+ abortScrollerAnimation(true);
}
// don't introduce any checks like mCurrentPage == currentPage here-- if we change the
// the default
@@ -875,26 +876,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
}
setMeasuredDimension(scaledWidthSize, scaledHeightSize);
-
- if (childCount > 0) {
- // Calculate the variable page spacing if necessary
- if (mAutoComputePageSpacing && mRecomputePageSpacing) {
- // The gap between pages in the PagedView should be equal to the gap from the page
- // to the edge of the screen (so it is not visible in the current screen). To
- // account for unequal padding on each side of the paged view, we take the maximum
- // of the left/right gap and use that as the gap between each page.
- int offset = (getViewportWidth() - getChildWidth(0)) / 2;
- int spacing = Math.max(offset, widthSize - offset -
- getChildAt(0).getMeasuredWidth());
- setPageSpacing(spacing);
- mRecomputePageSpacing = false;
- }
- }
- }
-
- public void setPageSpacing(int pageSpacing) {
- mPageSpacing = pageSpacing;
- requestLayout();
}
@Override
@@ -1029,7 +1010,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
// This ensures that when children are added, they get the correct transforms / alphas
// in accordance with any scroll effects.
mForceScreenScrolled = true;
- mRecomputePageSpacing = true;
updateFreescrollBounds();
invalidate();
}
@@ -1301,9 +1281,9 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
int offset = (getViewportWidth() - getChildWidth(mCurrentPage)) / 2;
if (isLayoutRtl()) {
return (x > (getViewportOffsetX() + getViewportWidth() -
- offset + mPageSpacing));
+ offset));
}
- return (x < getViewportOffsetX() + offset - mPageSpacing);
+ return (x < getViewportOffsetX() + offset);
}
/**
@@ -1312,10 +1292,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected boolean hitsNextPage(float x, float y) {
int offset = (getViewportWidth() - getChildWidth(mCurrentPage)) / 2;
if (isLayoutRtl()) {
- return (x < getViewportOffsetX() + offset - mPageSpacing);
+ return (x < getViewportOffsetX() + offset);
}
return (x > (getViewportOffsetX() + getViewportWidth() -
- offset + mPageSpacing));
+ offset));
}
/** Returns whether x and y originated within the buffered viewport */
@@ -1394,7 +1374,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
final boolean finishedScrolling = (mScroller.isFinished() || xDist < mTouchSlop);
if (finishedScrolling) {
mTouchState = TOUCH_STATE_REST;
- mScroller.abortAnimation();
+ abortScrollerAnimation(false);
} else {
if (isTouchPointInViewportWithBuffer((int) mDownMotionX, (int) mDownMotionY)) {
mTouchState = TOUCH_STATE_SCROLLING;
@@ -1506,7 +1486,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
protected float getScrollProgress(int screenCenter, View v, int page) {
final int halfScreenSize = getViewportWidth() / 2;
- int totalDistance = v.getMeasuredWidth() + mPageSpacing;
+ int offset = (getViewportWidth() - getChildWidth(page)) / 2;
+ int totalDistance = v.getMeasuredWidth() + offset;
int delta = screenCenter - (getScrollForPage(page) + halfScreenSize);
float scrollProgress = delta / (totalDistance * 1.0f);
@@ -1692,7 +1673,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
* will be false if being flinged.
*/
if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
+ abortScrollerAnimation(false);
}
// Remember where the motion event started
@@ -1890,7 +1871,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
} else {
if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
+ abortScrollerAnimation(true);
}
float scaleX = getScaleX();
@@ -2354,8 +2335,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
if (mContentIsRefreshable) {
// Force all scrolling-related behavior to end
- mScroller.forceFinished(true);
- mNextPage = INVALID_PAGE;
+ forceFinishScroller();
// Update all the pages
syncPages();
@@ -2595,9 +2575,10 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
int newX = 0;
if (slideFromLeft) {
if (i == 0) {
+ int pageSpace = (getViewportWidth() - getChildWidth(i)) / 2;
// Simulate the page being offscreen with the page spacing
oldX = getViewportOffsetX() + getChildOffset(i) - getChildWidth(i)
- - mPageSpacing;
+ - pageSpace;
} else {
oldX = getViewportOffsetX() + getChildOffset(i - 1);
}
diff --git a/src/com/android/launcher3/PagedViewIcon.java b/src/com/android/launcher3/PagedViewIcon.java
index 8bfe42d24..865c0b286 100644
--- a/src/com/android/launcher3/PagedViewIcon.java
+++ b/src/com/android/launcher3/PagedViewIcon.java
@@ -62,15 +62,19 @@ public class PagedViewIcon extends TextView {
// Ensure we are using the right text size
LauncherAppState app = LauncherAppState.getInstance();
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
- setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+ setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
}
public void applyFromApplicationInfo(AppInfo info, boolean scaleUp,
PagedViewIcon.PressedCallback cb) {
+ LauncherAppState app = LauncherAppState.getInstance();
+ DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
+
mIcon = info.iconBitmap;
mPressedCallback = cb;
setCompoundDrawables(null, Utilities.createIconDrawable(mIcon),
null, null);
+ setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
setText(info.title);
setTag(info);
}
diff --git a/src/com/android/launcher3/PagedViewWidget.java b/src/com/android/launcher3/PagedViewWidget.java
index 45320a484..d2f897a33 100644
--- a/src/com/android/launcher3/PagedViewWidget.java
+++ b/src/com/android/launcher3/PagedViewWidget.java
@@ -84,11 +84,11 @@ public class PagedViewWidget extends LinearLayout {
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
TextView name = (TextView) findViewById(R.id.widget_name);
if (name != null) {
- name.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+ name.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
}
TextView dims = (TextView) findViewById(R.id.widget_dims);
if (dims != null) {
- dims.setTextSize(TypedValue.COMPLEX_UNIT_SP, grid.iconTextSize);
+ dims.setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
}
}
diff --git a/src/com/android/launcher3/SavedWallpaperImages.java b/src/com/android/launcher3/SavedWallpaperImages.java
index c19692b9a..58add7022 100644
--- a/src/com/android/launcher3/SavedWallpaperImages.java
+++ b/src/com/android/launcher3/SavedWallpaperImages.java
@@ -60,13 +60,9 @@ public class SavedWallpaperImages extends BaseAdapter implements ListAdapter {
public void onClick(WallpaperPickerActivity a) {
String imageFilename = a.getSavedImages().getImageFilename(mDbId);
File file = new File(a.getFilesDir(), imageFilename);
- CropView v = a.getCropView();
- int rotation = WallpaperCropActivity.getRotationFromExif(file.getAbsolutePath());
- a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
- v.setTileSource(
- new BitmapRegionTileSource(a, file.getAbsolutePath(), 1024, rotation), null);
- v.moveToLeft();
- v.setTouchEnabled(false);
+ BitmapRegionTileSource.FilePathBitmapSource bitmapSource =
+ new BitmapRegionTileSource.FilePathBitmapSource(file.getAbsolutePath(), 1024);
+ a.setCropViewTileSource(bitmapSource, false, true, null);
}
@Override
public void onSave(WallpaperPickerActivity a) {
diff --git a/src/com/android/launcher3/SmoothPagedView.java b/src/com/android/launcher3/SmoothPagedView.java
index a45dbbf6e..64dcb34ca 100644
--- a/src/com/android/launcher3/SmoothPagedView.java
+++ b/src/com/android/launcher3/SmoothPagedView.java
@@ -52,8 +52,6 @@ public abstract class SmoothPagedView extends PagedView {
}
public float getInterpolation(float t) {
- // _o(t) = t * t * ((tension + 1) * t + tension)
- // o(t) = _o(t - 1) + 1
t -= 1.0f;
return t * t * ((mTension + 1) * t + mTension) + 1.0f;
}
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 30ec340b1..f9aec2b08 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -37,15 +37,16 @@ import android.graphics.RectF;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.util.FloatMath;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.WindowManager;
+import android.widget.Toast;
import com.android.gallery3d.common.Utils;
import com.android.gallery3d.exif.ExifInterface;
import com.android.photos.BitmapRegionTileSource;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
@@ -96,9 +97,6 @@ public class WallpaperCropActivity extends Activity {
return;
}
- int rotation = getRotationFromExif(this, imageUri);
- mCropView.setTileSource(new BitmapRegionTileSource(this, imageUri, 1024, rotation), null);
- mCropView.setTouchEnabled(true);
// Action bar
// Show the custom action bar view
final ActionBar actionBar = getActionBar();
@@ -111,6 +109,63 @@ public class WallpaperCropActivity extends Activity {
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
}
});
+
+ // Load image in background
+ final BitmapRegionTileSource.UriBitmapSource bitmapSource =
+ new BitmapRegionTileSource.UriBitmapSource(this, imageUri, 1024);
+ Runnable onLoad = new Runnable() {
+ public void run() {
+ if (bitmapSource.getLoadingState() != BitmapSource.State.LOADED) {
+ Toast.makeText(WallpaperCropActivity.this,
+ getString(R.string.wallpaper_load_fail),
+ Toast.LENGTH_LONG).show();
+ finish();
+ }
+ }
+ };
+ setCropViewTileSource(bitmapSource, true, false, onLoad);
+ }
+
+ public void setCropViewTileSource(
+ final BitmapRegionTileSource.BitmapSource bitmapSource, final boolean touchEnabled,
+ final boolean moveToLeft, final Runnable postExecute) {
+ final Context context = WallpaperCropActivity.this;
+ final View progressView = findViewById(R.id.loading);
+ final AsyncTask<Void, Void, Void> loadBitmapTask = new AsyncTask<Void, Void, Void>() {
+ protected Void doInBackground(Void...args) {
+ if (!isCancelled()) {
+ bitmapSource.loadInBackground();
+ }
+ return null;
+ }
+ protected void onPostExecute(Void arg) {
+ if (!isCancelled()) {
+ progressView.setVisibility(View.INVISIBLE);
+ if (bitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ mCropView.setTileSource(
+ new BitmapRegionTileSource(context, bitmapSource), null);
+ mCropView.setTouchEnabled(touchEnabled);
+ if (moveToLeft) {
+ mCropView.moveToLeft();
+ }
+ }
+ }
+ if (postExecute != null) {
+ postExecute.run();
+ }
+ }
+ };
+ // We don't want to show the spinner every time we load an image, because that would be
+ // annoying; instead, only start showing the spinner if loading the image has taken
+ // longer than 1 sec (ie 1000 ms)
+ progressView.postDelayed(new Runnable() {
+ public void run() {
+ if (loadBitmapTask.getStatus() != AsyncTask.Status.FINISHED) {
+ progressView.setVisibility(View.VISIBLE);
+ }
+ }
+ }, 1000);
+ loadBitmapTask.execute();
}
public boolean enableRotation() {
@@ -192,16 +247,18 @@ public class WallpaperCropActivity extends Activity {
private static int getRotationFromExifHelper(
String path, Resources res, int resId, Context context, Uri uri) {
ExifInterface ei = new ExifInterface();
+ InputStream is = null;
+ BufferedInputStream bis = null;
try {
if (path != null) {
ei.readExif(path);
} else if (uri != null) {
- InputStream is = context.getContentResolver().openInputStream(uri);
- BufferedInputStream bis = new BufferedInputStream(is);
+ is = context.getContentResolver().openInputStream(uri);
+ bis = new BufferedInputStream(is);
ei.readExif(bis);
} else {
- InputStream is = res.openRawResource(resId);
- BufferedInputStream bis = new BufferedInputStream(is);
+ is = res.openRawResource(resId);
+ bis = new BufferedInputStream(is);
ei.readExif(bis);
}
Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
@@ -210,6 +267,9 @@ public class WallpaperCropActivity extends Activity {
}
} catch (IOException e) {
Log.w(LOGTAG, "Getting exif data failed", e);
+ } finally {
+ Utils.closeSilently(bis);
+ Utils.closeSilently(is);
}
return 0;
}
@@ -372,7 +432,6 @@ public class WallpaperCropActivity extends Activity {
String mInFilePath;
byte[] mInImageBytes;
int mInResId = 0;
- InputStream mInStream;
RectF mCropBounds = null;
int mOutWidth, mOutHeight;
int mRotation;
@@ -445,37 +504,36 @@ public class WallpaperCropActivity extends Activity {
}
// Helper to setup input stream
- private void regenerateInputStream() {
+ private InputStream regenerateInputStream() {
if (mInUri == null && mInResId == 0 && mInFilePath == null && mInImageBytes == null) {
Log.w(LOGTAG, "cannot read original file, no input URI, resource ID, or " +
"image byte array given");
} else {
- Utils.closeSilently(mInStream);
try {
if (mInUri != null) {
- mInStream = new BufferedInputStream(
+ return new BufferedInputStream(
mContext.getContentResolver().openInputStream(mInUri));
} else if (mInFilePath != null) {
- mInStream = mContext.openFileInput(mInFilePath);
+ return mContext.openFileInput(mInFilePath);
} else if (mInImageBytes != null) {
- mInStream = new BufferedInputStream(
- new ByteArrayInputStream(mInImageBytes));
+ return new BufferedInputStream(new ByteArrayInputStream(mInImageBytes));
} else {
- mInStream = new BufferedInputStream(
- mResources.openRawResource(mInResId));
+ return new BufferedInputStream(mResources.openRawResource(mInResId));
}
} catch (FileNotFoundException e) {
Log.w(LOGTAG, "cannot read file: " + mInUri.toString(), e);
}
}
+ return null;
}
public Point getImageBounds() {
- regenerateInputStream();
- if (mInStream != null) {
+ InputStream is = regenerateInputStream();
+ if (is != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
- BitmapFactory.decodeStream(mInStream, null, options);
+ BitmapFactory.decodeStream(is, null, options);
+ Utils.closeSilently(is);
if (options.outWidth != 0 && options.outHeight != 0) {
return new Point(options.outWidth, options.outHeight);
}
@@ -493,22 +551,26 @@ public class WallpaperCropActivity extends Activity {
public boolean cropBitmap() {
boolean failure = false;
- regenerateInputStream();
WallpaperManager wallpaperManager = null;
if (mSetWallpaper) {
wallpaperManager = WallpaperManager.getInstance(mContext.getApplicationContext());
}
- if (mSetWallpaper && mNoCrop && mInStream != null) {
+
+
+ if (mSetWallpaper && mNoCrop) {
try {
- wallpaperManager.setStream(mInStream);
+ InputStream is = regenerateInputStream();
+ if (is != null) {
+ wallpaperManager.setStream(is);
+ Utils.closeSilently(is);
+ }
} catch (IOException e) {
Log.w(LOGTAG, "cannot write stream to wallpaper", e);
failure = true;
}
return !failure;
- }
- if (mInStream != null) {
+ } else {
// Find crop bounds (scaled to original image size)
Rect roundedTrueCrop = new Rect();
Matrix rotateMatrix = new Matrix();
@@ -521,6 +583,11 @@ public class WallpaperCropActivity extends Activity {
mCropBounds = new RectF(roundedTrueCrop);
Point bounds = getImageBounds();
+ if (bounds == null) {
+ Log.w(LOGTAG, "cannot get bounds for image");
+ failure = true;
+ return false;
+ }
float[] rotatedBounds = new float[] { bounds.x, bounds.y };
rotateMatrix.mapPoints(rotatedBounds);
@@ -531,7 +598,6 @@ public class WallpaperCropActivity extends Activity {
inverseRotateMatrix.mapRect(mCropBounds);
mCropBounds.offset(bounds.x/2, bounds.y/2);
- regenerateInputStream();
}
mCropBounds.roundOut(roundedTrueCrop);
@@ -543,15 +609,25 @@ public class WallpaperCropActivity extends Activity {
}
// See how much we're reducing the size of the image
- int scaleDownSampleSize = Math.min(roundedTrueCrop.width() / mOutWidth,
- roundedTrueCrop.height() / mOutHeight);
-
+ int scaleDownSampleSize = Math.max(1, Math.min(roundedTrueCrop.width() / mOutWidth,
+ roundedTrueCrop.height() / mOutHeight));
// Attempt to open a region decoder
BitmapRegionDecoder decoder = null;
+ InputStream is = null;
try {
- decoder = BitmapRegionDecoder.newInstance(mInStream, true);
+ is = regenerateInputStream();
+ if (is == null) {
+ Log.w(LOGTAG, "cannot get input stream for uri=" + mInUri.toString());
+ failure = true;
+ return false;
+ }
+ decoder = BitmapRegionDecoder.newInstance(is, false);
+ Utils.closeSilently(is);
} catch (IOException e) {
Log.w(LOGTAG, "cannot open region decoder for file: " + mInUri.toString(), e);
+ } finally {
+ Utils.closeSilently(is);
+ is = null;
}
Bitmap crop = null;
@@ -567,14 +643,15 @@ public class WallpaperCropActivity extends Activity {
if (crop == null) {
// BitmapRegionDecoder has failed, try to crop in-memory
- regenerateInputStream();
+ is = regenerateInputStream();
Bitmap fullSize = null;
- if (mInStream != null) {
+ if (is != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
if (scaleDownSampleSize > 1) {
options.inSampleSize = scaleDownSampleSize;
}
- fullSize = BitmapFactory.decodeStream(mInStream, null, options);
+ fullSize = BitmapFactory.decodeStream(is, null, options);
+ Utils.closeSilently(is);
}
if (fullSize != null) {
mCropBounds.left /= scaleDownSampleSize;
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index 2ad92181d..efc311070 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -42,6 +42,7 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LevelListDrawable;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
@@ -66,8 +67,10 @@ 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;
+import com.android.photos.BitmapRegionTileSource.BitmapSource;
import java.io.File;
import java.io.FileOutputStream;
@@ -83,13 +86,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
private static final String TEMP_WALLPAPER_TILES = "TEMP_WALLPAPER_TILES";
private static final String DEFAULT_WALLPAPER_THUMBNAIL_FILENAME = "default_thumb.jpg";
- private View mSelectedThumb;
+ private View mSelectedTile;
private boolean mIgnoreNextTap;
private OnClickListener mThumbnailOnClickListener;
private LinearLayout mWallpapersView;
private View mWallpaperStrip;
- private ImageView mDefaultWallpaperView;
private ActionMode.Callback mActionModeCallback;
private ActionMode mActionMode;
@@ -128,16 +130,39 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
public static class UriWallpaperInfo extends WallpaperTileInfo {
private Uri mUri;
+ private boolean mFirstClick = true;
+ private BitmapRegionTileSource.UriBitmapSource mBitmapSource;
public UriWallpaperInfo(Uri uri) {
mUri = uri;
}
@Override
- public void onClick(WallpaperPickerActivity a) {
- CropView v = a.getCropView();
- int rotation = WallpaperCropActivity.getRotationFromExif(a, mUri);
- a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
- v.setTileSource(new BitmapRegionTileSource(a, mUri, 1024, rotation), null);
- v.setTouchEnabled(true);
+ public void onClick(final WallpaperPickerActivity a) {
+ final Runnable onLoad;
+ if (!mFirstClick) {
+ onLoad = null;
+ } else {
+ mFirstClick = false;
+ onLoad = new Runnable() {
+ public void run() {
+ if (mBitmapSource != null &&
+ mBitmapSource.getLoadingState() == BitmapSource.State.LOADED) {
+ mView.setVisibility(View.VISIBLE);
+ a.selectTile(mView);
+ } else {
+ ViewGroup parent = (ViewGroup) mView.getParent();
+ if (parent != null) {
+ parent.removeView(mView);
+ Toast.makeText(a,
+ a.getString(R.string.image_load_fail),
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ };
+ }
+ mBitmapSource = new BitmapRegionTileSource.UriBitmapSource(
+ a, mUri, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+ a.setCropViewTileSource(mBitmapSource, true, false, onLoad);
}
@Override
public void onSave(final WallpaperPickerActivity a) {
@@ -175,11 +200,12 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
@Override
public void onClick(WallpaperPickerActivity a) {
- int rotation = WallpaperCropActivity.getRotationFromExif(mResources, mResId);
- BitmapRegionTileSource source = new BitmapRegionTileSource(
- mResources, a, mResId, 1024, rotation);
+ BitmapRegionTileSource.ResourceBitmapSource bitmapSource =
+ new BitmapRegionTileSource.ResourceBitmapSource(
+ mResources, mResId, BitmapRegionTileSource.MAX_PREVIEW_SIZE);
+ bitmapSource.loadInBackground();
+ BitmapRegionTileSource source = new BitmapRegionTileSource(a, bitmapSource);
CropView v = a.getCropView();
- a.getDefaultWallpaperView().setVisibility(View.INVISIBLE);
v.setTileSource(source, null);
Point wallpaperSize = WallpaperCropActivity.getDefaultWallpaperSize(
a.getResources(), a.getWindowManager());
@@ -211,15 +237,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
@Override
public void onClick(WallpaperPickerActivity a) {
- a.getCropView().setTouchEnabled(false);
- ImageView defaultWallpaperView = a.getDefaultWallpaperView();
- defaultWallpaperView.setVisibility(View.VISIBLE);
+ CropView c = a.getCropView();
+
Drawable defaultWallpaper = WallpaperManager.getInstance(a).getBuiltInDrawable(
- defaultWallpaperView.getWidth(), defaultWallpaperView.getHeight(),
- false, 0.5f, 0.5f);
- if (defaultWallpaper != null) {
- defaultWallpaperView.setBackgroundDrawable(defaultWallpaper);
- }
+ c.getWidth(), c.getHeight(), false, 0.5f, 0.5f);
+
+ c.setTileSource(
+ new DrawableTileSource(a, defaultWallpaper, DrawableTileSource.MAX_PREVIEW_SIZE), null);
+ c.setScale(1f);
+ c.setTouchEnabled(false);
}
@Override
public void onSave(WallpaperPickerActivity a) {
@@ -249,7 +275,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
setContentView(R.layout.wallpaper_picker);
mCropView = (CropView) findViewById(R.id.cropView);
- mDefaultWallpaperView = (ImageView) findViewById(R.id.defaultWallpaperView);
mWallpaperStrip = findViewById(R.id.wallpaper_strip);
mCropView.setTouchCallback(new CropView.TouchCallback() {
LauncherViewPropertyAnimator mAnim;
@@ -307,17 +332,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
return;
}
WallpaperTileInfo info = (WallpaperTileInfo) v.getTag();
- if (info.isSelectable()) {
- if (mSelectedThumb != null) {
- mSelectedThumb.setSelected(false);
- mSelectedThumb = null;
- }
- mSelectedThumb = v;
- v.setSelected(true);
- // TODO: Remove this once the accessibility framework and
- // services have better support for selection state.
- v.announceForAccessibility(
- getString(R.string.announce_selection, v.getContentDescription()));
+ if (info.isSelectable() && v.getVisibility() == View.VISIBLE) {
+ selectTile(v);
}
info.onClick(WallpaperPickerActivity.this);
}
@@ -410,7 +426,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
// Select the first item; wait for a layout pass so that we initialize the dimensions of
// cropView or the defaultWallpaperView first
- mDefaultWallpaperView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ mCropView.addOnLayoutChangeListener(new OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
@@ -443,8 +459,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
new View.OnClickListener() {
@Override
public void onClick(View v) {
- if (mSelectedThumb != null) {
- WallpaperTileInfo info = (WallpaperTileInfo) mSelectedThumb.getTag();
+ if (mSelectedTile != null) {
+ WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag();
info.onSave(WallpaperPickerActivity.this);
}
}
@@ -523,12 +539,25 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
CheckableFrameLayout c = (CheckableFrameLayout) mWallpapersView.getChildAt(i);
c.setChecked(false);
}
- mSelectedThumb.setSelected(true);
+ mSelectedTile.setSelected(true);
mActionMode = null;
}
};
}
+ private void selectTile(View v) {
+ if (mSelectedTile != null) {
+ mSelectedTile.setSelected(false);
+ mSelectedTile = null;
+ }
+ mSelectedTile = v;
+ v.setSelected(true);
+ // TODO: Remove this once the accessibility framework and
+ // services have better support for selection state.
+ v.announceForAccessibility(
+ getString(R.string.announce_selection, v.getContentDescription()));
+ }
+
private void initializeScrollForRtl() {
final HorizontalScrollView scroll =
(HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container);
@@ -687,26 +716,35 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
}
}
- private void addTemporaryWallpaperTile(Uri uri) {
+ private void addTemporaryWallpaperTile(final Uri uri) {
mTempWallpaperTiles.add(uri);
// Add a tile for the image picked from Gallery
- FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
+ final FrameLayout pickedImageThumbnail = (FrameLayout) getLayoutInflater().
inflate(R.layout.wallpaper_picker_item, mWallpapersView, false);
+ pickedImageThumbnail.setVisibility(View.GONE);
setWallpaperItemPaddingToZero(pickedImageThumbnail);
+ mWallpapersView.addView(pickedImageThumbnail, 0);
// Load the thumbnail
- ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
- Point defaultSize = getDefaultThumbnailSize(this.getResources());
- int rotation = WallpaperCropActivity.getRotationFromExif(this, uri);
- Bitmap thumb = createThumbnail(defaultSize, this, uri, null, null, 0, rotation, false);
- if (thumb != null) {
- image.setImageBitmap(thumb);
- Drawable thumbDrawable = image.getDrawable();
- thumbDrawable.setDither(true);
- } else {
- Log.e(TAG, "Error loading thumbnail for uri=" + uri);
- }
- mWallpapersView.addView(pickedImageThumbnail, 0);
+ final ImageView image = (ImageView) pickedImageThumbnail.findViewById(R.id.wallpaper_image);
+ final Point defaultSize = getDefaultThumbnailSize(this.getResources());
+ 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);
+
+ }
+ protected void onPostExecute(Bitmap thumb) {
+ if (thumb != null) {
+ image.setImageBitmap(thumb);
+ Drawable thumbDrawable = image.getDrawable();
+ thumbDrawable.setDither(true);
+ } else {
+ Log.e(TAG, "Error loading thumbnail for uri=" + uri);
+ }
+ }
+ }.execute();
UriWallpaperInfo info = new UriWallpaperInfo(uri);
pickedImageThumbnail.setTag(info);
@@ -886,10 +924,6 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
return mCropView;
}
- public ImageView getDefaultWallpaperView() {
- return mDefaultWallpaperView;
- }
-
public SavedWallpaperImages getSavedImages() {
return mSavedImages;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a1353c3c8..d742d429a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -22,6 +22,7 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
@@ -89,6 +90,9 @@ public class Workspace extends SmoothPagedView
private static final int CHILDREN_OUTLINE_FADE_OUT_DURATION = 375;
private static final int CHILDREN_OUTLINE_FADE_IN_DURATION = 100;
+ protected static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
+ protected static final int FADE_EMPTY_SCREEN_DURATION = 150;
+
private static final int BACKGROUND_FADE_OUT_DURATION = 350;
private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
private static final int FLING_THRESHOLD_VELOCITY = 500;
@@ -128,6 +132,8 @@ public class Workspace extends SmoothPagedView
private HashMap<Long, CellLayout> mWorkspaceScreens = new HashMap<Long, CellLayout>();
private ArrayList<Long> mScreenOrder = new ArrayList<Long>();
+ private Runnable mRemoveEmptyScreenRunnable;
+
/**
* CellInfo for the cell that is currently being dragged
*/
@@ -392,7 +398,6 @@ public class Workspace extends SmoothPagedView
InstallShortcutReceiver.disableAndFlushInstallQueue(getContext());
UninstallShortcutReceiver.disableAndFlushUninstallQueue(getContext());
- removeExtraEmptyScreen();
mDragSourceInternal = null;
mLauncher.onInteractionEnd();
}
@@ -627,6 +632,9 @@ public class Workspace extends SmoothPagedView
boolean lastChildOnScreen = false;
boolean childOnFinalScreen = false;
+ // Cancel any pending removal of empty screen
+ mRemoveEmptyScreenRunnable = null;
+
if (mDragSourceInternal != null) {
if (mDragSourceInternal.getChildCount() == 1) {
lastChildOnScreen = true;
@@ -654,13 +662,95 @@ public class Workspace extends SmoothPagedView
return false;
}
- public void removeExtraEmptyScreen() {
+ private void convertFinalScreenToEmptyScreenIfNecessary() {
+ if (hasExtraEmptyScreen() || mScreenOrder.size() == 0) return;
+ long finalScreenId = mScreenOrder.get(mScreenOrder.size() - 1);
+
+ if (finalScreenId == CUSTOM_CONTENT_SCREEN_ID) return;
+ CellLayout finalScreen = mWorkspaceScreens.get(finalScreenId);
+
+ // If the final screen is empty, convert it to the extra empty screen
+ if (finalScreen.getShortcutsAndWidgets().getChildCount() == 0) {
+ mWorkspaceScreens.remove(finalScreenId);
+ mScreenOrder.remove(finalScreenId);
+
+ // if this is the last non-custom content screen, convert it to the empty screen
+ mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, finalScreen);
+ mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
+ }
+ }
+
+ public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete) {
+ removeExtraEmptyScreen(animate, onComplete, 0, false);
+ }
+
+ public void removeExtraEmptyScreen(final boolean animate, final Runnable onComplete,
+ final int delay, final boolean stripEmptyScreens) {
+ if (delay > 0) {
+ postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ removeExtraEmptyScreen(animate, onComplete, 0, stripEmptyScreens);
+ }
+
+ }, delay);
+ return;
+ }
+
+ convertFinalScreenToEmptyScreenIfNecessary();
if (hasExtraEmptyScreen()) {
- CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
- mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
- mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
- removeView(cl);
+ int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
+ if (getNextPage() == emptyIndex) {
+ snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
+ fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
+ onComplete, stripEmptyScreens);
+ } else {
+ fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
+ onComplete, stripEmptyScreens);
+ }
+ return;
}
+ if (onComplete != null) {
+ onComplete.run();
+ }
+ }
+
+ private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
+ final boolean stripEmptyScreens) {
+ PropertyValuesHolder alpha = PropertyValuesHolder.ofFloat("alpha", 0f);
+ PropertyValuesHolder bgAlpha = PropertyValuesHolder.ofFloat("backgroundAlpha", 0f);
+
+ final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
+
+ mRemoveEmptyScreenRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (hasExtraEmptyScreen()) {
+ mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
+ mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
+ removeView(cl);
+ if (stripEmptyScreens) {
+ stripEmptyScreens();
+ }
+ }
+ }
+ };
+
+ ObjectAnimator oa = ObjectAnimator.ofPropertyValuesHolder(cl, alpha, bgAlpha);
+ oa.setDuration(duration);
+ oa.setStartDelay(delay);
+ oa.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ if (mRemoveEmptyScreenRunnable != null) {
+ mRemoveEmptyScreenRunnable.run();
+ }
+ if (onComplete != null) {
+ onComplete.run();
+ }
+ }
+ });
+ oa.start();
}
public boolean hasExtraEmptyScreen() {
@@ -753,6 +843,7 @@ public class Workspace extends SmoothPagedView
removeView(cl);
} else {
// if this is the last non-custom content screen, convert it to the empty screen
+ mRemoveEmptyScreenRunnable = null;
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
}
@@ -1103,11 +1194,15 @@ public class Workspace extends SmoothPagedView
}
protected void snapToPage(int whichPage, Runnable r) {
+ snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION, r);
+ }
+
+ protected void snapToPage(int whichPage, int duration, Runnable r) {
if (mDelayedSnapToPageRunnable != null) {
mDelayedSnapToPageRunnable.run();
}
mDelayedSnapToPageRunnable = r;
- snapToPage(whichPage, SLOW_PAGE_SNAP_ANIMATION_DURATION);
+ snapToPage(whichPage, duration);
}
protected void snapToScreenId(long screenId, Runnable r) {
@@ -2726,13 +2821,13 @@ 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)) {
- stripEmptyScreens();
+ removeExtraEmptyScreen(true, null, 0, true);
return;
}
if (addToExistingFolderIfNecessary(cell, dropTargetLayout, mTargetCell,
distance, d, false)) {
- stripEmptyScreens();
+ removeExtraEmptyScreen(true, null, 0, true);
return;
}
@@ -2840,7 +2935,7 @@ public class Workspace extends SmoothPagedView
if (finalResizeRunnable != null) {
finalResizeRunnable.run();
}
- stripEmptyScreens();
+ removeExtraEmptyScreen(true, null, 0, true);
}
};
mAnimatingViewIntoPlace = true;
@@ -3491,7 +3586,13 @@ public class Workspace extends SmoothPagedView
final Runnable exitSpringLoadedRunnable = new Runnable() {
@Override
public void run() {
- mLauncher.exitSpringLoadedDragModeDelayed(true, false, null);
+ removeExtraEmptyScreen(false, new Runnable() {
+ @Override
+ public void run() {
+ mLauncher.exitSpringLoadedDragModeDelayed(true,
+ Launcher.EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT, null);
+ }
+ });
}
};
@@ -3727,7 +3828,7 @@ public class Workspace extends SmoothPagedView
external, scalePreview);
Resources res = mLauncher.getResources();
- int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
+ final int duration = res.getInteger(R.integer.config_dropAnimMaxDuration) - 200;
// In the case where we've prebound the widget, we remove it from the DragLayer
if (finalView instanceof AppWidgetHostView && external) {
@@ -3835,11 +3936,11 @@ public class Workspace extends SmoothPagedView
final boolean isFlingToDelete, final boolean success) {
if (mDeferDropAfterUninstall) {
mDeferredAction = new Runnable() {
- public void run() {
- onDropCompleted(target, d, isFlingToDelete, success);
- mDeferredAction = null;
- }
- };
+ public void run() {
+ onDropCompleted(target, d, isFlingToDelete, success);
+ mDeferredAction = null;
+ }
+ };
return;
}
@@ -3857,7 +3958,7 @@ public class Workspace extends SmoothPagedView
// 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.
- stripEmptyScreens();
+ removeExtraEmptyScreen(true, null, 0, true);
}
} else if (mDragInfo != null) {
CellLayout cellLayout;
@@ -4085,6 +4186,7 @@ public class Workspace extends SmoothPagedView
}
}
mRestoredPages.clear();
+ mSavedStates = null;
}
@Override
diff --git a/src/com/android/photos/BitmapRegionTileSource.java b/src/com/android/photos/BitmapRegionTileSource.java
index 5f6401868..2f203af53 100644
--- a/src/com/android/photos/BitmapRegionTileSource.java
+++ b/src/com/android/photos/BitmapRegionTileSource.java
@@ -24,6 +24,9 @@ import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapRegionDecoder;
import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Build;
@@ -31,14 +34,114 @@ import android.os.Build.VERSION_CODES;
import android.util.Log;
import com.android.gallery3d.common.BitmapUtils;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.exif.ExifInterface;
import com.android.gallery3d.glrenderer.BasicTexture;
import com.android.gallery3d.glrenderer.BitmapTexture;
import com.android.photos.views.TiledImageRenderer;
import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
+interface SimpleBitmapRegionDecoder {
+ int getWidth();
+ int getHeight();
+ Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options);
+}
+
+class SimpleBitmapRegionDecoderWrapper implements SimpleBitmapRegionDecoder {
+ BitmapRegionDecoder mDecoder;
+ private SimpleBitmapRegionDecoderWrapper(BitmapRegionDecoder decoder) {
+ mDecoder = decoder;
+ }
+ public static SimpleBitmapRegionDecoderWrapper newInstance(
+ String pathName, boolean isShareable) {
+ try {
+ BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(pathName, isShareable);
+ if (d != null) {
+ return new SimpleBitmapRegionDecoderWrapper(d);
+ }
+ } catch (IOException e) {
+ Log.w("BitmapRegionTileSource", "getting decoder failed for path " + pathName, e);
+ return null;
+ }
+ return null;
+ }
+ public static SimpleBitmapRegionDecoderWrapper newInstance(
+ InputStream is, boolean isShareable) {
+ try {
+ BitmapRegionDecoder d = BitmapRegionDecoder.newInstance(is, isShareable);
+ if (d != null) {
+ return new SimpleBitmapRegionDecoderWrapper(d);
+ }
+ } catch (IOException e) {
+ Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+ return null;
+ }
+ return null;
+ }
+ public int getWidth() {
+ return mDecoder.getWidth();
+ }
+ public int getHeight() {
+ return mDecoder.getHeight();
+ }
+ public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
+ return mDecoder.decodeRegion(wantRegion, options);
+ }
+}
+
+class DumbBitmapRegionDecoder implements SimpleBitmapRegionDecoder {
+ //byte[] mStreamCopy;
+ Bitmap mBuffer;
+ Canvas mTempCanvas;
+ Paint mTempPaint;
+ private DumbBitmapRegionDecoder(Bitmap b) {
+ mBuffer = b;
+ }
+ public static DumbBitmapRegionDecoder newInstance(String pathName) {
+ Bitmap b = BitmapFactory.decodeFile(pathName);
+ if (b != null) {
+ return new DumbBitmapRegionDecoder(b);
+ }
+ return null;
+ }
+ public static DumbBitmapRegionDecoder newInstance(InputStream is) {
+ Bitmap b = BitmapFactory.decodeStream(is);
+ if (b != null) {
+ return new DumbBitmapRegionDecoder(b);
+ }
+ return null;
+ }
+ public int getWidth() {
+ return mBuffer.getWidth();
+ }
+ public int getHeight() {
+ return mBuffer.getHeight();
+ }
+ public Bitmap decodeRegion(Rect wantRegion, BitmapFactory.Options options) {
+ if (mTempCanvas == null) {
+ mTempCanvas = new Canvas();
+ mTempPaint = new Paint();
+ mTempPaint.setFilterBitmap(true);
+ }
+ int sampleSize = Math.max(options.inSampleSize, 1);
+ Bitmap newBitmap = Bitmap.createBitmap(
+ wantRegion.width() / sampleSize,
+ wantRegion.height() / sampleSize,
+ Bitmap.Config.ARGB_8888);
+ mTempCanvas.setBitmap(newBitmap);
+ mTempCanvas.save();
+ mTempCanvas.scale(1f / sampleSize, 1f / sampleSize);
+ mTempCanvas.drawBitmap(mBuffer, -wantRegion.left, -wantRegion.top, mTempPaint);
+ mTempCanvas.restore();
+ mTempCanvas.setBitmap(null);
+ return newBitmap;
+ }
+}
+
/**
* A {@link com.android.photos.views.TiledImageRenderer.TileSource} using
* {@link BitmapRegionDecoder} to wrap a local file
@@ -53,9 +156,214 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
private static final int GL_SIZE_LIMIT = 2048;
// This must be no larger than half the size of the GL_SIZE_LIMIT
// due to decodePreview being allowed to be up to 2x the size of the target
- private static final int MAX_PREVIEW_SIZE = 1024;
+ public static final int MAX_PREVIEW_SIZE = GL_SIZE_LIMIT / 2;
+
+ public static abstract class BitmapSource {
+ private SimpleBitmapRegionDecoder mDecoder;
+ private Bitmap mPreview;
+ private int mPreviewSize;
+ private int mRotation;
+ public enum State { NOT_LOADED, LOADED, ERROR_LOADING };
+ private State mState = State.NOT_LOADED;
+ public BitmapSource(int previewSize) {
+ mPreviewSize = previewSize;
+ }
+ public boolean loadInBackground() {
+ ExifInterface ei = new ExifInterface();
+ if (readExif(ei)) {
+ Integer ori = ei.getTagIntValue(ExifInterface.TAG_ORIENTATION);
+ if (ori != null) {
+ mRotation = ExifInterface.getRotationForOrientationValue(ori.shortValue());
+ }
+ }
+ mDecoder = loadBitmapRegionDecoder();
+ if (mDecoder == null) {
+ mState = State.ERROR_LOADING;
+ return false;
+ } else {
+ int width = mDecoder.getWidth();
+ int height = mDecoder.getHeight();
+ if (mPreviewSize != 0) {
+ int previewSize = Math.min(mPreviewSize, MAX_PREVIEW_SIZE);
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ opts.inPreferQualityOverSpeed = true;
+
+ float scale = (float) previewSize / Math.max(width, height);
+ opts.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
+ opts.inJustDecodeBounds = false;
+ mPreview = loadPreviewBitmap(opts);
+ }
+ mState = State.LOADED;
+ return true;
+ }
+ }
- BitmapRegionDecoder mDecoder;
+ public State getLoadingState() {
+ return mState;
+ }
+
+ public SimpleBitmapRegionDecoder getBitmapRegionDecoder() {
+ return mDecoder;
+ }
+
+ public Bitmap getPreviewBitmap() {
+ return mPreview;
+ }
+
+ public int getPreviewSize() {
+ return mPreviewSize;
+ }
+
+ public int getRotation() {
+ return mRotation;
+ }
+
+ public abstract boolean readExif(ExifInterface ei);
+ public abstract SimpleBitmapRegionDecoder loadBitmapRegionDecoder();
+ public abstract Bitmap loadPreviewBitmap(BitmapFactory.Options options);
+ }
+
+ public static class FilePathBitmapSource extends BitmapSource {
+ private String mPath;
+ public FilePathBitmapSource(String path, int previewSize) {
+ super(previewSize);
+ mPath = path;
+ }
+ @Override
+ public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+ SimpleBitmapRegionDecoder d;
+ d = SimpleBitmapRegionDecoderWrapper.newInstance(mPath, true);
+ if (d == null) {
+ d = DumbBitmapRegionDecoder.newInstance(mPath);
+ }
+ return d;
+ }
+ @Override
+ public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+ return BitmapFactory.decodeFile(mPath, options);
+ }
+ @Override
+ public boolean readExif(ExifInterface ei) {
+ try {
+ ei.readExif(mPath);
+ return true;
+ } catch (IOException e) {
+ Log.w("BitmapRegionTileSource", "getting decoder failed", e);
+ return false;
+ }
+ }
+ }
+
+ public static class UriBitmapSource extends BitmapSource {
+ private Context mContext;
+ private Uri mUri;
+ public UriBitmapSource(Context context, Uri uri, int previewSize) {
+ super(previewSize);
+ mContext = context;
+ mUri = uri;
+ }
+ private InputStream regenerateInputStream() throws FileNotFoundException {
+ InputStream is = mContext.getContentResolver().openInputStream(mUri);
+ return new BufferedInputStream(is);
+ }
+ @Override
+ public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+ try {
+ InputStream is = regenerateInputStream();
+ SimpleBitmapRegionDecoder regionDecoder =
+ SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
+ Utils.closeSilently(is);
+ if (regionDecoder == null) {
+ is = regenerateInputStream();
+ regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+ Utils.closeSilently(is);
+ }
+ return regionDecoder;
+ } 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
+ public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+ try {
+ InputStream is = regenerateInputStream();
+ Bitmap b = BitmapFactory.decodeStream(is, null, options);
+ Utils.closeSilently(is);
+ return b;
+ } catch (FileNotFoundException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return null;
+ }
+ }
+ @Override
+ public boolean readExif(ExifInterface ei) {
+ InputStream is = null;
+ try {
+ is = regenerateInputStream();
+ ei.readExif(is);
+ Utils.closeSilently(is);
+ return true;
+ } catch (FileNotFoundException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return false;
+ } catch (IOException e) {
+ Log.e("BitmapRegionTileSource", "Failed to load URI " + mUri, e);
+ return false;
+ } finally {
+ Utils.closeSilently(is);
+ }
+ }
+ }
+
+ public static class ResourceBitmapSource extends BitmapSource {
+ private Resources mRes;
+ private int mResId;
+ public ResourceBitmapSource(Resources res, int resId, int previewSize) {
+ super(previewSize);
+ mRes = res;
+ mResId = resId;
+ }
+ private InputStream regenerateInputStream() {
+ InputStream is = mRes.openRawResource(mResId);
+ return new BufferedInputStream(is);
+ }
+ @Override
+ public SimpleBitmapRegionDecoder loadBitmapRegionDecoder() {
+ InputStream is = regenerateInputStream();
+ SimpleBitmapRegionDecoder regionDecoder =
+ SimpleBitmapRegionDecoderWrapper.newInstance(is, false);
+ Utils.closeSilently(is);
+ if (regionDecoder == null) {
+ is = regenerateInputStream();
+ regionDecoder = DumbBitmapRegionDecoder.newInstance(is);
+ Utils.closeSilently(is);
+ }
+ return regionDecoder;
+ }
+ @Override
+ public Bitmap loadPreviewBitmap(BitmapFactory.Options options) {
+ return BitmapFactory.decodeResource(mRes, mResId, options);
+ }
+ @Override
+ public boolean readExif(ExifInterface ei) {
+ try {
+ InputStream is = regenerateInputStream();
+ ei.readExif(is);
+ Utils.closeSilently(is);
+ return true;
+ } catch (IOException e) {
+ Log.e("BitmapRegionTileSource", "Error reading resource", e);
+ return false;
+ }
+ }
+ }
+
+ SimpleBitmapRegionDecoder mDecoder;
int mWidth;
int mHeight;
int mTileSize;
@@ -68,58 +376,33 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
private BitmapFactory.Options mOptions;
private Canvas mCanvas;
- public BitmapRegionTileSource(Context context, String path, int previewSize, int rotation) {
- this(null, context, path, null, 0, previewSize, rotation);
- }
-
- public BitmapRegionTileSource(Context context, Uri uri, int previewSize, int rotation) {
- this(null, context, null, uri, 0, previewSize, rotation);
- }
-
- public BitmapRegionTileSource(Resources res,
- Context context, int resId, int previewSize, int rotation) {
- this(res, context, null, null, resId, previewSize, rotation);
- }
-
- private BitmapRegionTileSource(Resources res,
- Context context, String path, Uri uri, int resId, int previewSize, int rotation) {
+ public BitmapRegionTileSource(Context context, BitmapSource source) {
mTileSize = TiledImageRenderer.suggestedTileSize(context);
- mRotation = rotation;
- try {
- if (path != null) {
- mDecoder = BitmapRegionDecoder.newInstance(path, true);
- } else if (uri != null) {
- InputStream is = context.getContentResolver().openInputStream(uri);
- BufferedInputStream bis = new BufferedInputStream(is);
- mDecoder = BitmapRegionDecoder.newInstance(bis, true);
- } else {
- InputStream is = res.openRawResource(resId);
- BufferedInputStream bis = new BufferedInputStream(is);
- mDecoder = BitmapRegionDecoder.newInstance(bis, true);
- }
+ mRotation = source.getRotation();
+ mDecoder = source.getBitmapRegionDecoder();
+ if (mDecoder != null) {
mWidth = mDecoder.getWidth();
mHeight = mDecoder.getHeight();
- } catch (IOException e) {
- Log.w("BitmapRegionTileSource", "ctor failed", e);
- }
- mOptions = new BitmapFactory.Options();
- mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
- mOptions.inPreferQualityOverSpeed = true;
- mOptions.inTempStorage = new byte[16 * 1024];
- if (previewSize != 0) {
- previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
- // Although this is the same size as the Bitmap that is likely already
- // loaded, the lifecycle is different and interactions are on a different
- // thread. Thus to simplify, this source will decode its own bitmap.
- Bitmap preview = decodePreview(res, context, path, uri, resId, previewSize);
- if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
- mPreview = new BitmapTexture(preview);
- } else {
- Log.w(TAG, String.format(
- "Failed to create preview of apropriate size! "
- + " in: %dx%d, out: %dx%d",
- mWidth, mHeight,
- preview.getWidth(), preview.getHeight()));
+ mOptions = new BitmapFactory.Options();
+ mOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ mOptions.inPreferQualityOverSpeed = true;
+ mOptions.inTempStorage = new byte[16 * 1024];
+ int previewSize = source.getPreviewSize();
+ if (previewSize != 0) {
+ previewSize = Math.min(previewSize, MAX_PREVIEW_SIZE);
+ // Although this is the same size as the Bitmap that is likely already
+ // loaded, the lifecycle is different and interactions are on a different
+ // thread. Thus to simplify, this source will decode its own bitmap.
+ Bitmap preview = decodePreview(source, previewSize);
+ if (preview.getWidth() <= GL_SIZE_LIMIT && preview.getHeight() <= GL_SIZE_LIMIT) {
+ mPreview = new BitmapTexture(preview);
+ } else {
+ Log.w(TAG, String.format(
+ "Failed to create preview of apropriate size! "
+ + " in: %dx%d, out: %dx%d",
+ mWidth, mHeight,
+ preview.getWidth(), preview.getHeight()));
+ }
}
}
}
@@ -215,33 +498,15 @@ public class BitmapRegionTileSource implements TiledImageRenderer.TileSource {
* Note that the returned bitmap may have a long edge that's longer
* than the targetSize, but it will always be less than 2x the targetSize
*/
- private Bitmap decodePreview(
- Resources res, Context context, String file, Uri uri, int resId, int targetSize) {
- float scale = (float) targetSize / Math.max(mWidth, mHeight);
- mOptions.inSampleSize = BitmapUtils.computeSampleSizeLarger(scale);
- mOptions.inJustDecodeBounds = false;
-
- Bitmap result = null;
- if (file != null) {
- result = BitmapFactory.decodeFile(file, mOptions);
- } else if (uri != null) {
- try {
- InputStream is = context.getContentResolver().openInputStream(uri);
- BufferedInputStream bis = new BufferedInputStream(is);
- result = BitmapFactory.decodeStream(bis, null, mOptions);
- } catch (IOException e) {
- Log.w("BitmapRegionTileSource", "getting preview failed", e);
- }
- } else {
- result = BitmapFactory.decodeResource(res, resId, mOptions);
- }
+ private Bitmap decodePreview(BitmapSource source, int targetSize) {
+ Bitmap result = source.getPreviewBitmap();
if (result == null) {
return null;
}
// We need to resize down if the decoder does not support inSampleSize
// or didn't support the specified inSampleSize (some decoders only do powers of 2)
- scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
+ float scale = (float) targetSize / (float) (Math.max(result.getWidth(), result.getHeight()));
if (scale <= 0.5) {
result = BitmapUtils.resizeBitmapByScale(result, scale, true);
diff --git a/update_system_wallpaper_cropper.py b/update_system_wallpaper_cropper.py
new file mode 100644
index 000000000..5d24f6ffe
--- /dev/null
+++ b/update_system_wallpaper_cropper.py
@@ -0,0 +1,55 @@
+# This script is used to push the most up-to-date files from
+# Launcher into frameworks' version of the WallpaperCropActivity
+# (and supporting files)
+# The framework versions have some small modifications that are
+# necessary so do this with care
+import os
+import sys
+files = """
+src/android/util/Pools.java
+src/com/android/gallery3d/util/IntArray.java
+src/com/android/gallery3d/common/Utils.java
+src/com/android/gallery3d/exif/ByteBufferInputStream.java
+src/com/android/gallery3d/exif/CountedDataInputStream.java
+src/com/android/gallery3d/exif/ExifData.java
+src/com/android/gallery3d/exif/ExifInterface.java
+src/com/android/gallery3d/exif/ExifInvalidFormatException.java
+src/com/android/gallery3d/exif/ExifModifier.java
+src/com/android/gallery3d/exif/ExifOutputStream.java
+src/com/android/gallery3d/exif/ExifParser.java
+src/com/android/gallery3d/exif/ExifReader.java
+src/com/android/gallery3d/exif/ExifTag.java
+src/com/android/gallery3d/exif/IfdData.java
+src/com/android/gallery3d/exif/IfdId.java
+src/com/android/gallery3d/exif/JpegHeader.java
+src/com/android/gallery3d/exif/OrderedDataOutputStream.java
+src/com/android/gallery3d/exif/Rational.java
+src/com/android/gallery3d/glrenderer/BasicTexture.java
+src/com/android/gallery3d/glrenderer/BitmapTexture.java
+src/com/android/gallery3d/glrenderer/GLCanvas.java
+src/com/android/gallery3d/glrenderer/GLES20Canvas.java
+src/com/android/gallery3d/glrenderer/GLES20IdImpl.java
+src/com/android/gallery3d/glrenderer/GLId.java
+src/com/android/gallery3d/glrenderer/GLPaint.java
+src/com/android/gallery3d/glrenderer/RawTexture.java
+src/com/android/gallery3d/glrenderer/Texture.java
+src/com/android/gallery3d/glrenderer/UploadedTexture.java
+src/com/android/photos/BitmapRegionTileSource.java
+src/com/android/photos/views/BlockingGLTextureView.java
+src/com/android/photos/views/TiledImageRenderer.java
+src/com/android/photos/views/TiledImageView.java
+src/com/android/gallery3d/common/BitmapUtils.java
+src/com/android/launcher3/CropView.java
+src/com/android/launcher3/WallpaperCropActivity.java
+"""
+
+if len(sys.argv) != 2:
+ print "Usage: python update_sytem_wallpaper_cropper.py <framework_dir>"
+ exit()
+framework_dir = sys.argv[1] + "/packages/WallpaperCropper"
+for file_path in files.split():
+ dir = os.path.dirname(file_path)
+ dir = dir.replace("launcher3", "wallpapercropper")
+ cmd = 'cp %s %s/%s' % (file_path, framework_dir, dir)
+ print cmd
+ os.system(cmd)