OSDN Git Service

Merge "Import translations. DO NOT MERGE" into master-security-dev
authorBaligh Uddin <baligh@google.com>
Wed, 6 Mar 2013 06:04:11 +0000 (06:04 +0000)
committerAndroid (Google) Code Review <android-gerrit@google.com>
Wed, 6 Mar 2013 06:04:11 +0000 (06:04 +0000)
58 files changed:
AndroidManifest.xml
CleanSpec.mk
res/drawable/filtershow_drawing.png [new file with mode: 0644]
res/layout-land/camera_controls.xml [moved from res/layout-land/photo_module_content.xml with 55% similarity]
res/layout-land/camera_shutter_switcher.xml [deleted file]
res/layout-land/filtershow_activity.xml
res/layout-land/pano_module_capture.xml
res/layout-land/review_module_control.xml
res/layout-land/switcher_popup.xml
res/layout-land/video_module.xml [deleted file]
res/layout-port/camera_controls.xml [moved from res/layout-port/photo_module_content.xml with 56% similarity]
res/layout-port/camera_shutter_switcher.xml [deleted file]
res/layout-port/pano_module_capture.xml
res/layout-port/review_module_control.xml
res/layout-port/switcher_popup.xml
res/layout-port/video_module.xml [deleted file]
res/layout/camera_main.xml
res/layout/filtershow_history_operation_row.xml
res/layout/panorama_module.xml
res/layout/photo_module.xml
res/layout/preview_module_frame.xml [deleted file]
res/layout/preview_surface_view.xml [deleted file]
res/layout/video_module.xml [moved from res/layout/preview_module_frame_video.xml with 75% similarity]
res/menu/filtershow_activity_menu.xml
res/values/dimens.xml
src/com/android/camera/CameraActivity.java
src/com/android/camera/CameraModule.java
src/com/android/camera/PanoramaModule.java
src/com/android/camera/PhotoModule.java
src/com/android/camera/PreviewFrameLayout.java
src/com/android/camera/ShutterButton.java
src/com/android/camera/VideoModule.java
src/com/android/camera/ui/CameraSwitcher.java
src/com/android/camera/ui/RotatableLayout.java [new file with mode: 0644]
src/com/android/gallery3d/filtershow/FilterShowActivity.java
src/com/android/gallery3d/filtershow/HistoryAdapter.java
src/com/android/gallery3d/filtershow/cache/RenderingRequest.java
src/com/android/gallery3d/filtershow/filters/BaseFiltersManager.java
src/com/android/gallery3d/filtershow/filters/FilterDrawRepresentation.java
src/com/android/gallery3d/filtershow/filters/FilterRedEyeRepresentation.java
src/com/android/gallery3d/filtershow/imageshow/ImageShow.java
src/com/android/gallery3d/filtershow/imageshow/ImageVignette.java
src/com/android/gallery3d/filtershow/imageshow/MasterImage.java
src/com/android/gallery3d/filtershow/presets/ImagePreset.java
src/com/android/gallery3d/filtershow/ui/FilterIconButton.java
src/com/android/photos/PhotoSetFragment.java
src/com/android/photos/data/NotificationWatcher.java [new file with mode: 0644]
src/com/android/photos/data/PhotoDatabase.java
src/com/android/photos/data/PhotoProvider.java
src/com/android/photos/drawables/AutoThumbnailDrawable.java
src/com/android/photos/drawables/DataUriThumbnailDrawable.java [new file with mode: 0644]
src/com/android/photos/drawables/MtpThumbnailDrawable.java [new file with mode: 0644]
src_pd/com/android/gallery3d/filtershow/filters/FiltersManager.java
src_pd/com/android/photos/data/PhotoProviderAuthority.java [new file with mode: 0644]
tests/src/com/android/photos/data/PhotoDatabaseTest.java
tests/src/com/android/photos/data/PhotoDatabaseUtils.java
tests/src/com/android/photos/data/PhotoProviderTest.java
tests/src/com/android/photos/data/TestHelper.java [new file with mode: 0644]

index c33ab3d..abe76d2 100644 (file)
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 
-<manifest android:versionCode="40002"
-        android:versionName="1.1.40002"
+<manifest android:versionCode="40003"
+        android:versionName="1.1.40003"
         xmlns:android="http://schemas.android.com/apk/res/android"
         package="com.android.gallery3d">
 
index eff98bb..3a53b9c 100644 (file)
@@ -48,6 +48,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Camera*)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/APPS/Gallery*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Gallery*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Gallery*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Gallery*)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/res/drawable/filtershow_drawing.png b/res/drawable/filtershow_drawing.png
new file mode 100644 (file)
index 0000000..566773d
Binary files /dev/null and b/res/drawable/filtershow_drawing.png differ
similarity index 55%
rename from res/layout-land/photo_module_content.xml
rename to res/layout-land/camera_controls.xml
index d734f83..a909c51 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- 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.
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- This layout is shared by phone and tablet in landscape orientation. -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_app"
+    android:id="@+id/camera_controls"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent" >
 
-    <include layout="@layout/preview_module_frame"/>
-
-    <FrameLayout
+    <com.android.camera.ui.RotatableLayout
         style="@style/CameraControls"
+        android:id="@+id/menu_button"
         android:layout_gravity="center" >
 
         <View
             android:layout_marginRight="-2dip"
             android:layout_gravity="top|right"/>
 
-
-        <include layout="@layout/review_module_control"
-            android:layout_marginRight="2dip" />
-
         <com.android.camera.ui.PieMenuButton
             android:id="@+id/menu"
             style="@style/SwitcherButton"
             android:contentDescription="@string/accessibility_menu_button"
             android:layout_gravity="right|top"
             android:layout_marginRight="2dip" />
+    </com.android.camera.ui.RotatableLayout>
+
+    <com.android.camera.ui.RotatableLayout
+        style="@style/CameraControls"
+        android:id="@+id/switcher_control"
+        android:layout_gravity="right|center_horizontal" >
+        <com.android.camera.ui.CameraSwitcher
+            android:id="@+id/camera_switcher"
+            style="@style/SwitcherButton"
+            android:layout_gravity="right|bottom"
+            android:layout_marginRight="2dip"
+            android:contentDescription="@string/accessibility_mode_picker" />
+    </com.android.camera.ui.RotatableLayout>
 
-    </FrameLayout>
+    <com.android.camera.ShutterButton
+        android:id="@+id/shutter_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="right|center_vertical"
+        android:layout_marginRight="@dimen/shutter_offset"
+        android:clickable="true"
+        android:contentDescription="@string/accessibility_shutter_button"
+        android:focusable="true"
+        android:scaleType="center"
+        android:src="@drawable/btn_new_shutter" />
 
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-land/camera_shutter_switcher.xml b/res/layout-land/camera_shutter_switcher.xml
deleted file mode 100644 (file)
index 9c06749..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_shutter_switcher"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <View
-        android:id="@+id/controls"
-        style="@style/CameraControls"
-        android:layout_alignParentRight="true"
-        android:layout_centerVertical="true" />
-
-    <com.android.camera.ShutterButton
-        android:id="@+id/shutter_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentRight="true"
-        android:layout_centerVertical="true"
-        android:layout_marginRight="@dimen/shutter_offset"
-        android:clickable="true"
-        android:contentDescription="@string/accessibility_shutter_button"
-        android:focusable="true"
-        android:scaleType="center"
-        android:src="@drawable/btn_new_shutter" />
-
-    <com.android.camera.ui.CameraSwitcher
-        android:id="@+id/camera_switcher"
-        style="@style/SwitcherButton"
-        android:layout_alignBottom="@id/controls"
-        android:layout_alignParentRight="true"
-        android:layout_marginRight="2dip"
-        android:contentDescription="@string/accessibility_mode_picker" />
-
-</RelativeLayout>
\ No newline at end of file
index 8e74de9..8cef0ce 100644 (file)
 
             </LinearLayout>
 
+            <LinearLayout
+                    android:id="@+id/historyPanel"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:orientation="vertical"
+                    android:layout_weight="1"
+                    android:visibility="gone" >
+
+                <TextView
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:background="@android:color/transparent"
+                        android:gravity="center"
+                        android:padding="2dip"
+                        android:text="@string/history"
+                        android:textColor="@android:color/white"
+                        android:textSize="24sp"
+                        android:textStyle="bold" />
+
+                <ListView
+                        android:id="@+id/operationsList"
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:layout_weight="1" >
+                </ListView>
+
+                <LinearLayout
+                        android:layout_width="match_parent"
+                        android:layout_height="wrap_content"
+                        android:orientation="horizontal" >
+
+                    <Button
+                            android:id="@+id/resetOperationsButton"
+                            style="@style/FilterShowHistoryButton"
+                            android:gravity="center"
+                            android:text="@string/reset" />
+
+                    <Button
+                            android:id="@+id/saveOperationsButton"
+                            style="@style/FilterShowHistoryButton"
+                            android:text="@string/save"
+                            android:visibility="gone" />
+                </LinearLayout>
+            </LinearLayout>
+
 
             <FrameLayout
                     android:layout_gravity="bottom"
 
     </LinearLayout>
 
-    <LinearLayout
-            android:id="@+id/historyPanel"
-            android:layout_width="200dip"
-            android:layout_height="match_parent"
-            android:layout_gravity="right"
-            android:orientation="vertical"
-            android:visibility="invisible" >
-
-        <TextView
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:background="@android:color/transparent"
-                android:gravity="center"
-                android:padding="2dip"
-                android:text="@string/history"
-                android:textColor="@android:color/white"
-                android:textSize="24sp"
-                android:textStyle="bold" />
-
-        <ListView
-                android:id="@+id/operationsList"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_weight="1" >
-        </ListView>
-
-        <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="horizontal" >
-
-            <Button
-                    android:id="@+id/resetOperationsButton"
-                    style="@style/FilterShowHistoryButton"
-                    android:gravity="center"
-                    android:text="@string/reset" />
-
-            <Button
-                    android:id="@+id/saveOperationsButton"
-                    style="@style/FilterShowHistoryButton"
-                    android:text="@string/save"
-                    android:visibility="gone" />
-        </LinearLayout>
-    </LinearLayout>
-
 </FrameLayout>
index 6cad0bf..26cbfb1 100644 (file)
@@ -15,7 +15,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/camera_app_root"
+        android:id="@+id/camera_app"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
         android:layout_gravity="center"
index e732a2c..9f8b0cd 100644 (file)
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent">
-    <com.android.camera.ui.RotateImageView android:id="@+id/btn_done"
+<com.android.camera.ui.RotatableLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+        style="@style/CameraControls"
+        android:layout_gravity="right|center_vertical"
+        android:layout_marginRight="2dip">
+    <ImageView android:id="@+id/btn_done"
             style="@style/ReviewControlIcon"
             android:contentDescription="@string/accessibility_review_ok"
             android:visibility="gone"
+            android:scaleType="center"
             android:layout_gravity="top|right"
             android:background="@drawable/bg_pressed"
             android:src="@drawable/ic_menu_done_holo_light" />
         android:background="@drawable/bg_pressed"
         android:src="@drawable/ic_btn_shutter_retake" />
 
-    <com.android.camera.ui.RotateImageView android:id="@+id/btn_cancel"
+    <ImageView android:id="@+id/btn_cancel"
             style="@style/ReviewControlIcon"
             android:contentDescription="@string/accessibility_review_cancel"
             android:visibility="gone"
+            android:scaleType="center"
             android:layout_gravity="bottom|right"
             android:background="@drawable/bg_pressed"
             android:src="@drawable/ic_menu_cancel_holo_light" />
-</FrameLayout>
+</com.android.camera.ui.RotatableLayout>
index b949f96..fc2d7bc 100644 (file)
@@ -18,8 +18,7 @@
     android:orientation="horizontal"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_alignBottom="@id/camera_switcher"
-    android:layout_alignRight="@id/camera_switcher"
+    android:layout_gravity="bottom|right"
     android:layout_marginRight="8dip"
     android:layout_marginBottom="8dip"
     android:paddingLeft="8dip"
diff --git a/res/layout-land/video_module.xml b/res/layout-land/video_module.xml
deleted file mode 100644 (file)
index 972a7f9..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- This layout is shared by phone and tablet in landscape orientation. -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_app_root"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent">
-    <include layout="@layout/preview_module_frame_video"/>
-
-    <RelativeLayout
-        style="@style/CameraControls"
-        android:layout_centerVertical="true" >
-
-        <View
-            android:id="@+id/blocker"
-            android:layout_width="@dimen/switcher_size"
-            android:layout_height="match_parent"
-            android:background="@drawable/switcher_bg"
-            android:layout_alignParentRight="true"
-            android:clickable="true" />
-
-        <include layout="@layout/menu_indicators"
-            android:layout_width="80dip"
-            android:layout_height="80dip"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentTop="true"
-            android:layout_marginRight="-2dip"
-            android:layout_marginTop="-5dip" />
-
-        <include layout="@layout/bg_replacement_training_message" />
-
-        <include layout="@layout/review_module_control"
-            android:layout_marginRight="2dip" />
-
-        <com.android.camera.ui.PieMenuButton
-            android:id="@+id/menu"
-            style="@style/SwitcherButton"
-            android:layout_alignParentRight="true"
-            android:layout_alignParentTop="true"
-            android:layout_marginRight="2dip"
-            android:contentDescription="@string/accessibility_menu_button" />
-
-    </RelativeLayout>
-
-</RelativeLayout>
similarity index 56%
rename from res/layout-port/photo_module_content.xml
rename to res/layout-port/camera_controls.xml
index a82a7a1..caad868 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
+<!-- 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.
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<!-- This layout is shared by phone and tablet in landscape orientation. -->
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_app"
+    android:id="@+id/camera_controls"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent" >
 
-    <include layout="@layout/preview_module_frame"/>
-
-    <FrameLayout
+   <com.android.camera.ui.RotatableLayout
         style="@style/CameraControls"
+        android:id="@+id/menu_button"
         android:layout_gravity="center" >
 
         <View
@@ -40,9 +38,6 @@
             android:layout_marginBottom="-2dip"
             android:layout_marginRight="-5dip" />
 
-        <include layout="@layout/review_module_control"
-            android:layout_marginBottom="2dip" />
-
         <com.android.camera.ui.PieMenuButton
             android:id="@+id/menu"
             style="@style/SwitcherButton"
             android:layout_marginBottom="2dip"
             android:contentDescription="@string/accessibility_menu_button" />
 
-    </FrameLayout>
+    </com.android.camera.ui.RotatableLayout>
+
+   <com.android.camera.ui.RotatableLayout
+        style="@style/CameraControls"
+        android:id="@+id/switcher_control"
+        android:layout_gravity="bottom|center_horizontal">
+
+       <com.android.camera.ui.CameraSwitcher
+           android:id="@+id/camera_switcher"
+           style="@style/SwitcherButton"
+           android:layout_gravity="bottom|left"
+           android:layout_marginBottom="2dip"
+           android:contentDescription="@string/accessibility_mode_picker" />
+    </com.android.camera.ui.RotatableLayout>
+
+    <com.android.camera.ShutterButton
+        android:id="@+id/shutter_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="bottom|center_horizontal"
+        android:layout_marginBottom="@dimen/shutter_offset"
+        android:clickable="true"
+        android:contentDescription="@string/accessibility_shutter_button"
+        android:focusable="true"
+        android:scaleType="center"
+        android:src="@drawable/btn_new_shutter" />
 
