<?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">
$(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
<?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
+++ /dev/null
-<?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
</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>
-->
<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"
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>
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"
+++ /dev/null
-<?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>
<?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_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
+++ /dev/null
-<?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
-->
<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">
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>
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"
+++ /dev/null
-<?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>
<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
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
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>
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
+++ /dev/null
-<?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>
+++ /dev/null
-<?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"/>
<?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"
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>
<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"
<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>
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;
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;
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]);
}
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--;
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) {
}
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
}
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);
}
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);
}
}
public boolean needsSwitcher();
+ public boolean needsPieMenu();
+
public void onOrientationChanged(int orientation);
public void onShowSwitcherPopup();
}
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);
}
}
@Override
+ public boolean needsPieMenu() {
+ return false;
+ }
+
+ @Override
public void onShowSwitcherPopup() {
}
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;
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;
FocusOverlayManager.Listener,
CameraPreference.OnPreferenceChangedListener,
LocationManager.Listener,
- PreviewFrameLayout.OnSizeChangedListener,
ShutterButton.OnShutterButtonListener,
SurfaceHolder.Callback,
PieRenderer.PieListener,
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.
private PhotoController mPhotoControl;
private ZoomRenderer mZoomRenderer;
-
private String mSceneMode;
private Toast mNotSelectableToast;
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.
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));
if (isImageCaptureIntent()) {
if (mReviewCancelButton != null) {
- mGestures.addTouchReceiver((View) mReviewCancelButton);
+ mGestures.addTouchReceiver(mReviewCancelButton);
}
if (mReviewDoneButton != null) {
- mGestures.addTouchReceiver((View) mReviewDoneButton);
+ mGestures.addTouchReceiver(mReviewDoneButton);
}
}
}
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();
}
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);
}
return;
}
- if (full) {
- mPreviewSurfaceView.expand();
- } else {
- mPreviewSurfaceView.shrink();
- }
}
@Override
}
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) {
}
});
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);
}
});
- // 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();
}
}
}
}
- 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
if (mIsImageCaptureIntent) {
mOnScreenIndicators.setVisibility(View.GONE);
mMenu.setVisibility(View.GONE);
- Util.fadeIn((View) mReviewDoneButton);
+ Util.fadeIn(mReviewDoneButton);
mShutterButton.setVisibility(View.INVISIBLE);
Util.fadeIn(mReviewRetakeButton);
}
if (mIsImageCaptureIntent) {
mOnScreenIndicators.setVisibility(View.VISIBLE);
mMenu.setVisibility(View.VISIBLE);
- Util.fadeOut((View) mReviewDoneButton);
+ Util.fadeOut(mReviewDoneButton);
mShutterButton.setVisibility(View.VISIBLE);
Util.fadeOut(mReviewRetakeButton);
}
mLocationManager.recordLocation(recordLocation);
setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE);
- setPreviewFrameLayoutAspectRatio();
updateOnScreenIndicators();
}
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;
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);
@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) {
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
}
return result;
}
+
+ @Override
+ public void onConfigurationChanged(Configuration config) {
+ super.onConfigurationChanged(config);
+ RotatableLayout.rotate(this, config.orientation == Configuration.ORIENTATION_PORTRAIT);
+ }
}
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;
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;
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);
}
}
}
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.
private void setOrientationIndicator(int orientation, boolean animation) {
Rotatable[] indicators = {
- mBgLearningMessageRotater,
- mReviewDoneButton, mReviewPlayButton};
+ mBgLearningMessageRotater};
for (Rotatable indicator : indicators) {
if (indicator != null) indicator.setOrientation(orientation, animation);
}
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) {
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.
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.
Util.fadeOut(mShutterButton);
- Util.fadeIn((View) mReviewDoneButton);
+ Util.fadeIn(mReviewDoneButton);
Util.fadeIn(mReviewPlayButton);
mMenu.setVisibility(View.GONE);
mOnScreenIndicators.setVisibility(View.GONE);
mOnScreenIndicators.setVisibility(View.VISIBLE);
enableCameraControls(true);
- Util.fadeOut((View) mReviewDoneButton);
+ Util.fadeOut(mReviewDoneButton);
Util.fadeOut(mReviewPlayButton);
Util.fadeIn(mShutterButton);
}
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) {
}
}
});
- 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);
- }
}
}
@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
}
@Override
+ public boolean needsPieMenu() {
+ return true;
+ }
+
+ @Override
public void onPieOpened(int centerX, int centerY) {
mActivity.cancelActivityTouchHandling();
mActivity.setSwipingEnabled(false);
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;
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) {
// Verify that we weren't canceled
if (!showsPopup()) {
mPopup.setVisibility(View.INVISIBLE);
+ ((ViewGroup) mParent).removeView(mPopup);
+ mPopup = null;
}
}
};
--- /dev/null
+/*
+ * 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
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;
fillEditors();
loadXML();
+ if (getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE) {
+ mShowingImageStatePanel = true;
+ }
+
setDefaultPreset();
processIntent();
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));
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);
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() {
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);
View tinyPlanetView = findViewById(EditorTinyPlanet.ID);
if (tinyPlanetView != null) {
+ mShowingTinyPlanet = false;
tinyPlanetView.setVisibility(View.GONE);
}
mLoadBitmapTask = new LoadBitmapTask(tinyPlanetView);
return;
}
if (values[0]) {
+ mShowingTinyPlanet = true;
mTinyPlanetButton.setVisibility(View.VISIBLE);
}
}
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;
}
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);
}
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);
.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();
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;
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);
}
}
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;
request.setBitmap(bitmap);
ImagePreset passedPreset = new ImagePreset(preset);
passedPreset.setImageLoader(MasterImage.getImage().getImageLoader());
+ request.setOriginalImagePreset(preset);
if (type == PARTIAL_RENDERING) {
request.setBounds(bounds);
public void setDestination(Rect destination) {
mDestination = destination;
}
+
+ public ImagePreset getOriginalImagePreset() {
+ return mOriginalImagePreset;
+ }
+
+ public void setOriginalImagePreset(ImagePreset originalImagePreset) {
+ mOriginalImagePreset = originalImagePreset;
+ }
}
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));
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));
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);
setTextId(R.string.imageDraw);
setButtonId(R.id.drawOnImageButton);
setEditorId(EditorDraw.ID);
+ setOverlayId(R.drawable.filtershow_drawing);
+ setOverlayOnly(true);
}
@Override
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) {
translation.y = (int) (originalTranslation.y + translateY);
MasterImage.getImage().setTranslation(translation);
}
+ mTouchShowOriginal = false;
} else if (!mOriginalDisabled && !mActivity.isShowingHistoryPanel()
&& (System.currentTimeMillis() - mTouchShowOriginalDate
> mTouchShowOriginalDelayMin)
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;
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);
}
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;
return sMasterImage;
}
+ public static void setIconSeedSize(int iconSeedSize) {
+ sIconSeedSize = iconSeedSize;
+ }
+
public void addObserver(ImageShow observer) {
if (mObservers.contains(observer)) {
return;
mPreset.fillImageStateAdapter(mState);
if (addToHistory) {
mHistory.addHistoryItem(mPreset);
+ renderHistoryPreview();
}
updatePresets(true);
GeometryMetadata geo = mPreset.mGeoData;
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) {
invalidatePartialPreview();
needsUpdateFullResPreview();
FilteringPipeline.getPipeline().updatePreviewBuffer();
+ renderHistoryPreview();
}
public void setImageShowSize(int w, int h) {
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() {
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;
}
mTranslation.y = 0;
needsUpdateFullResPreview();
}
+
+ public Bitmap getThumbnailBitmap() {
+ return mThumbnailBitmap;
+ }
}
public final GeometryMetadata mGeoData = new GeometryMetadata();
private boolean mPartialRendering = false;
private Rect mPartialRenderingBounds;
+ private Bitmap mPreviewImage;
public ImagePreset() {
setup();
return mPartialRenderingBounds;
}
+ public Bitmap getPreviewImage() {
+ return mPreviewImage;
+ }
+
+ public void setPreviewImage(Bitmap previewImage) {
+ mPreviewImage = previewImage;
+ }
+
}
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);
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;
@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);
@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);
--- /dev/null
+/*
+ * 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();
+ }
+}
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 ";
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
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;
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.
*/
/**
* 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.
*/
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);
}
@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);
return rowsUpdated;
}
+ public void setMockNotification(ChangeNotification notification) {
+ mNotifier = notification;
+ }
+
protected static String addIdToSelection(int match, String selection) {
String where;
switch (match) {
}
protected SQLiteOpenHelper createDatabaseHelper() {
- return new PhotoDatabase(getContext());
+ return new PhotoDatabase(getContext(), DB_NAME);
}
private int modifyMetadata(SQLiteDatabase db, ContentValues values) {
}
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,
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);
}
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);
}
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();
// 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);
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);
} else {
scale = (float) dwidth / (float) vwidth;
}
- return (int) (scale + .5f);
+ int result = Math.round(scale);
+ return result > 0 ? result : 1;
}
private void refreshSampleSizeLocked() {
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) {
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
+ }
+
+}
package com.android.gallery3d.filtershow.filters;
import java.util.HashMap;
+import java.util.Vector;
public class FiltersManager extends BaseFiltersManager {
private static FiltersManager sInstance = null;
public static void reset() {
sInstance = null;
}
+
}
--- /dev/null
+/*
+ * 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";
+}
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() {
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);
- }
}
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();
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 {
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() {
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() {
}
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));
}
}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+ }
+ }
+
+}