OSDN Git Service

merge in jb-mr2-release history after reset to master
authorThe Android Automerger <android-build@android.com>
Mon, 25 Feb 2013 13:56:19 +0000 (05:56 -0800)
committerThe Android Automerger <android-build@android.com>
Mon, 25 Feb 2013 13:56:19 +0000 (05:56 -0800)
111 files changed:
AndroidManifest.xml
res/layout/photo_set.xml [new file with mode: 0644]
res/menu/gallery.xml [new file with mode: 0644]
res/values-af/filtershow_strings.xml
res/values-am/filtershow_strings.xml
res/values-ar/filtershow_strings.xml
res/values-be/filtershow_strings.xml
res/values-bg/filtershow_strings.xml
res/values-ca/filtershow_strings.xml
res/values-cs/filtershow_strings.xml
res/values-da/filtershow_strings.xml
res/values-de/filtershow_strings.xml
res/values-el/filtershow_strings.xml
res/values-en-rGB/filtershow_strings.xml
res/values-es-rUS/filtershow_strings.xml
res/values-es/filtershow_strings.xml
res/values-et/filtershow_strings.xml
res/values-fa/filtershow_strings.xml
res/values-fi/filtershow_strings.xml
res/values-fr/filtershow_strings.xml
res/values-hi/filtershow_strings.xml
res/values-hr/filtershow_strings.xml
res/values-hu/filtershow_strings.xml
res/values-in/filtershow_strings.xml
res/values-it/filtershow_strings.xml
res/values-iw/filtershow_strings.xml
res/values-ja/filtershow_strings.xml
res/values-ko/filtershow_strings.xml
res/values-lt/filtershow_strings.xml
res/values-lv/filtershow_strings.xml
res/values-ms/filtershow_strings.xml
res/values-nb/filtershow_strings.xml
res/values-nl/filtershow_strings.xml
res/values-pl/filtershow_strings.xml
res/values-pt-rPT/filtershow_strings.xml
res/values-pt/filtershow_strings.xml
res/values-ro/filtershow_strings.xml
res/values-ru/filtershow_strings.xml
res/values-sk/filtershow_strings.xml
res/values-sl/filtershow_strings.xml
res/values-sr/filtershow_strings.xml
res/values-sv/filtershow_strings.xml
res/values-sw/filtershow_strings.xml
res/values-th/filtershow_strings.xml
res/values-tl/filtershow_strings.xml
res/values-tr/filtershow_strings.xml
res/values-uk/filtershow_strings.xml
res/values-v14/styles.xml
res/values-vi/filtershow_strings.xml
res/values-zh-rCN/filtershow_strings.xml
res/values-zh-rTW/filtershow_strings.xml
res/values-zu/filtershow_strings.xml
res/values/filtershow_strings.xml
res/values/strings.xml
src/android/util/Pools.java [new file with mode: 0644]
src/com/android/camera/PhotoModule.java
src/com/android/gallery3d/app/AbstractGalleryActivity.java
src/com/android/gallery3d/app/PhotoDataAdapter.java
src/com/android/gallery3d/data/BitmapPool.java [deleted file]
src/com/android/gallery3d/data/DataManager.java
src/com/android/gallery3d/data/DecodeUtils.java
src/com/android/gallery3d/data/ImageCacheRequest.java
src/com/android/gallery3d/data/MediaItem.java
src/com/android/gallery3d/filtershow/FilterShowActivity.java
src/com/android/gallery3d/filtershow/PanelController.java
src/com/android/gallery3d/filtershow/cache/FilteringPipeline.java
src/com/android/gallery3d/filtershow/cache/ImageLoader.java
src/com/android/gallery3d/filtershow/cache/RenderingRequest.java
src/com/android/gallery3d/filtershow/filters/FilterCurvesRepresentation.java
src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
src/com/android/gallery3d/filtershow/filters/FilterFxRepresentation.java
src/com/android/gallery3d/filtershow/filters/FilterRepresentation.java
src/com/android/gallery3d/filtershow/filters/ImageFilter.java
src/com/android/gallery3d/filtershow/filters/ImageFilterBwFilter.java
src/com/android/gallery3d/filtershow/filters/ImageFilterContrast.java
src/com/android/gallery3d/filtershow/filters/ImageFilterEdge.java
src/com/android/gallery3d/filtershow/filters/ImageFilterExposure.java
src/com/android/gallery3d/filtershow/filters/ImageFilterHighlights.java
src/com/android/gallery3d/filtershow/filters/ImageFilterHue.java
src/com/android/gallery3d/filtershow/filters/ImageFilterKMeans.java
src/com/android/gallery3d/filtershow/filters/ImageFilterNegative.java
src/com/android/gallery3d/filtershow/filters/ImageFilterRS.java
src/com/android/gallery3d/filtershow/filters/ImageFilterSaturated.java
src/com/android/gallery3d/filtershow/filters/ImageFilterShadows.java
src/com/android/gallery3d/filtershow/filters/ImageFilterSharpen.java
src/com/android/gallery3d/filtershow/filters/ImageFilterVibrance.java
src/com/android/gallery3d/filtershow/filters/ImageFilterWBalance.java
src/com/android/gallery3d/filtershow/imageshow/ImageDraw.java
src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
src/com/android/gallery3d/filtershow/imageshow/ImageZoom.java
src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
src/com/android/gallery3d/filtershow/presets/ImagePreset.java
src/com/android/gallery3d/ingest/IngestService.java
src/com/android/gallery3d/ingest/data/MtpBitmapFetch.java
src/com/android/gallery3d/ui/AlbumLabelMaker.java
src/com/android/gallery3d/ui/AlbumSetSlidingWindow.java
src/com/android/gallery3d/ui/AlbumSlidingWindow.java
src/com/android/gallery3d/ui/BitmapLoader.java
src/com/android/gallery3d/ui/BitmapTileProvider.java
src/com/android/gallery3d/ui/TileImageView.java
src/com/android/gallery3d/ui/TileImageViewAdapter.java
src/com/android/gallery3d/ui/TiledScreenNail.java
src/com/android/photos/AlbumSetFragment.java [new file with mode: 0644]
src/com/android/photos/GalleryActivity.java [new file with mode: 0644]
src/com/android/photos/PhotoFragment.java [new file with mode: 0644]
src/com/android/photos/PhotoSetFragment.java [new file with mode: 0644]
src/com/android/photos/data/GalleryBitmapPool.java [new file with mode: 0644]
src/com/android/photos/data/MediaSetLoader.java [new file with mode: 0644]
src/com/android/photos/data/PhotoSetLoader.java [new file with mode: 0644]
src/com/android/photos/data/SparseArrayBitmapPool.java [new file with mode: 0644]

index 508aefe..3cd30cb 100644 (file)
              </intent-filter>
         </activity>
 
-        <activity android:name="com.android.gallery3d.app.Gallery" android:label="@string/app_name"
-                android:configChanges="keyboardHidden|orientation|screenSize">
+        <activity android:name="com.android.photos.GalleryActivity"
+                android:label="@string/app_name"
+                android:configChanges="keyboardHidden|orientation|screenSize"
+                android:theme="@style/Theme.Photos.Gallery"
+                android:uiOptions="splitActionBarWhenNarrow">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
             </intent-filter>
         </activity>
 
-        <!-- we add this activity-alias for shortcut backward compatibility -->
-        <!-- Note: The alias must put after the target activity -->
-        <activity-alias android:name="com.cooliris.media.Gallery"
-                android:targetActivity="com.android.gallery3d.app.Gallery"
-                android:configChanges="keyboardHidden|orientation|screenSize"
-                android:label="@string/app_name">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-            </intent-filter>
-        </activity-alias>
-
          <!-- This activity receives USB_DEVICE_ATTACHED intents and allows importing
          media from attached MTP devices, like cameras and camera phones -->
         <activity android:launchMode="singleInstance"
diff --git a/res/layout/photo_set.xml b/res/layout/photo_set.xml
new file mode 100644 (file)
index 0000000..2bb97bb
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:paddingLeft="8dp"
+    android:paddingRight="8dp" >
+
+    <ListView
+        android:id="@id/android:list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:drawSelectorOnTop="true" />
+
+    <TextView
+        android:id="@id/android:empty"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/empty_album" />
+
+</FrameLayout>
\ No newline at end of file
diff --git a/res/menu/gallery.xml b/res/menu/gallery.xml
new file mode 100644 (file)
index 0000000..dc36787
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android" >
+    <item
+        android:id="@+id/menu_camera"
+        android:icon="@android:drawable/ic_menu_camera"
+        android:showAsAction="ifRoom"
+        android:title="@string/menu_camera"/>
+    <item
+        android:id="@+id/menu_search"
+        android:icon="@android:drawable/ic_menu_search"
+        android:showAsAction="ifRoom"
+        android:title="@string/menu_search"/>
+    <item
+        android:id="@+id/menu_settings"
+        android:icon="@android:drawable/ic_menu_preferences"
+        android:showAsAction="never"
+        android:title="@string/settings"/>
+    <item
+        android:id="@+id/menu_help"
+        android:icon="@android:drawable/ic_menu_help"
+        android:showAsAction="never"
+        android:title="@string/help"/>
+</menu>
\ No newline at end of file
index 9f5751e..34e5ebf 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Outokleur"</string>
     <string name="hue" msgid="6231252147971086030">"Kleur"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Skaduwees"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kurwes"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjet"</string>
     <string name="redeye" msgid="4508883127049472069">"Rooi oog"</string>
index cf72a53..eb0549d 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"ራስ-ቀለም መሙላት"</string>
     <string name="hue" msgid="6231252147971086030">"የቀለም ድባብ"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"ጥላዎች"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"ጥምዞች"</string>
     <string name="vignette" msgid="934721068851885390">"ቪኜት"</string>
     <string name="redeye" msgid="4508883127049472069">"ቀይ አይን"</string>
index ee1bfca..0310852 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"لون تلقائي"</string>
     <string name="hue" msgid="6231252147971086030">"تدرج اللون"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"ظلال"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"المنحنيات"</string>
     <string name="vignette" msgid="934721068851885390">"نقوش صورة نصفية"</string>
     <string name="redeye" msgid="4508883127049472069">"العين الحمراء"</string>
index 06af904..73d96e0 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Aўтаколер"</string>
     <string name="hue" msgid="6231252147971086030">"Тон"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Цені"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Пэндзаль"</string>
     <string name="vignette" msgid="934721068851885390">"Віньетка"</string>
     <string name="redeye" msgid="4508883127049472069">"Чырвонае вока"</string>
index cc4daae..f6e9cdc 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Авт. цвят"</string>
     <string name="hue" msgid="6231252147971086030">"Нюанс"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Засенчване"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Извивки"</string>
     <string name="vignette" msgid="934721068851885390">"Винетиране"</string>
     <string name="redeye" msgid="4508883127049472069">"Червени очи"</string>
index 5f4b8e1..6c291e8 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autocolor"</string>
     <string name="hue" msgid="6231252147971086030">"To de color"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Ombres"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Corbes"</string>
     <string name="vignette" msgid="934721068851885390">"Vinyeta"</string>
     <string name="redeye" msgid="4508883127049472069">"Ulls vermells"</string>
index e0c59f5..9b1db5f 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autom. barva"</string>
     <string name="hue" msgid="6231252147971086030">"Odstín"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Stíny"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Křivky"</string>
     <string name="vignette" msgid="934721068851885390">"Viněta"</string>
     <string name="redeye" msgid="4508883127049472069">"Červené oči"</string>
index bc5ed33..58fa4fd 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Automatisk farve"</string>
     <string name="hue" msgid="6231252147971086030">"Nuance"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Skygger"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kurver"</string>
     <string name="vignette" msgid="934721068851885390">"Vignet"</string>
     <string name="redeye" msgid="4508883127049472069">"Røde øjne"</string>
index 0c660bc..c6ce09d 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autom. Farbe"</string>
     <string name="hue" msgid="6231252147971086030">"Farbton"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Schatten"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kurven"</string>
     <string name="vignette" msgid="934721068851885390">"Vignettierung"</string>
     <string name="redeye" msgid="4508883127049472069">"Rote Augen"</string>
index 40acfdc..0f23e0f 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Αυτόματο χρώμα"</string>
     <string name="hue" msgid="6231252147971086030">"Απόχρωση"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Σκιές"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Καμπύλες"</string>
     <string name="vignette" msgid="934721068851885390">"Βινιετάρισμα"</string>
     <string name="redeye" msgid="4508883127049472069">"Κόκκινα μάτια"</string>
index ad6e4e0..a1fba2f 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autocolour"</string>
     <string name="hue" msgid="6231252147971086030">"Hue"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Shadows"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curves"</string>
     <string name="vignette" msgid="934721068851885390">"Vignette"</string>
     <string name="redeye" msgid="4508883127049472069">"Red Eye"</string>
index e3a9a8e..96c7678 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Color autom."</string>
     <string name="hue" msgid="6231252147971086030">"Tono"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Sombras"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curvas"</string>
     <string name="vignette" msgid="934721068851885390">"Viñeta"</string>
     <string name="redeye" msgid="4508883127049472069">"Ojos rojos"</string>
index e3820fa..0f0d69e 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Color automático"</string>
     <string name="hue" msgid="6231252147971086030">"Tonalidad"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Sombras"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curvar"</string>
     <string name="vignette" msgid="934721068851885390">"Viñeta"</string>
     <string name="redeye" msgid="4508883127049472069">"Ojos rojos"</string>
index d7be6e5..f741463 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autom. värvid"</string>
     <string name="hue" msgid="6231252147971086030">"Värvitoon"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Varjud"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kõverad"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjett"</string>
     <string name="redeye" msgid="4508883127049472069">"Punasilmsus"</string>
index 3051612..30fb365 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"رنگ خودکار"</string>
     <string name="hue" msgid="6231252147971086030">"رنگ‌مایه"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"سایه‌ها"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"نمودارها"</string>
     <string name="vignette" msgid="934721068851885390">"محو لبه‌ها"</string>
     <string name="redeye" msgid="4508883127049472069">"قرمزی چشم"</string>
index 1c9491c..44ca6c3 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autom. värit"</string>
     <string name="hue" msgid="6231252147971086030">"Sävy"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Tummat alueet"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Valotuskäyrät"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjetti"</string>
     <string name="redeye" msgid="4508883127049472069">"Punasilmäisyys"</string>
index 868e61a..b6ffb6e 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Coloration auto"</string>
     <string name="hue" msgid="6231252147971086030">"Teinte"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Ombres"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Courbes"</string>
     <string name="vignette" msgid="934721068851885390">"Vignetage"</string>
     <string name="redeye" msgid="4508883127049472069">"Yeux rouges"</string>
index 88ac1a4..30d47c8 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"ऑटोकलर"</string>
     <string name="hue" msgid="6231252147971086030">"ह्यू"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"छाया"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"वक्र"</string>
     <string name="vignette" msgid="934721068851885390">"विनेट"</string>
     <string name="redeye" msgid="4508883127049472069">"रेड आई"</string>