-</FrameLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/res/layout-port/camera_shutter_switcher.xml b/res/layout-port/camera_shutter_switcher.xml
deleted file mode 100644 (file)
index db73fb0..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_shutter_switcher"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent" >
-
-    <View
-        android:id="@+id/controls"
-        style="@style/CameraControls"
-        android:layout_alignParentBottom="true"
-        android:layout_centerHorizontal="true" />
-
-    <com.android.camera.ShutterButton
-        android:id="@+id/shutter_button"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentBottom="true"
-        android:layout_centerHorizontal="true"
-        android:layout_marginBottom="@dimen/shutter_offset"
-        android:clickable="true"
-        android:contentDescription="@string/accessibility_shutter_button"
-        android:focusable="true"
-        android:scaleType="center"
-        android:src="@drawable/btn_new_shutter" />
-
-    <com.android.camera.ui.CameraSwitcher
-        android:id="@+id/camera_switcher"
-        style="@style/SwitcherButton"
-        android:layout_alignParentBottom="true"
-        android:layout_alignLeft="@id/controls"
-        android:layout_marginBottom="2dip"
-        android:contentDescription="@string/accessibility_mode_picker" />
-</RelativeLayout>
\ No newline at end of file
index 762447e..d9c9877 100644 (file)
@@ -15,7 +15,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/camera_app_root"
+        android:id="@+id/camera_app"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
         android:orientation="vertical">
index 5497754..3c4280e 100644 (file)
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent">
-    <com.android.camera.ui.RotateImageView android:id="@+id/btn_done"
+<com.android.camera.ui.RotatableLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+        style="@style/CameraControls"
+        android:layout_gravity="bottom|center_horizontal"
+        android:layout_marginBottom="2dip">
+    <ImageView android:id="@+id/btn_done"
             style="@style/ReviewControlIcon"
             android:contentDescription="@string/accessibility_review_ok"
             android:visibility="gone"
+            android:scaleType="center"
             android:layout_gravity="right|bottom"
             android:background="@drawable/bg_pressed"
             android:src="@drawable/ic_menu_done_holo_light" />
         android:background="@drawable/bg_pressed"
         android:src="@drawable/ic_btn_shutter_retake" />
 
-    <com.android.camera.ui.RotateImageView android:id="@+id/btn_cancel"
+    <ImageView android:id="@+id/btn_cancel"
             style="@style/ReviewControlIcon"
             android:contentDescription="@string/accessibility_review_cancel"
             android:visibility="gone"
+            android:scaleType="center"
             android:layout_gravity="left|bottom"
             android:background="@drawable/bg_pressed"
             android:src="@drawable/ic_menu_cancel_holo_light" />
-</FrameLayout>
+</com.android.camera.ui.RotatableLayout>
index b1481a3..8fe09a3 100644 (file)
@@ -18,8 +18,7 @@
     android:orientation="vertical"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_alignBottom="@id/camera_switcher"
-    android:layout_alignLeft="@id/camera_switcher"
+    android:layout_gravity="bottom|left"
     android:layout_marginLeft="8dip"
     android:layout_marginBottom="8dip"
     android:paddingLeft="16dip"
diff --git a/res/layout-port/video_module.xml b/res/layout-port/video_module.xml
deleted file mode 100644 (file)
index d8a6490..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2012 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<!-- This layout is shared by phone and tablet in landscape orientation. -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_app_root"
-    android:layout_height="match_parent"
-    android:layout_width="match_parent">
-    <include layout="@layout/preview_module_frame_video"/>
-
-    <RelativeLayout
-        style="@style/CameraControls"
-        android:layout_centerHorizontal="true" >
-
-        <View
-            android:id="@+id/blocker"
-            android:layout_width="match_parent"
-            android:layout_height="@dimen/switcher_size"
-            android:background="@drawable/switcher_bg"
-            android:clickable="true"
-            android:layout_alignParentBottom="true" />
-
-        <include layout="@layout/menu_indicators"
-            android:layout_width="80dip"
-            android:layout_height="80dip"
-            android:layout_marginRight="-5dip"
-            android:layout_marginBottom="-2dip"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentRight="true" />
-
-        <include layout="@layout/bg_replacement_training_message"/>
-
-        <include layout="@layout/review_module_control"
-            android:layout_marginBottom="2dip" />
-
-        <com.android.camera.ui.PieMenuButton
-            android:id="@+id/menu"
-            style="@style/SwitcherButton"
-            android:contentDescription="@string/accessibility_menu_button"
-            android:layout_alignParentBottom="true"
-            android:layout_alignParentRight="true"
-            android:layout_marginBottom="2dip" />
-
-    </RelativeLayout>
-
-</RelativeLayout>
index f5240fe..657c06c 100644 (file)
     <include layout="@layout/gl_root_group" />
 
     <FrameLayout
-        android:id="@+id/main_content"
+        android:id="@+id/camera_app_root"
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-    <include layout="@layout/camera_shutter_switcher" />
+    <include layout="@layout/camera_controls" />
 
 </RelativeLayout>
\ No newline at end of file
index dd9b66e..4042f71 100644 (file)
     xmlns:tools="http://schemas.android.com/tools"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:orientation="horizontal"
+    android:gravity="center_horizontal"
+    android:orientation="vertical"
+    android:padding="10dip"
     android:background="@drawable/filtershow_button_background">
 
     <ImageView
-        android:id="@+id/selectedMark"
-        android:src="@drawable/camera_crop"
-        android:background="@android:color/transparent"
-        android:layout_width="32dip"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:visibility="invisible"
-        >
-    </ImageView>
+            android:id="@+id/preview"
+            android:background="@android:color/transparent"
+            android:layout_width="match_parent"
+            android:layout_height="128dip"
+            android:scaleType="centerCrop"
+            android:cropToPadding="true"
+            android:paddingTop="10dip"
+            android:visibility="visible"
+            />
 
     <TextView
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/rowTextView"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1"
-        android:padding="10dip"
-        android:textSize="16dip" >
+            xmlns:android="http://schemas.android.com/apk/res/android"
+            android:id="@+id/rowTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:gravity="center_horizontal"
+            android:padding="5dip"
+            android:textSize="16dip">
     </TextView>
 
-    <ImageView
-        android:id="@+id/typeMark"
-        android:src="@drawable/filtershow_button_origin"
-        android:background="@android:color/transparent"
-        android:layout_width="32dip"
-        android:layout_height="match_parent"
-        android:scaleType="centerInside"
-        android:paddingRight="4dip"
-        android:visibility="visible"
-        >
-    </ImageView>
-
 </LinearLayout>
\ No newline at end of file
index 901bb6b..9ecbd07 100644 (file)
      limitations under the License.
 -->
 
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/pano_layout"
     android:layout_width="match_parent"
     android:layout_height="match_parent">
     <include layout="@layout/pano_module_capture" />
     <include layout="@layout/pano_review" />
-</RelativeLayout>
+</merge>
index b2ad702..abf094e 100644 (file)
  need to be recreated in onConfigurationChanged from old photo_module to this
  layout. -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/camera_app_root"
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="match_parent">
+    android:layout_height="match_parent"
+    android:layout_gravity="center">
     <include layout="@layout/count_down_to_capture"/>
-    <include layout="@layout/photo_module_content"/>
-</FrameLayout>
\ No newline at end of file
+
+    <ViewStub android:id="@+id/face_view_stub"
+        android:inflatedId="@+id/face_view"
+        android:layout="@layout/face_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone"/>
+    <com.android.camera.ui.RenderOverlay
+        android:id="@+id/render_overlay"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+</merge>
\ No newline at end of file
diff --git a/res/layout/preview_module_frame.xml b/res/layout/preview_module_frame.xml
deleted file mode 100644 (file)
index 66094c9..0000000
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/frame_layout"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layout_weight="1">
-    <com.android.camera.PreviewFrameLayout android:id="@+id/frame"
-            android:layout_centerInParent="true"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent">
-        <include layout="@layout/preview_surface_view"/>
-        <ViewStub android:id="@+id/face_view_stub"
-                android:inflatedId="@+id/face_view"
-                android:layout="@layout/face_view"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:visibility="gone"/>
-        <com.android.camera.ui.RenderOverlay
-            android:id="@+id/render_overlay"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent" />
-    </com.android.camera.PreviewFrameLayout>
-    <ImageView android:id="@+id/capture_anim_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layerType="hardware"
-            android:visibility="gone"/>
-</RelativeLayout>
diff --git a/res/layout/preview_surface_view.xml b/res/layout/preview_surface_view.xml
deleted file mode 100644 (file)
index cdaf0ee..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (c) 2012, The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<com.android.camera.ui.PreviewSurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/preview_surface_view"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:visibility="gone"/>
similarity index 75%
rename from res/layout/preview_module_frame_video.xml
rename to res/layout/video_module.xml
index 3418faf..790f3eb 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2007 The Android Open Source Project
+<!-- Copyright (C) 2012 The Android Open Source Project
 
      Licensed under the Apache License, Version 2.0 (the "License");
      you may not use this file except in compliance with the License.
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/frame_layout"
-        android:layout_height="match_parent"
-        android:layout_width="match_parent"
-        android:layout_weight="1">
+<!-- This layout is shared by phone and tablet in landscape orientation. -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/camera_app_root"
+    android:layout_height="match_parent"
+    android:layout_width="match_parent">
     <com.android.camera.PreviewFrameLayout android:id="@+id/frame"
             android:layout_height="match_parent"
             android:layout_width="match_parent"
-            android:layout_centerInParent="true">
-        <include layout="@layout/preview_surface_view"/>
+            android:layout_gravity="center">
+        <com.android.camera.ui.PreviewSurfaceView
+                android:id="@+id/preview_surface_view"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:visibility="gone"/>
         <FrameLayout android:id="@+id/preview_border"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
@@ -41,7 +45,7 @@
                 android:layout_width="match_parent"
                 android:visibility="gone"
                 android:background="@android:color/black"/>
-        <com.android.camera.ui.RotateImageView
+        <ImageView
                 android:id="@+id/btn_play"
                 style="@style/ReviewControlIcon"
                 android:layout_centerInParent="true"
                 android:onClick="onReviewPlayClicked"/>
     </com.android.camera.PreviewFrameLayout>
 
-    <ImageView android:id="@+id/capture_anim_view"
-            android:layout_width="match_parent"
-            android:layout_height="match_parent"
-            android:layerType="hardware"
-            android:visibility="gone"/>
-
-</RelativeLayout>
-
+</merge>
index 4ea7d17..f62bf39 100644 (file)
@@ -2,18 +2,19 @@
     <item
         android:id="@+id/menu_share"
         android:actionProviderClass="android.widget.ShareActionProvider"
-        android:showAsAction="always"
+        android:showAsAction="never"
         android:enabled="false"
+        android:visible="false"
         android:title="@string/share"/>
     <item
         android:id="@+id/undoButton"
         android:icon="@drawable/filtershow_button_undo"
-        android:showAsAction="never"
+        android:showAsAction="always"
         android:title="@string/filtershow_undo"/>
     <item
         android:id="@+id/redoButton"
         android:icon="@drawable/filtershow_button_redo"
-        android:showAsAction="never"
+        android:showAsAction="always"
         android:title="@string/filtershow_redo"/>
     <item
         android:id="@+id/resetHistoryButton"
index f7734bb..581c761 100644 (file)
@@ -89,4 +89,6 @@
     <dimen name="zoom_font_size">14pt</dimen>
     <dimen name="shutter_offset">-22dp</dimen>
     <dimen name="margin_systemui_offset">6dip</dimen>
+    <dimen name="size_thumbnail">200dip</dimen>
+    <dimen name="size_preview">600dip</dimen>
 </resources>
index b5ff011..eed3470 100644 (file)
@@ -41,6 +41,7 @@ import android.view.WindowManager;
 import android.widget.FrameLayout;
 
 import com.android.camera.ui.CameraSwitcher;
+import com.android.camera.ui.RotatableLayout;
 import com.android.gallery3d.R;
 import com.android.gallery3d.app.PhotoPage;
 import com.android.gallery3d.common.ApiHelper;
@@ -57,8 +58,10 @@ public class CameraActivity extends ActivityBase
     private FrameLayout mFrame;
     private ShutterButton mShutter;
     private CameraSwitcher mSwitcher;
-    private View mShutterSwitcher;
+    private View mCameraControls;
     private View mControlsBackground;
+    private View mPieMenuButton;
+    private View mSwitcherControl;
     private Drawable[] mDrawables;
     private int mCurrentModuleIndex;
     private MotionEvent mDown;
@@ -94,7 +97,7 @@ public class CameraActivity extends ActivityBase
     public void onCreate(Bundle state) {
         super.onCreate(state);
         setContentView(R.layout.camera_main);
-        mFrame = (FrameLayout) findViewById(R.id.main_content);
+        mFrame = (FrameLayout) findViewById(R.id.camera_app_root);
         mDrawables = new Drawable[DRAW_IDS.length];
         for (int i = 0; i < DRAW_IDS.length; i++) {
             mDrawables[i] = getResources().getDrawable(DRAW_IDS[i]);
@@ -115,10 +118,12 @@ public class CameraActivity extends ActivityBase
     }
 
     public void init() {
-        mControlsBackground = findViewById(R.id.controls);
-        mShutterSwitcher = findViewById(R.id.camera_shutter_switcher);
+        mControlsBackground = findViewById(R.id.blocker);
+        mCameraControls = findViewById(R.id.camera_controls);
         mShutter = (ShutterButton) findViewById(R.id.shutter_button);
         mSwitcher = (CameraSwitcher) findViewById(R.id.camera_switcher);
+        mPieMenuButton = findViewById(R.id.menu_button);
+        mSwitcherControl = findViewById(R.id.switcher_control);
         int totaldrawid = (LightCycleHelper.hasLightCycleCapture(this)
                                 ? DRAW_IDS.length : DRAW_IDS.length - 1);
         if (!ApiHelper.HAS_OLD_PANORAMA) totaldrawid--;
@@ -217,6 +222,11 @@ public class CameraActivity extends ActivityBase
                 mCurrentModule = LightCycleHelper.createPanoramaModule();
                 break;
         }
+        if (mCurrentModule.needsPieMenu()) {
+            mPieMenuButton.setVisibility(View.VISIBLE);
+        } else {
+            mPieMenuButton.setVisibility(View.INVISIBLE);
+        }
         openModule(mCurrentModule, canReuse);
         mCurrentModule.onOrientationChanged(mLastRawOrientation);
         if (mMediaSaveService != null) {
@@ -268,13 +278,13 @@ public class CameraActivity extends ActivityBase
     }
 
     public void hideUI() {
-        mControlsBackground.setVisibility(View.INVISIBLE);
+        mCameraControls.setVisibility(View.INVISIBLE);
         hideSwitcher();
         mShutter.setVisibility(View.GONE);
     }
 
     public void showUI() {
-        mControlsBackground.setVisibility(View.VISIBLE);
+        mCameraControls.setVisibility(View.VISIBLE);
         showSwitcher();
         mShutter.setVisibility(View.VISIBLE);
         // Force a layout change to show shutter button
@@ -310,20 +320,10 @@ public class CameraActivity extends ActivityBase
         }
         appRoot.setLayoutParams(lp);
 
-        // remove old switcher, shutter and shutter icon
-        View cameraControlsView = findViewById(R.id.camera_shutter_switcher);
-        appRoot.removeView(cameraControlsView);
-
-        // create new layout with the current orientation
-        LayoutInflater inflater = getLayoutInflater();
-        inflater.inflate(R.layout.camera_shutter_switcher, appRoot);
-        init();
+        // Reset the background after rotation
+        mControlsBackground.setBackgroundResource(0);  // remove the current background
+        mControlsBackground.setBackgroundResource(R.drawable.switcher_bg);
 
-        if (mShowCameraAppView) {
-            showUI();
-        } else {
-            hideUI();
-        }
         mCurrentModule.onConfigurationChanged(config);
     }
 
@@ -466,7 +466,7 @@ public class CameraActivity extends ActivityBase
         if ((mSwitcher != null) && mSwitcher.showsPopup() && !mSwitcher.isInsidePopup(m)) {
             return mSwitcher.onTouch(null, m);
         } else {
-            return mShutterSwitcher.dispatchTouchEvent(m)
+            return mSwitcherControl.dispatchTouchEvent(m)
                     || mCurrentModule.dispatchTouchEvent(m);
         }
     }
index 37eabd0..aa057b9 100644 (file)
@@ -68,6 +68,8 @@ public interface CameraModule {
 
     public boolean needsSwitcher();
 
+    public boolean needsPieMenu();
+
     public void onOrientationChanged(int orientation);
 
     public void onShowSwitcherPopup();
index 4edc686..d12c828 100644 (file)
@@ -714,13 +714,13 @@ public class PanoramaModule implements CameraModule,
     }
 
     private void createContentView() {
-        mActivity.getLayoutInflater().inflate(R.layout.panorama_module, (ViewGroup) mRootView);
+        mActivity.getLayoutInflater().inflate(R.layout.panorama_module, (ViewGroup) mRootView, true);
         Resources appRes = mActivity.getResources();
-        mCaptureLayout = (LinearLayout) mRootView.findViewById(R.id.camera_app_root);
+        mCaptureLayout = (LinearLayout) mRootView.findViewById(R.id.camera_app);
         mIndicatorColor = appRes.getColor(R.color.pano_progress_indication);
         mReviewBackground = appRes.getColor(R.color.review_background);
         mIndicatorColorFast = appRes.getColor(R.color.pano_progress_indication_fast);
-        mPanoLayout = (ViewGroup) mRootView.findViewById(R.id.pano_layout);
+        mPanoLayout = (ViewGroup) mRootView.findViewById(R.id.camera_app_root);
         mRotateDialog = new RotateDialogController(mActivity, R.layout.rotate_dialog);
         setViews(appRes);
     }
@@ -1308,6 +1308,11 @@ public class PanoramaModule implements CameraModule,
     }
 
     @Override
+    public boolean needsPieMenu() {
+        return false;
+    }
+
+    @Override
     public void onShowSwitcherPopup() {
     }
 
index e244d6b..664246a 100644 (file)
@@ -52,6 +52,7 @@ import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 import android.view.SurfaceHolder;
 import android.view.View;
+import android.view.ViewStub;
 import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.view.WindowManager;
@@ -66,11 +67,8 @@ import com.android.camera.ui.CountDownView;
 import com.android.camera.ui.FaceView;
 import com.android.camera.ui.PieRenderer;
 import com.android.camera.ui.PopupManager;
-import com.android.camera.ui.PreviewSurfaceView;
 import com.android.camera.ui.RenderOverlay;
-import com.android.camera.ui.Rotatable;
 import com.android.camera.ui.RotateTextToast;
-import com.android.camera.ui.TwoStateImageView;
 import com.android.camera.ui.ZoomRenderer;
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.ApiHelper;
@@ -92,7 +90,6 @@ public class PhotoModule
     FocusOverlayManager.Listener,
     CameraPreference.OnPreferenceChangedListener,
     LocationManager.Listener,
-    PreviewFrameLayout.OnSizeChangedListener,
     ShutterButton.OnShutterButtonListener,
     SurfaceHolder.Callback,
     PieRenderer.PieListener,
@@ -172,18 +169,15 @@ public class PhotoModule
     private ShutterButton mShutterButton;
     private boolean mFaceDetectionStarted = false;
 
-    private PreviewFrameLayout mPreviewFrameLayout;
     private Object mSurfaceTexture;
     private CountDownView mCountDownView;
 
-    // for API level 10
-    private PreviewSurfaceView mPreviewSurfaceView;
     private volatile SurfaceHolder mCameraSurfaceHolder;
 
     private FaceView mFaceView;
     private RenderOverlay mRenderOverlay;
-    private Rotatable mReviewCancelButton;
-    private Rotatable mReviewDoneButton;
+    private View mReviewCancelButton;
+    private View mReviewDoneButton;
     private View mReviewRetakeButton;
 
     // mCropValue and mSaveUri are used only if isImageCaptureIntent() is true.
@@ -288,7 +282,6 @@ public class PhotoModule
     private PhotoController mPhotoControl;
 
     private ZoomRenderer mZoomRenderer;
-
     private String mSceneMode;
     private Toast mNotSelectableToast;
 
@@ -466,7 +459,16 @@ public class PhotoModule
         mCameraStartUpThread = new CameraStartUpThread();
         mCameraStartUpThread.start();
 
-        mActivity.getLayoutInflater().inflate(R.layout.photo_module, (ViewGroup) mRootView);
+        mActivity.getLayoutInflater().inflate(R.layout.photo_module,
+                (ViewGroup) mRootView, true);
+        if (ApiHelper.HAS_FACE_DETECTION) {
+            ViewStub faceViewStub = (ViewStub) mRootView
+                    .findViewById(R.id.face_view_stub);
+            if (faceViewStub != null) {
+                faceViewStub.inflate();
+                mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
+            }
+        }
 
         // Surface texture is from camera screen nail and startPreview needs it.
         // This must be done before startPreview.
@@ -487,7 +489,6 @@ public class PhotoModule
 
         initializeControlByIntent();
         mQuickCapture = mActivity.getIntent().getBooleanExtra(EXTRA_QUICK_CAPTURE, false);
-        initializeMiscControls();
         mLocationManager = new LocationManager(mActivity, this);
         initOnScreenIndicator();
         mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture));
@@ -557,10 +558,10 @@ public class PhotoModule
 
             if (isImageCaptureIntent()) {
                 if (mReviewCancelButton != null) {
-                    mGestures.addTouchReceiver((View) mReviewCancelButton);
+                    mGestures.addTouchReceiver(mReviewCancelButton);
                 }
                 if (mReviewDoneButton != null) {
-                    mGestures.addTouchReceiver((View) mReviewDoneButton);
+                    mGestures.addTouchReceiver(mReviewDoneButton);
                 }
             }
         }
@@ -585,9 +586,17 @@ public class PhotoModule
         initializePhotoControl();
 
         // These depend on camera parameters.
-        setPreviewFrameLayoutAspectRatio();
-        mFocusManager.setPreviewSize(mPreviewFrameLayout.getWidth(),
-                mPreviewFrameLayout.getHeight());
+        int width = mActivity.getWindowManager().getDefaultDisplay().getWidth();
+        int height = mActivity.getWindowManager().getDefaultDisplay().getHeight();
+        mFocusManager.setPreviewSize(width, height);
+        // Full-screen screennail
+        if (Util.getDisplayRotation(mActivity) % 180 == 0) {
+            ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(width, height);
+        } else {
+            ((CameraScreenNail) mActivity.mCameraScreenNail).setPreviewFrameLayoutSize(height, width);
+        }
+        // Set touch focus listener.
+        mActivity.setSingleTapUpListener(mRootView);
         loadCameraPreferences();
         initializeZoom();
         updateOnScreenIndicators();
@@ -795,7 +804,7 @@ public class PhotoModule
     }
 
     private void initOnScreenIndicator() {
-        mOnScreenIndicators = mRootView.findViewById(R.id.on_screen_indicators);
+        mOnScreenIndicators = mActivity.findViewById(R.id.on_screen_indicators);
         mExposureIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_exposure_indicator);
         mFlashIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_flash_indicator);
         mSceneIndicator = (ImageView) mOnScreenIndicators.findViewById(R.id.menu_scenemode_indicator);
@@ -1217,11 +1226,6 @@ public class PhotoModule
             }
             return;
         }
-        if (full) {
-            mPreviewSurfaceView.expand();
-        } else {
-            mPreviewSurfaceView.shrink();
-        }
     }
 
     @Override
@@ -1623,8 +1627,8 @@ public class PhotoModule
     }
 
     private void initializeControlByIntent() {
-        mBlocker = mRootView.findViewById(R.id.blocker);
-        mMenu = mRootView.findViewById(R.id.menu);
+        mBlocker = mActivity.findViewById(R.id.blocker);
+        mMenu = mActivity.findViewById(R.id.menu);
         mMenu.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -1637,23 +1641,22 @@ public class PhotoModule
             }
         });
         if (mIsImageCaptureIntent) {
-
             mActivity.hideSwitcher();
-            // Cannot use RotateImageView for "done" and "cancel" button because
-            // the tablet layout uses RotateLayout, which cannot be cast to
-            // RotateImageView.
-            mReviewDoneButton = (Rotatable) mRootView.findViewById(R.id.btn_done);
-            mReviewCancelButton = (Rotatable) mRootView.findViewById(R.id.btn_cancel);
-            mReviewRetakeButton = mRootView.findViewById(R.id.btn_retake);
-            ((View) mReviewCancelButton).setVisibility(View.VISIBLE);
-
-            ((View) mReviewDoneButton).setOnClickListener(new OnClickListener() {
+            ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls);
+            mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
+
+            mReviewDoneButton = mActivity.findViewById(R.id.btn_done);
+            mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel);
+            mReviewRetakeButton = mActivity.findViewById(R.id.btn_retake);
+            mReviewCancelButton.setVisibility(View.VISIBLE);
+
+            mReviewDoneButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     onReviewDoneClicked(v);
                 }
             });
-            ((View) mReviewCancelButton).setOnClickListener(new OnClickListener() {
+            mReviewCancelButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     onReviewCancelClicked(v);
@@ -1667,13 +1670,6 @@ public class PhotoModule
                 }
             });
 
-            // Not grayed out upon disabled, to make the follow-up fade-out
-            // effect look smooth. Note that the review done button in tablet
-            // layout is not a TwoStateImageView.
-            if (mReviewDoneButton instanceof TwoStateImageView) {
-                ((TwoStateImageView) mReviewDoneButton).enableFilter(false);
-            }
-
             setupCaptureParams();
         }
     }
@@ -1700,62 +1696,10 @@ public class PhotoModule
         }
     }
 
-    private void initializeMiscControls() {
-        // startPreview needs this.
-        mPreviewFrameLayout = (PreviewFrameLayout) mRootView.findViewById(R.id.frame);
-        // Set touch focus listener.
-        mActivity.setSingleTapUpListener(mPreviewFrameLayout);
-
-        mFaceView = (FaceView) mRootView.findViewById(R.id.face_view);
-        mPreviewFrameLayout.setOnSizeChangedListener(this);
-        mPreviewFrameLayout.setOnLayoutChangeListener(mActivity);
-        if (!ApiHelper.HAS_SURFACE_TEXTURE) {
-            mPreviewSurfaceView =
-                    (PreviewSurfaceView) mRootView.findViewById(R.id.preview_surface_view);
-            mPreviewSurfaceView.setVisibility(View.VISIBLE);
-            mPreviewSurfaceView.getHolder().addCallback(this);
-        }
-    }
-
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         Log.v(TAG, "onConfigurationChanged");
         setDisplayOrientation();
-
-        // Only the views in photo_module_content need to be removed and recreated
-        // i.e. CountDownView won't be recreated
-        ViewGroup viewGroup = (ViewGroup) mRootView.findViewById(R.id.camera_app);
-        viewGroup.removeAllViews();
-        LayoutInflater inflater = mActivity.getLayoutInflater();
-        inflater.inflate(R.layout.photo_module_content, (ViewGroup) viewGroup);
-
-        // from onCreate()
-        initializeControlByIntent();
-
-        initializeFocusManager();
-        initializeMiscControls();
-        loadCameraPreferences();
-
-        // from initializeFirstTime()
-        mShutterButton = mActivity.getShutterButton();
-        mShutterButton.setOnShutterButtonListener(this);
-        initializeZoom();
-        initOnScreenIndicator();
-        updateOnScreenIndicators();
-        if (mFaceView != null) {
-            mFaceView.clear();
-            mFaceView.setVisibility(View.VISIBLE);
-            mFaceView.setDisplayOrientation(mDisplayOrientation);
-            CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId];
-            mFaceView.setMirror(info.facing == CameraInfo.CAMERA_FACING_FRONT);
-            mFaceView.resume();
-            mFocusManager.setFaceView(mFaceView);
-        }
-        initializeRenderOverlay();
-        onFullScreenChanged(mActivity.isInCameraApp());
-        if (mJpegImageData != null) {  // Jpeg data found, picture has been taken.
-            showPostCaptureAlert();
-        }
     }
 
     @Override
@@ -2258,7 +2202,7 @@ public class PhotoModule
         if (mIsImageCaptureIntent) {
             mOnScreenIndicators.setVisibility(View.GONE);
             mMenu.setVisibility(View.GONE);
-            Util.fadeIn((View) mReviewDoneButton);
+            Util.fadeIn(mReviewDoneButton);
             mShutterButton.setVisibility(View.INVISIBLE);
             Util.fadeIn(mReviewRetakeButton);
         }
@@ -2268,7 +2212,7 @@ public class PhotoModule
         if (mIsImageCaptureIntent) {
             mOnScreenIndicators.setVisibility(View.VISIBLE);
             mMenu.setVisibility(View.VISIBLE);
-            Util.fadeOut((View) mReviewDoneButton);
+            Util.fadeOut(mReviewDoneButton);
             mShutterButton.setVisibility(View.VISIBLE);
             Util.fadeOut(mReviewRetakeButton);
         }
@@ -2284,7 +2228,6 @@ public class PhotoModule
         mLocationManager.recordLocation(recordLocation);
 
         setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
-        setPreviewFrameLayoutAspectRatio();
         updateOnScreenIndicators();
     }
 
@@ -2431,12 +2374,6 @@ public class PhotoModule
                 Util.FOCUS_MODE_CONTINUOUS_PICTURE);
     }
 
-    // PreviewFrameLayout size has changed.
-    @Override
-    public void onSizeChanged(int width, int height) {
-        if (mFocusManager != null) mFocusManager.setPreviewSize(width, height);
-    }
-
     @Override
     public void onCountDownFinished() {
         mSnapshotOnIdle = false;
@@ -2444,17 +2381,16 @@ public class PhotoModule
         mFocusManager.onShutterUp();
     }
 