index ee4b51d..99a3721 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Automatska boja"</string>
     <string name="hue" msgid="6231252147971086030">"Nijansa"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Sjenke"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Krivulje"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjeta"</string>
     <string name="redeye" msgid="4508883127049472069">"Crvene oči"</string>
index cb0a78e..f577e0b 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Automat. szín"</string>
     <string name="hue" msgid="6231252147971086030">"Színárnyalat"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Árnyékok"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Görbék"</string>
     <string name="vignette" msgid="934721068851885390">"Vignetta"</string>
     <string name="redeye" msgid="4508883127049472069">"Vörösszem"</string>
index eb30676..39f407f 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Warna Otomatis"</string>
     <string name="hue" msgid="6231252147971086030">"Rona"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Bayangan"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kurva"</string>
     <string name="vignette" msgid="934721068851885390">"Vinyet"</string>
     <string name="redeye" msgid="4508883127049472069">"Mata Merah"</string>
index 8de522c..ac4294a 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Colore autom."</string>
     <string name="hue" msgid="6231252147971086030">"Tonalità"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Ombre"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curve"</string>
     <string name="vignette" msgid="934721068851885390">"Vignetta"</string>
     <string name="redeye" msgid="4508883127049472069">"Occhi rossi"</string>
index 36f7a51..c740611 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"צבע אוטומטי"</string>
     <string name="hue" msgid="6231252147971086030">"גוון"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"צלליות"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"קימורים"</string>
     <string name="vignette" msgid="934721068851885390">"עמעום קצוות"</string>
     <string name="redeye" msgid="4508883127049472069">"עיניים אדומות"</string>
index 8ad69e1..289faef 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"自動色補正"</string>
     <string name="hue" msgid="6231252147971086030">"色彩"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"影"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"カーブ"</string>
     <string name="vignette" msgid="934721068851885390">"周辺減光"</string>
     <string name="redeye" msgid="4508883127049472069">"赤目処理"</string>
index 9a7005b..b2eb59a 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"자동 색상"</string>
     <string name="hue" msgid="6231252147971086030">"색조"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"그림자"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"곡선"</string>
     <string name="vignette" msgid="934721068851885390">"비네트"</string>
     <string name="redeye" msgid="4508883127049472069">"적목현상 없애기"</string>
index 06e6d28..819e668 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autom. spalva"</string>
     <string name="hue" msgid="6231252147971086030">"Atspalvis"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Šešėliai"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kreivės"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjetė"</string>
     <string name="redeye" msgid="4508883127049472069">"Raudonos akys"</string>
index 9ac6f7f..29e1afd 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Aut. krāsu pal."</string>
     <string name="hue" msgid="6231252147971086030">"Nokrāsa"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Ēnas"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Līknes"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjete"</string>
     <string name="redeye" msgid="4508883127049472069">"Sarkano acu ef."</string>
index ee7287c..d175a44 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autowarna"</string>
     <string name="hue" msgid="6231252147971086030">"Rona"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Bayang-bayang"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Lengkung"</string>
     <string name="vignette" msgid="934721068851885390">"Vignet"</string>
     <string name="redeye" msgid="4508883127049472069">"Mata Merah"</string>
index 3f0f06a..84cee67 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autofarger"</string>
     <string name="hue" msgid="6231252147971086030">"Nyanse"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Skygger"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kurver"</string>
     <string name="vignette" msgid="934721068851885390">"Vignettering"</string>
     <string name="redeye" msgid="4508883127049472069">"Røde øyne"</string>
index 8bbbe3b..1876a4a 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Auto-kleur"</string>
     <string name="hue" msgid="6231252147971086030">"Kleurschakering"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Schaduw"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curven"</string>
     <string name="vignette" msgid="934721068851885390">"Vervloeien"</string>
     <string name="redeye" msgid="4508883127049472069">"Rode ogen"</string>
index 48a45cc..a658e50 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autokolor"</string>
     <string name="hue" msgid="6231252147971086030">"Odcień"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Cienie"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Krzywe"</string>
     <string name="vignette" msgid="934721068851885390">"Winietowanie"</string>
     <string name="redeye" msgid="4508883127049472069">"Czerwone oczy"</string>
index e94bf45..9dd5bb8 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Cor automática"</string>
     <string name="hue" msgid="6231252147971086030">"Tonalidade"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Sombras"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curvas"</string>
     <string name="vignette" msgid="934721068851885390">"Vinheta"</string>
     <string name="redeye" msgid="4508883127049472069">"Olhos Vermelhos"</string>
index 8c80270..3ab7d02 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Cor automática"</string>
     <string name="hue" msgid="6231252147971086030">"Matiz"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Sombras"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curvas"</string>
     <string name="vignette" msgid="934721068851885390">"Vinheta"</string>
     <string name="redeye" msgid="4508883127049472069">"Olhos vermelhos"</string>
index 46be094..688965b 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Culoare auto."</string>
     <string name="hue" msgid="6231252147971086030">"Tonalitate"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Umbre"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Curbe"</string>
     <string name="vignette" msgid="934721068851885390">"Vignetare"</string>
     <string name="redeye" msgid="4508883127049472069">"Ochi roşii"</string>
index 42d6db5..cb69521 100644 (file)
@@ -45,7 +45,7 @@
     <string name="aspect5to7_effect" msgid="5122395569059384741">"5:7"</string>
     <string name="aspect7to5_effect" msgid="5780001758108328143">"7:5"</string>
     <string name="aspect9to16_effect" msgid="7740468012919660728">"16:9"</string>
-    <string name="aspectNone_effect" msgid="6263330561046574134">"Ð\9eÑ\80игинал"</string>
+    <string name="aspectNone_effect" msgid="6263330561046574134">"Ð\92Ñ\80Ñ\83Ñ\87нÑ\83Ñ\8e"</string>
     <!-- no translation found for aspectOriginal_effect (5678516555493036594) -->
     <skip />
     <string name="Fixed" msgid="8017376448916924565">"Постоянное"</string>
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Авторежим"</string>
     <string name="hue" msgid="6231252147971086030">"Оттенок"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Тени"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Кривые"</string>
     <string name="vignette" msgid="934721068851885390">"Виньет-ние"</string>
     <string name="redeye" msgid="4508883127049472069">"Красные глаза"</string>
index a49bd3c..3d57b44 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autom. farba"</string>
     <string name="hue" msgid="6231252147971086030">"Odtieň"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Tiene"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Krivky"</string>
     <string name="vignette" msgid="934721068851885390">"Vineta"</string>
     <string name="redeye" msgid="4508883127049472069">"Červené oči"</string>
index e0539e3..dbf2e4b 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Samodej. barva"</string>
     <string name="hue" msgid="6231252147971086030">"Odtenek"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Sence"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Krivulje"</string>
     <string name="vignette" msgid="934721068851885390">"Vinjeta"</string>
     <string name="redeye" msgid="4508883127049472069">"Rdeče oči"</string>
index d5837d9..af002bf 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Аутоматска боја"</string>
     <string name="hue" msgid="6231252147971086030">"Нијанса"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Сенке"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Криве"</string>
     <string name="vignette" msgid="934721068851885390">"Вињета"</string>
     <string name="redeye" msgid="4508883127049472069">"Црвене очи"</string>
index f43c7d2..12057c3 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autofärg"</string>
     <string name="hue" msgid="6231252147971086030">"Nyans"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Skuggor"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Kurvor"</string>
     <string name="vignette" msgid="934721068851885390">"Vignette"</string>
     <string name="redeye" msgid="4508883127049472069">"Röda ögon"</string>
index 9f26a05..9b5f243 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Rangi otomatiki"</string>
     <string name="hue" msgid="6231252147971086030">"Rangi"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Vivuli"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Pindo"</string>
     <string name="vignette" msgid="934721068851885390">"Vignete"</string>
     <string name="redeye" msgid="4508883127049472069">"Jicho Jekundu"</string>
index 1f86b14..f508315 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"ให้สีอัตโนมัติ"</string>
     <string name="hue" msgid="6231252147971086030">"สี"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"เงา"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"เส้นโค้ง"</string>
     <string name="vignette" msgid="934721068851885390">"วิกเน็ตต์"</string>
     <string name="redeye" msgid="4508883127049472069">"ตาแดง"</string>
index 8ea767a..fee5bfe 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Autocolor"</string>
     <string name="hue" msgid="6231252147971086030">"Hue"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Mga Shadow"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Mga Kurba"</string>
     <string name="vignette" msgid="934721068851885390">"Vignette"</string>
     <string name="redeye" msgid="4508883127049472069">"Red Eye"</string>
index cdc8189..d9808ae 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Otomatik Renk"</string>
     <string name="hue" msgid="6231252147971086030">"Hue"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Gölgeler"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Eğriler"</string>
     <string name="vignette" msgid="934721068851885390">"Vinyet"</string>
     <string name="redeye" msgid="4508883127049472069">"Kırmızı Göz"</string>
index a05ef93..e2f4d92 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Автоколір"</string>
     <string name="hue" msgid="6231252147971086030">"Тон"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Тіні"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Криві"</string>
     <string name="vignette" msgid="934721068851885390">"Віньєтка"</string>
     <string name="redeye" msgid="4508883127049472069">"Червоні очі"</string>
index 18f3440..18610cb 100644 (file)
@@ -22,4 +22,6 @@
     <style name="ActionBarTwoLineItem">
         <item name="android:background">?android:attr/activatedBackgroundIndicator</item>
     </style>
+    <style name="Theme.Photos.Gallery" parent="android:Theme.Holo.Light">
+    </style>
 </resources>
index 0f4c9d3..7e2d56b 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"Màu tự động"</string>
     <string name="hue" msgid="6231252147971086030">"Màu sắc"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Bóng"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Đồ thị màu"</string>
     <string name="vignette" msgid="934721068851885390">"Làm mờ nét ảnh"</string>
     <string name="redeye" msgid="4508883127049472069">"Mắt đỏ"</string>
index 8876ce8..958f009 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"自动调整色彩"</string>
     <string name="hue" msgid="6231252147971086030">"色调"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"阴影"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"曲线"</string>
     <string name="vignette" msgid="934721068851885390">"晕影"</string>
     <string name="redeye" msgid="4508883127049472069">"红眼"</string>
index 8f205fb..eb4526c 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"自動色彩校正"</string>
     <string name="hue" msgid="6231252147971086030">"色調"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"陰影"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"曲線"</string>
     <string name="vignette" msgid="934721068851885390">"暈影"</string>
     <string name="redeye" msgid="4508883127049472069">"紅眼"</string>
index 5798c10..5439804 100644 (file)
@@ -59,6 +59,8 @@
     <string name="wbalance" msgid="6346581563387083613">"I-Autocolor"</string>
     <string name="hue" msgid="6231252147971086030">"I-Hue"</string>
     <string name="shadow_recovery" msgid="3928572915300287152">"Izithunzi"</string>
+    <!-- no translation found for highlight_recovery (8262208470735204243) -->
+    <skip />
     <string name="curvesRGB" msgid="915010781090477550">"Ukugobeka"</string>
     <string name="vignette" msgid="934721068851885390">"I-Vignette"</string>
     <string name="redeye" msgid="4508883127049472069">"Iso elibomvu"</string>
index a845c34..3e9e355 100644 (file)
@@ -23,6 +23,8 @@
     <string name="cannot_load_image">Cannot load the image!</string>
     <!--  String displayed when showing the original image [CHAR LIMIT=NONE] -->
     <string name="original_picture_text">@string/original</string>
+    <!--  String displayed when setting the homepage wallpaper in the background [CHAR LIMIT=NONE] -->
+    <string name="setting_wallpaper">Setting wallpaper</string>
 
     <!--  generic strings -->
 
index fae3466..263b8b1 100644 (file)
@@ -1002,4 +1002,12 @@ CHAR LIMIT = NONE] -->
     <!-- Positive answer for first run dialog asking if the user wants to remember photo locations [CHAR LIMIT = 20] -->
     <string name="remember_location_yes">Yes</string>
 
+    <!-- Menu item to launch the camera app [CHAR LIMIT=25] -->
+    <string name="menu_camera">Camera</string>
+    <!-- Menu item to search for photos [CHAR LIMIT=25] -->
+    <string name="menu_search">Search</string>
+    <!-- Title for the all photos tab [CHAR LIMIT=25] -->
+    <string name="tab_photos">Photos</string>
+    <!-- Title for the albums tab [CHAR LIMIT=25] -->
+    <string name="tab_albums">Albums</string>
 </resources>