-    void setPreviewFrameLayoutAspectRatio() {
-        // Set the preview frame aspect ratio according to the picture size.
-        Size size = mParameters.getPictureSize();
-        mPreviewFrameLayout.setAspectRatio((double) size.width / size.height);
-    }
-
     @Override
     public boolean needsSwitcher() {
         return !mIsImageCaptureIntent;
     }
 
+    @Override
+    public boolean needsPieMenu() {
+        return true;
+    }
+
     public void showPopup(AbstractSettingPopup popup) {
         mActivity.hideUI();
         mBlocker.setVisibility(View.INVISIBLE);
index 87e3c8d..03ef91c 100644 (file)
@@ -54,15 +54,6 @@ public class PreviewFrameLayout extends RelativeLayout implements LayoutChangeNo
     @Override
     protected void onFinishInflate() {
         mBorder = findViewById(R.id.preview_border);
-        if (ApiHelper.HAS_FACE_DETECTION) {
-            ViewStub faceViewStub = (ViewStub) findViewById(R.id.face_view_stub);
-            /* preview_frame_video.xml does not have face view stub, so we need to
-             * check that.
-             */
-            if (faceViewStub != null) {
-                faceViewStub.inflate();
-            }
-        }
     }
 
     public void setAspectRatio(double ratio) {
index a1bbb1a..41775a1 100755 (executable)
 package com.android.camera;
 
 import android.content.Context;
+import android.content.res.Configuration;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.view.View;
 import android.widget.ImageView;
 
+import com.android.camera.ui.RotatableLayout;
+
 /**
  * A button designed to be used for the on-screen shutter button.
  * It's currently an {@code ImageView} that can call a delegate when the
@@ -127,4 +130,10 @@ public class ShutterButton extends ImageView {
         }
         return result;
     }
+
+    @Override
+    public void onConfigurationChanged(Configuration config) {
+        super.onConfigurationChanged(config);
+        RotatableLayout.rotate(this, config.orientation == Configuration.ORIENTATION_PORTRAIT);
+    }
 }
index 245ef59..7bda657 100644 (file)
@@ -73,7 +73,6 @@ import com.android.camera.ui.Rotatable;
 import com.android.camera.ui.RotateImageView;
 import com.android.camera.ui.RotateLayout;
 import com.android.camera.ui.RotateTextToast;
-import com.android.camera.ui.TwoStateImageView;
 import com.android.camera.ui.ZoomRenderer;
 import com.android.gallery3d.R;
 import com.android.gallery3d.common.ApiHelper;
@@ -142,14 +141,13 @@ public class VideoModule implements CameraModule,
     private SurfaceHolder.Callback mSurfaceViewCallback;
     private PreviewSurfaceView mPreviewSurfaceView;
     private CameraScreenNail.OnFrameDrawnListener mFrameDrawnListener;
-    private View mReviewControl;
 
     // An review image having same size as preview. It is displayed when
     // recording is stopped in capture intent.
     private ImageView mReviewImage;
-    private Rotatable mReviewCancelButton;
-    private Rotatable mReviewDoneButton;
-    private RotateImageView mReviewPlayButton;
+    private View mReviewCancelButton;
+    private View mReviewDoneButton;
+    private View mReviewPlayButton;
     private ShutterButton mShutterButton;
     private TextView mRecordingTimeView;
     private RotateLayout mBgLearningMessageRotater;
@@ -414,13 +412,13 @@ public class VideoModule implements CameraModule,
 
         if (isVideoCaptureIntent()) {
             if (mReviewCancelButton != null) {
-                mGestures.addTouchReceiver((View) mReviewCancelButton);
+                mGestures.addTouchReceiver(mReviewCancelButton);
             }
             if (mReviewDoneButton != null) {
-                mGestures.addTouchReceiver((View) mReviewDoneButton);
+                mGestures.addTouchReceiver(mReviewDoneButton);
             }
             if (mReviewPlayButton != null) {
-                mGestures.addTouchReceiver((View) mReviewPlayButton);
+                mGestures.addTouchReceiver(mReviewPlayButton);
             }
         }
     }
@@ -449,7 +447,7 @@ public class VideoModule implements CameraModule,
 
         mContentResolver = mActivity.getContentResolver();
 
-        mActivity.getLayoutInflater().inflate(R.layout.video_module, (ViewGroup) mRootView);
+        mActivity.getLayoutInflater().inflate(R.layout.video_module, (ViewGroup) mRootView, true);
 
         // Surface texture is from camera screen nail and startPreview needs it.
         // This must be done before startPreview.
@@ -589,8 +587,7 @@ public class VideoModule implements CameraModule,
 
     private void setOrientationIndicator(int orientation, boolean animation) {
         Rotatable[] indicators = {
-                mBgLearningMessageRotater,
-                mReviewDoneButton, mReviewPlayButton};
+                mBgLearningMessageRotater};
         for (Rotatable indicator : indicators) {
             if (indicator != null) indicator.setOrientation(orientation, animation);
         }
@@ -598,14 +595,6 @@ public class VideoModule implements CameraModule,
             mGestures.setOrientation(orientation);
         }
 
-        // We change the orientation of the review cancel button only for tablet
-        // UI because there's a label along with the X icon. For phone UI, we
-        // don't change the orientation because there's only a symmetrical X
-        // icon.
-        if (mReviewCancelButton instanceof RotateLayout) {
-            mReviewCancelButton.setOrientation(orientation, animation);
-        }
-
         // We change the orientation of the linearlayout only for phone UI because when in portrait
         // the width is not enough.
         if (mLabelsLinearLayout != null) {
@@ -1636,7 +1625,6 @@ public class VideoModule implements CameraModule,
             mActivity.hideSwitcher();
             mRecordingTimeView.setText("");
             mRecordingTimeView.setVisibility(View.VISIBLE);
-            if (mReviewControl != null) mReviewControl.setVisibility(View.GONE);
             // The camera is not allowed to be accessed in older api levels during
             // recording. It is therefore necessary to hide the zoom UI on older
             // platforms.
@@ -1650,7 +1638,6 @@ public class VideoModule implements CameraModule,
             mShutterButton.setImageResource(R.drawable.btn_new_shutter_video);
             mActivity.showSwitcher();
             mRecordingTimeView.setVisibility(View.GONE);
-            if (mReviewControl != null) mReviewControl.setVisibility(View.VISIBLE);
             if (!ApiHelper.HAS_ZOOM_WHEN_RECORDING
                     && mParameters.isZoomSupported()) {
                 // TODO: enable zoom UI here.
@@ -1679,7 +1666,7 @@ public class VideoModule implements CameraModule,
 
         Util.fadeOut(mShutterButton);
 
-        Util.fadeIn((View) mReviewDoneButton);
+        Util.fadeIn(mReviewDoneButton);
         Util.fadeIn(mReviewPlayButton);
         mMenu.setVisibility(View.GONE);
         mOnScreenIndicators.setVisibility(View.GONE);
@@ -1695,7 +1682,7 @@ public class VideoModule implements CameraModule,
         mOnScreenIndicators.setVisibility(View.VISIBLE);
         enableCameraControls(true);
 
-        Util.fadeOut((View) mReviewDoneButton);
+        Util.fadeOut(mReviewDoneButton);
         Util.fadeOut(mReviewPlayButton);
 
         Util.fadeIn(mShutterButton);
@@ -2115,8 +2102,8 @@ public class VideoModule implements CameraModule,
     }
 
     private void initializeControlByIntent() {
-        mBlocker = mRootView.findViewById(R.id.blocker);
-        mMenu = mRootView.findViewById(R.id.menu);
+        mBlocker = mActivity.findViewById(R.id.blocker);
+        mMenu = mActivity.findViewById(R.id.menu);
         mMenu.setOnClickListener(new OnClickListener() {
             @Override
             public void onClick(View v) {
@@ -2125,46 +2112,40 @@ public class VideoModule implements CameraModule,
                 }
             }
         });
-        mOnScreenIndicators = mRootView.findViewById(R.id.on_screen_indicators);
-        mFlashIndicator = (ImageView) mRootView.findViewById(R.id.menu_flash_indicator);
+        mOnScreenIndicators = mActivity.findViewById(R.id.on_screen_indicators);
+        mFlashIndicator = (ImageView) mActivity.findViewById(R.id.menu_flash_indicator);
         if (mIsVideoCaptureIntent) {
             mActivity.hideSwitcher();
+            ViewGroup cameraControls = (ViewGroup) mActivity.findViewById(R.id.camera_controls);
+            mActivity.getLayoutInflater().inflate(R.layout.review_module_control, cameraControls);
             // Cannot use RotateImageView for "done" and "cancel" button because
             // the tablet layout uses RotateLayout, which cannot be cast to
             // RotateImageView.
-            mReviewDoneButton = (Rotatable) mRootView.findViewById(R.id.btn_done);
-            mReviewCancelButton = (Rotatable) mRootView.findViewById(R.id.btn_cancel);
-            mReviewPlayButton = (RotateImageView) mRootView.findViewById(R.id.btn_play);
+            mReviewDoneButton = mActivity.findViewById(R.id.btn_done);
+            mReviewCancelButton = mActivity.findViewById(R.id.btn_cancel);
+            mReviewPlayButton = mActivity.findViewById(R.id.btn_play);
 
-            ((View) mReviewCancelButton).setVisibility(View.VISIBLE);
+            mReviewCancelButton.setVisibility(View.VISIBLE);
 
-            ((View) mReviewDoneButton).setOnClickListener(new OnClickListener() {
+            mReviewDoneButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     onReviewDoneClicked(v);
                 }
             });
-            ((View) mReviewCancelButton).setOnClickListener(new OnClickListener() {
+            mReviewCancelButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     onReviewCancelClicked(v);
                 }
             });
 
-            ((View) mReviewPlayButton).setOnClickListener(new OnClickListener() {
+            mReviewPlayButton.setOnClickListener(new OnClickListener() {
                 @Override
                 public void onClick(View v) {
                     onReviewPlayClicked(v);
                 }
             });
-
-
-            // Not grayed out upon disabled, to make the follow-up fade-out
-            // effect look smooth. Note that the review done button in tablet
-            // layout is not a TwoStateImageView.
-            if (mReviewDoneButton instanceof TwoStateImageView) {
-                ((TwoStateImageView) mReviewDoneButton).enableFilter(false);
-            }
         }
     }
 
@@ -2203,28 +2184,8 @@ public class VideoModule implements CameraModule,
 
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
+        Log.v(TAG, "onConfigurationChanged");
         setDisplayOrientation();
-        // Change layout in response to configuration change
-        LayoutInflater inflater = mActivity.getLayoutInflater();
-        ((ViewGroup) mRootView).removeAllViews();
-        inflater.inflate(R.layout.video_module, (ViewGroup) mRootView);
-
-        // from onCreate()
-        initializeControlByIntent();
-        initializeOverlay();
-        initializeSurfaceView();
-        initializeMiscControls();
-        showTimeLapseUI(mCaptureTimeLapse);
-        initializeVideoSnapshot();
-
-        // from onResume()
-        showVideoSnapshotUI(false);
-        initializeZoom();
-        onFullScreenChanged(mActivity.isInCameraApp());
-        updateOnScreenIndicators();
-        if (mIsVideoCaptureIntent && mVideoFileDescriptor != null) {
-            showCaptureResult();
-        }
     }
 
     @Override
@@ -2785,6 +2746,11 @@ public class VideoModule implements CameraModule,
     }
 
     @Override
+    public boolean needsPieMenu() {
+        return true;
+    }
+
+    @Override
     public void onPieOpened(int centerX, int centerY) {
         mActivity.cancelActivityTouchHandling();
         mActivity.setSwipingEnabled(false);
index 8d2cd71..3fa4a01 100644 (file)
@@ -30,6 +30,7 @@ import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
+import android.widget.FrameLayout.LayoutParams;
 import android.widget.LinearLayout;
 
 import com.android.gallery3d.R;
@@ -177,6 +178,16 @@ public class CameraSwitcher extends RotateImageView
         mParent.setOnTouchListener(null);
     }
 
+    @Override
+    public void onConfigurationChanged(Configuration config) {
+        if (showsPopup()) {
+            ((ViewGroup) mParent).removeView(mPopup);
+            mPopup = null;
+            initPopup();
+            mPopup.setVisibility(View.VISIBLE);
+        }
+    }
+
     private void showSwitcher() {
         mShowingPopup = true;
         if (mPopup == null) {
@@ -245,6 +256,8 @@ public class CameraSwitcher extends RotateImageView
                     // Verify that we weren't canceled
                     if (!showsPopup()) {
                         mPopup.setVisibility(View.INVISIBLE);
+                        ((ViewGroup) mParent).removeView(mPopup);
+                        mPopup = null;
                     }
                 }
             };
diff --git a/src/com/android/camera/ui/RotatableLayout.java b/src/com/android/camera/ui/RotatableLayout.java
new file mode 100644 (file)
index 0000000..9c5ebd3
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * 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.camera.ui;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.FrameLayout;
+
+/* RotatableLayout rotates itself as well as all its children when orientation
+ * changes. Specifically, when going from portrait to landscape, camera
+ * controls move from the bottom of the screen to right side of the screen
+ * (i.e. counter clockwise). Similarly, when the screen changes to portrait, we
+ * need to move the controls from right side to the bottom of the screen, which
+ * is a clockwise rotation.
+ */
+
+public class RotatableLayout extends FrameLayout {
+
+    public RotatableLayout(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public RotatableLayout(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public RotatableLayout(Context context) {
+        super(context);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration config) {
+        super.onConfigurationChanged(config);
+        // rotate the layout itself and all its children
+        boolean clockwise = (config.orientation == Configuration.ORIENTATION_PORTRAIT);
+        rotate(this, clockwise);
+        int childCount = getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            View child = getChildAt(i);
+            rotate(child, clockwise);
+        }
+    }
+
+    public static void rotate(View view, boolean isClockwise) {
+        if (isClockwise) {
+            rotateClockwise(view);
+        } else {
+            rotateCounterClockwise(view);
+        }
+    }
+
+    private static boolean contains(int value, int mask) {
+        return (value & mask) == mask;
+    }
+
+    public static void rotateClockwise(View view) {
+        if (view == null) return;
+        LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        int gravity = lp.gravity;
+        int ngravity = 0;
+        // rotate gravity
+        if (contains(gravity, Gravity.LEFT)) {
+            ngravity |= Gravity.TOP;
+        }
+        if (contains(gravity, Gravity.RIGHT)) {
+            ngravity |= Gravity.BOTTOM;
+        }
+        if (contains(gravity, Gravity.TOP)) {
+            ngravity |= Gravity.RIGHT;
+        }
+        if (contains(gravity, Gravity.BOTTOM)) {
+            ngravity |= Gravity.LEFT;
+        }
+        if (contains(gravity, Gravity.CENTER)) {
+            ngravity |= Gravity.CENTER;
+        }
+        if (contains(gravity, Gravity.CENTER_HORIZONTAL)) {
+            ngravity |= Gravity.CENTER_VERTICAL;
+        }
+        if (contains(gravity, Gravity.CENTER_VERTICAL)) {
+            ngravity |= Gravity.CENTER_HORIZONTAL;
+        }
+        lp.gravity = ngravity;
+        int ml = lp.leftMargin;
+        int mr = lp.rightMargin;
+        int mt = lp.topMargin;
+        int mb = lp.bottomMargin;
+        lp.leftMargin = mb;
+        lp.rightMargin = mt;
+        lp.topMargin = ml;
+        lp.bottomMargin = mr;
+        int width = lp.width;
+        int height = lp.height;
+        lp.width = height;
+        lp.height = width;
+        view.setLayoutParams(lp);
+    }
+
+    public static void rotateCounterClockwise(View view) {
+        if (view == null) return;
+        LayoutParams lp = (LayoutParams) view.getLayoutParams();
+        int gravity = lp.gravity;
+        int ngravity = 0;
+        // change gravity
+        if (contains(gravity, Gravity.RIGHT)) {
+            ngravity |= Gravity.TOP;
+        }
+        if (contains(gravity, Gravity.LEFT)) {
+            ngravity |= Gravity.BOTTOM;
+        }
+        if (contains(gravity, Gravity.TOP)) {
+            ngravity |= Gravity.LEFT;
+        }
+        if (contains(gravity, Gravity.BOTTOM)) {
+            ngravity |= Gravity.RIGHT;
+        }
+        if (contains(gravity, Gravity.CENTER)) {
+            ngravity |= Gravity.CENTER;
+        }
+        if (contains(gravity, Gravity.CENTER_HORIZONTAL)) {
+            ngravity |= Gravity.CENTER_VERTICAL;
+        }
+        if (contains(gravity, Gravity.CENTER_VERTICAL)) {
+            ngravity |= Gravity.CENTER_HORIZONTAL;
+        }
+        lp.gravity = ngravity;
+        int ml = lp.leftMargin;
+        int mr = lp.rightMargin;
+        int mt = lp.topMargin;
+        int mb = lp.bottomMargin;
+        lp.leftMargin = mt;
+        lp.rightMargin = mb;
+        lp.topMargin = mr;
+        lp.bottomMargin = ml;
+        int width = lp.width;
+        int height = lp.height;
+        lp.width = height;
+        lp.height = width;
+        view.setLayoutParams(lp);
+    }
+}
\ No newline at end of file
index 26a70d4..4b59565 100644 (file)
@@ -123,6 +123,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
     protected static final boolean ANIMATE_PANELS = true;
     private static int mImageBorderSize = 4; // in percent
 
+    private boolean mShowingTinyPlanet = false;
     private boolean mShowingHistoryPanel = false;
     private boolean mShowingImageStatePanel = false;
 
@@ -150,6 +151,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         fillEditors();
 
         loadXML();
+        if (getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE) {
+            mShowingImageStatePanel = true;
+        }
+
         setDefaultPreset();
 
         processIntent();
@@ -194,11 +200,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         mPanelController.addPanel(R.id.geometryButton, R.id.geometryList, 2);
         mPanelController.addPanel(R.id.colorsButton, R.id.colorsFxList, 3);
 
-        fillFilters();
-        fillGeometry();
         fillFx((LinearLayout) findViewById(R.id.listFilters), R.id.fxButton);
         LoadBordersTask loadBorders = new LoadBordersTask((LinearLayout) findViewById(R.id.listBorders));
         loadBorders.execute();
+        fillGeometry();
+        fillFilters();
 
         mPanelController.addView(findViewById(R.id.applyEffect));
 
@@ -221,24 +227,28 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         mPanelController.setCurrentPanel(R.id.fxButton);
     }
 
+    private void fillPanel(Vector<FilterRepresentation> representations, int layoutId, int buttonId) {
+        ImageButton button = (ImageButton) findViewById(buttonId);
+        LinearLayout layout = (LinearLayout) findViewById(layoutId);
+
+        for (FilterRepresentation representation : representations) {
+            setupFilterRepresentationButton(representation, layout, button);
+        }
+    }
+
     private void fillFilters() {
         Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
-
         FiltersManager filtersManager = FiltersManager.getManager();
         filtersManager.addEffects(filtersRepresentations);
-
-        ImageButton colorsButton = (ImageButton) findViewById(R.id.colorsButton);
-        for (FilterRepresentation representation : filtersRepresentations) {
-            setupFilterRepresentationButton(representation,
-                    (LinearLayout) findViewById(R.id.listColorsFx), colorsButton);
-        }
+        fillPanel(filtersRepresentations, R.id.listColorsFx, R.id.colorsButton);
     }
 
     private void fillGeometry() {
-        // TODO: move to a separate function.
+        Vector<FilterRepresentation> filtersRepresentations = new Vector<FilterRepresentation>();
+        FiltersManager filtersManager = FiltersManager.getManager();
+
         GeometryMetadata geo = new GeometryMetadata();
         int[] editorsId = geo.getEditorIds();
-        ImageButton geometryButton = (ImageButton) findViewById(R.id.geometryButton);
         for (int i = 0; i < editorsId.length; i++) {
             int editorId = editorsId[i];
             GeometryMetadata geometry = new GeometryMetadata(geo);
@@ -247,9 +257,11 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
             geometry.setTextId(editorInfo.getTextId());
             geometry.setOverlayId(editorInfo.getOverlayId());
             geometry.setOverlayOnly(editorInfo.getOverlayOnly());
-            setupFilterRepresentationButton(
-                    geometry, (LinearLayout) findViewById(R.id.listGeometry), geometryButton);
+            filtersRepresentations.add(geometry);
         }
+
+        filtersManager.addTools(filtersRepresentations);
+        fillPanel(filtersRepresentations, R.id.listGeometry, R.id.geometryButton);
     }
 
     private void processIntent() {
@@ -341,6 +353,8 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         ImageShow.setOriginalTextSize((int) getPixelsFromDip(18));
         ImageShow.setOriginalText(res.getString(R.string.original_picture_text));
         mIconSeedSize = res.getDimensionPixelSize(R.dimen.thumbnail_size);
+        // TODO: pick correct value
+        // MasterImage.setIconSeedSize(mIconSeedSize);
 
         Drawable curveHandle = res.getDrawable(R.drawable.camera_crop);
         int curveHandleSize = (int) res.getDimension(R.dimen.crop_indicator_size);
@@ -362,6 +376,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
 
         View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
         if (tinyPlanetView != null) {
+            mShowingTinyPlanet = false;
             tinyPlanetView.setVisibility(View.GONE);
         }
         mLoadBitmapTask = new LoadBitmapTask(tinyPlanetView);
@@ -442,6 +457,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
                 return;
             }
             if (values[0]) {
+                mShowingTinyPlanet = true;
                 mTinyPlanetButton.setVisibility(View.VISIBLE);
             }
         }
@@ -836,7 +852,7 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         final View viewList = findViewById(R.id.imageStatePanel);
 
         if (mShowingHistoryPanel) {
-            findViewById(R.id.historyPanel).setVisibility(View.INVISIBLE);
+            findViewById(R.id.historyPanel).setVisibility(View.GONE);
             mShowingHistoryPanel = false;
         }
 
@@ -856,9 +872,19 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         super.onConfigurationChanged(newConfig);
         setDefaultValues();
         loadXML();
+        if (getResources().getConfiguration().orientation
+                == Configuration.ORIENTATION_LANDSCAPE) {
+            mShowingImageStatePanel = true;
+        }
         if (mShowingHistoryPanel) {
             toggleHistoryPanel();
         }
+        if (mShowingTinyPlanet == false) {
+            View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
+            if (tinyPlanetView != null) {
+                tinyPlanetView.setVisibility(View.GONE);
+            }
+        }
         final View loading = findViewById(R.id.loading);
         loading.setVisibility(View.GONE);
     }
@@ -887,16 +913,19 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
         final View viewList = findViewById(R.id.historyPanel);
 
         if (mShowingImageStatePanel) {
-            findViewById(R.id.imageStatePanel).setVisibility(View.INVISIBLE);
-            mShowingImageStatePanel = false;
+            findViewById(R.id.imageStatePanel).setVisibility(View.GONE);
         }
 
         int translate = translateMainPanel(viewList);
         if (!mShowingHistoryPanel) {
             mShowingHistoryPanel = true;
-            if (PanelController.useAnimations()) {
-                view.animate().setDuration(200).x(translate)
-                    .withLayer().withEndAction(new Runnable() {
+            if (getResources().getConfiguration().orientation
+                    == Configuration.ORIENTATION_PORTRAIT) {
+                // If portrait, always remove the state panel
+                mShowingImageStatePanel = false;
+                if (PanelController.useAnimations()) {
+                    view.animate().setDuration(200).x(translate)
+                            .withLayer().withEndAction(new Runnable() {
                         @Override
                         public void run() {
                             viewList.setAlpha(0);
@@ -905,21 +934,35 @@ public class FilterShowActivity extends Activity implements OnItemClickListener,
                                     .alpha(1.0f).start();
                         }
                     }).start();
+                } else {
+                    view.setX(translate);
+                    viewList.setAlpha(0);
+                    viewList.setVisibility(View.VISIBLE);
+                    viewList.animate().setDuration(100)
+                            .alpha(1.0f).start();
+                }
             } else {
-                view.setX(translate);
-                viewList.setAlpha(0);
+                findViewById(R.id.filtersPanel).setVisibility(View.GONE);
                 viewList.setVisibility(View.VISIBLE);
-                viewList.animate().setDuration(100)
-                        .alpha(1.0f).start();
             }
         } else {
             mShowingHistoryPanel = false;
-            viewList.setVisibility(View.INVISIBLE);
-            if (PanelController.useAnimations()) {
-                view.animate().setDuration(200).x(0).withLayer()
-                    .start();
+            if (getResources().getConfiguration().orientation
+                    == Configuration.ORIENTATION_PORTRAIT) {
+                viewList.setVisibility(View.INVISIBLE);
+                if (PanelController.useAnimations()) {
+                    view.animate().setDuration(200).x(0).withLayer()
+                            .start();
+                } else {
+                    view.setX(0);
+                }
             } else {
-                view.setX(0);
+                viewList.setVisibility(View.GONE);
+                findViewById(R.id.filtersPanel).setVisibility(View.VISIBLE);
+                // In landscape, bring back the state panel if it was there
+                if (mShowingImageStatePanel) {
+                    findViewById(R.id.imageStatePanel).setVisibility(View.VISIBLE);
+                }
             }
         }
         invalidateOptionsMenu();
index c94a007..a10e66b 100644 (file)
@@ -17,6 +17,8 @@
 package com.android.gallery3d.filtershow;
 
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Color;
 import android.view.LayoutInflater;
 import android.view.MenuItem;
 import android.view.View;
@@ -195,30 +197,15 @@ public class HistoryAdapter extends ArrayAdapter<ImagePreset> {
             if (itemView != null) {
                 itemView.setText(item.historyName());
             }
-            ImageView markView = (ImageView) view.findViewById(R.id.selectedMark);
-            if (position == mCurrentPresetPosition) {
-                markView.setVisibility(View.VISIBLE);
-            } else {
-                markView.setVisibility(View.INVISIBLE);
+            ImageView preview = (ImageView) view.findViewById(R.id.preview);
+            Bitmap bmp = item.getPreviewImage();
+            if (bmp != null) {
+                preview.setImageBitmap(bmp);
             }
-            ImageView typeView = (ImageView) view.findViewById(R.id.typeMark);
-            // TODO: use type of last filter, not a string, to discriminate.
-            if (position == getCount() - 1) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_effects);
-            } else if (item.historyName().equalsIgnoreCase(mBorders)) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_border);
-            } else if (item.historyName().equalsIgnoreCase(mStraighten)) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_fix);
-            } else if (item.historyName().equalsIgnoreCase(mCrop)) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_fix);
-            } else if (item.historyName().equalsIgnoreCase(mRotate)) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_fix);
-            } else if (item.historyName().equalsIgnoreCase(mMirror)) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_fix);
-            } else if (item.isFx()) {
-                typeView.setImageResource(R.drawable.ic_photoeditor_effects);
+            if (position == mCurrentPresetPosition) {
+                view.setBackgroundColor(Color.WHITE);
             } else {
-                typeView.setImageResource(R.drawable.ic_photoeditor_color);
+                view.setBackgroundResource(R.drawable.filtershow_button_background);
             }
         }
 