diff --git a/src/android/util/Pools.java b/src/android/util/Pools.java
new file mode 100644 (file)
index 0000000..40bab1e
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+/**
+ * Helper class for crating pools of objects. An example use looks like this:
+ * <pre>
+ * public class MyPooledClass {
+ *
+ *     private static final SynchronizedPool<MyPooledClass> sPool =
+ *             new SynchronizedPool<MyPooledClass>(10);
+ *
+ *     public static MyPooledClass obtain() {
+ *         MyPooledClass instance = sPool.acquire();
+ *         return (instance != null) ? instance : new MyPooledClass();
+ *     }
+ *
+ *     public void recycle() {
+ *          // Clear state if needed.
+ *          sPool.release(this);
+ *     }
+ *
+ *     . . .
+ * }
+ * </pre>
+ *
+ * @hide
+ */
+public final class Pools {
+
+    /**
+     * Interface for managing a pool of objects.
+     *
+     * @param <T> The pooled type.
+     */
+    public static interface Pool<T> {
+
+        /**
+         * @return An instance from the pool if such, null otherwise.
+         */
+        public T acquire();
+
+        /**
+         * Release an instance to the pool.
+         *
+         * @param instance The instance to release.
+         * @return Whether the instance was put in the pool.
+         *
+         * @throws IllegalStateException If the instance is already in the pool.
+         */
+        public boolean release(T instance);
+    }
+
+    private Pools() {
+        /* do nothing - hiding constructor */
+    }
+
+    /**
+     * Simple (non-synchronized) pool of objects.
+     *
+     * @param <T> The pooled type.
+     */
+    public static class SimplePool<T> implements Pool<T> {
+        private final Object[] mPool;
+
+        private int mPoolSize;
+
+        /**
+         * Creates a new instance.
+         *
+         * @param maxPoolSize The max pool size.
+         *
+         * @throws IllegalArgumentException If the max pool size is less than zero.
+         */
+        public SimplePool(int maxPoolSize) {
+            if (maxPoolSize <= 0) {
+                throw new IllegalArgumentException("The max pool size must be > 0");
+            }
+            mPool = new Object[maxPoolSize];
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public T acquire() {
+            if (mPoolSize > 0) {
+                final int lastPooledIndex = mPoolSize - 1;
+                T instance = (T) mPool[lastPooledIndex];
+                mPool[lastPooledIndex] = null;
+                mPoolSize--;
+                return instance;
+            }
+            return null;
+        }
+
+        @Override
+        public boolean release(T instance) {
+            if (isInPool(instance)) {
+                throw new IllegalStateException("Already in the pool!");
+            }
+            if (mPoolSize < mPool.length) {
+                mPool[mPoolSize] = instance;
+                mPoolSize++;
+                return true;
+            }
+            return false;
+        }
+
+        private boolean isInPool(T instance) {
+            for (int i = 0; i < mPoolSize; i++) {
+                if (mPool[i] == instance) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    /**
+     * Synchronized) pool of objects.
+     *
+     * @param <T> The pooled type.
+     */
+    public static class SynchronizedPool<T> extends SimplePool<T> {
+        private final Object mLock = new Object();
+
+        /**
+         * Creates a new instance.
+         *
+         * @param maxPoolSize The max pool size.
+         *
+         * @throws IllegalArgumentException If the max pool size is less than zero.
+         */
+        public SynchronizedPool(int maxPoolSize) {
+            super(maxPoolSize);
+        }
+
+        @Override
+        public T acquire() {
+            synchronized (mLock) {
+                return super.acquire();
+            }
+        }
+
+        @Override
+        public boolean release(T element) {
+            synchronized (mLock) {
+                return super.release(element);
+            }
+        }
+    }
+}
\ No newline at end of file
index 15f4046..e244d6b 100644 (file)
@@ -1586,10 +1586,9 @@ public class PhotoModule
         mCountDownView.cancelCountDown();
         // Close the camera now because other activities may need to use it.
         closeCamera();
-        if (mSurfaceTexture != null) {
-            ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
-            mSurfaceTexture = null;
-        }
+        // Release surface texture.
+        ((CameraScreenNail) mActivity.mCameraScreenNail).releaseSurfaceTexture();
+        mSurfaceTexture = null;
         resetScreenOn();
 
         // Clear UI.
index c9cce81..d960942 100644 (file)
@@ -38,9 +38,9 @@ import android.view.WindowManager;
 
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.ApiHelper;
-import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.DataManager;
 import com.android.gallery3d.data.MediaItem;
+import com.android.photos.data.GalleryBitmapPool;
 import com.android.gallery3d.ui.GLRoot;
 import com.android.gallery3d.ui.GLRootView;
 import com.android.gallery3d.util.LightCycleHelper.PanoramaViewHelper;
@@ -222,16 +222,10 @@ public class AbstractGalleryActivity extends Activity implements GalleryContext
         } finally {
             mGLRootView.unlockRenderThread();
         }
-        clearBitmapPool(MediaItem.getMicroThumbPool());
-        clearBitmapPool(MediaItem.getThumbPool());
-
+        GalleryBitmapPool.getInstance().clear();
         MediaItem.getBytesBufferPool().clear();
     }
 
-    private static void clearBitmapPool(BitmapPool pool) {
-        if (pool != null) pool.clear();
-    }
-
     @Override
     protected void onDestroy() {
         super.onDestroy();
index faff146..d409315 100644 (file)
@@ -23,7 +23,6 @@ import android.os.Message;
 
 import com.android.gallery3d.common.BitmapUtils;
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.ContentListener;
 import com.android.gallery3d.data.LocalMediaItem;
 import com.android.gallery3d.data.MediaItem;
@@ -550,8 +549,8 @@ public class PhotoDataAdapter implements PhotoPage.Model {
     }
 
     @Override
-    public Bitmap getTile(int level, int x, int y, int tileSize, BitmapPool pool) {
-        return mTileProvider.getTile(level, x, y, tileSize, pool);
+    public Bitmap getTile(int level, int x, int y, int tileSize) {
+        return mTileProvider.getTile(level, x, y, tileSize);
     }
 
     @Override
diff --git a/src/com/android/gallery3d/data/BitmapPool.java b/src/com/android/gallery3d/data/BitmapPool.java
deleted file mode 100644 (file)
index 5bc6d67..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.data;
-
-import android.graphics.Bitmap;
-
-import com.android.gallery3d.common.Utils;
-
-import java.util.ArrayList;
-
-public class BitmapPool {
-    @SuppressWarnings("unused")
-    private static final String TAG = "BitmapPool";
-
-    private final ArrayList<Bitmap> mPool;
-    private final int mPoolLimit;
-
-    // mOneSize is true if the pool can only cache Bitmap with one size.
-    private final boolean mOneSize;
-    private final int mWidth, mHeight;  // only used if mOneSize is true
-
-    // Construct a BitmapPool which caches bitmap with the specified size.
-    public BitmapPool(int width, int height, int poolLimit) {
-        mWidth = width;
-        mHeight = height;
-        mPoolLimit = poolLimit;
-        mPool = new ArrayList<Bitmap>(poolLimit);
-        mOneSize = true;
-    }
-
-    // Construct a BitmapPool which caches bitmap with any size;
-    public BitmapPool(int poolLimit) {
-        mWidth = -1;
-        mHeight = -1;
-        mPoolLimit = poolLimit;
-        mPool = new ArrayList<Bitmap>(poolLimit);
-        mOneSize = false;
-    }
-
-    // Get a Bitmap from the pool.
-    public synchronized Bitmap getBitmap() {
-        Utils.assertTrue(mOneSize);
-        int size = mPool.size();
-        return size > 0 ? mPool.remove(size - 1) : null;
-    }
-
-    // Get a Bitmap from the pool with the specified size.
-    public synchronized Bitmap getBitmap(int width, int height) {
-        Utils.assertTrue(!mOneSize);
-        for (int i = mPool.size() - 1; i >= 0; i--) {
-            Bitmap b = mPool.get(i);
-            if (b.getWidth() == width && b.getHeight() == height) {
-                return mPool.remove(i);
-            }
-        }
-        return null;
-    }
-
-    // Put a Bitmap into the pool, if the Bitmap has a proper size. Otherwise
-    // the Bitmap will be recycled. If the pool is full, an old Bitmap will be
-    // recycled.
-    public void recycle(Bitmap bitmap) {
-        if (bitmap == null) return;
-        if (mOneSize && ((bitmap.getWidth() != mWidth) ||
-                (bitmap.getHeight() != mHeight))) {
-            bitmap.recycle();
-            return;
-        }
-        synchronized (this) {
-            if (mPool.size() >= mPoolLimit) mPool.remove(0);
-            mPool.add(bitmap);
-        }
-    }
-
-    public synchronized void clear() {
-        mPool.clear();
-    }
-
-    public boolean isOneSize() {
-        return mOneSize;
-    }
-}
index 8fcf400..38865e9 100644 (file)
 
 package com.android.gallery3d.data;
 
+import android.content.Context;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Handler;
 
 import com.android.gallery3d.app.GalleryApp;
 import com.android.gallery3d.app.StitchingChangeListener;
-import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.common.Utils;
 import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
 import com.android.gallery3d.data.MediaSet.ItemConsumer;
@@ -65,6 +65,11 @@ public class DataManager implements StitchingChangeListener {
     // to prevent concurrency issue.
     public static final Object LOCK = new Object();
 
+    public static DataManager from(Context context) {
+        GalleryApp app = (GalleryApp) context.getApplicationContext();
+        return app.getDataManager();
+    }
+
     private static final String TAG = "DataManager";
 
     // This is the path for the media set seen by the user at top level.
index 4d3c996..fa70915 100644 (file)
@@ -28,6 +28,7 @@ import android.util.FloatMath;
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.common.BitmapUtils;
 import com.android.gallery3d.common.Utils;
+import com.android.photos.data.GalleryBitmapPool;
 import com.android.gallery3d.ui.Log;
 import com.android.gallery3d.util.ThreadPool.CancelListener;
 import com.android.gallery3d.util.ThreadPool.JobContext;
@@ -246,21 +247,17 @@ public class DecodeUtils {
     }
 
     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-    public static Bitmap decode(JobContext jc, byte[] data, int offset,
-            int length, BitmapFactory.Options options, BitmapPool pool) {
-        if (pool == null) {
-            return decode(jc, data, offset, length, options);
-        }
-
+    public static Bitmap decodeUsingPool(JobContext jc, byte[] data, int offset,
+            int length, BitmapFactory.Options options) {
         if (options == null) options = new BitmapFactory.Options();
         if (options.inSampleSize < 1) options.inSampleSize = 1;
         options.inPreferredConfig = Bitmap.Config.ARGB_8888;
         options.inBitmap = (options.inSampleSize == 1)
-                ? findCachedBitmap(pool, jc, data, offset, length, options) : null;
+                ? findCachedBitmap(jc, data, offset, length, options) : null;
         try {
             Bitmap bitmap = decode(jc, data, offset, length, options);
             if (options.inBitmap != null && options.inBitmap != bitmap) {
-                pool.recycle(options.inBitmap);
+                GalleryBitmapPool.getInstance().put(options.inBitmap);
                 options.inBitmap = null;
             }
             return bitmap;
@@ -268,7 +265,7 @@ public class DecodeUtils {
             if (options.inBitmap == null) throw e;
 
             Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap");
-            pool.recycle(options.inBitmap);
+            GalleryBitmapPool.getInstance().put(options.inBitmap);
             options.inBitmap = null;
             return decode(jc, data, offset, length, options);
         }
@@ -277,21 +274,17 @@ public class DecodeUtils {
     // This is the same as the method above except the source data comes
     // from a file descriptor instead of a byte array.
     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
-    public static Bitmap decode(JobContext jc,
-            FileDescriptor fileDescriptor, Options options, BitmapPool pool) {
-        if (pool == null) {
-            return decode(jc, fileDescriptor, options);
-        }
-
+    public static Bitmap decodeUsingPool(JobContext jc,
+            FileDescriptor fileDescriptor, Options options) {
         if (options == null) options = new BitmapFactory.Options();
         if (options.inSampleSize < 1) options.inSampleSize = 1;
         options.inPreferredConfig = Bitmap.Config.ARGB_8888;
         options.inBitmap = (options.inSampleSize == 1)
-                ? findCachedBitmap(pool, jc, fileDescriptor, options) : null;
+                ? findCachedBitmap(jc, fileDescriptor, options) : null;
         try {
             Bitmap bitmap = DecodeUtils.decode(jc, fileDescriptor, options);
             if (options.inBitmap != null && options.inBitmap != bitmap) {
-                pool.recycle(options.inBitmap);
+                GalleryBitmapPool.getInstance().put(options.inBitmap);
                 options.inBitmap = null;
             }
             return bitmap;
@@ -299,23 +292,21 @@ public class DecodeUtils {
             if (options.inBitmap == null) throw e;
 
             Log.w(TAG, "decode fail with a given bitmap, try decode to a new bitmap");
-            pool.recycle(options.inBitmap);
+            GalleryBitmapPool.getInstance().put(options.inBitmap);
             options.inBitmap = null;
             return decode(jc, fileDescriptor, options);
         }
     }
 
-    private static Bitmap findCachedBitmap(BitmapPool pool, JobContext jc,
-            byte[] data, int offset, int length, Options options) {
-        if (pool.isOneSize()) return pool.getBitmap();
+    private static Bitmap findCachedBitmap(JobContext jc, byte[] data,
+            int offset, int length, Options options) {
         decodeBounds(jc, data, offset, length, options);
-        return pool.getBitmap(options.outWidth, options.outHeight);
+        return GalleryBitmapPool.getInstance().get(options.outWidth, options.outHeight);
     }
 
-    private static Bitmap findCachedBitmap(BitmapPool pool, JobContext jc,
-            FileDescriptor fileDescriptor, Options options) {
-        if (pool.isOneSize()) return pool.getBitmap();
+    private static Bitmap findCachedBitmap(JobContext jc, FileDescriptor fileDescriptor,
+            Options options) {
         decodeBounds(jc, fileDescriptor, options);
-        return pool.getBitmap(options.outWidth, options.outHeight);
+        return GalleryBitmapPool.getInstance().get(options.outWidth, options.outHeight);
     }
 }
index 3f937e3..4756149 100644 (file)
@@ -60,13 +60,11 @@ abstract class ImageCacheRequest implements Job<Bitmap> {
                 options.inPreferredConfig = Bitmap.Config.ARGB_8888;
                 Bitmap bitmap;
                 if (mType == MediaItem.TYPE_MICROTHUMBNAIL) {
-                    bitmap = DecodeUtils.decode(jc,
-                            buffer.data, buffer.offset, buffer.length, options,
-                            MediaItem.getMicroThumbPool());
+                    bitmap = DecodeUtils.decodeUsingPool(jc,
+                            buffer.data, buffer.offset, buffer.length, options);
                 } else {
-                    bitmap = DecodeUtils.decode(jc,
-                            buffer.data, buffer.offset, buffer.length, options,
-                            MediaItem.getThumbPool());
+                    bitmap = DecodeUtils.decodeUsingPool(jc,
+                            buffer.data, buffer.offset, buffer.length, options);
                 }
                 if (bitmap == null && !jc.isCancelled()) {
                     Log.w(TAG, "decode cached failed " + debugTag());
index 19084d4..59ea865 100644 (file)
@@ -42,15 +42,10 @@ public abstract class MediaItem extends MediaObject {
     private static final int BYTESBUFFER_SIZE = 200 * 1024;
 
     private static int sMicrothumbnailTargetSize = 200;
-    private static BitmapPool sMicroThumbPool;
     private static final BytesBufferPool sMicroThumbBufferPool =
             new BytesBufferPool(BYTESBUFFE_POOL_SIZE, BYTESBUFFER_SIZE);
 
     private static int sThumbnailTargetSize = 640;
-    private static final BitmapPool sThumbPool =
-            ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_FACTORY
-            ? new BitmapPool(4)
-            : null;
 
     // TODO: fix default value for latlng and change this.
     public static final double INVALID_LATLNG = 0f;
@@ -126,33 +121,14 @@ public abstract class MediaItem extends MediaObject {
         }
     }
 
-    public static BitmapPool getMicroThumbPool() {
-        if (ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_FACTORY && sMicroThumbPool == null) {
-            initializeMicroThumbPool();
-        }
-        return sMicroThumbPool;
-    }
-
-    public static BitmapPool getThumbPool() {
-        return sThumbPool;
-    }
-
     public static BytesBufferPool getBytesBufferPool() {
         return sMicroThumbBufferPool;
     }
 
-    private static void initializeMicroThumbPool() {
-        sMicroThumbPool =
-                ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_FACTORY
-                ? new BitmapPool(sMicrothumbnailTargetSize, sMicrothumbnailTargetSize, 16)
-                : null;
-    }
-
     public static void setThumbnailSizes(int size, int microSize) {
         sThumbnailTargetSize = size;
         if (sMicrothumbnailTargetSize != microSize) {
             sMicrothumbnailTargetSize = microSize;
-            initializeMicroThumbPool();
         }
     }
 }
index 8da8113..fd30ac0 100644 (file)
@@ -71,6 +71,7 @@ import com.android.gallery3d.filtershow.imageshow.ImageZoom;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.presets.ImagePreset;
 import com.android.gallery3d.filtershow.provider.SharedImageProvider;
+import com.android.gallery3d.filtershow.tools.BitmapTask;
 import com.android.gallery3d.filtershow.tools.SaveCopyTask;
 import com.android.gallery3d.filtershow.ui.FilterIconButton;
 import com.android.gallery3d.filtershow.ui.FramedTextButton;
@@ -151,6 +152,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
+        ImageFilter.setActivityForMemoryToasts(this);
         setResources();
 
         Resources res = getResources();
@@ -519,6 +521,9 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         if (mLoadBitmapTask != null) {
             mLoadBitmapTask.cancel(false);
         }
+        MasterImage.reset();
+        FilteringPipeline.reset();
+        ImageFilter.resetStatics();
         super.onDestroy();
     }
 
@@ -1089,11 +1094,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
                     mCropExtras.getOutputFormat(), this);
         }
         if (mSaveAsWallpaper) {
-            try {
-                WallpaperManager.getInstance(this).setBitmap(filtered);
-            } catch (IOException e) {
-                Log.w(LOGTAG, "fail to set wall paper", e);
-            }
+            setWallpaperInBackground(filtered);
         }
         if (mReturnAsExtra) {
             if (filtered != null) {
@@ -1116,6 +1117,28 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         }
     }
 
+    void setWallpaperInBackground(final Bitmap bmap) {
+        Toast.makeText(this, R.string.setting_wallpaper, Toast.LENGTH_LONG).show();
+        BitmapTask.Callbacks<FilterShowActivity> cb = new BitmapTask.Callbacks<FilterShowActivity>() {
+            @Override
+            public void onComplete(Bitmap result) {}
+
+            @Override
+            public void onCancel() {}
+
+            @Override
+            public Bitmap onExecute(FilterShowActivity param) {
+                try {
+                    WallpaperManager.getInstance(param).setBitmap(bmap);
+                } catch (IOException e) {
+                    Log.w(LOGTAG, "fail to set wall paper", e);
+                }
+                return null;
+            }
+        };
+        (new BitmapTask<FilterShowActivity>(cb)).execute(this);
+    }
+
     public void done() {
         if (mOutputted) {
             hideSavingProgress();
index cff07ff..5bda246 100644 (file)
@@ -375,11 +375,13 @@ public class PanelController implements OnClickListener {
         ImageShow image = null;
         mActivity.hideImageViews();
         for (View view : mImageViews) {
+            image = (ImageShow) view;
             if (view.getId() == id) {
                 view.setVisibility(View.VISIBLE);
-                image = (ImageShow) view;
+                image.select();
             } else {
                 view.setVisibility(View.GONE);
+                image.unselect();
             }
         }
         return image;
@@ -498,7 +500,7 @@ public class PanelController implements OnClickListener {
         }
         mUtilityPanel.hideAccessoryViews();
 
-        if (view instanceof FilterIconButton) {
+        if (view instanceof FilterIconButton && view.getId() != R.id.applyEffect) {
             mCurrentEditor = null;
             FilterIconButton component = (FilterIconButton) view;
             FilterRepresentation representation = component.getFilterRepresentation();
@@ -555,8 +557,8 @@ public class PanelController implements OnClickListener {
                 if (mCurrentImage instanceof ImageCrop && mUtilityPanel.firstTimeCropDisplayed) {
                     ((ImageCrop) mCurrentImage).clear();
                     mUtilityPanel.firstTimeCropDisplayed = false;
+                    ((ImageCrop) mCurrentImage).setFixedAspect(mFixedAspect);
                 }
-                ((ImageCrop) mCurrentImage).setFixedAspect(mFixedAspect);
                 break;
             }
             case R.id.rotateButton: {
index 419abe8..0af4063 100644 (file)
@@ -31,7 +31,7 @@ import com.android.gallery3d.filtershow.presets.ImagePreset;
 
 public class FilteringPipeline implements Handler.Callback {
 
-    private final static FilteringPipeline gPipeline = new FilteringPipeline();
+    private static FilteringPipeline sPipeline;
     private static final String LOGTAG = "FilteringPipeline";
     private ImagePreset mPreviousGeometryPreset = null;
     private ImagePreset mPreviousFiltersPreset = null;
@@ -48,6 +48,7 @@ public class FilteringPipeline implements Handler.Callback {
     private final static int NEW_RENDERING_REQUEST = 1;
     private final static int COMPUTE_PRESET = 2;
     private final static int COMPUTE_RENDERING_REQUEST = 3;
+    private final static int COMPUTE_PARTIAL_RENDERING_REQUEST = 4;
 
     private Handler mProcessingHandler = null;
     private final Handler mUIHandler = new Handler() {
@@ -81,7 +82,13 @@ public class FilteringPipeline implements Handler.Callback {
                 mUIHandler.sendMessage(uimsg);
                 break;
             }
-            case COMPUTE_RENDERING_REQUEST: {
+            case COMPUTE_RENDERING_REQUEST:
+            case COMPUTE_PARTIAL_RENDERING_REQUEST: {
+                if (msg.what == COMPUTE_PARTIAL_RENDERING_REQUEST) {
+                    if (mProcessingHandler.hasMessages(COMPUTE_PARTIAL_RENDERING_REQUEST)) {
+                        return false;
+                    }
+                }
                 RenderingRequest request = (RenderingRequest) msg.obj;
                 render(request);
                 Message uimsg = mUIHandler.obtainMessage(NEW_RENDERING_REQUEST);
@@ -95,6 +102,7 @@ public class FilteringPipeline implements Handler.Callback {
 
     private static float RESIZE_FACTOR = 0.8f;
     private static float MAX_PROCESS_TIME = 100; // in ms
+    private static long HIRES_DELAY = 100; // in ms
     private float mResizeFactor = 1.0f;
     private long mResizeTime = 0;
 
@@ -109,7 +117,10 @@ public class FilteringPipeline implements Handler.Callback {
     }
 
     public static FilteringPipeline getPipeline() {
-        return gPipeline;
+        if (sPipeline == null) {
+            sPipeline = new FilteringPipeline();
+        }
+        return sPipeline;
     }
 
     public synchronized void setOriginal(Bitmap bitmap) {
@@ -165,9 +176,17 @@ public class FilteringPipeline implements Handler.Callback {
         if (mOriginalAllocation == null) {
             return;
         }
-        Message msg = mProcessingHandler.obtainMessage(COMPUTE_RENDERING_REQUEST);
+        int type = COMPUTE_RENDERING_REQUEST;
+        if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
+            type = COMPUTE_PARTIAL_RENDERING_REQUEST;
+        }
+        Message msg = mProcessingHandler.obtainMessage(type);
         msg.obj = request;
-        mProcessingHandler.sendMessage(msg);
+        if (type == COMPUTE_PARTIAL_RENDERING_REQUEST) {
+            mProcessingHandler.sendMessageDelayed(msg, HIRES_DELAY);
+        } else {
+            mProcessingHandler.sendMessage(msg);
+        }
     }
 
     public synchronized void updatePreviewBuffer() {
@@ -210,11 +229,15 @@ public class FilteringPipeline implements Handler.Callback {
         if (request.getType() == RenderingRequest.GEOMETRY_RENDERING) {
             return "GEOMETRY_RENDERING";
         }
+        if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
+            return "PARTIAL_RENDERING";
+        }
         return "UNKNOWN TYPE!";
     }
 
     private void render(RenderingRequest request) {
-        if (request.getBitmap() == null
+        if ((request.getType() != RenderingRequest.PARTIAL_RENDERING
+                && request.getBitmap() == null)
                 || request.getImagePreset() == null) {
             return;
         }
@@ -225,11 +248,21 @@ public class FilteringPipeline implements Handler.Callback {
         Bitmap bitmap = request.getBitmap();
         ImagePreset preset = request.getImagePreset();
         setPresetParameters(preset);
+
+        if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
+            bitmap = MasterImage.getImage().getImageLoader().getScaleOneImageForPreset(null, preset, request.getBounds(), request.getDestination(), false);
+            if (bitmap == null) {
+                return;
+            }
+            bitmap = preset.applyGeometry(bitmap);
+        }
+
         if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
             FiltersManager.getManager().resetBitmapsRS();
         }
 
-        if (request.getType() != RenderingRequest.ICON_RENDERING) {
+        if (request.getType() != RenderingRequest.ICON_RENDERING
+                && request.getType() != RenderingRequest.PARTIAL_RENDERING) {
             updateOriginalAllocation(preset);
         }
         if (DEBUG) {
@@ -243,9 +276,11 @@ public class FilteringPipeline implements Handler.Callback {
         } else if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
             mFiltersOnlyOriginalAllocation.copyTo(bitmap);
         }
+
         if (request.getType() == RenderingRequest.FULL_RENDERING
                 || request.getType() == RenderingRequest.FILTERS_RENDERING
-                || request.getType() == RenderingRequest.ICON_RENDERING) {
+                || request.getType() == RenderingRequest.ICON_RENDERING
+                || request.getType() == RenderingRequest.PARTIAL_RENDERING) {
             Bitmap bmp = preset.apply(bitmap);
             request.setBitmap(bmp);
         }
@@ -305,4 +340,8 @@ public class FilteringPipeline implements Handler.Callback {
     public float getPreviewScaleFactor() {
         return mPreviewScaleFactor;
     }
+
+    public static void reset() {
+        sPipeline = null;
+    }
 }
index 42e72e6..b502a2f 100644 (file)
@@ -62,6 +62,8 @@ import java.io.OutputStream;
 import java.util.Vector;
 import java.util.concurrent.locks.ReentrantLock;
 
+
+// TODO: this class has waaaay to much bitmap copying.  Cleanup.
 public class ImageLoader {
 
     private static final String LOGTAG = "ImageLoader";
@@ -265,12 +267,12 @@ public class ImageLoader {
                 bitmap.getHeight(), matrix, true);
     }
 
-    private Bitmap loadRegionBitmap(Uri uri, Rect bounds) {
+    private Bitmap loadRegionBitmap(Uri uri, BitmapFactory.Options options, Rect bounds) {
         InputStream is = null;
         try {
             is = mContext.getContentResolver().openInputStream(uri);
             BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(is, false);
-            return decoder.decodeRegion(bounds, null);
+            return decoder.decodeRegion(bounds, options);
         } catch (FileNotFoundException e) {
             Log.e(LOGTAG, "FileNotFoundException: " + uri);
         } catch (Exception e) {
@@ -370,21 +372,36 @@ public class ImageLoader {
     // FIXME: this currently does the loading + filtering on the UI thread --
     // need to move this to a background thread.
     public Bitmap getScaleOneImageForPreset(ImageShow caller, ImagePreset imagePreset, Rect bounds,
-            boolean force) {
+                                            Rect destination, boolean force) {
         mLoadingLock.lock();
         Bitmap bmp = mZoomCache.getImage(imagePreset, bounds);
         if (force || bmp == null) {
-            bmp = loadRegionBitmap(mUri, bounds);
+            BitmapFactory.Options options = null;
+            if (destination != null) {
+                options = new BitmapFactory.Options();
+                if (bounds.width() > destination.width()) {
+                    int sampleSize = 1;
+                    int w = bounds.width();
+                    while (w > destination.width()) {
+                        sampleSize *= 2;
+                        w /= sampleSize;
+                    }
+                    options.inSampleSize = sampleSize;
+                }
+            }
+            bmp = loadRegionBitmap(mUri, options, bounds);
+            if (destination != null) {
+                mLoadingLock.unlock();
+                return bmp;
+            }
             if (bmp != null) {
-                // TODO: this workaround for RS might not be needed ultimately
-                Bitmap bmp2 = bmp.copy(Bitmap.Config.ARGB_8888, true);
                 float scaleFactor = imagePreset.getScaleFactor();
                 imagePreset.setScaleFactor(1.0f);
-                bmp2 = imagePreset.apply(bmp2);
+                bmp = imagePreset.apply(bmp);
                 imagePreset.setScaleFactor(scaleFactor);
-                mZoomCache.setImage(imagePreset, bounds, bmp2);
+                mZoomCache.setImage(imagePreset, bounds, bmp);
                 mLoadingLock.unlock();
-                return bmp2;
+                return bmp;
             }
         }
         mLoadingLock.unlock();
index 1e9f6b8..3ec74e2 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.gallery3d.filtershow.cache;
 
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import com.android.gallery3d.app.Log;
 import com.android.gallery3d.filtershow.imageshow.MasterImage;
 import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -27,29 +28,46 @@ public class RenderingRequest {
     private Bitmap mBitmap = null;
     private ImagePreset mImagePreset = null;
     private RenderingRequestCaller mCaller = null;
+    private Rect mBounds = null;
+    private Rect mDestination = null;
     private int mType = FULL_RENDERING;
-    public static int FULL_RENDERING = 0;
-    public static int FILTERS_RENDERING = 1;
-    public static int GEOMETRY_RENDERING = 2;
-    public static int ICON_RENDERING = 3;
+    public static final int FULL_RENDERING = 0;
+    public static final int FILTERS_RENDERING = 1;
+    public static final int GEOMETRY_RENDERING = 2;
+    public static final int ICON_RENDERING = 3;
+    public static final int PARTIAL_RENDERING = 4;
     private static final Bitmap.Config mConfig = Bitmap.Config.ARGB_8888;
 
+    public static void post(Bitmap source, ImagePreset preset, int type, RenderingRequestCaller caller) {
+        RenderingRequest.post(source, preset, type, caller, null, null);
+    }
+
     public static void post(Bitmap source, ImagePreset preset, int type,
-                            RenderingRequestCaller caller) {
-        if (source == null || preset == null || caller == null) {
+                            RenderingRequestCaller caller, Rect bounds, Rect destination) {
+        if ((type != PARTIAL_RENDERING && source == null) || preset == null || caller == null) {
             Log.v(LOGTAG, "something null: source: " + source + " or preset: " + preset + " or caller: " + caller);
             return;
         }
         RenderingRequest request = new RenderingRequest();
         Bitmap bitmap = null;
-        if (type == FULL_RENDERING || type == GEOMETRY_RENDERING || type == ICON_RENDERING) {
+        if (type == FULL_RENDERING
+                || type == GEOMETRY_RENDERING
+                || type == ICON_RENDERING) {
             bitmap = preset.applyGeometry(source);
-        } else {
+        } else if (type != PARTIAL_RENDERING) {
             bitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), mConfig);
         }
+
         request.setBitmap(bitmap);
         ImagePreset passedPreset = new ImagePreset(preset);
         passedPreset.setImageLoader(MasterImage.getImage().getImageLoader());
+
+        if (type == PARTIAL_RENDERING) {
+            request.setBounds(bounds);
+            request.setDestination(destination);
+            passedPreset.setPartialRendering(true, bounds);
+        }
+
         request.setImagePreset(passedPreset);
         request.setType(type);
         request.setCaller(caller);
@@ -103,4 +121,20 @@ public class RenderingRequest {
     public void setCaller(RenderingRequestCaller caller) {
         mCaller = caller;
     }
+
+    public Rect getBounds() {
+        return mBounds;
+    }
+
+    public void setBounds(Rect bounds) {
+        mBounds = bounds;
+    }
+
+    public Rect getDestination() {
+        return mDestination;
+    }
+
+    public void setDestination(Rect destination) {
+        mDestination = destination;
+    }
 }
index 6c83170..3511c67 100644 (file)
@@ -19,6 +19,7 @@ public class FilterCurvesRepresentation extends FilterRepresentation {
         setShowEditingControls(false);
         setShowParameterValue(false);
         setShowUtilityPanel(true);
+        setSupportsPartialRendering(true);
         for (int i = 0; i < mSplines.length; i++) {
             mSplines[i] = new Spline();
             mSplines[i].reset();
index e41f0a6..8b8504b 100644 (file)
@@ -150,6 +150,10 @@ public class FilterDrawRepresentation extends FilterRepresentation {
         mCurrent = null;
     }
 
+    public void clearCurrentSection() {
+        mCurrent = null;
+    }
+
     public void clear() {
         mCurrent = null;
         mDrawing.clear();
index 859bf32..d4128dc 100644 (file)
@@ -37,6 +37,7 @@ public class FilterFxRepresentation extends FilterRepresentation {
         setShowEditingControls(false);
         setShowParameterValue(false);
         setShowUtilityPanel(false);
+        setSupportsPartialRendering(true);
     }
 
     public String toString() {
index 513cdcd..83f2a1b 100644 (file)
@@ -25,6 +25,7 @@ public class FilterRepresentation implements Cloneable {
     private String mName;
     private int mPriority = TYPE_NORMAL;
     private Class mFilterClass;
+    private boolean mSupportsPartialRendering = false;
     private int mTextId = 0;
     private int mEditorId = BasicEditor.ID;
     private int mButtonId = 0;
@@ -52,6 +53,7 @@ public class FilterRepresentation implements Cloneable {
         representation.setName(getName());
         representation.setPriority(getPriority());
         representation.setFilterClass(getFilterClass());
+        representation.setSupportsPartialRendering(supportsPartialRendering());
         representation.setTextId(getTextId());
         representation.setEditorId(getEditorId());
         representation.setButtonId(getButtonId());
@@ -70,6 +72,7 @@ public class FilterRepresentation implements Cloneable {
         if (representation.mFilterClass == representation.mFilterClass
                 && representation.mName.equalsIgnoreCase(mName)
                 && representation.mPriority == mPriority
+                && representation.mSupportsPartialRendering == mSupportsPartialRendering
                 && representation.mTextId == mTextId
                 && representation.mEditorId == mEditorId
                 && representation.mButtonId == mButtonId
@@ -106,6 +109,14 @@ public class FilterRepresentation implements Cloneable {
         return false;
     }
 
+    public boolean supportsPartialRendering() {
+        return mSupportsPartialRendering;
+    }
+
+    public void setSupportsPartialRendering(boolean value) {
+        mSupportsPartialRendering = value;
+    }
+
     public void useParametersFrom(FilterRepresentation a) {
     }
 
index 614c6a0..1b7a367 100644 (file)
@@ -18,8 +18,10 @@ package com.android.gallery3d.filtershow.filters;
 
 import android.graphics.Bitmap;
 import android.graphics.Matrix;
+import android.widget.Toast;
 
 import com.android.gallery3d.R;
+import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.editors.BasicEditor;
 import com.android.gallery3d.filtershow.imageshow.GeometryMetadata;
 import com.android.gallery3d.filtershow.presets.ImagePreset;
@@ -31,6 +33,30 @@ public abstract class ImageFilter implements Cloneable {
     protected String mName = "Original";
     private final String LOGTAG = "ImageFilter";
 
+    // TODO: Temporary, for dogfood note memory issues with toasts for better
+    // feedback. Remove this when filters actually work in low memory
+    // situations.
+    private static FilterShowActivity sActivity = null;
+
+    public static void setActivityForMemoryToasts(FilterShowActivity activity) {
+        sActivity = activity;
+    }
+
+    public static void resetStatics() {
+        sActivity = null;
+    }
+
+    public void displayLowMemoryToast() {
+        if (sActivity != null) {
+            sActivity.runOnUiThread(new Runnable() {
+                public void run() {
+                    Toast.makeText(sActivity, "Memory too low for filter " + getName() +
+                            ", please file a bug report", Toast.LENGTH_SHORT).show();
+                }
+            });
+        }
+    }
+
     public void setName(String name) {
         mName = name;
     }
@@ -45,8 +71,8 @@ public abstract class ImageFilter implements Cloneable {
     }
 
     /**
-     * Called on small bitmaps to create button icons for each filter.
-     * Override this to provide filter-specific button icons.
+     * Called on small bitmaps to create button icons for each filter. Override
+     * this to provide filter-specific button icons.
      */
     public Bitmap iconApply(Bitmap bitmap, float scaleFactor, int quality) {
         return apply(bitmap, scaleFactor, quality);
index c92ac01..a4626cd 100644 (file)
@@ -36,6 +36,7 @@ public class ImageFilterBwFilter extends SimpleImageFilter {
         representation.setMinimum(-180);
         representation.setTextId(R.string.bwfilter);
         representation.setButtonId(R.id.bwfilterButton);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 2f94e3d..2097f0d 100644 (file)
@@ -37,6 +37,7 @@ public class ImageFilterContrast extends SimpleImageFilter {
         representation.setMinimum(-100);
         representation.setMaximum(100);
         representation.setDefaultValue(0);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 55c7095..46a9a29 100644 (file)
@@ -32,6 +32,7 @@ public class ImageFilterEdge extends SimpleImageFilter {
         representation.setFilterClass(ImageFilterEdge.class);
         representation.setTextId(R.string.edge);
         representation.setButtonId(R.id.edgeButton);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 7a8df71..b0b0b2d 100644 (file)
@@ -36,6 +36,7 @@ public class ImageFilterExposure extends SimpleImageFilter {
         representation.setMinimum(-100);
         representation.setMaximum(100);
         representation.setDefaultValue(0);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index b9a1465..12f032d 100644 (file)
@@ -41,6 +41,7 @@ public class ImageFilterHighlights extends SimpleImageFilter {
         representation.setMinimum(-100);
         representation.setMaximum(100);
         representation.setDefaultValue(0);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 8c484c7..b1f9f73 100644 (file)
@@ -39,6 +39,7 @@ public class ImageFilterHue extends SimpleImageFilter {
         representation.setTextId(R.string.hue);
         representation.setButtonId(R.id.hueButton);
         representation.setEditorId(BasicEditor.ID);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index f48bd04..6f785ef 100644 (file)
@@ -45,6 +45,7 @@ public class ImageFilterKMeans extends SimpleImageFilter {
         representation.setPreviewValue(4);
         representation.setTextId(R.string.kmeans);
         representation.setButtonId(R.id.kmeansButton);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 841c5c9..c256686 100644 (file)
@@ -19,6 +19,7 @@ public class ImageFilterNegative extends ImageFilter {
         representation.setShowEditingControls(false);
         representation.setShowParameterValue(false);
         representation.setEditorId(ImageOnlyEditor.ID);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index d529790..a3467ed 100644 (file)
@@ -43,9 +43,11 @@ public abstract class ImageFilterRS extends ImageFilter {
                 || (bitmap.getHeight() != sOldBitmap.getHeight())) {
             if (mInPixelsAllocation != null) {
                 mInPixelsAllocation.destroy();
+                mInPixelsAllocation = null;
             }
             if (mOutPixelsAllocation != null) {
                 mOutPixelsAllocation.destroy();
+                mOutPixelsAllocation = null;
             }
             Bitmap bitmapBuffer = bitmap.copy(mBitmapConfig, true);
             mOutPixelsAllocation = Allocation.createFromBitmap(mRS, bitmapBuffer,
@@ -87,6 +89,11 @@ public abstract class ImageFilterRS extends ImageFilter {
             Log.e(LOGTAG, "Illegal argument? " + e);
         } catch (android.renderscript.RSRuntimeException e) {
             Log.e(LOGTAG, "RS runtime exception ? " + e);
+        } catch (java.lang.OutOfMemoryError e) {
+            // Many of the renderscript filters allocated large (>16Mb resources) in order to apply.
+            System.gc();
+            displayLowMemoryToast();
+            Log.e(LOGTAG, "not enough memory for filter " + getName(), e);
         }
         return bitmap;
     }
index 6cd8332..0febe49 100644 (file)
@@ -37,6 +37,7 @@ public class ImageFilterSaturated extends SimpleImageFilter {
         representation.setMinimum(-100);
         representation.setMaximum(100);
         representation.setDefaultValue(0);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index e178239..fd67ee8 100644 (file)
@@ -37,6 +37,7 @@ public class ImageFilterShadows extends SimpleImageFilter {
         representation.setMinimum(-100);
         representation.setMaximum(100);
         representation.setDefaultValue(0);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 9c99d57..8afa474 100644 (file)
@@ -37,7 +37,8 @@ public class ImageFilterSharpen extends ImageFilterRS {
         representation.setTextId(R.string.sharpness);
         representation.setButtonId(R.id.sharpenButton);
         representation.setOverlayId(R.drawable.filtershow_button_colors_sharpen);
-        representation.setEditorId(R.id.imageZoom);
+        representation.setEditorId(R.id.imageShow);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index a57af71..ea315d3 100644 (file)
@@ -36,6 +36,7 @@ public class ImageFilterVibrance extends SimpleImageFilter {
         representation.setMinimum(-100);
         representation.setMaximum(100);
         representation.setDefaultValue(0);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index 2f48523..c4c293a 100644 (file)
@@ -37,6 +37,7 @@ public class ImageFilterWBalance extends ImageFilter {
         representation.setShowEditingControls(false);
         representation.setShowParameterValue(false);
         representation.setEditorId(ImageOnlyEditor.ID);
+        representation.setSupportsPartialRendering(true);
         return representation;
     }
 
index c46baf5..479652c 100644 (file)
@@ -27,11 +27,13 @@ public class ImageDraw extends ImageShow {
     public ImageDraw(Context context, AttributeSet attrs) {
         super(context, attrs);
         resetParameter();
+        super.setOriginalDisabled(true);
     }
 
     public ImageDraw(Context context) {
         super(context);
         resetParameter();
+        super.setOriginalDisabled(true);
     }
 
     public void setEditor(EditorDraw editorDraw) {
@@ -82,27 +84,32 @@ public class ImageDraw extends ImageShow {
     float[] mTmpPoint = new float[2]; // so we do not malloc
     @Override
     public boolean onTouchEvent(MotionEvent event) {
-        super.onTouchEvent(event);
-
-        if (event.getPointerCount() != 1) {
-            return true;
+        boolean ret = super.onTouchEvent(event);
+        if (event.getPointerCount() > 1) {
+            if (mFRep.getCurrentDrawing() != null) {
+                mFRep.clearCurrentSection();
+                mEditorDraw.commitLocalRepresentation();
+            }
+            return ret;
         }
-
-        if (didFinishScalingOperation()) {
-            return true;
+        if (event.getAction() != MotionEvent.ACTION_DOWN) {
+            if (mFRep.getCurrentDrawing() == null) {
+                return ret;
+            }
         }
 
         ImageFilterDraw filter = (ImageFilterDraw) getCurrentFilter();
 
         if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            calcScreenMapping();
             mTmpPoint[0] = event.getX();
             mTmpPoint[1] = event.getY();
             mToOrig.mapPoints(mTmpPoint);
             mFRep.startNewSection(mType, mCurrentColor, mCurrentSize, mTmpPoint[0], mTmpPoint[1]);
-
         }
 
         if (event.getAction() == MotionEvent.ACTION_MOVE) {
+
             int historySize = event.getHistorySize();
             final int pointerCount = event.getPointerCount();
             for (int h = 0; h < historySize; h++) {
@@ -127,19 +134,10 @@ public class ImageDraw extends ImageShow {
         return true;
     }
 
-    Matrix mRotateToScreen;
-    Matrix mToScreen;
-    Matrix mToOrig = new Matrix();
+    Matrix mRotateToScreen = new Matrix();
+    Matrix mToOrig;
     private void calcScreenMapping() {
-
-        GeometryMetadata geo = getImagePreset().mGeoData;
-        mToScreen = geo.getOriginalToScreen(false,
-                mImageLoader.getOriginalBounds().width(),
-                mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
-        mRotateToScreen = geo.getOriginalToScreen(true,
-                mImageLoader.getOriginalBounds().width(),
-                mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
-        mRotateToScreen.invert(mToOrig);
+        mToOrig = getScreenToImageMatrix(true);
         mToOrig.invert(mRotateToScreen);
     }
 
index 94c9ae1..39e0cc8 100644 (file)
@@ -21,6 +21,7 @@ import android.graphics.*;
 import android.net.Uri;
 import android.os.Handler;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.*;
 import android.view.GestureDetector.OnDoubleTapListener;
 import android.view.GestureDetector.OnGestureListener;
@@ -29,6 +30,7 @@ import android.widget.LinearLayout;
 import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.PanelController;
 import com.android.gallery3d.filtershow.cache.ImageLoader;
+import com.android.gallery3d.filtershow.cache.RenderingRequestCaller;
 import com.android.gallery3d.filtershow.filters.ImageFilter;
 import com.android.gallery3d.filtershow.presets.ImagePreset;
 
@@ -55,7 +57,7 @@ public class ImageShow extends View implements OnGestureListener,
     private ScaleGestureDetector mScaleGestureDetector = null;
 
     protected Rect mImageBounds = new Rect();
-
+    private boolean mOriginalDisabled = false;
     private boolean mTouchShowOriginal = false;
     private long mTouchShowOriginalDate = 0;
     private final long mTouchShowOriginalDelayMin = 200; // 200ms
@@ -228,6 +230,10 @@ public class ImageShow extends View implements OnGestureListener,
      */
     protected Matrix getImageToScreenMatrix(boolean reflectRotation) {
         GeometryMetadata geo = getImagePreset().mGeoData;
+        if (geo == null || mImageLoader == null
+                || mImageLoader.getOriginalBounds() == null) {
+            return new Matrix();
+        }
         Matrix m = geo.getOriginalToScreen(reflectRotation,
                 mImageLoader.getOriginalBounds().width(),
                 mImageLoader.getOriginalBounds().height(), getWidth(), getHeight());
@@ -290,7 +296,7 @@ public class ImageShow extends View implements OnGestureListener,
 
     @Override
     public void onDraw(Canvas canvas) {
-
+        MasterImage.getImage().setImageShowSize(getWidth(), getHeight());
         canvas.save();
         // TODO: center scale on gesture
         float cx = canvas.getWidth()/2.0f;
@@ -314,6 +320,12 @@ public class ImageShow extends View implements OnGestureListener,
                     1.5f * mTextPadding, mPaint);
         }
 
+        Bitmap partialPreview = MasterImage.getImage().getPartialImage();
+        if (partialPreview != null) {
+            Rect src = new Rect(0, 0, partialPreview.getWidth(), partialPreview.getHeight());
+            Rect dest = new Rect(0, 0, getWidth(), getHeight());
+            canvas.drawBitmap(partialPreview, src, dest, mPaint);
+        }
         drawToast(canvas);
     }
 
@@ -503,6 +515,14 @@ public class ImageShow extends View implements OnGestureListener,
         return mScaleGestureDetector.isInProgress();
     }
 
+    protected boolean isOriginalDisabled() {
+        return mOriginalDisabled;
+    }
+
+    protected void setOriginalDisabled(boolean originalDisabled) {
+        mOriginalDisabled = originalDisabled;
+    }
+
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         super.onTouchEvent(event);
@@ -538,8 +558,9 @@ public class ImageShow extends View implements OnGestureListener,
                     Point translation = MasterImage.getImage().getTranslation();
                     translation.x = (int) (originalTranslation.x + translateX);
                     translation.y = (int) (originalTranslation.y + translateY);
+                    MasterImage.getImage().setTranslation(translation);
                 }
-            } else if (!mActivity.isShowingHistoryPanel()
+            } else if (!mOriginalDisabled && !mActivity.isShowingHistoryPanel()
                     && (System.currentTimeMillis() - mTouchShowOriginalDate
                             > mTouchShowOriginalDelayMin)
                     && event.getPointerCount() == 1) {
@@ -593,6 +614,9 @@ public class ImageShow extends View implements OnGestureListener,
 
     @Override
     public boolean onFling(MotionEvent startEvent, MotionEvent endEvent, float arg2, float arg3) {
+        if (mActivity == null) {
+            return false;
+        }
         if ((!mActivity.isShowingHistoryPanel() && startEvent.getX() > endEvent.getX())
                 || (mActivity.isShowingHistoryPanel() && endEvent.getX() > startEvent.getX())) {
             if (!mTouchShowOriginal
@@ -670,4 +694,5 @@ public class ImageShow extends View implements OnGestureListener,
         }
         return false;
     }
+
 }
index 729ac7f..a51d102 100644 (file)
@@ -20,6 +20,7 @@ import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Matrix;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.gallery3d.filtershow.editors.EditorVignette;
@@ -48,16 +49,28 @@ public class ImageVignette extends ImageShow {
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         int mask = event.getActionMasked();
-        if (MotionEvent.ACTION_UP == mask) {
-            mActiveHandle = -1;
-        }
-        if (MotionEvent.ACTION_DOWN == mask && event.getPointerCount() == 1) {
-            mActiveHandle = mElipse.getCloseHandle(event.getX(), event.getY());
-        }
-        if (mActiveHandle == -1 || event.getPointerCount() > 1) {
-            mActiveHandle = -1;
-
-            return super.onTouchEvent(event);
+        if (mActiveHandle == -1) {
+            if (MotionEvent.ACTION_DOWN != mask) {
+                return super.onTouchEvent(event);
+            }
+            if (event.getPointerCount() == 1) {
+                mActiveHandle = mElipse.getCloseHandle(event.getX(), event.getY());
+            }
+            if (mActiveHandle == -1) {
+                return super.onTouchEvent(event);
+            }
+        } else {
+            switch (mask) {
+                case MotionEvent.ACTION_UP:
+                    mActiveHandle = -1;
+                    break;
+                case MotionEvent.ACTION_DOWN:
+                    if (event.getPointerCount() == 1) {
+                        Log.v(LOGTAG, "################### ACTION_DOWN odd " + mActiveHandle
+                                + " touches=1");
+                    }
+                    break;
+            }
         }
         float x = event.getX();
         float y = event.getY();
@@ -121,9 +134,7 @@ public class ImageVignette extends ImageShow {
     @Override
     public void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-        if (mElipse.isUndefined()) {
-            setRepresentation(mVignetteRep);
-        }
+        setRepresentation(mVignetteRep);
         mElipse.draw(canvas);
 
     }
index 0e32dc1..eb568c3 100644 (file)
@@ -90,7 +90,7 @@ public class ImageZoom extends ImageShow {
         Bitmap filteredImage = null;
         if ((mZoomedIn || mTouchDown) && mImageLoader != null) {
             filteredImage = mImageLoader.getScaleOneImageForPreset(this, getImagePreset(),
-                    mZoomBounds, false);
+                    mZoomBounds, null, false);
         } else {
             filteredImage = getFilteredImage();
         }
index 905d2c3..9eafe22 100644 (file)
@@ -16,9 +16,7 @@
 
 package com.android.gallery3d.filtershow.imageshow;
 
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.RectF;
+import android.graphics.*;
 
 import com.android.gallery3d.filtershow.FilterShowActivity;
 import com.android.gallery3d.filtershow.HistoryAdapter;
@@ -45,6 +43,7 @@ public class MasterImage implements RenderingRequestCaller {
 
     private Bitmap mGeometryOnlyBitmap = null;
     private Bitmap mFiltersOnlyBitmap = null;
+    private Bitmap mPartialBitmap = null;
 
     private ImageLoader mLoader = null;
     private HistoryAdapter mHistory = null;
@@ -62,6 +61,8 @@ public class MasterImage implements RenderingRequestCaller {
     private Point mTranslation = new Point();
     private Point mOriginalTranslation = new Point();
 
+    private Point mImageShowSize = new Point();
+
     private MasterImage() {
     }
 
@@ -193,6 +194,10 @@ public class MasterImage implements RenderingRequestCaller {
         return mGeometryOnlyBitmap;
     }
 
+    public Bitmap getPartialImage() {
+        return mPartialBitmap;
+    }
+
     public void notifyObservers() {
         for (ImageShow observer : mObservers) {
             observer.invalidate();
@@ -221,6 +226,7 @@ public class MasterImage implements RenderingRequestCaller {
             }
         }
         invalidatePreview();
+        needsUpdateFullResPreview();
         mActivity.enableSave(hasModifications());
     }
 
@@ -237,11 +243,67 @@ public class MasterImage implements RenderingRequestCaller {
         updatePresets(false);
     }
 
+    public void invalidatePartialPreview() {
+        if (mPartialBitmap != null) {
+            mPartialBitmap = null;
+            notifyObservers();
+        }
+    }
+
     public void invalidatePreview() {
         mFilteredPreview.invalidate();
+        invalidatePartialPreview();
+        needsUpdateFullResPreview();
         FilteringPipeline.getPipeline().updatePreviewBuffer();
     }
 
+    public void setImageShowSize(int w, int h) {
+        if (mImageShowSize.x != w || mImageShowSize.y != h) {
+            mImageShowSize.set(w, h);
+            needsUpdateFullResPreview();
+        }
+    }
+
+    private Matrix getImageToScreenMatrix(boolean reflectRotation) {
+        GeometryMetadata geo = mPreset.mGeoData;
+        if (geo == null || mLoader == null
+                || mLoader.getOriginalBounds() == null
+                || mImageShowSize.x == 0) {
+            return new Matrix();
+        }
+        Matrix m = geo.getOriginalToScreen(reflectRotation,
+                mLoader.getOriginalBounds().width(),
+                mLoader.getOriginalBounds().height(), mImageShowSize.x, mImageShowSize.y);
+        Point translate = getTranslation();
+        float scaleFactor = getScaleFactor();
+        m.postTranslate(translate.x, translate.y);
+        m.postScale(scaleFactor, scaleFactor, mImageShowSize.x/2.0f, mImageShowSize.y/2.0f);
+        return m;
+    }
+
+    private Matrix getScreenToImageMatrix(boolean reflectRotation) {
+        Matrix m = getImageToScreenMatrix(reflectRotation);
+        Matrix invert = new Matrix();
+        m.invert(invert);
+        return invert;
+    }
+
+    public void needsUpdateFullResPreview() {
+        if (!mPreset.canDoPartialRendering()) {
+            invalidatePartialPreview();
+            return;
+        }
+        Matrix m = getScreenToImageMatrix(true);
+        RectF r = new RectF(0, 0, mImageShowSize.x, mImageShowSize.y);
+        RectF dest = new RectF();
+        m.mapRect(dest, r);
+        Rect bounds = new Rect();
+        dest.roundOut(bounds);
+        RenderingRequest.post(null, mPreset, RenderingRequest.PARTIAL_RENDERING,
+                this, bounds, new Rect(0, 0, mImageShowSize.x, mImageShowSize.y));
+        invalidatePartialPreview();
+    }
+
     @Override
     public void available(RenderingRequest request) {
         if (request.getBitmap() == null) {
@@ -253,6 +315,10 @@ public class MasterImage implements RenderingRequestCaller {
         if (request.getType() == RenderingRequest.FILTERS_RENDERING) {
             mFiltersOnlyBitmap = request.getBitmap();
         }
+        if (request.getType() == RenderingRequest.PARTIAL_RENDERING) {
+            mPartialBitmap = request.getBitmap();
+            notifyObservers();
+        }
     }
 
     public static void reset() {
@@ -275,6 +341,7 @@ public class MasterImage implements RenderingRequestCaller {
 
     public void setScaleFactor(float scaleFactor) {
         mScaleFactor = scaleFactor;
+        needsUpdateFullResPreview();
     }
 
     public Point getTranslation() {
@@ -282,7 +349,9 @@ public class MasterImage implements RenderingRequestCaller {
     }
 
     public void setTranslation(Point translation) {
-        mTranslation = translation;
+        mTranslation.x = translation.x;
+        mTranslation.y = translation.y;
+        needsUpdateFullResPreview();
     }
 
     public Point getOriginalTranslation() {
@@ -297,5 +366,6 @@ public class MasterImage implements RenderingRequestCaller {
     public void resetTranslation() {
         mTranslation.x = 0;
         mTranslation.y = 0;
+        needsUpdateFullResPreview();
     }
 }
index 84266c5..ae5a034 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.gallery3d.filtershow.presets;
 
 import android.graphics.Bitmap;
+import android.graphics.Rect;
 import android.util.Log;
 
 import com.android.gallery3d.filtershow.ImageStateAdapter;
@@ -52,6 +53,8 @@ public class ImagePreset {
     private boolean mDoApplyFilters = true;
 
     public final GeometryMetadata mGeoData = new GeometryMetadata();
+    private boolean mPartialRendering = false;
+    private Rect mPartialRenderingBounds;
 
     public ImagePreset() {
         setup();
@@ -421,6 +424,22 @@ public class ImagePreset {
         return bitmap;
     }
 
+    public boolean canDoPartialRendering() {
+        if (mGeoData.hasModifications()) {
+            return false;
+        }
+        for (int i = 0; i < mFilters.size(); i++) {
+            FilterRepresentation representation = null;
+            synchronized (mFilters) {
+                representation = mFilters.elementAt(i);
+            }
+            if (!representation.supportsPartialRendering()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
     public void fillImageStateAdapter(ImageStateAdapter imageStateAdapter) {
         if (imageStateAdapter == null) {
             return;
@@ -446,4 +465,17 @@ public class ImagePreset {
     public void setScaleFactor(float value) {
         mScaleFactor = value;
     }
+
+    public void setPartialRendering(boolean partialRendering, Rect bounds) {
+        mPartialRendering = partialRendering;
+        mPartialRenderingBounds = bounds;
+    }
+
+    public boolean isPartialRendering() {
+        return mPartialRendering;
+    }
+
+    public Rect getPartialRenderingBounds() {
+        return mPartialRenderingBounds;
+    }
 }
index 5e0ca0b..fa421e7 100644 (file)
@@ -187,7 +187,6 @@ public class IngestService extends Service implements ImportTask.Listener,
     public void deviceRemoved(MtpDevice device) {
         if (device == mDevice) {
             setDevice(null);
-            MtpBitmapFetch.onDeviceDisconnected(device);
         }
     }
 
index 46a2051..5e1fb34 100644 (file)
@@ -25,19 +25,14 @@ import android.util.DisplayMetrics;
 import android.view.WindowManager;
 
 import com.android.camera.Exif;
-import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-
-import java.util.ArrayList;
+import com.android.photos.data.GalleryBitmapPool;
 
 public class MtpBitmapFetch {
-    private static final int BITMAP_POOL_SIZE = 32;
-    private static BitmapPool sThumbnailPool = new BitmapPool(BITMAP_POOL_SIZE);
     private static int sMaxSize = 0;
 
     public static void recycleThumbnail(Bitmap b) {
         if (b != null) {
-            sThumbnailPool.recycle(b);
+            GalleryBitmapPool.getInstance().put(b);
         }
     }
 
@@ -48,7 +43,7 @@ public class MtpBitmapFetch {
         o.inJustDecodeBounds = true;
         BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length, o);
         if (o.outWidth == 0 || o.outHeight == 0) return null;
-        o.inBitmap = sThumbnailPool.getBitmap(o.outWidth, o.outHeight);
+        o.inBitmap = GalleryBitmapPool.getInstance().get(o.outWidth, o.outHeight);
         o.inMutable = true;
         o.inJustDecodeBounds = false;
         o.inSampleSize = 1;
@@ -86,10 +81,6 @@ public class MtpBitmapFetch {
         return new BitmapWithMetadata(created, Exif.getOrientation(imageBytes));
     }
 
-    public static void onDeviceDisconnected(MtpDevice device) {
-        sThumbnailPool.clear();
-    }
-
     public static void configureForContext(Context context) {
         DisplayMetrics metrics = new DisplayMetrics();
         WindowManager wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
index 6eeeec0..da1cac0 100644 (file)
@@ -27,8 +27,8 @@ import android.text.TextPaint;
 import android.text.TextUtils;
 
 import com.android.gallery3d.R;
-import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.DataSourceType;
+import com.android.photos.data.GalleryBitmapPool;
 import com.android.gallery3d.util.ThreadPool;
 import com.android.gallery3d.util.ThreadPool.JobContext;
 
@@ -41,7 +41,8 @@ public class AlbumLabelMaker {
     private final Context mContext;
 
     private int mLabelWidth;
-    private BitmapPool mBitmapPool;
+    private int mBitmapWidth;
+    private int mBitmapHeight;
 
     private final LazyLoadedBitmap mLocalSetIcon;
     private final LazyLoadedBitmap mPicasaIcon;
@@ -109,8 +110,8 @@ public class AlbumLabelMaker {
         if (mLabelWidth == width) return;
         mLabelWidth = width;
         int borders = 2 * BORDER_SIZE;
-        mBitmapPool = new BitmapPool(
-                width + borders, mSpec.labelBackgroundHeight + borders, 16);
+        mBitmapWidth = width + borders;
+        mBitmapHeight = mSpec.labelBackgroundHeight + borders;
     }
 
     public ThreadPool.Job<Bitmap> requestLabel(
@@ -152,7 +153,7 @@ public class AlbumLabelMaker {
 
             synchronized (this) {
                 labelWidth = mLabelWidth;
-                bitmap = mBitmapPool.getBitmap();
+                bitmap = GalleryBitmapPool.getInstance().get(mBitmapWidth, mBitmapHeight);
             }
 
             if (bitmap == null) {
@@ -200,10 +201,6 @@ public class AlbumLabelMaker {
     }
 
     public void recycleLabel(Bitmap label) {
-        mBitmapPool.recycle(label);
-    }
-
-    public void clearRecycledLabels() {
-        if (mBitmapPool != null) mBitmapPool.clear();
+        GalleryBitmapPool.getInstance().put(label);
     }
 }
index d5a15b4..8149df4 100644 (file)
@@ -23,7 +23,6 @@ import com.android.gallery3d.R;
 import com.android.gallery3d.app.AbstractGalleryActivity;
 import com.android.gallery3d.app.AlbumSetDataLoader;
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.DataSourceType;
 import com.android.gallery3d.data.MediaItem;
 import com.android.gallery3d.data.MediaObject;
@@ -403,7 +402,6 @@ public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
         for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
             freeSlotContent(i);
         }
-        mLabelMaker.clearRecycledLabels();
     }
 
     public void resume() {
@@ -429,12 +427,6 @@ public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
         }
 
         @Override
-        protected void recycleBitmap(Bitmap bitmap) {
-            BitmapPool pool = MediaItem.getMicroThumbPool();
-            if (pool != null) pool.recycle(bitmap);
-        }
-
-        @Override
         protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
             return mThreadPool.submit(mMediaItem.requestImage(
                     MediaItem.TYPE_MICROTHUMBNAIL), l);
@@ -505,11 +497,6 @@ public class AlbumSetSlidingWindow implements AlbumSetDataLoader.DataListener {
         }
 
         @Override
-        protected void recycleBitmap(Bitmap bitmap) {
-            mLabelMaker.recycleLabel(bitmap);
-        }
-
-        @Override
         protected void onLoadComplete(Bitmap bitmap) {
             mHandler.obtainMessage(MSG_UPDATE_ALBUM_ENTRY, this).sendToTarget();
         }
index 8cd2cf5..fec7d1e 100644 (file)
@@ -22,11 +22,10 @@ import android.os.Message;
 import com.android.gallery3d.app.AbstractGalleryActivity;
 import com.android.gallery3d.app.AlbumDataLoader;
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.MediaItem;
 import com.android.gallery3d.data.MediaObject;
-import com.android.gallery3d.data.Path;
 import com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
+import com.android.gallery3d.data.Path;
 import com.android.gallery3d.glrenderer.Texture;
 import com.android.gallery3d.glrenderer.TiledTexture;
 import com.android.gallery3d.util.Future;
@@ -296,12 +295,6 @@ public class AlbumSlidingWindow implements AlbumDataLoader.DataListener {
         }
 
         @Override
-        protected void recycleBitmap(Bitmap bitmap) {
-            BitmapPool pool = MediaItem.getMicroThumbPool();
-            if (pool != null) pool.recycle(bitmap);
-        }
-
-        @Override
         protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l) {
             return mThreadPool.submit(
                     mItem.requestImage(MediaItem.TYPE_MICROTHUMBNAIL), this);
index 4f07cc0..a708a90 100644 (file)
@@ -18,6 +18,7 @@ package com.android.gallery3d.ui;
 
 import android.graphics.Bitmap;
 
+import com.android.photos.data.GalleryBitmapPool;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.FutureListener;
 
@@ -51,7 +52,7 @@ public abstract class BitmapLoader implements FutureListener<Bitmap> {
             mBitmap = future.get();
             if (mState == STATE_RECYCLED) {
                 if (mBitmap != null) {
-                    recycleBitmap(mBitmap);
+                    GalleryBitmapPool.getInstance().put(mBitmap);
                     mBitmap = null;
                 }
                 return; // don't call callback
@@ -84,7 +85,7 @@ public abstract class BitmapLoader implements FutureListener<Bitmap> {
     public synchronized void recycle() {
         mState = STATE_RECYCLED;
         if (mBitmap != null) {
-            recycleBitmap(mBitmap);
+            GalleryBitmapPool.getInstance().put(mBitmap);
             mBitmap = null;
         }
         if (mTask != null) mTask.cancel();
@@ -103,6 +104,5 @@ public abstract class BitmapLoader implements FutureListener<Bitmap> {
     }
 
     abstract protected Future<Bitmap> submitBitmapTask(FutureListener<Bitmap> l);
-    abstract protected void recycleBitmap(Bitmap bitmap);
     abstract protected void onLoadComplete(Bitmap bitmap);
 }
index c3466e7..e1a8b76 100644 (file)
@@ -21,7 +21,7 @@ import android.graphics.Bitmap.Config;
 import android.graphics.Canvas;
 
 import com.android.gallery3d.common.BitmapUtils;
-import com.android.gallery3d.data.BitmapPool;
+import com.android.photos.data.GalleryBitmapPool;
 
 import java.util.ArrayList;
 
@@ -71,12 +71,11 @@ public class BitmapTileProvider implements TileImageView.TileSource {
     }
 
     @Override
-    public Bitmap getTile(int level, int x, int y, int tileSize,
-            BitmapPool pool) {
+    public Bitmap getTile(int level, int x, int y, int tileSize) {
         x >>= level;
         y >>= level;
 
-        Bitmap result = pool == null ? null : pool.getBitmap();
+        Bitmap result = GalleryBitmapPool.getInstance().get(tileSize, tileSize);
         if (result == null) {
             result = Bitmap.createBitmap(tileSize, tileSize, mConfig);
         } else {
index f1c31e4..3185c75 100644 (file)
@@ -27,10 +27,9 @@ import android.util.FloatMath;
 import android.view.WindowManager;
 
 import com.android.gallery3d.app.GalleryContext;
-import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
 import com.android.gallery3d.data.DecodeUtils;
+import com.android.photos.data.GalleryBitmapPool;
 import com.android.gallery3d.glrenderer.GLCanvas;
 import com.android.gallery3d.glrenderer.UploadedTexture;
 import com.android.gallery3d.util.Future;
@@ -50,8 +49,6 @@ public class TileImageView extends GLView {
     // TILE_SIZE must be 2^N
     private static int sTileSize;
 
-    private static BitmapPool sTilePool;
-
     /*
      *  This is the tile state in the CPU side.
      *  Life of a Tile:
@@ -143,8 +140,7 @@ public class TileImageView extends GLView {
         // still refers to the coordinate on the original image.
         //
         // The method would be called in another thread.
-        public Bitmap getTile(int level, int x, int y, int tileSize,
-                BitmapPool pool);
+        public Bitmap getTile(int level, int x, int y, int tileSize);
     }
 
     public static boolean isHighResolution(Context context) {
@@ -164,10 +160,6 @@ public class TileImageView extends GLView {
             } else {
                 sTileSize = 256;
             }
-            sTilePool =
-                    ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER
-                    ? new BitmapPool(sTileSize, sTileSize, 128)
-                    : null;
         }
     }
 
@@ -399,7 +391,6 @@ public class TileImageView extends GLView {
             }
         }
         setScreenNail(null);
-        if (sTilePool != null) sTilePool.clear();
     }
 
     public void prepareTextures() {
@@ -508,7 +499,7 @@ public class TileImageView extends GLView {
             if (tile.mTileState == STATE_RECYCLING) {
                 tile.mTileState = STATE_RECYCLED;
                 if (tile.mDecodedTile != null) {
-                    if (sTilePool != null) sTilePool.recycle(tile.mDecodedTile);
+                    GalleryBitmapPool.getInstance().put(tile.mDecodedTile);
                     tile.mDecodedTile = null;
                 }
                 mRecycledQueue.push(tile);
@@ -536,7 +527,7 @@ public class TileImageView extends GLView {
         }
         tile.mTileState = STATE_RECYCLED;
         if (tile.mDecodedTile != null) {
-            if (sTilePool != null) sTilePool.recycle(tile.mDecodedTile);
+            GalleryBitmapPool.getInstance().put(tile.mDecodedTile);
             tile.mDecodedTile = null;
         }
         mRecycledQueue.push(tile);
@@ -675,7 +666,7 @@ public class TileImageView extends GLView {
 
         @Override
         protected void onFreeBitmap(Bitmap bitmap) {
-            if (sTilePool != null) sTilePool.recycle(bitmap);
+            GalleryBitmapPool.getInstance().put(bitmap);
         }
 
         boolean decode() {
@@ -683,7 +674,7 @@ public class TileImageView extends GLView {
             // by (1 << mTilelevel) from a region in the original image.
             try {
                 mDecodedTile = DecodeUtils.ensureGLCompatibleBitmap(mModel.getTile(
-                        mTileLevel, mX, mY, sTileSize, sTilePool));
+                        mTileLevel, mX, mY, sTileSize));
             } catch (Throwable t) {
                 Log.w(TAG, "fail to decode tile", t);
             }
index 0d20b07..0c1f66d 100644 (file)
@@ -26,7 +26,7 @@ import android.graphics.Rect;
 
 import com.android.gallery3d.common.ApiHelper;
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
+import com.android.photos.data.GalleryBitmapPool;
 
 public class TileImageViewAdapter implements TileImageView.TileSource {
     private static final String TAG = "TileImageViewAdapter";
@@ -84,7 +84,7 @@ public class TileImageViewAdapter implements TileImageView.TileSource {
     // (44, 44, 256, 256) from the original photo and down sample it to 106.
     @TargetApi(ApiHelper.VERSION_CODES.HONEYCOMB)
     @Override
-    public Bitmap getTile(int level, int x, int y, int tileSize, BitmapPool pool) {
+    public Bitmap getTile(int level, int x, int y, int tileSize) {
         if (!ApiHelper.HAS_REUSING_BITMAP_IN_BITMAP_REGION_DECODER) {
             return getTileWithoutReusingBitmap(level, x, y, tileSize);
         }
@@ -106,7 +106,7 @@ public class TileImageViewAdapter implements TileImageView.TileSource {
                     .contains(wantRegion);
         }
 
-        Bitmap bitmap = pool == null ? null : pool.getBitmap();
+        Bitmap bitmap = GalleryBitmapPool.getInstance().get(tileSize, tileSize);
         if (bitmap != null) {
             if (needClear) bitmap.eraseColor(0);
         } else {
@@ -126,7 +126,7 @@ public class TileImageViewAdapter implements TileImageView.TileSource {
             }
         } finally {
             if (options.inBitmap != bitmap && options.inBitmap != null) {
-                if (pool != null) pool.recycle(options.inBitmap);
+                GalleryBitmapPool.getInstance().put(options.inBitmap);
                 options.inBitmap = null;
             }
         }
index ab24f5b..860e230 100644 (file)
@@ -20,8 +20,7 @@ import android.graphics.Bitmap;
 import android.graphics.RectF;
 
 import com.android.gallery3d.common.Utils;
-import com.android.gallery3d.data.BitmapPool;
-import com.android.gallery3d.data.MediaItem;
+import com.android.photos.data.GalleryBitmapPool;
 import com.android.gallery3d.glrenderer.GLCanvas;
 import com.android.gallery3d.glrenderer.TiledTexture;
 
@@ -83,11 +82,6 @@ public class TiledScreenNail implements ScreenNail {
         mHeight = Math.round(scale * height);
     }
 
-    private static void recycleBitmap(BitmapPool pool, Bitmap bitmap) {
-        if (pool == null || bitmap == null) return;
-        pool.recycle(bitmap);
-    }
-
     // Combines the two ScreenNails.
     // Returns the used one and recycle the unused one.
     public ScreenNail combine(ScreenNail other) {
@@ -106,7 +100,7 @@ public class TiledScreenNail implements ScreenNail {
         mWidth = newer.mWidth;
         mHeight = newer.mHeight;
         if (newer.mTexture != null) {
-            recycleBitmap(MediaItem.getThumbPool(), mBitmap);
+            if (mBitmap != null) GalleryBitmapPool.getInstance().put(mBitmap);
             if (mTexture != null) mTexture.recycle();
             mBitmap = newer.mBitmap;
             mTexture = newer.mTexture;
@@ -143,8 +137,10 @@ public class TiledScreenNail implements ScreenNail {
             mTexture.recycle();
             mTexture = null;
         }
-        recycleBitmap(MediaItem.getThumbPool(), mBitmap);
-        mBitmap = null;
+        if (mBitmap != null) {
+            GalleryBitmapPool.getInstance().put(mBitmap);
+            mBitmap = null;
+        }
     }
 
     public static void disableDrawPlaceholder() {
diff --git a/src/com/android/photos/AlbumSetFragment.java b/src/com/android/photos/AlbumSetFragment.java
new file mode 100644 (file)
index 0000000..2d348c2
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * 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.photos;
+
+import android.app.Fragment;
+
+
+public class AlbumSetFragment extends Fragment {
+
+}
diff --git a/src/com/android/photos/GalleryActivity.java b/src/com/android/photos/GalleryActivity.java
new file mode 100644 (file)
index 0000000..46b5140
--- /dev/null
@@ -0,0 +1,125 @@
+/*
+ * 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.photos;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.Activity;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+import com.android.camera.CameraActivity;
+import com.android.gallery3d.R;
+
+public class GalleryActivity extends Activity {
+
+    private final String FTAG_PHOTOSET = "PhotoSet";
+    private final String FTAG_ALBUMSET = "AlbumSet";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setupActionBar();
+    }
+
+    private void setupActionBar() {
+        ActionBar ab = getActionBar();
+        ab.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
+        ab.setDisplayShowHomeEnabled(false);
+        ab.setDisplayShowTitleEnabled(false);
+        Tab tab = ab.newTab();
+        tab.setText(R.string.tab_photos);
+        tab.setTabListener(new TabListener<PhotoSetFragment>(this,
+                FTAG_PHOTOSET, PhotoSetFragment.class));
+        ab.addTab(tab, true);
+        tab = ab.newTab();
+        tab.setText(R.string.tab_albums);
+        tab.setTabListener(new TabListener<AlbumSetFragment>(this,
+                FTAG_ALBUMSET, AlbumSetFragment.class));
+        ab.addTab(tab);
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.gallery, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch (item.getItemId()) {
+        case R.id.menu_camera:
+            Intent intent = new Intent(this, CameraActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivity(intent);
+            return true;
+        default:
+            return super.onOptionsItemSelected(item);
+        }
+    }
+
+    private static class TabListener<T extends Fragment> implements ActionBar.TabListener {
+        private Fragment mFragment;
+        private final Activity mActivity;
+        private final String mTag;
+        private final Class<T> mClass;
+
+        /** Constructor used each time a new tab is created.
+          * @param activity  The host Activity, used to instantiate the fragment
+          * @param tag  The identifier tag for the fragment
+          * @param clz  The fragment's Class, used to instantiate the fragment
+          */
+        public TabListener(Activity activity, String tag, Class<T> clz) {
+            mActivity = activity;
+            mTag = tag;
+            mClass = clz;
+        }
+
+        /* The following are each of the ActionBar.TabListener callbacks */
+
+        @Override
+        public void onTabSelected(Tab tab, FragmentTransaction ft) {
+            // Check if the fragment is already initialized
+            if (mFragment == null) {
+                // If not, instantiate and add it to the activity
+                mFragment = Fragment.instantiate(mActivity, mClass.getName());
+                ft.add(android.R.id.content, mFragment, mTag);
+            } else {
+                // If it exists, simply attach it in order to show it
+                ft.attach(mFragment);
+            }
+        }
+
+        @Override
+        public void onTabUnselected(Tab tab, FragmentTransaction ft) {
+            if (mFragment != null) {
+                // Detach the fragment, because another one is being attached
+                ft.detach(mFragment);
+            }
+        }
+
+        @Override
+        public void onTabReselected(Tab tab, FragmentTransaction ft) {
+            // User selected the already selected tab. Usually do nothing.
+        }
+    }
+}
diff --git a/src/com/android/photos/PhotoFragment.java b/src/com/android/photos/PhotoFragment.java
new file mode 100644 (file)
index 0000000..3be6313
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * 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.photos;
+
+import android.app.Fragment;
+
+
+public class PhotoFragment extends Fragment {
+
+}
diff --git a/src/com/android/photos/PhotoSetFragment.java b/src/com/android/photos/PhotoSetFragment.java
new file mode 100644 (file)
index 0000000..e9bfce5
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * 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.photos;
+
+import android.app.Fragment;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.Loader;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.MediaStore.Files.FileColumns;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.CursorAdapter;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+
+import com.android.gallery3d.R;
+import com.android.photos.data.PhotoSetLoader;
+
+
+public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor> {
+
+    private static final int LOADER_PHOTOSET = 1;
+
+    private ListView mPhotoSetView;
+    private View mEmptyView;
+    private CursorAdapter mAdapter;
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        View root = inflater.inflate(R.layout.photo_set, container, false);
+        mPhotoSetView = (ListView) root.findViewById(android.R.id.list);
+        mEmptyView = root.findViewById(android.R.id.empty);
+        mEmptyView.setVisibility(View.GONE);
+        mAdapter = new SimpleCursorAdapter(getActivity(),
+                android.R.layout.simple_list_item_1, null,
+                new String[] { FileColumns.DATA },
+                new int[] { android.R.id.text1 }, 0);
+        mPhotoSetView.setAdapter(mAdapter);
+        getLoaderManager().initLoader(LOADER_PHOTOSET, null, this);
+        updateEmptyStatus();
+        return root;
+    }
+
+    private void updateEmptyStatus() {
+        boolean empty = (mAdapter == null || mAdapter.getCount() == 0);
+        mPhotoSetView.setVisibility(empty ? View.GONE : View.VISIBLE);
+        mEmptyView.setVisibility(empty ? View.VISIBLE : View.GONE);
+    }
+
+    @Override
+    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        return new PhotoSetLoader(getActivity());
+    }
+
+    @Override
+    public void onLoadFinished(Loader<Cursor> loader,
+            Cursor data) {
+        mAdapter.swapCursor(data);
+        updateEmptyStatus();
+    }
+
+    @Override
+    public void onLoaderReset(Loader<Cursor> loader) {
+    }
+}
diff --git a/src/com/android/photos/data/GalleryBitmapPool.java b/src/com/android/photos/data/GalleryBitmapPool.java
new file mode 100644 (file)
index 0000000..cddc160
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * 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.photos.data;
+
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.util.Pools.Pool;
+import android.util.Pools.SimplePool;
+
+import com.android.photos.data.SparseArrayBitmapPool.Node;
+
+public class GalleryBitmapPool {
+
+    private static final int CAPACITY_BYTES = 20971520;
+    private static final int POOL_INDEX_NONE = -1;
+    private static final int POOL_INDEX_SQUARE = 0;
+    private static final int POOL_INDEX_PHOTO = 1;
+    private static final int POOL_INDEX_MISC = 2;
+
+    private static final Point[] COMMON_PHOTO_ASPECT_RATIOS =
+        { new Point(4, 3), new Point(3, 2), new Point(16, 9) };
+
+    private int mCapacityBytes;
+    private SparseArrayBitmapPool [] mPools;
+    private Pool<Node> mSharedNodePool = new SimplePool<Node>(128);
+
+    private GalleryBitmapPool(int capacityBytes) {
+        mPools = new SparseArrayBitmapPool[3];
+        mPools[POOL_INDEX_SQUARE] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
+        mPools[POOL_INDEX_PHOTO] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
+        mPools[POOL_INDEX_MISC] = new SparseArrayBitmapPool(capacityBytes / 3, mSharedNodePool);
+        mCapacityBytes = capacityBytes;
+    }
+
+    private static GalleryBitmapPool sInstance;
+    public static GalleryBitmapPool getInstance() {
+        if (sInstance == null) {
+            sInstance = new GalleryBitmapPool(CAPACITY_BYTES);
+        }
+        return sInstance;
+    }
+
+    private SparseArrayBitmapPool getPoolForDimensions(int width, int height) {
+        int index = getPoolIndexForDimensions(width, height);
+        if (index == POOL_INDEX_NONE) {
+            return null;
+        } else {
+            return mPools[index];
+        }
+    }
+
+    private int getPoolIndexForDimensions(int width, int height) {
+        if (width <= 0 || height <= 0) {
+            return POOL_INDEX_NONE;
+        }
+        if (width == height) {
+            return POOL_INDEX_SQUARE;
+        }
+        int min, max;
+        if (width > height) {
+            min = height;
+            max = width;
+        } else {
+            min = width;
+            max = height;
+        }
+        for (Point ar : COMMON_PHOTO_ASPECT_RATIOS) {
+            if (min * ar.x == max * ar.y) {
+                return POOL_INDEX_PHOTO;
+            }
+        }
+        return POOL_INDEX_MISC;
+    }
+
+    public synchronized int getCapacity() {
+        return mCapacityBytes;
+    }
+
+    public synchronized int getSize() {
+        int total = 0;
+        for (SparseArrayBitmapPool p : mPools) {
+            total += p.getSize();
+        }
+        return total;
+    }
+
+    public Bitmap get(int width, int height) {
+        SparseArrayBitmapPool pool = getPoolForDimensions(width, height);
+        if (pool == null) {
+            return null;
+        } else {
+            return pool.get(width, height);
+        }
+    }
+
+    public boolean put(Bitmap b) {
+        if (b == null) {
+            return false;
+        }
+        SparseArrayBitmapPool pool = getPoolForDimensions(b.getWidth(), b.getHeight());
+        if (pool == null) {
+            b.recycle();
+            return false;
+        } else {
+            return pool.put(b);
+        }
+    }
+
+    public void clear() {
+        for (SparseArrayBitmapPool p : mPools) {
+            p.clear();
+        }
+    }
+}
diff --git a/src/com/android/photos/data/MediaSetLoader.java b/src/com/android/photos/data/MediaSetLoader.java
new file mode 100644 (file)
index 0000000..4afb7d9
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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.photos.data;
+
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+
+import com.android.gallery3d.data.ContentListener;
+import com.android.gallery3d.data.DataManager;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.MediaSet.SyncListener;
+import com.android.gallery3d.util.Future;
+
+/**
+ * Proof of concept, don't use
+ */
+public class MediaSetLoader extends AsyncTaskLoader<MediaSet> {
+
+    private static final SyncListener sNullListener = new SyncListener() {
+        @Override
+        public void onSyncDone(MediaSet mediaSet, int resultCode) {
+        }
+    };
+
+    private MediaSet mMediaSet;
+    private Future<Integer> mSyncTask = null;
+    private ContentListener mObserver = new ContentListener() {
+        @Override
+        public void onContentDirty() {
+            onContentChanged();
+        }
+    };
+
+    public MediaSetLoader(Context context, String path) {
+        super(context);
+        mMediaSet = DataManager.from(getContext()).getMediaSet(path);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        mMediaSet.addContentListener(mObserver);
+        mSyncTask = mMediaSet.requestSync(sNullListener);
+        forceLoad();
+    }
+
+    @Override
+    protected boolean onCancelLoad() {
+        if (mSyncTask != null) {
+            mSyncTask.cancel();
+            mSyncTask = null;
+        }
+        return super.onCancelLoad();
+    }
+
+    @Override
+    protected void onStopLoading() {
+        super.onStopLoading();
+        cancelLoad();
+        mMediaSet.removeContentListener(mObserver);
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        onStopLoading();
+    }
+
+    @Override
+    public MediaSet loadInBackground() {
+        mMediaSet.loadIfDirty();
+        return mMediaSet;
+    }
+
+}
diff --git a/src/com/android/photos/data/PhotoSetLoader.java b/src/com/android/photos/data/PhotoSetLoader.java
new file mode 100644 (file)
index 0000000..8c511a5
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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.photos.data;
+
+import android.content.Context;
+import android.content.CursorLoader;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.provider.MediaStore.Files;
+import android.provider.MediaStore.Files.FileColumns;
+
+public class PhotoSetLoader extends CursorLoader {
+
+    private static final Uri CONTENT_URI = Files.getContentUri("external");
+    private static final String[] PROJECTION = new String[] {
+        FileColumns._ID,
+        FileColumns.DATA,
+        FileColumns.WIDTH,
+        FileColumns.HEIGHT,
+        FileColumns.DATE_ADDED,
+        FileColumns.MEDIA_TYPE,
+    };
+    private static final String SORT_ORDER = FileColumns.DATE_ADDED + " DESC";
+    private static final String SELECTION =
+            FileColumns.MEDIA_TYPE + " == " + FileColumns.MEDIA_TYPE_IMAGE
+            + " OR "
+            + FileColumns.MEDIA_TYPE + " == " + FileColumns.MEDIA_TYPE_VIDEO;
+
+    public static final int INDEX_ID = 0;
+    public static final int INDEX_DATA = 1;
+    public static final int INDEX_WIDTH = 2;
+    public static final int INDEX_HEIGHT = 3;
+    public static final int INDEX_DATE_ADDED = 4;
+    public static final int INDEX_MEDIA_TYPE = 5;
+
+    private static final Uri GLOBAL_CONTENT_URI = Uri.parse("content://" + MediaStore.AUTHORITY + "/external/");
+    private final ContentObserver mGlobalObserver = new ForceLoadContentObserver();
+
+    public PhotoSetLoader(Context context) {
+        super(context, CONTENT_URI, PROJECTION, SELECTION, null, SORT_ORDER);
+    }
+
+    @Override
+    protected void onStartLoading() {
+        super.onStartLoading();
+        getContext().getContentResolver().registerContentObserver(GLOBAL_CONTENT_URI,
+                true, mGlobalObserver);
+    }
+
+    @Override
+    protected void onReset() {
+        super.onReset();
+        getContext().getContentResolver().unregisterContentObserver(mGlobalObserver);
+    }
+}
diff --git a/src/com/android/photos/data/SparseArrayBitmapPool.java b/src/com/android/photos/data/SparseArrayBitmapPool.java
new file mode 100644 (file)
index 0000000..8512590
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * 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.photos.data;
+
+import android.graphics.Bitmap;
+import android.util.SparseArray;
+
+import android.util.Pools.Pool;
+
+public class SparseArrayBitmapPool {
+
+    private static final int BITMAPS_TO_KEEP_AFTER_UNNEEDED_HINT = 4;
+    private int mCapacityBytes;
+    private SparseArray<Node> mStore = new SparseArray<Node>();
+    private int mSizeBytes = 0;
+
+    private Pool<Node> mNodePool;
+    private Node mPoolNodesHead = null;
+    private Node mPoolNodesTail = null;
+
+    protected static class Node {
+        Bitmap bitmap;
+        Node prevInBucket;
+        Node nextInBucket;
+        Node nextInPool;
+        Node prevInPool;
+    }
+
+    public SparseArrayBitmapPool(int capacityBytes, Pool<Node> nodePool) {
+        mCapacityBytes = capacityBytes;
+        mNodePool = nodePool;
+    }
+
+    public synchronized void setCapacity(int capacityBytes) {
+        mCapacityBytes = capacityBytes;
+        freeUpCapacity(0);
+    }
+
+    private void freeUpCapacity(int bytesNeeded) {
+        int targetSize = mCapacityBytes - bytesNeeded;
+        while (mPoolNodesTail != null && mSizeBytes > targetSize) {
+            unlinkAndRecycleNode(mPoolNodesTail, true);
+        }
+    }
+
+    private void unlinkAndRecycleNode(Node n, boolean recycleBitmap) {
+        // Remove the node from its spot in its bucket
+        if (n.prevInBucket != null) {
+            n.prevInBucket.nextInBucket = n.nextInBucket;
+        } else {
+            mStore.put(n.bitmap.getWidth(), n.nextInBucket);
+        }
+        if (n.nextInBucket != null) {
+            n.nextInBucket.prevInBucket = n.prevInBucket;
+        }
+
+        // Remove the node from its spot in the list of pool nodes
+        if (n.prevInPool != null) {
+            n.prevInPool.nextInPool = n.nextInPool;
+        } else {
+            mPoolNodesHead = n.nextInPool;
+        }
+        if (n.nextInPool != null) {
+            n.nextInPool.prevInPool = n.prevInPool;
+        } else {
+            mPoolNodesTail = n.prevInPool;
+        }
+
+        // Recycle the node
+        n.nextInBucket = null;
+        n.nextInPool = null;
+        n.prevInBucket = null;
+        n.prevInPool = null;
+        mSizeBytes -= n.bitmap.getByteCount();
+        if (recycleBitmap) n.bitmap.recycle();
+        n.bitmap = null;
+        mNodePool.release(n);
+    }
+
+    public synchronized int getCapacity() {
+        return mCapacityBytes;
+    }
+
+    public synchronized int getSize() {
+        return mSizeBytes;
+    }
+
+    public synchronized Bitmap get(int width, int height) {
+        Node cur = mStore.get(width);
+        while (cur != null) {
+            if (cur.bitmap.getHeight() == height) {
+                Bitmap b = cur.bitmap;
+                unlinkAndRecycleNode(cur, false);
+                return b;
+            }
+            cur = cur.nextInBucket;
+        }
+        return null;
+    }
+
+    public synchronized boolean put(Bitmap b) {
+        if (b == null) {
+            return false;
+        }
+        int bytes = b.getByteCount();
+        freeUpCapacity(bytes);
+        Node newNode = mNodePool.acquire();
+        if (newNode == null) {
+            newNode = new Node();
+        }
+        newNode.bitmap = b;
+        newNode.prevInBucket = null;
+        newNode.prevInPool = null;
+        newNode.nextInPool = mPoolNodesHead;
+        mPoolNodesHead = newNode;
+        int key = b.getWidth();
+        newNode.nextInBucket = mStore.get(key);
+        if (newNode.nextInBucket != null) {
+            newNode.nextInBucket.prevInBucket = newNode;
+        }
+        mStore.put(key, newNode);
+        if (newNode.nextInPool == null) {
+            mPoolNodesTail = newNode;
+        }
+        mSizeBytes += bytes;
+        return true;
+    }
+
+    public synchronized void clear() {
+        freeUpCapacity(mCapacityBytes);
+    }
+}