index 3ec74e2..e81f47f 100644 (file)
@@ -27,6 +27,7 @@ public class RenderingRequest {
     private boolean mIsDirect = false;
     private Bitmap mBitmap = null;
     private ImagePreset mImagePreset = null;
+    private ImagePreset mOriginalImagePreset = null;
     private RenderingRequestCaller mCaller = null;
     private Rect mBounds = null;
     private Rect mDestination = null;
@@ -61,6 +62,7 @@ public class RenderingRequest {
         request.setBitmap(bitmap);
         ImagePreset passedPreset = new ImagePreset(preset);
         passedPreset.setImageLoader(MasterImage.getImage().getImageLoader());
+        request.setOriginalImagePreset(preset);
 
         if (type == PARTIAL_RENDERING) {
             request.setBounds(bounds);
@@ -137,4 +139,12 @@ public class RenderingRequest {
     public void setDestination(Rect destination) {
         mDestination = destination;
     }
+
+    public ImagePreset getOriginalImagePreset() {
+        return mOriginalImagePreset;
+    }
+
+    public void setOriginalImagePreset(ImagePreset originalImagePreset) {
+        mOriginalImagePreset = originalImagePreset;
+    }
 }
index 820bdbf..1fe2ac6 100644 (file)
@@ -74,7 +74,6 @@ public abstract class BaseFiltersManager {
 
     public void addEffects(Vector<FilterRepresentation> representations) {
         representations.add(getRepresentation(ImageFilterTinyPlanet.class));
-        representations.add(getRepresentation(ImageFilterRedEye.class));
         representations.add(getRepresentation(ImageFilterWBalance.class));
         representations.add(getRepresentation(ImageFilterExposure.class));
         representations.add(getRepresentation(ImageFilterVignette.class));
@@ -84,7 +83,6 @@ public abstract class BaseFiltersManager {
         representations.add(getRepresentation(ImageFilterVibrance.class));
         representations.add(getRepresentation(ImageFilterSharpen.class));
         representations.add(getRepresentation(ImageFilterCurves.class));
-        representations.add(getRepresentation(ImageFilterDraw.class));
         representations.add(getRepresentation(ImageFilterHue.class));
         representations.add(getRepresentation(ImageFilterSaturated.class));
         representations.add(getRepresentation(ImageFilterBwFilter.class));
@@ -93,6 +91,11 @@ public abstract class BaseFiltersManager {
         representations.add(getRepresentation(ImageFilterKMeans.class));
     }
 
+    public void addTools(Vector<FilterRepresentation> representations) {
+        representations.add(getRepresentation(ImageFilterRedEye.class));
+        representations.add(getRepresentation(ImageFilterDraw.class));
+    }
+
     public void resetBitmapsRS() {
         for (Class c : mFilters.keySet()) {
             ImageFilter filter = mFilters.get(c);
index 8b8504b..dc59b0c 100644 (file)
@@ -54,6 +54,8 @@ public class FilterDrawRepresentation extends FilterRepresentation {
         setTextId(R.string.imageDraw);
         setButtonId(R.id.drawOnImageButton);
         setEditorId(EditorDraw.ID);
+        setOverlayId(R.drawable.filtershow_drawing);
+        setOverlayOnly(true);
     }
 
     @Override
index fb39660..3f823ea 100644 (file)
@@ -30,6 +30,7 @@ public class FilterRedEyeRepresentation extends FilterPointRepresentation {
         super("RedEye",R.string.redeye,EditorRedEye.ID);
         setFilterClass(ImageFilterRedEye.class);
         setOverlayId(R.drawable.photoeditor_effect_redeye);
+        setOverlayOnly(true);
     }
 
     public void addRect(RectF rect, RectF bounds) {
index 926f14a..1edfd79 100644 (file)
@@ -572,6 +572,7 @@ public class ImageShow extends View implements OnGestureListener,
                     translation.y = (int) (originalTranslation.y + translateY);
                     MasterImage.getImage().setTranslation(translation);
                 }
+                mTouchShowOriginal = false;
             } else if (!mOriginalDisabled && !mActivity.isShowingHistoryPanel()
                     && (System.currentTimeMillis() - mTouchShowOriginalDate
                             > mTouchShowOriginalDelayMin)
@@ -629,14 +630,16 @@ public class ImageShow extends View implements OnGestureListener,
         if (mActivity == null) {
             return false;
         }
+        if (endEvent.getPointerCount() == 2) {
+            return false;
+        }
         if ((!mActivity.isShowingHistoryPanel() && startEvent.getX() > endEvent.getX())
                 || (mActivity.isShowingHistoryPanel() && endEvent.getX() > startEvent.getX())) {
             if (!mTouchShowOriginal
                     || (mTouchShowOriginal &&
                             (System.currentTimeMillis() - mTouchShowOriginalDate
                             < mTouchShowOriginalDelayMax))) {
-                // TODO fix gesture.
-                // mActivity.toggleHistoryPanel();
+                mActivity.toggleHistoryPanel();
             }
         }
         return true;
index f43ea6c..c55e5ae 100644 (file)
@@ -150,6 +150,16 @@ public class ImageVignette extends ImageShow {
         if (mVignetteRep == null) {
             return;
         }
+        Matrix toImg = getScreenToImageMatrix(false);
+        Matrix toScr = new Matrix();
+        toImg.invert(toScr);
+        float[] c = new float[] {
+                mVignetteRep.getCenterX(), mVignetteRep.getCenterY() };
+        toScr.mapPoints(c);
+        mElipse.setCenter(c[0], c[1]);
+        mElipse.setRadius(toScr.mapRadius(mVignetteRep.getRadiusX()),
+                toScr.mapRadius(mVignetteRep.getRadiusY()));
+
         mElipse.draw(canvas);
     }
 
index f96d94a..209c3bd 100644 (file)
@@ -35,8 +35,12 @@ import java.util.Vector;
 public class MasterImage implements RenderingRequestCaller {
 
     private static final String LOGTAG = "MasterImage";
+    private boolean DEBUG  = false;
 
     private static MasterImage sMasterImage = null;
+    private static int sIconSeedSize = 128;
+    private static float sHistoryPreviewSize = 128.0f;
+    private Bitmap mThumbnailBitmap;
 
     private ImageFilter mCurrentFilter = null;
     private ImagePreset mPreset = null;
@@ -91,6 +95,10 @@ public class MasterImage implements RenderingRequestCaller {
         return sMasterImage;
     }
 
+    public static void setIconSeedSize(int iconSeedSize) {
+        sIconSeedSize = iconSeedSize;
+    }
+
     public void addObserver(ImageShow observer) {
         if (mObservers.contains(observer)) {
             return;
@@ -125,6 +133,7 @@ public class MasterImage implements RenderingRequestCaller {
         mPreset.fillImageStateAdapter(mState);
         if (addToHistory) {
             mHistory.addHistoryItem(mPreset);
+            renderHistoryPreview();
         }
         updatePresets(true);
         GeometryMetadata geo = mPreset.mGeoData;
@@ -134,6 +143,23 @@ public class MasterImage implements RenderingRequestCaller {
         mPreviousGeometry = new GeometryMetadata(geo);
     }
 
+    private void renderHistoryPreview() {
+        ImagePreset historyPreset = mPreset;
+        if (historyPreset != null) {
+            Bitmap preview = mLoader.getOriginalBitmapSmall();
+            if (preview != null) {
+                float s = Math.min(preview.getWidth(), preview.getHeight());
+                float f = sHistoryPreviewSize / s;
+                int w = (int) (preview.getWidth() * f);
+                int h = (int) (preview.getHeight() * f);
+                Bitmap historyPreview = Bitmap.createScaledBitmap(preview, w, h, true);
+                historyPreset.setPreviewImage(historyPreview);
+                RenderingRequest.post(historyPreview,
+                        historyPreset, RenderingRequest.ICON_RENDERING, this);
+            }
+        }
+    }
+
     private void setGeometry() {
         Bitmap image = mLoader.getOriginalBitmapLarge();
         if (image == null) {
@@ -285,6 +311,7 @@ public class MasterImage implements RenderingRequestCaller {
         invalidatePartialPreview();
         needsUpdateFullResPreview();
         FilteringPipeline.getPipeline().updatePreviewBuffer();
+        renderHistoryPreview();
     }
 
     public void setImageShowSize(int w, int h) {
@@ -349,6 +376,12 @@ public class MasterImage implements RenderingRequestCaller {
             mPartialBitmap = request.getBitmap();
             notifyObservers();
         }
+        if (request.getType() == RenderingRequest.ICON_RENDERING) {
+            // History preview images
+            ImagePreset preset = request.getOriginalImagePreset();
+            preset.setPreviewImage(request.getBitmap());
+            mHistory.notifyDataSetChanged();
+        }
     }
 
     public static void reset() {
@@ -368,11 +401,59 @@ public class MasterImage implements RenderingRequestCaller {
 
     public void hasNewGeometry() {
         updatePresets(true);
+        computeThumbnailBitmap();
         for (GeometryListener listener : mGeometryListeners) {
             listener.geometryChanged();
         }
     }
 
+    private Bitmap createSquareImage(Bitmap dst, Bitmap image, Rect destination) {
+        if (image != null) {
+            Canvas canvas = new Canvas(dst);
+            int iw = image.getWidth();
+            int ih = image.getHeight();
+            int x = 0;
+            int y = 0;
+            int size = 0;
+            Rect source = null;
+            if (iw > ih) {
+                size = ih;
+                x = (int) ((iw - size) / 2.0f);
+                y = 0;
+            } else {
+                size = iw;
+                x = 0;
+                y = (int) ((ih - size) / 2.0f);
+            }
+            source = new Rect(x, y, x + size, y + size);
+            canvas.drawBitmap(image, source, destination, new Paint());
+        }
+        return dst;
+    }
+
+    public void computeThumbnailBitmap() {
+        Bitmap bmap = mLoader.getOriginalBitmapSmall();
+        if (bmap == null) {
+            return;
+        }
+        ImagePreset geoPreset = new ImagePreset(MasterImage.getImage().getGeometryPreset());
+        bmap = geoPreset.applyGeometry(bmap);
+        float w = bmap.getWidth();
+        float h = bmap.getHeight();
+        float s = Math.min(w, h);
+        float f = sIconSeedSize / s;
+        w = w * f;
+        h = h * f;
+        s = Math.min(w, h);
+        Bitmap bmap2 = Bitmap.createScaledBitmap(bmap, (int) s, (int) s, true);
+        bmap = createSquareImage(bmap2, bmap, new Rect(0, 0, (int) s, (int) s));
+        if (DEBUG) {
+            Log.v(LOGTAG, "Create thumbnail of size " + bmap.getWidth() + " x " + bmap.getHeight()
+                    + " seed size: " + sIconSeedSize);
+        }
+        mThumbnailBitmap = bmap;
+    }
+
     public float getScaleFactor() {
         return mScaleFactor;
     }
@@ -406,4 +487,8 @@ public class MasterImage implements RenderingRequestCaller {
         mTranslation.y = 0;
         needsUpdateFullResPreview();
     }
+
+    public Bitmap getThumbnailBitmap() {
+        return mThumbnailBitmap;
+    }
 }
index 82592a0..ca74a87 100644 (file)
@@ -54,6 +54,7 @@ public class ImagePreset {
     public final GeometryMetadata mGeoData = new GeometryMetadata();
     private boolean mPartialRendering = false;
     private Rect mPartialRenderingBounds;
+    private Bitmap mPreviewImage;
 
     public ImagePreset() {
         setup();
@@ -522,4 +523,12 @@ public class ImagePreset {
         return mPartialRenderingBounds;
     }
 
+    public Bitmap getPreviewImage() {
+        return mPreviewImage;
+    }
+
+    public void setPreviewImage(Bitmap previewImage) {
+        mPreviewImage = previewImage;
+    }
+
 }
index 752fef0..9d50d5a 100644 (file)
@@ -74,11 +74,8 @@ public class FilterIconButton extends IconButton implements View.OnClickListener
             return super.drawImage(dst, image, destination);
         }
         if (mIconBitmap == null && mPreset == null) {
-            ImageLoader loader = MasterImage.getImage().getLoader();
-            if (loader != null) {
-                ImagePreset geoPreset = new ImagePreset(MasterImage.getImage().getGeometryPreset());
-                image = geoPreset.applyGeometry(image);
-                dst = super.drawImage(dst, image, destination);
+            dst = MasterImage.getImage().getThumbnailBitmap();
+            if (dst != null) {
                 ImagePreset mPreset = new ImagePreset();
                 mPreset.addFilter(mFilterRepresentation);
                 mPreset.setDoApplyGeometry(false);
index 6c0e2e7..0e9efa4 100644 (file)
@@ -33,7 +33,7 @@ import android.widget.ImageView;
 
 import com.android.gallery3d.R;
 import com.android.photos.data.PhotoSetLoader;
-import com.android.photos.drawables.AutoThumbnailDrawable;
+import com.android.photos.drawables.DataUriThumbnailDrawable;
 import com.android.photos.views.GalleryThumbnailView;
 import com.android.photos.views.GalleryThumbnailView.GalleryThumbnailAdapter;
 
@@ -105,7 +105,7 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor
         @Override
         public void bindView(View view, Context context, Cursor cursor) {
             ImageView iv = (ImageView) view;
-            AutoThumbnailDrawable drawable = (AutoThumbnailDrawable) iv.getDrawable();
+            DataUriThumbnailDrawable drawable = (DataUriThumbnailDrawable) iv.getDrawable();
             int width = cursor.getInt(PhotoSetLoader.INDEX_WIDTH);
             int height = cursor.getInt(PhotoSetLoader.INDEX_HEIGHT);
             String path = cursor.getString(PhotoSetLoader.INDEX_DATA);
@@ -116,7 +116,7 @@ public class PhotoSetFragment extends Fragment implements LoaderCallbacks<Cursor
         @Override
         public View newView(Context context, Cursor cursor, ViewGroup parent) {
             ImageView iv = new ImageView(context);
-            AutoThumbnailDrawable drawable = new AutoThumbnailDrawable();
+            DataUriThumbnailDrawable drawable = new DataUriThumbnailDrawable();
             iv.setImageDrawable(drawable);
             int padding = (int) Math.ceil(2 * context.getResources().getDisplayMetrics().density);
             iv.setPadding(padding, padding, padding, padding);
diff --git a/src/com/android/photos/data/NotificationWatcher.java b/src/com/android/photos/data/NotificationWatcher.java
new file mode 100644 (file)
index 0000000..8cf0e3c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.net.Uri;
+
+import com.android.photos.data.PhotoProvider.ChangeNotification;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Used for capturing notifications from PhotoProvider without relying on
+ * ContentResolver. MockContentResolver does not allow sending notification to
+ * ContentObservers, so PhotoProvider allows this alternative for testing.
+ */
+public class NotificationWatcher implements ChangeNotification {
+    private Set<Uri> mUris = new HashSet<Uri>();
+
+    @Override
+    public void notifyChange(Uri uri) {
+        mUris.add(uri);
+    }
+
+    public boolean isNotified(Uri uri) {
+        return mUris.contains(uri);
+    }
+
+    public int notificationCount() {
+        return mUris.size();
+    }
+
+    public void reset() {
+        mUris.clear();
+    }
+}
index 64a857f..35de185 100644 (file)
@@ -30,7 +30,6 @@ import com.android.photos.data.PhotoProvider.Photos;
 public class PhotoDatabase extends SQLiteOpenHelper {
     @SuppressWarnings("unused")
     private static final String TAG = PhotoDatabase.class.getSimpleName();
-    static final String DB_NAME = "photo.db";
     static final int DB_VERSION = 1;
 
     private static final String SQL_CREATE_TABLE = "CREATE TABLE ";
@@ -72,8 +71,8 @@ public class PhotoDatabase extends SQLiteOpenHelper {
         createTable(db, Metadata.TABLE, CREATE_METADATA);
     }
 
-    public PhotoDatabase(Context context) {
-        super(context, DB_NAME, null, DB_VERSION);
+    public PhotoDatabase(Context context, String dbName) {
+        super(context, dbName, null, DB_VERSION);
     }
 
     @Override
index eefa373..27afb58 100644 (file)
 package com.android.photos.data;
 
 import android.content.ContentProvider;
-import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.UriMatcher;
 import android.database.Cursor;
 import android.database.DatabaseUtils;
 import android.database.sqlite.SQLiteDatabase;
 import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteStatement;
+import android.database.sqlite.SQLiteQueryBuilder;
 import android.net.Uri;
 import android.os.CancellationSignal;
 import android.provider.BaseColumns;
@@ -48,10 +47,18 @@ import java.util.List;
 public class PhotoProvider extends ContentProvider {
     @SuppressWarnings("unused")
     private static final String TAG = PhotoProvider.class.getSimpleName();
-    static final String AUTHORITY = "com.android.gallery3d.photoprovider";
+
+    protected static final String DB_NAME = "photo.db";
+    public static final String AUTHORITY = PhotoProviderAuthority.AUTHORITY;
     static final Uri BASE_CONTENT_URI = new Uri.Builder().scheme("content").authority(AUTHORITY)
             .build();
 
+    // Used to allow mocking out the change notification because
+    // MockContextResolver disallows system-wide notification.
+    public static interface ChangeNotification {
+        void notifyChange(Uri uri);
+    }
+
     /**
      * Contains columns that can be accessed via PHOTOS_CONTENT_URI.
      */
@@ -185,7 +192,7 @@ public class PhotoProvider extends ContentProvider {
         /**
          * Foreign key to the photos._id. Long value.
          */
-        public static final String PHOTO_ID = "photos_id";
+        public static final String PHOTO_ID = "photo_id";
         /**
          * One of IMAGE_TYPE_* values.
          */
@@ -221,6 +228,11 @@ public class PhotoProvider extends ContentProvider {
         Photos.MIME_TYPE,
     };
 
+    private static final String[] BASE_COLUMNS_ID = {
+        BaseColumns._ID,
+    };
+
+    protected ChangeNotification mNotifier = null;
     private SQLiteOpenHelper mOpenHelper;
     protected static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
 
@@ -294,6 +306,11 @@ public class PhotoProvider extends ContentProvider {
     }
 
     @Override
+    public void shutdown() {
+        getDatabaseHelper().close();
+    }
+
+    @Override
     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
             String sortOrder) {
         return query(uri, projection, selection, selectionArgs, sortOrder, null);
@@ -334,6 +351,10 @@ public class PhotoProvider extends ContentProvider {
         return rowsUpdated;
     }
 
+    public void setMockNotification(ChangeNotification notification) {
+        mNotifier = notification;
+    }
+
     protected static String addIdToSelection(int match, String selection) {
         String where;
         switch (match) {
@@ -400,7 +421,7 @@ public class PhotoProvider extends ContentProvider {
     }
 
     protected SQLiteOpenHelper createDatabaseHelper() {
-        return new PhotoDatabase(getContext());
+        return new PhotoDatabase(getContext(), DB_NAME);
     }
 
     private int modifyMetadata(SQLiteDatabase db, ContentValues values) {
@@ -433,30 +454,21 @@ public class PhotoProvider extends ContentProvider {
     }
 
     protected void notifyChanges(Uri uri) {
-        ContentResolver resolver = getContext().getContentResolver();
-        resolver.notifyChange(uri, null, false);
+        if (mNotifier != null) {
+            mNotifier.notifyChange(uri);
+        } else {
+            getContext().getContentResolver().notifyChange(uri, null, false);
+        }
     }
 
     protected static IllegalArgumentException unknownUri(Uri uri) {
         return new IllegalArgumentException("Unknown Uri format: " + uri);
     }
 
-    protected static String nestSql(String base, String columnMatch, String nested) {
-        StringBuilder sql = new StringBuilder(base);
-        sql.append(WHERE);
-        sql.append(columnMatch);
-        sql.append(IN);
-        sql.append(NESTED_SELECT_START);
-        sql.append(nested);
-        sql.append(NESTED_SELECT_END);
-        return sql.toString();
-    }
-
-    protected static String addWhere(String base, String where) {
-        if (where == null || where.isEmpty()) {
-            return base;
-        }
-        return base + WHERE + where;
+    protected static String nestWhere(String matchColumn, String table, String nestedWhere) {
+        String query = SQLiteQueryBuilder.buildQueryString(false, table, BASE_COLUMNS_ID,
+                nestedWhere, null, null, null, null);
+        return matchColumn + IN + NESTED_SELECT_START + query + NESTED_SELECT_END;
     }
 
     protected static int deleteCascade(SQLiteDatabase db, int match, String selection,
@@ -464,38 +476,28 @@ public class PhotoProvider extends ContentProvider {
         switch (match) {
             case MATCH_PHOTO:
             case MATCH_PHOTO_ID: {
-                String selectPhotoIdsSql = addWhere(SELECT_PHOTO_ID, selection);
-                deleteCascadeMetadata(db, selectPhotoIdsSql, selectionArgs, changeUris);
+                deleteCascadeMetadata(db, selection, selectionArgs, changeUris);
                 break;
             }
             case MATCH_ALBUM:
             case MATCH_ALBUM_ID: {
-                String selectAlbumIdSql = addWhere(SELECT_ALBUM_ID, selection);
-                deleteCascadePhotos(db, selectAlbumIdSql, selectionArgs, changeUris);
+                deleteCascadePhotos(db, selection, selectionArgs, changeUris);
                 break;
             }
         }
         String table = getTableFromMatch(match, uri);
-        changeUris.add(uri);
-        return db.delete(table, selection, selectionArgs);
-    }
-
-    protected static void execSql(SQLiteDatabase db, String sql, String[] args) {
-        if (args == null) {
-            db.execSQL(sql);
-        } else {
-            db.execSQL(sql, args);
+        int deleted = db.delete(table, selection, selectionArgs);
+        if (deleted > 0) {
+            changeUris.add(uri);
         }
+        return deleted;
     }
 
     private static void deleteCascadePhotos(SQLiteDatabase db, String albumSelect,
             String[] selectArgs, List<Uri> changeUris) {
-        String selectPhotoIdSql = nestSql(SELECT_PHOTO_ID, Photos.ALBUM_ID, albumSelect);
-        deleteCascadeMetadata(db, selectPhotoIdSql, selectArgs, changeUris);
-        String deletePhotoSql = nestSql(DELETE_PHOTOS, Photos.ALBUM_ID, albumSelect);
-        SQLiteStatement statement = db.compileStatement(deletePhotoSql);
-        statement.bindAllArgsAsStrings(selectArgs);
-        int deleted = statement.executeUpdateDelete();
+        String photoWhere = nestWhere(Photos.ALBUM_ID, Albums.TABLE, albumSelect);
+        deleteCascadeMetadata(db, photoWhere, selectArgs, changeUris);
+        int deleted = db.delete(Photos.TABLE, photoWhere, selectArgs);
         if (deleted > 0) {
             changeUris.add(Photos.CONTENT_URI);
         }
@@ -503,10 +505,8 @@ public class PhotoProvider extends ContentProvider {
 
     private static void deleteCascadeMetadata(SQLiteDatabase db, String photosSelect,
             String[] selectArgs, List<Uri> changeUris) {
-        String deleteMetadataSql = nestSql(DELETE_METADATA, Metadata.PHOTO_ID, photosSelect);
-        SQLiteStatement statement = db.compileStatement(deleteMetadataSql);
-        statement.bindAllArgsAsStrings(selectArgs);
-        int deleted = statement.executeUpdateDelete();
+        String metadataWhere = nestWhere(Metadata.PHOTO_ID, Photos.TABLE, photosSelect);
+        int deleted = db.delete(Metadata.TABLE, metadataWhere, selectArgs);
         if (deleted > 0) {
             changeUris.add(Metadata.CONTENT_URI);
         }
index 95283e1..09b3441 100644 (file)
@@ -25,26 +25,25 @@ import android.graphics.Paint;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
-import android.media.ExifInterface;
-import android.text.TextUtils;
 import android.util.Log;
 
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
+import com.android.photos.data.GalleryBitmapPool;
+
+import java.io.InputStream;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
-public class AutoThumbnailDrawable extends Drawable {
+public abstract class AutoThumbnailDrawable<T> extends Drawable {
 
-    private static final String TAG = "AutoMipMapDrawable";
+    private static final String TAG = "AutoThumbnailDrawable";
 
     private static ExecutorService sThreadPool = Executors.newSingleThreadExecutor();
+    private static GalleryBitmapPool sBitmapPool = GalleryBitmapPool.getInstance();
     private static byte[] sTempStorage = new byte[64 * 1024];
 
     // UI thread only
     private Paint mPaint = new Paint();
     private Matrix mDrawMatrix = new Matrix();
-    private int mSampleSize = 1;
 
     // Decoder thread only
     private BitmapFactory.Options mOptions = new BitmapFactory.Options();
@@ -52,10 +51,11 @@ public class AutoThumbnailDrawable extends Drawable {
     // Shared, guarded by mLock
     private Object mLock = new Object();
     private Bitmap mBitmap;
-    private String mDataUri;
+    protected T mData;
     private boolean mIsQueued;
     private int mImageWidth, mImageHeight;
     private Rect mBounds = new Rect();
+    private int mSampleSize = 1;
 
     public AutoThumbnailDrawable() {
         mPaint.setAntiAlias(true);
@@ -64,18 +64,32 @@ public class AutoThumbnailDrawable extends Drawable {
         mOptions.inTempStorage = sTempStorage;
     }
 
-    public void setImage(String dataUri, int width, int height) {
-        if (TextUtils.equals(mDataUri, dataUri)) return;
+    protected abstract byte[] getPreferredImageBytes(T data);
+    protected abstract InputStream getFallbackImageStream(T data);
+    protected abstract boolean dataChangedLocked(T data);
+
+    public void setImage(T data, int width, int height) {
+        if (!dataChangedLocked(data)) return;
         synchronized (mLock) {
             mImageWidth = width;
             mImageHeight = height;
-            mDataUri = dataUri;
-            mBitmap = null;
+            mData = data;
+            setBitmapLocked(null);
             refreshSampleSizeLocked();
         }
         invalidateSelf();
     }
 
+    private void setBitmapLocked(Bitmap b) {
+        if (b == mBitmap) {
+            return;
+        }
+        if (mBitmap != null) {
+            sBitmapPool.put(mBitmap);
+        }
+        mBitmap = b;
+    }
+
     @Override
     protected void onBoundsChange(Rect bounds) {
         super.onBoundsChange(bounds);
@@ -148,7 +162,8 @@ public class AutoThumbnailDrawable extends Drawable {
         } else {
             scale = (float) dwidth / (float) vwidth;
         }
-        return (int) (scale + .5f);
+        int result = Math.round(scale);
+        return result > 0 ? result : 1;
     }
 
     private void refreshSampleSizeLocked() {
@@ -208,62 +223,78 @@ public class AutoThumbnailDrawable extends Drawable {
     private final Runnable mLoadBitmap = new Runnable() {
         @Override
         public void run() {
-            // TODO: Use bitmap pool
-            String data;
-            int sampleSize;
+            T data;
+            synchronized (mLock) {
+                data = mData;
+            }
+            int preferredSampleSize = 1;
+            byte[] preferred = getPreferredImageBytes(data);
+            boolean hasPreferred = (preferred != null && preferred.length > 0);
+            if (hasPreferred) {
+                mOptions.inJustDecodeBounds = true;
+                BitmapFactory.decodeByteArray(preferred, 0, preferred.length, mOptions);
+                mOptions.inJustDecodeBounds = false;
+            }
+            int sampleSize, width, height;
             synchronized (mLock) {
-                data = mDataUri;
-                sampleSize = calculateSampleSizeLocked(mImageWidth, mImageHeight);
-                mSampleSize = sampleSize;
+                if (dataChangedLocked(data)) {
+                    return;
+                }
+                width = mImageWidth;
+                height = mImageHeight;
+                if (hasPreferred) {
+                    preferredSampleSize = calculateSampleSizeLocked(
+                            mOptions.outWidth, mOptions.outHeight);
+                }
+                sampleSize = calculateSampleSizeLocked(width, height);
                 mIsQueued = false;
             }
-            FileInputStream fis = null;
+            Bitmap b = null;
+            InputStream is = null;
             try {
-                ExifInterface exif = new ExifInterface(data);
-                if (exif.hasThumbnail()) {
-                    byte[] thumbnail = exif.getThumbnail();
-                    mOptions.inJustDecodeBounds = true;
-                    BitmapFactory.decodeByteArray(thumbnail, 0,
-                            thumbnail.length, mOptions);
-                    int exifThumbSampleSize = calculateSampleSizeLocked(
-                            mOptions.outWidth, mOptions.outHeight);
-                    mOptions.inJustDecodeBounds = false;
-                    mOptions.inSampleSize = exifThumbSampleSize;
-                    mBitmap = BitmapFactory.decodeByteArray(thumbnail, 0,
-                            thumbnail.length, mOptions);
-                    if (mBitmap != null) {
-                        synchronized (mLock) {
-                            if (TextUtils.equals(data, mDataUri)) {
-                                scheduleSelf(mUpdateBitmap, 0);
-                            }
-                        }
-                        return;
+                if (hasPreferred) {
+                    mOptions.inSampleSize = preferredSampleSize;
+                    mOptions.inBitmap = sBitmapPool.get(
+                            mOptions.outWidth / preferredSampleSize,
+                            mOptions.outHeight / preferredSampleSize);
+                    b = BitmapFactory.decodeByteArray(preferred, 0, preferred.length, mOptions);
+                    if (mOptions.inBitmap != null && b != mOptions.inBitmap) {
+                        sBitmapPool.put(mOptions.inBitmap);
+                        mOptions.inBitmap = null;
+                    }
+                }
+                if (b == null) {
+                    is = getFallbackImageStream(data);
+                    mOptions.inSampleSize = sampleSize;
+                    mOptions.inBitmap = sBitmapPool.get(width / sampleSize, height / sampleSize);
+                    b = BitmapFactory.decodeStream(is, null, mOptions);
+                    if (mOptions.inBitmap != null && b != mOptions.inBitmap) {
+                        sBitmapPool.put(mOptions.inBitmap);
+                        mOptions.inBitmap = null;
                     }
                 }
-                fis = new FileInputStream(data);
-                FileDescriptor fd = fis.getFD();
-                mOptions.inSampleSize = sampleSize;
-                mBitmap = BitmapFactory.decodeFileDescriptor(fd, null, mOptions);
             } catch (Exception e) {
-                Log.d("AsyncBitmap", "Failed to fetch bitmap", e);
+                Log.d(TAG, "Failed to fetch bitmap", e);
                 return;
             } finally {
                 try {
-                    if (fis != null) {
-                        fis.close();
+                    if (is != null) {
+                        is.close();
                     }
                 } catch (Exception e) {}
-            }
-            synchronized (mLock) {
-                if (TextUtils.equals(data, mDataUri)) {
-                    scheduleSelf(mUpdateBitmap, 0);
+                if (b != null) {
+                    synchronized (mLock) {
+                        if (!dataChangedLocked(data)) {
+                            setBitmapLocked(b);
+                            scheduleSelf(mUpdateBitmap, 0);
+                        }
+                    }
                 }
             }
         }
     };
 
     private final Runnable mUpdateBitmap = new Runnable() {
-
         @Override
         public void run() {
             synchronized (AutoThumbnailDrawable.this) {
diff --git a/src/com/android/photos/drawables/DataUriThumbnailDrawable.java b/src/com/android/photos/drawables/DataUriThumbnailDrawable.java
new file mode 100644 (file)
index 0000000..c83b0c8
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * 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.drawables;
+
+import android.media.ExifInterface;
+import android.text.TextUtils;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class DataUriThumbnailDrawable extends AutoThumbnailDrawable<String> {
+
+    @Override
+    protected byte[] getPreferredImageBytes(String data) {
+        byte[] thumbnail = null;
+        try {
+            ExifInterface exif = new ExifInterface(data);
+            if (exif.hasThumbnail()) {
+                thumbnail = exif.getThumbnail();
+             }
+        } catch (IOException e) { }
+        return thumbnail;
+    }
+
+    @Override
+    protected InputStream getFallbackImageStream(String data) {
+        try {
+            return new FileInputStream(data);
+        } catch (FileNotFoundException e) {
+            return null;
+        }
+    }
+
+    @Override
+    protected boolean dataChangedLocked(String data) {
+        return !TextUtils.equals(mData, data);
+    }
+}
diff --git a/src/com/android/photos/drawables/MtpThumbnailDrawable.java b/src/com/android/photos/drawables/MtpThumbnailDrawable.java
new file mode 100644 (file)
index 0000000..e35e069
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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.drawables;
+
+import android.mtp.MtpDevice;
+import android.mtp.MtpObjectInfo;
+
+import com.android.gallery3d.ingest.MtpDeviceIndex;
+
+import java.io.InputStream;
+
+public class MtpThumbnailDrawable extends AutoThumbnailDrawable<MtpObjectInfo> {
+    public void setImage(MtpObjectInfo data) {
+        if (data == null) {
+            setImage(null, 0, 0);
+        } else {
+            setImage(data, data.getImagePixWidth(), data.getImagePixHeight());
+        }
+    }
+
+    @Override
+    protected byte[] getPreferredImageBytes(MtpObjectInfo data) {
+        if (data == null) {
+            return null;
+        }
+        MtpDevice device = MtpDeviceIndex.getInstance().getDevice();
+        if (device != null) {
+            return device.getThumbnail(data.getObjectHandle());
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    protected InputStream getFallbackImageStream(MtpObjectInfo data) {
+        // No fallback
+        return null;
+    }
+
+    @Override
+    protected boolean dataChangedLocked(MtpObjectInfo data) {
+        // We only fetch the MtpObjectInfo once when creating
+        // the index so checking the reference is enough
+        return mData == data;
+    }
+
+}
index b05b546..d6b8718 100644 (file)
@@ -17,6 +17,7 @@
 package com.android.gallery3d.filtershow.filters;
 
 import java.util.HashMap;
+import java.util.Vector;
 
 public class FiltersManager extends BaseFiltersManager {
     private static FiltersManager sInstance = null;
@@ -36,4 +37,5 @@ public class FiltersManager extends BaseFiltersManager {
     public static void reset() {
         sInstance = null;
     }
+
 }
diff --git a/src_pd/com/android/photos/data/PhotoProviderAuthority.java b/src_pd/com/android/photos/data/PhotoProviderAuthority.java
new file mode 100644 (file)
index 0000000..0ac76cb
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * 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;
+
+interface PhotoProviderAuthority {
+    public static final String AUTHORITY = "com.android.gallery3d.photoprovider";
+}
index 48e79d4..d8c5e42 100644 (file)
@@ -30,29 +30,32 @@ import java.io.IOException;
 public class PhotoDatabaseTest extends InstrumentationTestCase {
 
     private PhotoDatabase mDBHelper;
+    private static final String DB_NAME = "dummy.db";
 
     @Override
-    protected void setUp() {
+    protected void setUp() throws Exception {
+        super.setUp();
         Context context = getInstrumentation().getTargetContext();
-        mDBHelper = new PhotoDatabase(context);
+        context.deleteDatabase(DB_NAME);
+        mDBHelper = new PhotoDatabase(context, DB_NAME);
     }
 
     @Override
-    protected void tearDown() {
+    protected void tearDown() throws Exception {
         mDBHelper.close();
+        mDBHelper = null;
+        Context context = getInstrumentation().getTargetContext();
+        context.deleteDatabase(DB_NAME);
+        super.tearDown();
     }
 
     public void testCreateDatabase() throws IOException {
         Context context = getInstrumentation().getTargetContext();
-        File dbFile = context.getDatabasePath(PhotoDatabase.DB_NAME);
-        if (dbFile.exists()) {
-            dbFile.delete();
-        }
+        File dbFile = context.getDatabasePath(DB_NAME);
         SQLiteDatabase db = getReadableDB();
         db.beginTransaction();
         db.endTransaction();
         assertTrue(dbFile.exists());
-        dbFile.delete();
     }
 
     public void testTables() {
index 6fd73e1..73a6c78 100644 (file)
@@ -108,10 +108,4 @@ public class PhotoDatabaseUtils {
         values.put(Metadata.VALUE, value);
         return db.insert(Metadata.TABLE, null, values) != -1;
     }
-
-    public static void deleteAllContent(SQLiteDatabase db) {
-        db.delete(Metadata.TABLE, null, null);
-        db.delete(Photos.TABLE, null, null);
-        db.delete(Albums.TABLE, null, null);
-    }
 }
index ad913b0..525abec 100644 (file)
@@ -18,21 +18,18 @@ package com.android.photos.data;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
-import android.content.Context;
-import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
 import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
 import android.provider.BaseColumns;
-import android.test.InstrumentationTestCase;
+import android.test.ProviderTestCase2;
 
 import com.android.photos.data.PhotoProvider.Albums;
 import com.android.photos.data.PhotoProvider.Metadata;
 import com.android.photos.data.PhotoProvider.Photos;
 
-public class PhotoProviderTest extends InstrumentationTestCase {
+public class PhotoProviderTest extends ProviderTestCase2<PhotoProvider> {
     @SuppressWarnings("unused")
     private static final String TAG = PhotoProviderTest.class.getSimpleName();
 
@@ -51,87 +48,25 @@ public class PhotoProviderTest extends InstrumentationTestCase {
     private static final String WHERE_METADATA = Metadata.PHOTO_ID + " = ? AND " + Metadata.KEY
             + " = ?";
 
-    private static final long WAIT_FOR_CHANGE_MILLIS = 200;
-
     private long mAlbumId;
     private long mPhotoId;
     private long mMetadataId;
 
-    private PhotoDatabase mDBHelper;
+    private SQLiteOpenHelper mDBHelper;
     private ContentResolver mResolver;
+    private NotificationWatcher mNotifications = new NotificationWatcher();
 
-    private static class WatchContentObserverThread extends Thread {
-        private WatchContentObserver mObserver;
-        private Looper mLooper;
-
-        @Override
-        public void run() {
-            Looper.prepare();
-            mLooper = Looper.myLooper();
-            WatchContentObserver observer = new WatchContentObserver();
-            synchronized (this) {
-                mObserver = observer;
-                this.notifyAll();
-            }
-            Looper.loop();
-        }
-
-        public void waitForObserver() throws InterruptedException {
-            synchronized (this) {
-                while (mObserver == null) {
-                    this.wait();
-                }
-            }
-        }
-
-        public WatchContentObserver getObserver() {
-            return mObserver;
-        }
-
-        public void stopLooper() {
-            mLooper.quit();
-        }
-    };
-
-    private static class WatchContentObserver extends ContentObserver {
-        private boolean mOnChangeReceived = false;
-        private Uri mUri = null;
-
-        public WatchContentObserver() {
-            super(new Handler());
-        }
-
-        @Override
-        public synchronized void onChange(boolean selfChange, Uri uri) {
-            mOnChangeReceived = true;
-            mUri = uri;
-            notifyAll();
-        }
-
-        @Override
-        public synchronized void onChange(boolean selfChange) {
-            mOnChangeReceived = true;
-            notifyAll();
-        }
-
-        public boolean waitForNotification() {
-            synchronized (this) {
-                if (!mOnChangeReceived) {
-                    try {
-                        wait(WAIT_FOR_CHANGE_MILLIS);
-                    } catch (InterruptedException e) {
-                    }
-                }
-            }
-            return mOnChangeReceived;
-        }
-    };
+    public PhotoProviderTest() {
+        super(PhotoProvider.class, PhotoProvider.AUTHORITY);
+    }
 
     @Override
-    protected void setUp() {
-        Context context = getInstrumentation().getTargetContext();
-        mDBHelper = new PhotoDatabase(context);
-        mResolver = context.getContentResolver();
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getMockContentResolver();
+        PhotoProvider provider = (PhotoProvider) getProvider();
+        provider.setMockNotification(mNotifications);
+        mDBHelper = provider.getDatabaseHelper();
         SQLiteDatabase db = mDBHelper.getWritableDatabase();
         db.beginTransaction();
         try {
@@ -150,23 +85,18 @@ public class PhotoProviderTest extends InstrumentationTestCase {
             mMetadataId = cursor.getLong(0);
             cursor.close();
             db.setTransactionSuccessful();
+            mNotifications.reset();
         } finally {
             db.endTransaction();
         }
     }
 
     @Override
-    protected void tearDown() {
-        SQLiteDatabase db = mDBHelper.getWritableDatabase();
-        db.beginTransaction();
-        try {
-            PhotoDatabaseUtils.deleteAllContent(db);
-            db.setTransactionSuccessful();
-        } finally {
-            db.endTransaction();
-        }
+    protected void tearDown() throws Exception {
         mDBHelper.close();
         mDBHelper = null;
+        super.tearDown();
+        getMockContext().deleteDatabase(PhotoProvider.DB_NAME);
     }
 
     public void testDelete() {
@@ -204,85 +134,46 @@ public class PhotoProviderTest extends InstrumentationTestCase {
         assertEquals(0, cursor.getCount());
         cursor.close();
     }
+
     // Delete the album and ensure that the photos referring to the album are
     // deleted.
     public void testDeleteAlbumCascade() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Photos.CONTENT_URI, true, observer);
-        try {
-            Uri albumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId);
-            mResolver.delete(albumUri, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, Photos.CONTENT_URI);
-            Cursor cursor = mResolver.query(Photos.CONTENT_URI,
-                    PhotoDatabaseUtils.PROJECTION_PHOTOS, null, null, null);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
-    }
-
-    // Delete the album and ensure that the metadata referring to photos in that
-    // album are deleted.
-    public void testDeleteAlbumCascade2() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Metadata.CONTENT_URI, true, observer);
-        try {
-            Uri albumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId);
-            mResolver.delete(albumUri, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, Metadata.CONTENT_URI);
-            Cursor cursor = mResolver.query(Metadata.CONTENT_URI,
-                    PhotoDatabaseUtils.PROJECTION_METADATA, null, null, null);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
+        Uri albumUri = ContentUris.withAppendedId(Albums.CONTENT_URI, mAlbumId);
+        mResolver.delete(albumUri, null, null);
+        assertTrue(mNotifications.isNotified(Photos.CONTENT_URI));
+        assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+        assertTrue(mNotifications.isNotified(albumUri));
+        assertEquals(3, mNotifications.notificationCount());
+        Cursor cursor = mResolver.query(Photos.CONTENT_URI, PhotoDatabaseUtils.PROJECTION_PHOTOS,
+                null, null, null);
+        assertEquals(0, cursor.getCount());
+        cursor.close();
     }
 
     // Delete all albums and ensure that photos in any album are deleted.
-    public void testDeleteAlbumCascade3() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Photos.CONTENT_URI, true, observer);
-        try {
-            mResolver.delete(Albums.CONTENT_URI, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, Photos.CONTENT_URI);
-            Cursor cursor = mResolver.query(Photos.CONTENT_URI,
-                    PhotoDatabaseUtils.PROJECTION_PHOTOS, null, null, null);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
+    public void testDeleteAlbumCascade2() {
+        mResolver.delete(Albums.CONTENT_URI, null, null);
+        assertTrue(mNotifications.isNotified(Photos.CONTENT_URI));
+        assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+        assertTrue(mNotifications.isNotified(Albums.CONTENT_URI));
+        assertEquals(3, mNotifications.notificationCount());
+        Cursor cursor = mResolver.query(Photos.CONTENT_URI, PhotoDatabaseUtils.PROJECTION_PHOTOS,
+                null, null, null);
+        assertEquals(0, cursor.getCount());
+        cursor.close();
     }
 
     // Delete a photo and ensure that the metadata for that photo are deleted.
     public void testDeletePhotoCascade() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Metadata.CONTENT_URI, true, observer);
-        try {
-            Uri albumUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
-            mResolver.delete(albumUri, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, Metadata.CONTENT_URI);
-            Cursor cursor = mResolver.query(Metadata.CONTENT_URI,
-                    PhotoDatabaseUtils.PROJECTION_METADATA, null, null, null);
-            assertEquals(0, cursor.getCount());
-            cursor.close();
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
+        Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
+        mResolver.delete(photoUri, null, null);
+        assertTrue(mNotifications.isNotified(photoUri));
+        assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
+        assertEquals(2, mNotifications.notificationCount());
+        Cursor cursor = mResolver.query(Metadata.CONTENT_URI,
+                PhotoDatabaseUtils.PROJECTION_METADATA, null, null, null);
+        assertEquals(0, cursor.getCount());
+        cursor.close();
     }
 
     public void testGetType() {
@@ -399,65 +290,19 @@ public class PhotoProviderTest extends InstrumentationTestCase {
     }
 
     public void testUpdatePhotoNotification() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Photos.CONTENT_URI, true, observer);
-        try {
-            Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
-            ContentValues values = new ContentValues();
-            values.put(Photos.MIME_TYPE, "not-a/mime-type");
-            mResolver.update(photoUri, values, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, photoUri);
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
+        Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
+        ContentValues values = new ContentValues();
+        values.put(Photos.MIME_TYPE, "not-a/mime-type");
+        mResolver.update(photoUri, values, null, null);
+        assertTrue(mNotifications.isNotified(photoUri));
     }
 
     public void testUpdateMetadataNotification() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Metadata.CONTENT_URI, true, observer);
-        try {
-            ContentValues values = new ContentValues();
-            values.put(Metadata.PHOTO_ID, mPhotoId);
-            values.put(Metadata.KEY, META_KEY);
-            values.put(Metadata.VALUE, "hello world");
-            mResolver.update(Metadata.CONTENT_URI, values, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, Metadata.CONTENT_URI);
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
-    }
-
-    public void testDeletePhotoNotification() {
-        WatchContentObserverThread observerThread = createObserverThread();
-        WatchContentObserver observer = observerThread.getObserver();
-        mResolver.registerContentObserver(Photos.CONTENT_URI, true, observer);
-        try {
-            Uri photoUri = ContentUris.withAppendedId(Photos.CONTENT_URI, mPhotoId);
-            mResolver.delete(photoUri, null, null);
-            assertTrue(observer.waitForNotification());
-            assertEquals(observer.mUri, photoUri);
-        } finally {
-            mResolver.unregisterContentObserver(observer);
-            observerThread.stopLooper();
-        }
-    }
-
-    private WatchContentObserverThread createObserverThread() {
-        WatchContentObserverThread thread = new WatchContentObserverThread();
-        thread.start();
-        try {
-            thread.waitForObserver();
-            return thread;
-        } catch (InterruptedException e) {
-            thread.stopLooper();
-            fail("Interruption while waiting for observer being created.");
-            return null;
-        }
+        ContentValues values = new ContentValues();
+        values.put(Metadata.PHOTO_ID, mPhotoId);
+        values.put(Metadata.KEY, META_KEY);
+        values.put(Metadata.VALUE, "hello world");
+        mResolver.update(Metadata.CONTENT_URI, values, null, null);
+        assertTrue(mNotifications.isNotified(Metadata.CONTENT_URI));
     }
 }
diff --git a/tests/src/com/android/photos/data/TestHelper.java b/tests/src/com/android/photos/data/TestHelper.java
new file mode 100644 (file)
index 0000000..338e160
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * 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.util.Log;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import java.lang.reflect.Method;
+
+public class TestHelper {
+    private static String TAG = TestHelper.class.getSimpleName();
+
+    public interface TestInitialization {
+        void initialize(TestCase testCase);
+    }
+
+    public static void addTests(Class<? extends TestCase> testClass, TestSuite suite,
+            TestInitialization initialization) {
+        for (Method method : testClass.getDeclaredMethods()) {
+            if (method.getName().startsWith("test") && method.getParameterTypes().length == 0) {
+                TestCase test;
+                try {
+                    test = testClass.newInstance();
+                    test.setName(method.getName());
+                    initialization.initialize(test);
+                    suite.addTest(test);
+                } catch (IllegalArgumentException e) {
+                    Log.e(TAG, "Failed to create test case", e);
+                } catch (InstantiationException e) {
+                    Log.e(TAG, "Failed to create test case", e);
+                } catch (IllegalAccessException e) {
+                    Log.e(TAG, "Failed to create test case", e);
+                }
+            }
+        }
+    }
+
+}