diff options
92 files changed, 2483 insertions, 290 deletions
diff --git a/assets/dependency.json b/assets/dependency.json index b8c396895..cbc96587d 100644 --- a/assets/dependency.json +++ b/assets/dependency.json @@ -42,15 +42,69 @@ "100": {"pref_camera2_longshot_key":"off", "pref_camera2_mono_only_key":"off", - "pref_camera2_flashmode_key":"off"} + "pref_camera2_flashmode_key":"off", + "pref_camera2_coloreffect_key":"0"} , "0": {"pref_camera2_clearsight_key":"off", "pref_camera2_mono_preview_key":"off", "pref_camera2_mpo_key":"off"} , + "18": + {"pref_camera2_coloreffect_key":"0"} + , + "102": + {"pref_camera2_coloreffect_key":"0"} + , + "101": + {"pref_camera2_coloreffect_key":"0"} + , + "3": + {"pref_camera2_coloreffect_key":"0"} + , + "4": + {"pref_camera2_coloreffect_key":"0"} + , + "13": + {"pref_camera2_coloreffect_key":"0"} + , + "15": + {"pref_camera2_coloreffect_key":"0"} + , + "10": + {"pref_camera2_coloreffect_key":"0"} + , + "5": + {"pref_camera2_coloreffect_key":"0"} + , + "8": + {"pref_camera2_coloreffect_key":"0"} + , + "9": + {"pref_camera2_coloreffect_key":"0"} + , + "103": + {"pref_camera2_coloreffect_key":"0"} + , + "104": + {"pref_camera2_coloreffect_key":"0"} + , "105": - {"pref_camera2_flashmode_key":"off"} + {"pref_camera2_flashmode_key":"off", + "pref_camera2_coloreffect_key":"0", + "pref_camera2_longshot_key":"off"} + , + "106": + {"pref_camera2_coloreffect_key":"0"} + , + "107": + {"pref_camera2_coloreffect_key":"0"} + , + "108": + {"pref_camera2_coloreffect_key":"0"} + , + "109": + {"pref_camera2_coloreffect_key":"0"} }, "pref_camera2_clearsight_key": { diff --git a/res/drawable-hdpi/bar_beautify_rectangle_blue.png b/res/drawable-hdpi/bar_beautify_rectangle_blue.png Binary files differnew file mode 100755 index 000000000..37b6cdafb --- /dev/null +++ b/res/drawable-hdpi/bar_beautify_rectangle_blue.png diff --git a/res/drawable-hdpi/bar_beautify_rectangle_white.png b/res/drawable-hdpi/bar_beautify_rectangle_white.png Binary files differnew file mode 100755 index 000000000..06bc65042 --- /dev/null +++ b/res/drawable-hdpi/bar_beautify_rectangle_white.png diff --git a/res/drawable-hdpi/ic_beautify_oval.png b/res/drawable-hdpi/ic_beautify_oval.png Binary files differnew file mode 100755 index 000000000..be86fdc03 --- /dev/null +++ b/res/drawable-hdpi/ic_beautify_oval.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_auto.png b/res/drawable-hdpi/ic_scene_mode_black_auto.png Binary files differnew file mode 100644 index 000000000..3f68e8e47 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_auto.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_backlight.png b/res/drawable-hdpi/ic_scene_mode_black_backlight.png Binary files differnew file mode 100644 index 000000000..a70502f79 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_backlight.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_beach.png b/res/drawable-hdpi/ic_scene_mode_black_beach.png Binary files differnew file mode 100644 index 000000000..d66a5ff23 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_beach.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_best_photo.png b/res/drawable-hdpi/ic_scene_mode_black_best_photo.png Binary files differnew file mode 100644 index 000000000..5ddd1aac2 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_best_photo.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_blur_buster.png b/res/drawable-hdpi/ic_scene_mode_black_blur_buster.png Binary files differnew file mode 100644 index 000000000..a5b930f5e --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_blur_buster.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_candlelight.png b/res/drawable-hdpi/ic_scene_mode_black_candlelight.png Binary files differnew file mode 100644 index 000000000..cc8606e09 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_candlelight.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_chroma_flash.png b/res/drawable-hdpi/ic_scene_mode_black_chroma_flash.png Binary files differnew file mode 100644 index 000000000..9c8c1108e --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_chroma_flash.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_dual_camera.png b/res/drawable-hdpi/ic_scene_mode_black_dual_camera.png Binary files differnew file mode 100644 index 000000000..16b803601 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_dual_camera.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_flowers.png b/res/drawable-hdpi/ic_scene_mode_black_flowers.png Binary files differnew file mode 100644 index 000000000..527329e75 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_flowers.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_hdr.png b/res/drawable-hdpi/ic_scene_mode_black_hdr.png Binary files differnew file mode 100644 index 000000000..3baa45485 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_hdr.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_landscape.png b/res/drawable-hdpi/ic_scene_mode_black_landscape.png Binary files differnew file mode 100644 index 000000000..45a247e8d --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_landscape.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_night.png b/res/drawable-hdpi/ic_scene_mode_black_night.png Binary files differnew file mode 100644 index 000000000..6cf2ce8eb --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_night.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_optizoom.png b/res/drawable-hdpi/ic_scene_mode_black_optizoom.png Binary files differnew file mode 100644 index 000000000..331705738 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_optizoom.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_panorama.png b/res/drawable-hdpi/ic_scene_mode_black_panorama.png Binary files differnew file mode 100644 index 000000000..38c4a1b39 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_panorama.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_portrait.png b/res/drawable-hdpi/ic_scene_mode_black_portrait.png Binary files differnew file mode 100644 index 000000000..564e3da51 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_portrait.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_pro_mode.png b/res/drawable-hdpi/ic_scene_mode_black_pro_mode.png Binary files differnew file mode 100644 index 000000000..bb9cd76ac --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_pro_mode.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_sharp_photo.png b/res/drawable-hdpi/ic_scene_mode_black_sharp_photo.png Binary files differnew file mode 100644 index 000000000..a475cc724 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_sharp_photo.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_snow.png b/res/drawable-hdpi/ic_scene_mode_black_snow.png Binary files differnew file mode 100644 index 000000000..2ac492c08 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_snow.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_sports.png b/res/drawable-hdpi/ic_scene_mode_black_sports.png Binary files differnew file mode 100644 index 000000000..932be9821 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_sports.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_sunset.png b/res/drawable-hdpi/ic_scene_mode_black_sunset.png Binary files differnew file mode 100644 index 000000000..68b653527 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_sunset.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_time_lapse.png b/res/drawable-hdpi/ic_scene_mode_black_time_lapse.png Binary files differnew file mode 100644 index 000000000..868e51d3d --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_time_lapse.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_tracking_focus.png b/res/drawable-hdpi/ic_scene_mode_black_tracking_focus.png Binary files differnew file mode 100644 index 000000000..e86a330c2 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_tracking_focus.png diff --git a/res/drawable-hdpi/ic_scene_mode_black_ubifocus.png b/res/drawable-hdpi/ic_scene_mode_black_ubifocus.png Binary files differnew file mode 100644 index 000000000..eb174e728 --- /dev/null +++ b/res/drawable-hdpi/ic_scene_mode_black_ubifocus.png diff --git a/res/drawable-mdpi/bar_beautify_rectangle_blue.png b/res/drawable-mdpi/bar_beautify_rectangle_blue.png Binary files differnew file mode 100755 index 000000000..454787c9c --- /dev/null +++ b/res/drawable-mdpi/bar_beautify_rectangle_blue.png diff --git a/res/drawable-mdpi/bar_beautify_rectangle_white.png b/res/drawable-mdpi/bar_beautify_rectangle_white.png Binary files differnew file mode 100755 index 000000000..70b4c951f --- /dev/null +++ b/res/drawable-mdpi/bar_beautify_rectangle_white.png diff --git a/res/drawable-mdpi/ic_beautify_oval.png b/res/drawable-mdpi/ic_beautify_oval.png Binary files differnew file mode 100755 index 000000000..373a99a28 --- /dev/null +++ b/res/drawable-mdpi/ic_beautify_oval.png diff --git a/res/drawable/auto_blue.png b/res/drawable/auto_blue.png Binary files differnew file mode 100644 index 000000000..acf632e16 --- /dev/null +++ b/res/drawable/auto_blue.png diff --git a/res/drawable/beautify_progressbar_style.xml b/res/drawable/beautify_progressbar_style.xml new file mode 100755 index 000000000..962d8dedb --- /dev/null +++ b/res/drawable/beautify_progressbar_style.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (c) 2016, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:id="@android:id/background" + android:drawable="@drawable/bar_beautify_rectangle_white" /> + <item android:id="@android:id/progress"> + <clip android:drawable="@drawable/bar_beautify_rectangle_blue" /> + </item> +</layer-list>
\ No newline at end of file diff --git a/res/drawable/cloudy.png b/res/drawable/cloudy.png Binary files differnew file mode 100644 index 000000000..b29ab9e5d --- /dev/null +++ b/res/drawable/cloudy.png diff --git a/res/drawable/cloudy_blue.png b/res/drawable/cloudy_blue.png Binary files differnew file mode 100644 index 000000000..303a2703e --- /dev/null +++ b/res/drawable/cloudy_blue.png diff --git a/res/drawable/fluorescent.png b/res/drawable/fluorescent.png Binary files differnew file mode 100644 index 000000000..d25f7473e --- /dev/null +++ b/res/drawable/fluorescent.png diff --git a/res/drawable/fluorescent_blue.png b/res/drawable/fluorescent_blue.png Binary files differnew file mode 100644 index 000000000..7fb7836dd --- /dev/null +++ b/res/drawable/fluorescent_blue.png diff --git a/res/drawable/icon_exposure.png b/res/drawable/icon_exposure.png Binary files differnew file mode 100644 index 000000000..ff5397251 --- /dev/null +++ b/res/drawable/icon_exposure.png diff --git a/res/drawable/icon_exposure_blue.png b/res/drawable/icon_exposure_blue.png Binary files differnew file mode 100644 index 000000000..544b98ab4 --- /dev/null +++ b/res/drawable/icon_exposure_blue.png diff --git a/res/drawable/icon_iso.png b/res/drawable/icon_iso.png Binary files differnew file mode 100644 index 000000000..85662af5e --- /dev/null +++ b/res/drawable/icon_iso.png diff --git a/res/drawable/icon_iso_blue.png b/res/drawable/icon_iso_blue.png Binary files differnew file mode 100644 index 000000000..001d8c9f5 --- /dev/null +++ b/res/drawable/icon_iso_blue.png diff --git a/res/drawable/icon_manual.png b/res/drawable/icon_manual.png Binary files differnew file mode 100644 index 000000000..53e2ff5ec --- /dev/null +++ b/res/drawable/icon_manual.png diff --git a/res/drawable/icon_manual_blue.png b/res/drawable/icon_manual_blue.png Binary files differnew file mode 100644 index 000000000..52b2daa19 --- /dev/null +++ b/res/drawable/icon_manual_blue.png diff --git a/res/drawable/icon_white_balance.png b/res/drawable/icon_white_balance.png Binary files differnew file mode 100644 index 000000000..1fbd36fd3 --- /dev/null +++ b/res/drawable/icon_white_balance.png diff --git a/res/drawable/icon_white_balance_blue.png b/res/drawable/icon_white_balance_blue.png Binary files differnew file mode 100644 index 000000000..ca84b89d8 --- /dev/null +++ b/res/drawable/icon_white_balance_blue.png diff --git a/res/drawable/icon_x.png b/res/drawable/icon_x.png Binary files differnew file mode 100644 index 000000000..a807409c8 --- /dev/null +++ b/res/drawable/icon_x.png diff --git a/res/drawable/incandecent.png b/res/drawable/incandecent.png Binary files differnew file mode 100644 index 000000000..d601e4b5c --- /dev/null +++ b/res/drawable/incandecent.png diff --git a/res/drawable/incandecent_blue.png b/res/drawable/incandecent_blue.png Binary files differnew file mode 100644 index 000000000..0c3eabf13 --- /dev/null +++ b/res/drawable/incandecent_blue.png diff --git a/res/drawable/promode.png b/res/drawable/promode.png Binary files differnew file mode 100644 index 000000000..53e2ff5ec --- /dev/null +++ b/res/drawable/promode.png diff --git a/res/drawable/radial_menu.png b/res/drawable/radial_menu.png Binary files differnew file mode 100644 index 000000000..a5757f1a0 --- /dev/null +++ b/res/drawable/radial_menu.png diff --git a/res/drawable/selected_dot.png b/res/drawable/selected_dot.png Binary files differnew file mode 100644 index 000000000..9114f6ce6 --- /dev/null +++ b/res/drawable/selected_dot.png diff --git a/res/drawable/sunlight.png b/res/drawable/sunlight.png Binary files differnew file mode 100644 index 000000000..cc33dcc3d --- /dev/null +++ b/res/drawable/sunlight.png diff --git a/res/drawable/sunlight_blue.png b/res/drawable/sunlight_blue.png Binary files differnew file mode 100644 index 000000000..c390ec653 --- /dev/null +++ b/res/drawable/sunlight_blue.png diff --git a/res/drawable/white_dot.png b/res/drawable/white_dot.png Binary files differnew file mode 100644 index 000000000..847fd84fe --- /dev/null +++ b/res/drawable/white_dot.png diff --git a/res/layout/bestpicture_page.xml b/res/layout/bestpicture_page.xml index d6b6022c8..5a1b604f0 100644..100755 --- a/res/layout/bestpicture_page.xml +++ b/res/layout/bestpicture_page.xml @@ -34,9 +34,9 @@ IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. <ImageView android:id="@+id/image_view" android:adjustViewBounds="true" - android:scaleType="fitXY" - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:scaleType="fitCenter" + android:layout_width="match_parent" + android:layout_height="match_parent" android:layout_gravity="center" /> <RelativeLayout android:layout_width="match_parent" diff --git a/res/layout/capture_module.xml b/res/layout/capture_module.xml index fc05fd0ca..44d5c59c6 100644..100755 --- a/res/layout/capture_module.xml +++ b/res/layout/capture_module.xml @@ -67,30 +67,27 @@ android:layout_width="match_parent" android:layout_height="match_parent" /> - <com.android.camera.ui.RotateLayout - android:id="@+id/recording_time_rect" + + <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"> - - <LinearLayout - xmlns:android="http://schemas.android.com/apk/res/android" + <com.android.camera.ui.RotateLayout + android:id="@+id/recording_time_rect" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="horizontal"> - - <com.android.camera.PauseButton - android:id="@+id/video_pause" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="70dp" - android:padding="23dp" - android:src="@drawable/btn_pause_recording"/> - + android:layout_alignParentTop="true" + android:layout_alignParentLeft="true" + android:visibility="gone" + android:layout_marginLeft="24dp" + android:layout_marginTop="80dp"> <include android:id="@+id/labels" layout="@layout/viewfinder_labels_video"/> - </LinearLayout> - </com.android.camera.ui.RotateLayout> + </com.android.camera.ui.RotateLayout> + </FrameLayout> + + <include + layout="@layout/scene_mode_label"/> <include style="@style/CameraControls" diff --git a/res/layout/one_ui_layout.xml b/res/layout/one_ui_layout.xml index 2a87b6cd2..53174698c 100644..100755 --- a/res/layout/one_ui_layout.xml +++ b/res/layout/one_ui_layout.xml @@ -21,6 +21,11 @@ android:layout_width="match_parent" android:layout_height="match_parent"> + <com.android.camera.ui.ProMode + android:id="@+id/promode_slider" + android:layout_height="wrap_content" + android:layout_width="wrap_content" /> + <com.android.camera.ui.FlashToggleButton android:id="@+id/flash_button" style="@style/OneUIMenuButton" @@ -35,8 +40,8 @@ <com.android.camera.ShutterButton android:id="@+id/shutter_button" - android:layout_width="70dp" - android:layout_height="70dp" + android:layout_width="@dimen/one_ui_bottom_large" + android:layout_height="@dimen/one_ui_bottom_large" android:clickable="true" android:contentDescription="@string/accessibility_shutter_button" android:focusable="true" @@ -45,14 +50,24 @@ <com.android.camera.ui.RotateImageView android:id="@+id/video_button" - android:layout_width="55dp" - android:layout_height="55dp" + android:layout_width="@dimen/one_ui_bottom_small" + android:layout_height="@dimen/one_ui_bottom_small" android:clickable="true" android:contentDescription="@string/accessibility_shutter_button" android:focusable="true" android:scaleType="fitCenter" android:src="@drawable/video_capture" /> + <com.android.camera.PauseButton + android:id="@+id/video_pause" + android:layout_width="@dimen/one_ui_bottom_small" + android:layout_height="@dimen/one_ui_bottom_small" + android:focusable="true" + android:clickable="true" + android:scaleType="fitCenter" + android:visibility="gone" + android:src="@drawable/btn_pause_recording"/> + <com.android.camera.ui.RotateImageView android:id="@+id/preview_thumb" android:layout_width="@dimen/capture_size" @@ -91,42 +106,53 @@ android:id="@+id/makeup_seekbar_layout" android:visibility="gone"> <ImageView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingLeft="5dp" - android:paddingRight="5dp" + android:layout_width="12dp" + android:layout_height="20dp" + android:layout_marginLeft="23dp" + android:layout_marginRight="23dp" android:id="@+id/seekbar_toggle"> </ImageView> <RelativeLayout - android:layout_width="match_parent" + android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/seekbar_body"> <com.android.camera.ui.RotateLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" + android:layout_width="24dp" + android:layout_height="24dp" android:layout_alignParentLeft="true" android:id="@+id/makeup_low_text"> <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/makeup_seekbar_low"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textSize="12sp" + android:gravity="center" + android:text="@string/makeup_seekbar_low" + android:textColor="@android:color/white"> </TextView> </com.android.camera.ui.RotateLayout> <SeekBar - android:layout_width="match_parent" + android:layout_width="220dp" android:layout_height="wrap_content" - android:paddingLeft="100dp" - android:paddingRight="100dp" + android:maxHeight="3dip" + android:minHeight="1dip" + android:layout_marginLeft="10dp" + android:layout_toRightOf="@+id/makeup_low_text" + android:progressDrawable="@drawable/beautify_progressbar_style" + android:thumb="@drawable/ic_beautify_oval" android:id="@+id/makeup_seekbar"/> <com.android.camera.ui.RotateLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" + android:layout_width="24dp" + android:layout_height="24dp" + android:layout_toRightOf="@+id/makeup_seekbar" + android:layout_marginLeft="10dp" android:id="@+id/makeup_high_text"> <TextView - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/makeup_seekbar_high"> + android:layout_width="match_parent" + android:layout_height="match_parent" + android:textSize="12sp" + android:gravity="center" + android:text="@string/makeup_seekbar_high" + android:textColor="@android:color/white"> </TextView> </com.android.camera.ui.RotateLayout> </RelativeLayout> @@ -156,4 +182,11 @@ android:visibility="gone" /> </LinearLayout> + <include layout="@layout/pro_mode_layout" /> + + <ImageView + android:id="@+id/promode_close_button" + android:layout_height="20dp" + android:layout_width="20dp" + android:src="@drawable/icon_x" /> </com.android.camera.ui.OneUICameraControls> diff --git a/res/layout/pano_capture_module.xml b/res/layout/pano_capture_module.xml index e382570ef..c8809af37 100644 --- a/res/layout/pano_capture_module.xml +++ b/res/layout/pano_capture_module.xml @@ -34,6 +34,10 @@ android:id="@+id/preview_process_view" android:layout_width="match_parent" android:layout_height="match_parent"/> + + <include + layout="@layout/scene_mode_label"/> + <include style="@style/CameraControls" layout="@layout/camera_controls" diff --git a/res/layout/pro_mode_layout.xml b/res/layout/pro_mode_layout.xml new file mode 100644 index 000000000..54ccb556c --- /dev/null +++ b/res/layout/pro_mode_layout.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) 2016, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/pro_mode_layout" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:visibility="invisible"> + + <com.android.camera.ui.RotateLayout + android:id="@+id/exposure_rotate_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <LinearLayout + android:id="@+id/exposure_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical"> + + <ImageView + android:id="@+id/exposure" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_gravity="center" + android:src="@drawable/icon_exposure" /> + + <TextView + android:id="@+id/exposure_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + </LinearLayout> + </com.android.camera.ui.RotateLayout> + + <com.android.camera.ui.RotateLayout + android:id="@+id/manual_rotate_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <LinearLayout + android:id="@+id/manual_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical"> + + <ImageView + android:id="@+id/manual" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_gravity="center" + android:src="@drawable/icon_manual" /> + + <TextView + android:id="@+id/manual_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + </LinearLayout> + </com.android.camera.ui.RotateLayout> + + <com.android.camera.ui.RotateLayout + android:id="@+id/white_balance_rotate_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <LinearLayout + android:id="@+id/white_balance_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical"> + + <ImageView + android:id="@+id/white_balance" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_gravity="center" + android:src="@drawable/icon_white_balance" /> + + <TextView + android:id="@+id/white_balance_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + </LinearLayout> + </com.android.camera.ui.RotateLayout> + + <com.android.camera.ui.RotateLayout + android:id="@+id/iso_rotate_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <LinearLayout + android:id="@+id/iso_layout" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:orientation="vertical"> + + <ImageView + android:id="@+id/iso" + android:layout_width="30dp" + android:layout_height="30dp" + android:layout_gravity="center" + android:src="@drawable/icon_iso" /> + + <TextView + android:id="@+id/iso_value" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + </LinearLayout> + </com.android.camera.ui.RotateLayout> + +</LinearLayout> diff --git a/res/layout/scene_mode_instructional.xml b/res/layout/scene_mode_instructional.xml new file mode 100644 index 000000000..588dc3549 --- /dev/null +++ b/res/layout/scene_mode_instructional.xml @@ -0,0 +1,102 @@ +<?xml version="1.0" encoding="utf-8"?> +<!--Copyright (c) 2016, The Linux Foundation. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of The Linux Foundation nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/mode_layout_rect" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@android:color/transparent" + android:gravity="center"> + + <LinearLayout + android:id="@+id/title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="30dp" + android:layout_marginTop="15dp" + android:orientation="horizontal" + android:gravity="center"> + <ImageView + android:id="@+id/scene_mode_icon" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/chroma_flash"/> + <TextView + android:id="@+id/scene_mode_name" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:layout_toRightOf="@id/scene_mode_icon" + android:text="@string/pref_camera_scenemode_entry_chromaflash" + android:textColor="@android:color/black" + android:textAppearance="?android:attr/textAppearanceMedium" /> + + </LinearLayout> + + <TextView + android:id="@+id/scene_mode_instructional" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:layout_marginLeft="30dp" + android:layout_marginRight="30dp" + android:text="@string/pref_camera2_scene_mode_chroma_flash_instructional_content" + android:textColor="@android:color/black" + android:layout_below="@id/title" /> + <CheckBox + android:id="@+id/remember_selected" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="15dp" + android:layout_marginLeft="30dp" + android:layout_below="@id/scene_mode_instructional" + android:text="@string/pref_camera2_not_show_again" + android:textColor="@android:color/black"/> + + <View + android:id="@+id/separator" + android:layout_width="wrap_content" + android:layout_height="1dp" + android:layout_marginTop="15dp" + android:layout_marginBottom="15dp" + android:layout_below="@id/remember_selected" + android:background="#c0c0c0" /> + + <Button + android:id="@+id/scene_mode_instructional_ok" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/separator" + android:background="@android:color/transparent" + android:layout_marginRight="30dp" + android:layout_alignParentRight="true" + android:text="@string/pref_camera2_scene_mode_instructional_ok" + android:textColor="@android:color/black"/> +</RelativeLayout> + diff --git a/res/layout/scene_mode_label.xml b/res/layout/scene_mode_label.xml new file mode 100644 index 000000000..e0d8d43b8 --- /dev/null +++ b/res/layout/scene_mode_label.xml @@ -0,0 +1,56 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (c) 2016, The Linux Foundation. All rights reserved. + Not a Contribution. + + 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="90dp" + android:layout_marginRight="20dp" + android:gravity="right"> + <com.android.camera.ui.RotateLayout + android:id="@+id/scene_mode_label_rect" + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + <LinearLayout + android:id="@+id/scene_mode_label_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginRight="20dp" + android:background="#90000000" + android:orientation="horizontal" + android:gravity="center_vertical"> + <TextView + android:id="@+id/scene_mode_label" + android:singleLine="true" + android:text="@string/pref_camera_scenemode_entry_chromaflash" + android:textColor="@android:color/white" + android:textSize="16dp" + style="@style/OnViewfinderSceneLabel" /> + <ImageView + android:id="@+id/scene_mode_label_close" + android:layout_width="15dp" + android:layout_height="15dp" + android:layout_marginLeft="10dp" + android:layout_marginRight="10dp" + android:layout_marginTop="5dp" + android:layout_marginBottom="5dp" + android:src="@drawable/x" /> + </LinearLayout> + </com.android.camera.ui.RotateLayout> +</LinearLayout>
\ No newline at end of file diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml index 80c695d58..fbb70b984 100644 --- a/res/values/camera2arrays.xml +++ b/res/values/camera2arrays.xml @@ -157,6 +157,7 @@ <item>107</item> <item>108</item> <item>104</item> + <item>109</item> </string-array> <!-- Camera Preferences Scene Mode dialog box entries --> @@ -183,6 +184,7 @@ <item>@string/pref_camera_scenemode_entry_sharpshooter</item> <item>@string/pref_camera_scenemode_entry_trackingfocus</item> <item>@string/pref_camera_scenemode_entry_panorama</item> + <item>@string/pref_camera_scenemode_entry_promode</item> </string-array> <array name="pref_camera2_scenemode_thumbnails" translatable="false"> @@ -208,8 +210,62 @@ <item>@drawable/sharp_photo</item> <item>@drawable/tracking_focus</item> <item>@drawable/scene_panorama</item> + <item>@drawable/promode</item> </array> + <array name="pref_camera2_scenemode_black_thumbnails" translatable="false"> + <item>@drawable/ic_scene_mode_black_auto</item> + <item>@drawable/ic_scene_mode_black_dual_camera</item> + <item>@drawable/ic_scene_mode_black_hdr</item> + <item>@drawable/ic_scene_mode_black_ubifocus</item> + <item>@drawable/ic_scene_mode_black_optizoom</item> + <item>@drawable/ic_scene_mode_black_portrait</item> + <item>@drawable/ic_scene_mode_black_landscape</item> + <item>@drawable/ic_scene_mode_black_sports</item> + <item>@drawable/ic_scene_mode_black_flowers</item> + <item>@drawable/ic_scene_mode_black_backlight</item> + <item>@drawable/ic_scene_mode_black_candlelight</item> + <item>@drawable/ic_scene_mode_black_sunset</item> + <item>@drawable/ic_scene_mode_black_night</item> + <item>@drawable/ic_scene_mode_black_beach</item> + <item>@drawable/ic_scene_mode_black_snow</item> + <item>@drawable/ic_scene_mode_smartauto</item> + <item>@drawable/ic_scene_mode_black_best_photo</item> + <item>@drawable/ic_scene_mode_black_chroma_flash</item> + <item>@drawable/ic_scene_mode_black_blur_buster</item> + <item>@drawable/ic_scene_mode_black_sharp_photo</item> + <item>@drawable/ic_scene_mode_black_tracking_focus</item> + <item>@drawable/ic_scene_mode_black_panorama</item> + <item>@drawable/ic_scene_mode_black_dual_camera</item> + </array> + + <!-- Camera Preferences Scene Mode dialog box entries --> + <string-array name="pref_camera2_scenemode_instructional_entries" translatable="false"> + <item>@string/pref_camera_scenemode_entry_auto</item> + <item>@string/pref_camera2_scene_mode_dual_camera_instructional_content</item> + <item>@string/pref_camera2_scene_mode_hdr_instructional_content</item> + <item>@string/pref_camera2_scene_mode_ubi_focus_instructional_content</item> + <item>@string/pref_camera2_scene_mode_opti_zoom_instructional_content</item> + <item>""</item> + <item>""</item> + <item>@string/pref_camera2_scene_mode_sports_instructional_content</item> + <item>""</item> + <item>""</item> + <item>""</item> + <item>""</item> + <item>""</item> + <item>""</item> + <item>""</item> + <item>""</item> + <item>@string/pref_camera2_scene_mode_best_photo_instructional_content</item> + <item>@string/pref_camera2_scene_mode_chroma_flash_instructional_content</item> + <item>@string/pref_camera2_scene_mode_blur_buster_instructional_content</item> + <item>@string/pref_camera2_scene_mode_sharp_photo_instructional_content</item> + <item>@string/pref_camera2_scene_mode_tracking_focus_instructional_content</item> + <item>@string/pref_camera2_scene_mode_panorama_instructional_content</item> + <item>@string/pref_camera2_scene_mode_pro_instructional_content</item> + </string-array> + <string-array name="pref_camera2_whitebalance_entryvalues" translatable="false"> <item>1</item> <item>2</item> diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 2a58a33e2..d89d0a990 100644..100755 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -180,4 +180,7 @@ <dimen name="refocus_circle_diameter_3">95dp</dimen> <dimen name="refocus_cross_length">19dp</dimen> <dimen name="refocus_stroke_width">2dp</dimen> + + <dimen name="one_ui_bottom_large">75dp</dimen> + <dimen name="one_ui_bottom_small">55dp</dimen> </resources> diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml index 34c9b32cf..509fae318 100644 --- a/res/values/qcomstrings.xml +++ b/res/values/qcomstrings.xml @@ -1029,6 +1029,7 @@ <string name="pref_camera_scenemode_entry_sharpshooter" translatable="true">SharpPhoto</string> <string name="pref_camera_scenemode_entry_trackingfocus" translatable="true">TrackingFocus</string> <string name="pref_camera_scenemode_entry_panorama" translatable="true">Panorama</string> + <string name="pref_camera_scenemode_entry_promode" translatable="true">ProMode</string> <string name="bestpicture_done" translatable="true">DONE</string> <string name="bestpicture_at_least_one_picture" translatable="true">At least, one picture has to be chosen.</string> @@ -1040,10 +1041,10 @@ <string name="pref_camera2_shutter_sound_entry_off" translatable="true">Off</string> <string name="pref_camera2_shutter_sound_title" translatable="true">Shutter Sound</string> - <string name="pref_camera2_timer_entry_off" translatable="false">Off</string> - <string name="pref_camera2_timer_entry_2sec" translatable="false">2 seconds</string> - <string name="pref_camera2_timer_entry_5sec" translatable="false">5 seconds</string> - <string name="pref_camera2_timer_entry_10sec" translatable="false">10 seconds</string> + <string name="pref_camera2_timer_entry_off" translatable="true">Off</string> + <string name="pref_camera2_timer_entry_2sec" translatable="true">2 seconds</string> + <string name="pref_camera2_timer_entry_5sec" translatable="true">5 seconds</string> + <string name="pref_camera2_timer_entry_10sec" translatable="true">10 seconds</string> <string name="pref_camera2_timer_value_off" translatable="false">0</string> <string name="pref_camera2_timer_value_2sec" translatable="false">2</string> @@ -1056,5 +1057,28 @@ <string name="makeup_ui_ok_button" translatable="true">OK</string> <string name="makeup_seekbar_low" translatable="true">low</string> <string name="makeup_seekbar_high" translatable="true">high</string> + + <string name="perf_camera2_version_info" translatable="true">Version Info</string> + <string name="perf_camera2_preferences_category_general" translatable="true">General</string> + <string name="perf_camera2_preferences_category_photo" translatable="true">Still camera</string> + <string name="perf_camera2_preferences_category_video" translatable="true">Video camera</string> + <string name="perf_camera2_preferences_category_system" translatable="true">System</string> + + <string name="pref_camera2_scene_mode_dual_camera_instructional_content" translatable="true">In dual camera mode, your pictures will be clearer. You can also apply background effects, by editing your dual camera pictures in the Gallery.</string> + <string name="pref_camera2_scene_mode_ubi_focus_instructional_content" translatable="true">With UbiFocus, your camera will take five pictures and combine them into one picture,where everything is in focus. You can choose to re-focus, after you take the picture.</string> + <string name="pref_camera2_scene_mode_opti_zoom_instructional_content" translatable="true">Use Optizoom when you want to zoom in for a clear picture.You can zoom before, or after taking the picture.</string> + <string name="pref_camera2_scene_mode_sports_instructional_content" translatable="true">Sports mode helps you take better sports or action shots.\n\nImporves your photos, but has no effect on video recording.</string> + <string name="pref_camera2_scene_mode_hdr_instructional_content" translatable="true">When you are in mixed lighting conditions such as low light and backlit scenes select "HDR" for best possible pictures.</string> + <string name="pref_camera2_scene_mode_best_photo_instructional_content" translatable="true">Allows you to take a burst of 10 photos, and you can choose the best one(s).</string> + <string name="pref_camera2_scene_mode_chroma_flash_instructional_content" translatable="true">With ChromaFlash, the lighting in your flash photos will be optimized for all of the people and objects in your pictures.This feature applies only to the back camera.</string> + <string name="pref_camera2_scene_mode_sharp_photo_instructional_content" translatable="true">Sharp Phot reduces blur that is due to motion in the scene, or due to hand motion.</string> + <string name="pref_camera2_scene_mode_tracking_focus_instructional_content" translatable="true">Allows you to track and focus a selected person or object while taking pictures or videos. To start, position the focus rectangle on a person or object you want to track.</string> + <string name="pref_camera2_scene_mode_time_lapse_instructional_content" translatable="true">With Time Lapse, you can transform a long video recording into a few seconds of video.This effect applies to videos, but not photos.</string> + <string name="pref_camera2_scene_mode_panorama_instructional_content" translatable="true">Allows you to pan left or right to take a wide landscape photo. You can alse pan up or down to take tall photos.</string> + <string name="pref_camera2_scene_mode_blur_buster_instructional_content" translatable="true">BlurBuster reduces blur from shaky hands.It can be helpful when taking photos in difficult places.</string> + <string name="pref_camera2_scene_mode_pro_instructional_content" translatable="true">With Pro Mode, you can manually control settings for ISO,Exposure, White Balance, and Focus. You will have easy access to all of these advanced settings</string> + + <string name="pref_camera2_not_show_again">Do not show again</string> + <string name="pref_camera2_scene_mode_instructional_ok" translatable="true">OK</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index 082c5ed8a..8432c11e9 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -316,4 +316,18 @@ <style name="PanoViewHorizontalBar"> <item name="android:background">@android:color/transparent</item> </style> + + <style name="OnViewfinderSceneLabel"> + <item name="android:gravity">center</item> + <item name="android:layout_width">wrap_content</item> + <item name="android:layout_height">wrap_content</item> + <item name="android:layout_marginLeft">10dp</item> + <item name="android:layout_marginRight">10dp</item> + <item name="android:layout_marginTop">5dp</item> + <item name="android:layout_marginBottom">5dp</item> + <item name="android:singleLine">true</item> + <item name="android:textColor">@android:color/white</item> + <item name="android:textSize">16dp</item> + <item name="android:background">@android:color/transparent</item> + </style> </resources> diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml index f3603c443..8ad300fe7 100644 --- a/res/xml/capture_preferences.xml +++ b/res/xml/capture_preferences.xml @@ -124,6 +124,14 @@ camera:title="@string/pref_camera_scenemode_title"/> <IconListPreference + camera:defaultValue="@string/pref_camera2_scenemode_default" + camera:entries="@array/pref_camera2_scenemode_instructional_entries" + camera:entryValues="@array/pref_camera2_scenemode_entryvalues" + camera:key="pref_camera2_scenemode_instructional" + camera:thumbnails="@array/pref_camera2_scenemode_black_thumbnails" + camera:title="@string/pref_camera_scenemode_title"/> + + <IconListPreference camera:defaultValue="@string/setting_off_value" camera:entries="@array/pref_camera2_redeyereduction_entries" camera:entryValues="@array/pref_camera2_redeyereduction_entryvalues" diff --git a/res/xml/setting_menu_preferences.xml b/res/xml/setting_menu_preferences.xml index 87660713a..80097f497 100644 --- a/res/xml/setting_menu_preferences.xml +++ b/res/xml/setting_menu_preferences.xml @@ -31,7 +31,7 @@ <PreferenceCategory android:key="general" android:layout="@layout/preferences_category" - android:title="GENERAL"> + android:title="@string/perf_camera2_preferences_category_general"> <SwitchPreference android:defaultValue="false" android:icon="@drawable/gps_location" @@ -60,7 +60,7 @@ <PreferenceCategory android:key="photo" android:layout="@layout/preferences_category" - android:title="STILL CAMERA"> + android:title="@string/perf_camera2_preferences_category_photo"> <ListPreference android:defaultValue="@string/pref_camera2_timer_value_off" android:entries="@array/pref_camera2_timer_entries" @@ -169,7 +169,7 @@ <PreferenceCategory android:key="video" android:layout="@layout/preferences_category" - android:title="VIDEO CAMERA"> + android:title="@string/perf_camera2_preferences_category_video"> <ListPreference android:entries="@array/pref_camera2_video_quality_entries" android:entryValues="@array/pref_camera2_video_quality_entryvalues" @@ -196,11 +196,14 @@ android:layout="@layout/preference" android:title="@string/pref_camera_dis_title" /> - <SwitchPreference - android:defaultValue="false" + <ListPreference + android:defaultValue="@string/pref_camera_noise_reduction_default" + android:entries="@array/pref_camera2_noise_reduction_entries" + android:entryValues="@array/pref_camera2_noise_reduction_entryvalues" android:icon="@drawable/noise_reduction" android:key="pref_camera2_noise_reduction_key" android:layout="@layout/preference" + android:summary="%s" android:title="@string/pref_camera_noise_reduction_title" /> <ListPreference diff --git a/rs/rotator.rs b/rs/rotator.rs index 5a27e00f7..62257d2cf 100644..100755 --- a/rs/rotator.rs +++ b/rs/rotator.rs @@ -35,32 +35,51 @@ rs_allocation gIn; uint32_t width; uint32_t height; uint32_t pad; -bool gFlip; +uint32_t degree; uchar __attribute__((kernel)) rotate90andMerge(uint32_t x, uint32_t y) { uchar yValue = rsGetElementAt_uchar(gIn, x + y*width); - if(gFlip) { + if(degree == 180) { if(x >= width - pad) return (uchar)0; rsSetElementAt_uchar(gOut, yValue, (width-1-x-pad)*height + height - 1 - y); - } else { + } else if (degree == 90) { rsSetElementAt_uchar(gOut, yValue, x*height + height - 1 - y); + } else if (degree == 270) { + if(x >= width - pad) + return (uchar)0; + rsSetElementAt_uchar(gOut, yValue, (width-1-x-pad)*height + y); + } else if (degree == 0) { + if(x >= width - pad) + return (uchar)0; + rsSetElementAt_uchar(gOut, yValue, x*height + y); } + if(x%2 == 0 && y%2 == 0) { uint32_t ySize = width*height; uint32_t index = ySize + x + ((y/2) * width); uchar vValue = rsGetElementAt_uchar(gIn, index); uchar uValue = rsGetElementAt_uchar(gIn, index + 1); - if(gFlip) { + if(degree == 180) { if(x >= width - pad) return (uchar)0; rsSetElementAt_uchar(gOut, uValue, ySize + (width-2-x-pad)/2*height + height - 1 - y); rsSetElementAt_uchar(gOut, vValue, ySize + (width-2-x-pad)/2*height + height - 1 - y - 1); - } else { + } else if (degree == 90) { rsSetElementAt_uchar(gOut, uValue, ySize + x/2*height + height - 1 - y); rsSetElementAt_uchar(gOut, vValue, ySize + x/2*height + height - 1 - y - 1); + } else if (degree == 270) { + if(x >= (width - pad)) + return (uchar)0; + rsSetElementAt_uchar(gOut, uValue, ySize + (width-1-x-pad)/2*height + y -1); + rsSetElementAt_uchar(gOut, vValue, ySize + (width-1-x-pad)/2*height + y); + } else if (degree == 0) { + if(x >= (width - pad)) + return (uchar)0; + rsSetElementAt_uchar(gOut, uValue, ySize + x/2*height + y - 1); + rsSetElementAt_uchar(gOut, vValue, ySize + x/2*height + y); } } return (uchar)0; diff --git a/src/com/android/camera/AndroidCameraManagerImpl.java b/src/com/android/camera/AndroidCameraManagerImpl.java index 486629895..438be2e90 100755..100644 --- a/src/com/android/camera/AndroidCameraManagerImpl.java +++ b/src/com/android/camera/AndroidCameraManagerImpl.java @@ -200,6 +200,30 @@ class AndroidCameraManagerImpl implements CameraManager { return true; } + public boolean waitDone(long timeout) { + final Object waitDoneLock = new Object(); + final Runnable unlockRunnable = new Runnable() { + @Override + public void run() { + synchronized (waitDoneLock) { + waitDoneLock.notifyAll(); + } + } + }; + + synchronized (waitDoneLock) { + mCameraHandler.post(unlockRunnable); + try { + waitDoneLock.wait(timeout); + mCameraHandler.removeCallbacks(unlockRunnable); + } catch (InterruptedException ex) { + Log.v(TAG, "waitDone interrupted"); + return false; + } + } + return true; + } + /** * This method does not deal with the API level check. Everyone should * check first for supported operations before sending message to this handler. @@ -499,7 +523,7 @@ class AndroidCameraManagerImpl implements CameraManager { @Override public void stopPreview() { mCameraHandler.sendEmptyMessage(STOP_PREVIEW); - mCameraHandler.waitDone(); + mCameraHandler.waitDone(200); } @Override diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 26e7ca933..d3c39793a 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -234,7 +234,6 @@ public class CameraActivity extends Activity private View mPreviewCover; private FrameLayout mPreviewContentLayout; private boolean mPaused = true; - private boolean mHasCriticalPermissions; private boolean mForceReleaseCamera = false; private Uri[] mNfcPushUris = new Uri[1]; @@ -310,7 +309,6 @@ public class CameraActivity extends Activity public void onCameraDisabled(int cameraId) { UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL, "security"); - CameraUtil.showErrorAndFinish(CameraActivity.this, R.string.camera_disabled); } @@ -319,27 +317,21 @@ public class CameraActivity extends Activity public void onDeviceOpenFailure(int cameraId) { UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL, "open"); - - CameraUtil.showErrorAndFinish(CameraActivity.this, - R.string.cannot_connect_camera); + showOpenCameraErrorDialog(); } @Override public void onReconnectionFailure(CameraManager mgr) { UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_OPEN_FAIL, "reconnect"); - - CameraUtil.showErrorAndFinish(CameraActivity.this, - R.string.cannot_connect_camera); + showOpenCameraErrorDialog(); } @Override public void onStartPreviewFailure(int cameraId) { UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_START_PREVIEW_FAIL, "startpreview"); - - CameraUtil.showErrorAndFinish(CameraActivity.this, - R.string.cannot_connect_camera); + showOpenCameraErrorDialog(); } }; @@ -1448,11 +1440,6 @@ public class CameraActivity extends Activity @Override public void onCreate(Bundle state) { super.onCreate(state); - if (checkPermissions() || !mHasCriticalPermissions) { - Log.v(TAG, "onCreate: Missing critical permissions."); - finish(); - return; - } // Check if this is in the secure camera mode. Intent intent = getIntent(); String action = intent.getAction(); @@ -1464,8 +1451,6 @@ public class CameraActivity extends Activity mSecureCamera = intent.getBooleanExtra(SECURE_CAMERA_EXTRA, false); } - mCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null); - if (mSecureCamera) { // Change the window flags so that secure camera can show when locked Window win = getWindow(); @@ -1479,9 +1464,20 @@ public class CameraActivity extends Activity Log.d(TAG, "acquire wake lock"); } win.setAttributes(params); + } + if (mSecureCamera && !hasCriticalPermissions()) { + return; + } + if (isStartRequsetPermission()) { + Log.v(TAG, "onCreate: Missing critical permissions."); + finish(); + return; } + + mCursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + null, null, null, null); GcamHelper.init(getContentResolver()); getWindow().requestFeature(Window.FEATURE_ACTION_BAR); @@ -1677,6 +1673,10 @@ public class CameraActivity extends Activity @Override public void onPause() { + if (mSecureCamera && !hasCriticalPermissions()) { + super.onPause(); + return; + } // Delete photos that are pending deletion performDeletion(); mOrientationListener.disable(); @@ -1725,9 +1725,8 @@ public class CameraActivity extends Activity * Critical permissions are: camera, microphone and storage. The app cannot run without them. * Non-critical permission is location. */ - private boolean checkPermissions() { - boolean requestPermission = false; - + private boolean hasCriticalPermissions() { + boolean hasCriticalPermission = false; if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.RECORD_AUDIO) == @@ -1736,27 +1735,38 @@ public class CameraActivity extends Activity PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { - mHasCriticalPermissions = true; + hasCriticalPermission = true; } else { - mHasCriticalPermissions = false; + hasCriticalPermission = false; } + return hasCriticalPermission; + } + + private boolean isStartRequsetPermission() { + boolean isStartPermissionActivity = false; final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean isRequestShown = prefs.getBoolean(CameraSettings.KEY_REQUEST_PERMISSION, false); - if(!isRequestShown || !mHasCriticalPermissions) { - Log.v(TAG, "Request permission"); + + if(!mSecureCamera && (!isRequestShown || !hasCriticalPermissions())) { + Log.v(TAG, "Start Request Permission"); Intent intent = new Intent(this, PermissionsActivity.class); startActivity(intent); SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(CameraSettings.KEY_REQUEST_PERMISSION, true); editor.apply(); - requestPermission = true; - } - return requestPermission; + isStartPermissionActivity = true; + } + return isStartPermissionActivity; } @Override public void onResume() { - if (checkPermissions() || !mHasCriticalPermissions) { + if (mSecureCamera && !hasCriticalPermissions()) { + super.onResume(); + showOpenCameraErrorDialog(); + return; + } + if (isStartRequsetPermission()) { super.onResume(); Log.v(TAG, "onResume: Missing critical permissions."); finish(); @@ -1814,6 +1824,9 @@ public class CameraActivity extends Activity @Override public void onStart() { super.onStart(); + if (mSecureCamera && !hasCriticalPermissions()) { + return; + } bindMediaSaveService(); mPanoramaViewHelper.onStart(); } @@ -1821,6 +1834,9 @@ public class CameraActivity extends Activity @Override protected void onStop() { super.onStop(); + if (mSecureCamera && !hasCriticalPermissions()) { + return; + } mPanoramaViewHelper.onStop(); unbindMediaSaveService(); } @@ -2352,4 +2368,14 @@ public class CameraActivity extends Activity public CameraModule getCurrentModule() { return mCurrentModule; } + + private void showOpenCameraErrorDialog() { + if (!hasCriticalPermissions()) { + CameraUtil.showErrorAndFinish(CameraActivity.this, + R.string.error_permissions); + } else { + CameraUtil.showErrorAndFinish(CameraActivity.this, + R.string.cannot_connect_camera); + } + } } diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index 069fb8701..97c4eae0a 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -1027,6 +1027,10 @@ public class CameraSettings { return; } + if (numOfCameras > 2 ) { + numOfCameras = 2; + } + CharSequence[] entryValues = new CharSequence[numOfCameras]; for (int i = 0; i < numOfCameras; ++i) { entryValues[i] = "" + i; diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 42f94b6f5..e870161f3 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -56,11 +56,14 @@ import android.media.ImageReader; import android.media.MediaActionSound; import android.media.MediaMetadataRetriever; import android.media.MediaRecorder; +import android.media.EncoderCapabilities; +import android.media.EncoderCapabilities.VideoEncoderCap; import android.net.Uri; import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; +import android.os.Bundle; import android.os.Message; import android.os.SystemClock; import android.provider.MediaStore; @@ -72,10 +75,10 @@ import android.view.OrientationEventListener; import android.view.Surface; import android.view.SurfaceHolder; import android.view.View; +import android.view.WindowManager; import android.widget.Toast; import com.android.camera.exif.ExifInterface; -import com.android.camera.Exif; import com.android.camera.imageprocessor.filter.BlurbusterFilter; import com.android.camera.imageprocessor.filter.ChromaflashFilter; import com.android.camera.imageprocessor.filter.ImageFilter; @@ -94,6 +97,7 @@ import com.android.camera.util.CameraUtil; import com.android.camera.util.PersistUtil; import com.android.camera.util.SettingTranslation; import com.android.camera.util.ApiHelper; +import com.android.camera.util.AccessibilityUtils; import com.android.internal.util.MemInfoReader; import org.codeaurora.snapcam.R; @@ -104,7 +108,6 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Comparator; import java.util.Date; import java.util.LinkedList; import java.util.List; @@ -116,7 +119,7 @@ public class CaptureModule implements CameraModule, PhotoController, MediaSaveService.Listener, ClearSightImageProcessor.Callback, SettingsManager.Listener, LocationManager.Listener, CountDownView.OnCountDownFinishedListener, - MediaRecorder.OnInfoListener{ + MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener { public static final int DUAL_MODE = 0; public static final int BAYER_MODE = 1; public static final int MONO_MODE = 2; @@ -168,6 +171,8 @@ public class CaptureModule implements CameraModule, PhotoController, private static final int NORMAL_SESSION_MAX_FPS = 60; + private static final int SCREEN_DELAY = 2 * 60 * 1000; + MeteringRectangle[][] mAFRegions = new MeteringRectangle[MAX_NUM_CAM][]; MeteringRectangle[][] mAERegions = new MeteringRectangle[MAX_NUM_CAM][]; CaptureRequest.Key<Byte> BayerMonoLinkEnableKey = @@ -193,6 +198,12 @@ public class CaptureModule implements CameraModule, PhotoController, public static CameraCharacteristics.Key<Byte> MetaDataMonoOnlyKey = new CameraCharacteristics.Key<>("org.codeaurora.qcamera3.sensor_meta_data.is_mono_only", Byte.class); + public static CaptureRequest.Key<Integer> SELECT_PRIORITY = + new CaptureRequest.Key<>("org.codeaurora.qcamera3.iso_exp_priority.select_priority", + Integer.class); + public static CaptureRequest.Key<Long> ISO_EXP = + new CaptureRequest.Key<>("org.codeaurora.qcamera3.iso_exp_priority.use_iso_exp_priority", + Long.class); private boolean[] mTakingPicture = new boolean[MAX_NUM_CAM]; private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE; private int mLastResultAFState = -1; @@ -206,7 +217,8 @@ public class CaptureModule implements CameraModule, PhotoController, private boolean mIsLinked = false; private long mCaptureStartTime; private boolean mPaused = true; - private boolean mSurfaceReady = false; + private Semaphore mSurfaceReadyLock = new Semaphore(1); + private boolean mSurfaceReady = true; private boolean[] mCameraOpened = new boolean[MAX_NUM_CAM]; private CameraDevice[] mCameraDevice = new CameraDevice[MAX_NUM_CAM]; private String[] mCameraId = new String[MAX_NUM_CAM]; @@ -224,6 +236,14 @@ public class CaptureModule implements CameraModule, PhotoController, private int mDisplayOrientation; private boolean mIsRefocus = false; private int mChosenImageFormat; + private Toast mToast; + + private boolean mStartRecPending = false; + private boolean mStopRecPending = false; + + boolean mUnsupportedResolution = false; + + private static final int SDCARD_SIZE_LIMIT = 4000 * 1024 * 1024; /** * A {@link CameraCaptureSession } for camera preview. @@ -281,6 +301,7 @@ public class CaptureModule implements CameraModule, PhotoController, private int mTimeBetweenTimeLapseFrameCaptureMs = 0; private boolean mCaptureTimeLapse = false; private CamcorderProfile mProfile; + private static final int CLEAR_SCREEN_DELAY = 4; private static final int UPDATE_RECORD_TIME = 5; private ContentValues mCurrentVideoValues; private String mVideoFilename; @@ -300,6 +321,7 @@ public class CaptureModule implements CameraModule, PhotoController, private MediaActionSound mSound; private Size mSupportedMaxPictureSize; + private class SelfieThread extends Thread { public void run() { try { @@ -527,8 +549,6 @@ public class CaptureModule implements CameraModule, PhotoController, public void onError(CameraDevice cameraDevice, int error) { int id = Integer.parseInt(cameraDevice.getId()); Log.e(TAG, "onError " + id + " " + error); - cameraDevice.close(); - mCameraDevice[id] = null; mCameraOpenCloseLock.release(); mCamerasOpened = false; if (null != mActivity) { @@ -622,10 +642,10 @@ public class CaptureModule implements CameraModule, PhotoController, break; } case STATE_AF_AE_LOCKED: { - Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); - Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); - Log.d(TAG, "STATE_AF_AE_LOCKED id: " + id + " afState:" + afState + " aeState:" + aeState); - break; + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + Log.d(TAG, "STATE_AF_AE_LOCKED id: " + id + " afState:" + afState + " aeState:" + aeState); + break; } case STATE_WAITING_TOUCH_FOCUS: break; @@ -781,8 +801,15 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void applyFocusDistance(CaptureRequest.Builder builder, String value) { + if (value == null) return; + float valueF = Float.valueOf(value); + builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF); + builder.set(CaptureRequest.LENS_FOCUS_DISTANCE, valueF); + } + private void createSessions() { - if (mPaused || !mCamerasOpened || !mSurfaceReady) return; + if (mPaused || !mCamerasOpened ) return; if (isBackCamera()) { switch (getCameraMode()) { case DUAL_MODE: @@ -811,12 +838,42 @@ public class CaptureModule implements CameraModule, PhotoController, return builder; } + private void waitForPreviewSurfaceReady() { + try { + if (!mSurfaceReady) { + if (!mSurfaceReadyLock.tryAcquire(2000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting for surface."); + throw new RuntimeException("Time out waiting for surface."); + } + mSurfaceReadyLock.release(); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private void updatePreviewSurfaceReadyState(boolean rdy) { + if (rdy != mSurfaceReady) { + if (rdy) { + Log.i(TAG, "Preview Surface is ready!"); + mSurfaceReadyLock.release(); + mSurfaceReady = true; + } else { + try { + Log.i(TAG, "Preview Surface is not ready!"); + mSurfaceReady = false; + mSurfaceReadyLock.acquire(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } private void createSession(final int id) { - if (mPaused || !mCameraOpened[id] || !mSurfaceReady) return; + if (mPaused || !mCameraOpened[id]) return; Log.d(TAG, "createSession " + id); List<Surface> list = new LinkedList<Surface>(); try { - Surface surface = getPreviewSurfaceForSession(id); // We set up a CaptureRequest.Builder with the output Surface. mPreviewRequestBuilder[id] = getRequestBuilder(id); mPreviewRequestBuilder[id].setTag(id); @@ -866,7 +923,10 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { - Log.e(TAG, "cameracapturesession - onConfigureFailed "+id); + Log.e(TAG, "cameracapturesession - onConfigureFailed "+ id); + if (mActivity.isFinishing()) { + return; + } new AlertDialog.Builder(mActivity) .setTitle("Camera Initialization Failed") .setMessage("Closing SnapdragonCamera") @@ -886,6 +946,8 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "cameracapturesession - onClosed"); } }; + waitForPreviewSurfaceReady(); + Surface surface = getPreviewSurfaceForSession(id); if(id == getMainCameraId()) { mFrameProcessor.setOutputSurface(surface); @@ -934,6 +996,9 @@ public class CaptureModule implements CameraModule, PhotoController, } public void setAFModeToPreview(int id, int afMode) { + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } Log.d(TAG, "setAFModeToPreview " + afMode); mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_MODE, afMode); applyAFRegions(mPreviewRequestBuilder[id], id); @@ -942,14 +1007,17 @@ public class CaptureModule implements CameraModule, PhotoController, try { mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] .build(), mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } public void setFlashModeToPreview(int id, boolean isFlashOn) { Log.d(TAG, "setFlashModeToPreview " + isFlashOn); - if(isFlashOn) { + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } + if (isFlashOn) { mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH); mPreviewRequestBuilder[id].set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE); } else { @@ -962,29 +1030,32 @@ public class CaptureModule implements CameraModule, PhotoController, try { mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] .build(), mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } public void setFocusDistanceToPreview(int id, float fd) { + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } mPreviewRequestBuilder[id].set(CaptureRequest.LENS_FOCUS_DISTANCE, fd); mPreviewRequestBuilder[id].setTag(id); try { - if(id == MONO_ID && !canStartMonoPreview()) { + if (id == MONO_ID && !canStartMonoPreview()) { mCaptureSession[id].capture(mPreviewRequestBuilder[id] .build(), mCaptureCallback, mCameraHandler); } else { mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] .build(), mCaptureCallback, mCameraHandler); } - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } public void reinit() { - mSettingsManager.reinit(getMainCameraId()); + mSettingsManager.init(); } public boolean isRefocus() { @@ -1009,8 +1080,6 @@ public class CaptureModule implements CameraModule, PhotoController, mCameraOpened[i] = false; mTakingPicture[i] = false; } - mSurfaceReady = false; - for (int i = 0; i < MAX_NUM_CAM; i++) { mState[i] = STATE_PREVIEW; } @@ -1073,7 +1142,9 @@ public class CaptureModule implements CameraModule, PhotoController, * Lock the focus as the first step for a still image capture. */ private void lockFocus(int id) { - if (mActivity == null || mCameraDevice[id] == null) { + if (mActivity == null || mCameraDevice[id] == null + || !checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + mUI.enableShutter(true); warningToast("Camera is not ready yet to take a picture."); return; } @@ -1116,14 +1187,15 @@ public class CaptureModule implements CameraModule, PhotoController, mLockRequestHashCode[id] = request.hashCode(); mState[id] = STATE_WAITING_AF_LOCK; mCaptureSession[id].capture(request, mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } private void autoFocusTrigger(int id) { Log.d(TAG, "autoFocusTrigger " + id); - if (null == mActivity || null == mCameraDevice[id]) { + if (null == mActivity || null == mCameraDevice[id] + || !checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { warningToast("Camera is not ready yet to take a picture."); return; } @@ -1137,9 +1209,10 @@ public class CaptureModule implements CameraModule, PhotoController, mState[id] = STATE_WAITING_TOUCH_FOCUS; mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); setAFModeToPreview(id, mControlAFMode); - Message message = mCameraHandler.obtainMessage(CANCEL_TOUCH_FOCUS, mCameraId[id]); + Message message = mCameraHandler.obtainMessage( + CANCEL_TOUCH_FOCUS, Integer.valueOf(mCameraId[id]), 0); mCameraHandler.sendMessageDelayed(message, CANCEL_TOUCH_FOCUS_DELAY); - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } @@ -1213,10 +1286,12 @@ public class CaptureModule implements CameraModule, PhotoController, Location location = mLocationManager.getCurrentLocation(); if(location != null) { - Log.d(TAG, "captureStillPicture gps: " + location.toString()); + // make copy so that we don't alter the saved location since we may re-use it + location = new Location(location); // workaround for Google bug. Need to convert timestamp from ms -> sec location.setTime(location.getTime()/1000); captureBuilder.set(CaptureRequest.JPEG_GPS_LOCATION, location); + Log.d(TAG, "captureStillPicture gps: " + location.toString()); } else { Log.d(TAG, "captureStillPicture no location - getRecordLocation: " + getRecordLocation()); } @@ -1299,6 +1374,13 @@ public class CaptureModule implements CameraModule, PhotoController, unlockFocus(id); } }, mCaptureCallbackHandler); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.enableVideo(false); + } + }); + } else { checkAndPlayShutterSound(id); if(isMpoOn()) { @@ -1323,7 +1405,7 @@ public class CaptureModule implements CameraModule, PhotoController, private void captureVideoSnapshot(final int id) { Log.d(TAG, "captureStillPicture " + id); try { - if (null == mActivity || null == mCameraDevice[id]) { + if (null == mActivity || null == mCameraDevice[id] || mCurrentSession == null) { warningToast("Camera is not ready yet to take a video snapshot."); return; } @@ -1373,6 +1455,9 @@ public class CaptureModule implements CameraModule, PhotoController, */ private void runPrecaptureSequence(int id) { Log.d(TAG, "runPrecaptureSequence: " + id); + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } try { CaptureRequest.Builder builder = getRequestBuilder(id); builder.setTag(id); @@ -1383,7 +1468,7 @@ public class CaptureModule implements CameraModule, PhotoController, mState[id] = STATE_WAITING_PRECAPTURE; mCaptureSession[id].capture(request, mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } @@ -1528,6 +1613,9 @@ public class CaptureModule implements CameraModule, PhotoController, */ public void unlockFocus(int id) { Log.d(TAG, "unlockFocus " + id); + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } try { CaptureRequest.Builder builder = getRequestBuilder(id); builder.setTag(id); @@ -1555,15 +1643,12 @@ public class CaptureModule implements CameraModule, PhotoController, public void run() { mUI.stopSelfieFlash(); mUI.enableShutter(true); + mUI.enableVideo(true); } }); } - } catch (NullPointerException e) { - Log.w(TAG, "Session is already closed"); - } catch (IllegalStateException e) { + } catch (NullPointerException | IllegalStateException | CameraAccessException e) { Log.w(TAG, "Session is already closed"); - } catch (CameraAccessException e) { - e.printStackTrace(); } } @@ -1584,6 +1669,15 @@ public class CaptureModule implements CameraModule, PhotoController, } } + public boolean isAllSessionClosed() { + for (int i = MAX_NUM_CAM - 1; i >= 0; i--) { + if (mCaptureSession[i] != null) { + return false; + } + } + return true; + } + private void closeSessions() { for (int i = MAX_NUM_CAM-1; i >= 0; i--) { if (null != mCaptureSession[i]) { @@ -1666,13 +1760,16 @@ public class CaptureModule implements CameraModule, PhotoController, * Lock the exposure for capture */ private void lockExposure(int id) { + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } Log.d(TAG, "lockExposure: " + id); try { applySettingsForLockExposure(mPreviewRequestBuilder[id], id); mState[id] = STATE_WAITING_AE_LOCK; mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id].build(), mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } @@ -1828,6 +1925,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onPauseBeforeSuper() { mPaused = true; + mToast = null; mUI.onPause(); if (mIsRecordingVideo) { stopRecordingVideo(getMainCameraId()); @@ -1839,6 +1937,7 @@ public class CaptureModule implements CameraModule, PhotoController, if (selfieThread != null) { selfieThread.interrupt(); } + resetScreenOn(); mUI.stopSelfieFlash(); } @@ -1855,6 +1954,7 @@ public class CaptureModule implements CameraModule, PhotoController, mFirstPreviewLoaded = false; stopBackgroundThread(); mLastJpegData = null; + setProModeVisible(); } @Override @@ -1869,6 +1969,7 @@ public class CaptureModule implements CameraModule, PhotoController, } mLongshotActive = false; mZoomValue = 1.0f; + updatePreviewSurfaceReadyState(false); } private ArrayList<Integer> getFrameProcFilterId() { @@ -2025,7 +2126,6 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "onResume " + getCameraMode()); initializeValues(); updatePreviewSize(); - mUI.showSurfaceView(); mCameraIdList = new ArrayList<>(); if (mSound == null) { @@ -2054,6 +2154,7 @@ public class CaptureModule implements CameraModule, PhotoController, msg.arg1 = FRONT_ID; mCameraHandler.sendMessage(msg); } + mUI.showSurfaceView(); if (!mFirstTimeInitialized) { initializeFirstTime(); } else { @@ -2067,6 +2168,8 @@ public class CaptureModule implements CameraModule, PhotoController, } }); mUI.enableShutter(true); + mUI.enableVideo(true); + setProModeVisible(); String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); if(isPanoSetting(scene)) { @@ -2284,16 +2387,17 @@ public class CaptureModule implements CameraModule, PhotoController, } private boolean isTouchToFocusAllowed() { - if (isTakingPicture() || mIsRecordingVideo || isSceneModeOn()) return false; + if (isTakingPicture() || mIsRecordingVideo || isTouchAfEnabledSceneMode()) return false; return true; } - private boolean isSceneModeOn() { + private boolean isTouchAfEnabledSceneMode() { String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); if (scene == null) return false; int mode = Integer.parseInt(scene); - if (mode != SettingsManager.SCENE_MODE_DUAL_INT && mode != CaptureRequest - .CONTROL_SCENE_MODE_DISABLED) return true; + if (mode != CaptureRequest.CONTROL_SCENE_MODE_DISABLED + && mode < SettingsManager.SCENE_MODE_CUSTOM_START) + return true; return false; } @@ -2331,8 +2435,8 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onCountDownFinished() { - mUI.showUIAfterCountDown(); checkSelfieFlashAndTakePicture(); + mUI.showUIAfterCountDown(); } @Override @@ -2372,17 +2476,16 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onPreviewUIReady() { + updatePreviewSurfaceReadyState(true); + if (mPaused || mIsRecordingVideo) { return; } - Log.d(TAG, "onPreviewUIReady"); - mSurfaceReady = true; - createSessions(); } @Override public void onPreviewUIDestroyed() { - mSurfaceReady = false; + updatePreviewSurfaceReadyState(false); } @Override @@ -2532,34 +2635,58 @@ public class CaptureModule implements CameraModule, PhotoController, } } - private void startRecordingVideo(int cameraId) { + private boolean startRecordingVideo(final int cameraId) { if (null == mCameraDevice[cameraId]) { - return; + return false; } Log.d(TAG, "StartRecordingVideo " + cameraId); + mStartRecPending = true; mIsRecordingVideo = true; mMediaRecorderPausing = false; mUI.hideUIwhileRecording(); - mUI.clearFocus(); - mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, mCameraId[cameraId]); - mState[cameraId] = STATE_PREVIEW; - mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; - closePreviewSession(); - mFrameProcessor.onClose(); - boolean changed = mUI.setPreviewSize(mVideoPreviewSize.getWidth(), - mVideoPreviewSize.getHeight()); - if (changed) { - mUI.hideSurfaceView(); - mUI.showSurfaceView(); + + mActivity.updateStorageSpaceAndHint(); + if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { + Log.w(TAG, "Storage issue, ignore the start request"); + mStartRecPending = false; + mIsRecordingVideo = false; + return false; } - mUI.resetTrackingFocus(); try { setUpMediaRecorder(cameraId); + if (mUnsupportedResolution == true ) { + Log.v(TAG, "Unsupported Resolution according to target"); + mStartRecPending = false; + mIsRecordingVideo = false; + return false; + } + if (mMediaRecorder == null) { + Log.e(TAG, "Fail to initialize media recorder"); + mStartRecPending = false; + mIsRecordingVideo = false; + return false; + } + + requestAudioFocus(); + mUI.clearFocus(); + mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, mCameraId[cameraId]); + mState[cameraId] = STATE_PREVIEW; + mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; + closePreviewSession(); + mFrameProcessor.onClose(); + boolean changed = mUI.setPreviewSize(mVideoPreviewSize.getWidth(), + mVideoPreviewSize.getHeight()); + if (changed) { + mUI.hideSurfaceView(); + mUI.showSurfaceView(); + } + mUI.resetTrackingFocus(); + createVideoSnapshotImageReader(); mVideoRequestBuilder = mCameraDevice[cameraId].createCaptureRequest(CameraDevice.TEMPLATE_RECORD); mVideoRequestBuilder.setTag(cameraId); - + mPreviewRequestBuilder[cameraId] = mVideoRequestBuilder; List<Surface> surfaces = new ArrayList<>(); Surface surface = getPreviewSurfaceForSession(cameraId); @@ -2585,11 +2712,12 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onConfigured(CameraCaptureSession cameraCaptureSession) { mCurrentSession = cameraCaptureSession; + mCaptureSession[cameraId] = cameraCaptureSession; CameraConstrainedHighSpeedCaptureSession session = (CameraConstrainedHighSpeedCaptureSession) mCurrentSession; try { - List list = session - .createHighSpeedRequestList(mVideoRequestBuilder.build()); + List list = CameraUtil + .createHighSpeedRequestList(mVideoRequestBuilder.build(),cameraId); session.setRepeatingBurst(list, mCaptureCallback, mCameraHandler); } catch (CameraAccessException e) { Log.e(TAG, "Failed to start high speed video recording " @@ -2604,7 +2732,17 @@ public class CaptureModule implements CameraModule, PhotoController, + e.getMessage()); e.printStackTrace(); } - mMediaRecorder.start(); + try { + mMediaRecorder.start(); // Recording is now started + } catch (RuntimeException e) { + Toast.makeText(mActivity,"Could not start media recorder.\n " + + "Can't start video recording.", Toast.LENGTH_LONG).show(); + releaseMediaRecorder(); + releaseAudioFocus(); + mStartRecPending = false; + mIsRecordingVideo = false; + return; + } mUI.clearFocus(); mUI.resetPauseButton(); mRecordingTotalTime = 0L; @@ -2612,6 +2750,7 @@ public class CaptureModule implements CameraModule, PhotoController, mUI.enableShutter(false); mUI.showRecordingUI(true, true); updateRecordingTime(); + keepScreenOn(); } @Override @@ -2628,20 +2767,34 @@ public class CaptureModule implements CameraModule, PhotoController, public void onConfigured(CameraCaptureSession cameraCaptureSession) { Log.d(TAG, "StartRecordingVideo session onConfigured"); mCurrentSession = cameraCaptureSession; + mCaptureSession[cameraId] = cameraCaptureSession; try { setUpVideoCaptureRequestBuilder(mVideoRequestBuilder); mCurrentSession.setRepeatingRequest(mVideoRequestBuilder.build(), - mCaptureCallback, mCameraHandler); + mCaptureCallback, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); + } catch (IllegalStateException e) { + e.printStackTrace(); + } + try { + mMediaRecorder.start(); // Recording is now started + } catch (RuntimeException e) { + Toast.makeText(mActivity,"Could not start media recorder.\n " + + "Can't start video recording.", Toast.LENGTH_LONG).show(); + releaseMediaRecorder(); + releaseAudioFocus(); + mStartRecPending = false; + mIsRecordingVideo = false; + return; } - mMediaRecorder.start(); mUI.clearFocus(); mUI.resetPauseButton(); mRecordingTotalTime = 0L; mRecordingStartTime = SystemClock.uptimeMillis(); mUI.showRecordingUI(true, false); updateRecordingTime(); + keepScreenOn(); } @Override @@ -2655,6 +2808,8 @@ public class CaptureModule implements CameraModule, PhotoController, } catch (IOException e) { e.printStackTrace(); } + mStartRecPending = false; + return true; } private void updateTimeLapseSetting() { @@ -2724,7 +2879,7 @@ public class CaptureModule implements CameraModule, PhotoController, private void applyVideoStabilization(CaptureRequest.Builder builder) { String value = mSettingsManager.getValue(SettingsManager.KEY_DIS); if (value == null) return; - if (value.equals("enable")) { + if (value.equals("on")) { builder.set(CaptureRequest.CONTROL_VIDEO_STABILIZATION_MODE, CaptureRequest .CONTROL_VIDEO_STABILIZATION_MODE_ON); } else { @@ -2828,14 +2983,31 @@ public class CaptureModule implements CameraModule, PhotoController, private void stopRecordingVideo(int cameraId) { Log.d(TAG, "stopRecordingVideo " + cameraId); + mStopRecPending = true; + boolean shouldAddToMediaStoreNow = false; // Stop recording mFrameProcessor.setVideoOutputSurface(null); mFrameProcessor.onClose(); closePreviewSession(); - mMediaRecorder.stop(); - mMediaRecorder.reset(); - mMediaRecorder.setOnInfoListener(null); - saveVideo(); + try { + mMediaRecorder.setOnErrorListener(null); + mMediaRecorder.setOnInfoListener(null); + mMediaRecorder.stop(); + shouldAddToMediaStoreNow = true; + AccessibilityUtils.makeAnnouncement(mUI.getVideoButton(), + mActivity.getString(R.string.video_recording_stopped)); + } catch (RuntimeException e) { + Log.w(TAG, "MediaRecoder stop fail", e); + if (mVideoFilename != null) deleteVideoFile(mVideoFilename); + } + if (shouldAddToMediaStoreNow) { + saveVideo(); + } + keepScreenOnAwhile(); + // release media recorder + releaseMediaRecorder(); + releaseAudioFocus(); + mUI.showRecordingUI(false, false); mUI.enableShutter(true); @@ -2848,11 +3020,11 @@ public class CaptureModule implements CameraModule, PhotoController, if (changed) { mUI.hideSurfaceView(); mUI.showSurfaceView(); - } else { - createSession(cameraId); } + createSessions(); mUI.showUIafterRecording(); mUI.resetTrackingFocus(); + mStopRecPending = false; } private void closePreviewSession() { @@ -2933,9 +3105,20 @@ public class CaptureModule implements CameraModule, PhotoController, if (mCaptureTimeLapse) { size = CameraSettings.getTimeLapseQualityFor(size); } + + Intent intent = mActivity.getIntent(); + Bundle myExtras = intent.getExtras(); + + if (mMediaRecorder == null) mMediaRecorder = new MediaRecorder(); + updateHFRSetting(); boolean hfr = mHighSpeedCapture && !mHighSpeedRecordingMode; + mProfile = CamcorderProfile.get(cameraId, size); + int videoWidth = mProfile.videoFrameWidth; + int videoHeight = mProfile.videoFrameHeight; + mUnsupportedResolution = false; + int videoEncoder = SettingTranslation .getVideoEncoder(mSettingsManager.getValue(SettingsManager.KEY_VIDEO_ENCODER)); int audioEncoder = SettingTranslation @@ -2988,6 +3171,54 @@ public class CaptureModule implements CameraModule, PhotoController, mMediaRecorder.setVideoEncodingBitRate(scaledBitrate); } + long requestedSizeLimit = 0; + if (isVideoCaptureIntent() && myExtras != null) { + requestedSizeLimit = myExtras.getLong(MediaStore.EXTRA_SIZE_LIMIT); + } + //check if codec supports the resolution, otherwise throw toast + List<VideoEncoderCap> videoEncoders = EncoderCapabilities.getVideoEncoders(); + for (VideoEncoderCap videoEnc: videoEncoders) { + if (videoEnc.mCodec == videoEncoder) { + if (videoWidth > videoEnc.mMaxFrameWidth || + videoWidth < videoEnc.mMinFrameWidth || + videoHeight > videoEnc.mMaxFrameHeight || + videoHeight < videoEnc.mMinFrameHeight) { + Log.e(TAG, "Selected codec " + videoEncoder + + " does not support "+ videoWidth + "x" + videoHeight + + " resolution"); + Log.e(TAG, "Codec capabilities: " + + "mMinFrameWidth = " + videoEnc.mMinFrameWidth + " , " + + "mMinFrameHeight = " + videoEnc.mMinFrameHeight + " , " + + "mMaxFrameWidth = " + videoEnc.mMaxFrameWidth + " , " + + "mMaxFrameHeight = " + videoEnc.mMaxFrameHeight); + mUnsupportedResolution = true; + RotateTextToast.makeText(mActivity, R.string.error_app_unsupported, + Toast.LENGTH_LONG).show(); + return; + } + break; + } + } + + // Set maximum file size. + long maxFileSize = mActivity.getStorageSpaceBytes() - Storage.LOW_STORAGE_THRESHOLD_BYTES; + if (requestedSizeLimit > 0 && requestedSizeLimit < maxFileSize) { + maxFileSize = requestedSizeLimit; + } + + if (Storage.isSaveSDCard() && maxFileSize > SDCARD_SIZE_LIMIT) { + maxFileSize = SDCARD_SIZE_LIMIT; + } + Log.i(TAG, "MediaRecorder setMaxFileSize: " + maxFileSize); + try { + mMediaRecorder.setMaxFileSize(maxFileSize); + } catch (RuntimeException exception) { + // We are going to ignore failure of setMaxFileSize here, as + // a) The composer selected may simply not support it, or + // b) The underlying media framework may not handle 64-bit range + // on the size restriction. + } + Location loc = mLocationManager.getCurrentLocation(); if (loc != null) { mMediaRecorder.setLocation((float) loc.getLatitude(), @@ -3004,16 +3235,30 @@ public class CaptureModule implements CameraModule, PhotoController, } else { mMediaRecorder.setOrientationHint(rotation); } - mMediaRecorder.prepare(); + try { + mMediaRecorder.prepare(); + } catch (IOException e) { + Log.e(TAG, "prepare failed for " + mVideoFilename, e); + releaseMediaRecorder(); + throw new RuntimeException(e); + } + + mMediaRecorder.setOnErrorListener(this); mMediaRecorder.setOnInfoListener(this); } public void onVideoButtonClick() { + if (isRecorderReady() == false) return; + if (getCameraMode() == DUAL_MODE) return; if (mIsRecordingVideo) { stopRecordingVideo(getMainCameraId()); } else { - startRecordingVideo(getMainCameraId()); + if (!startRecordingVideo(getMainCameraId())) { + // Show ui when start recording failed. + mUI.showUIafterRecording(); + releaseMediaRecorder(); + } } } @@ -3099,7 +3344,7 @@ public class CaptureModule implements CameraModule, PhotoController, } private void estimateJpegFileSize() { - String quality = mSettingsManager.getValue(SettingsManager + String quality = mSettingsManager.getValue(SettingsManager .KEY_JPEG_QUALITY); int[] ratios = mActivity.getResources().getIntArray(R.array.jpegquality_compression_ratio); String[] qualities = mActivity.getResources().getStringArray( @@ -3159,6 +3404,10 @@ public class CaptureModule implements CameraModule, PhotoController, return true; } + if ( mIsRecordingVideo ) { + Log.e(TAG, " cancel longshot:not supported when recording"); + return true; + } return false; } @@ -3202,6 +3451,9 @@ public class CaptureModule implements CameraModule, PhotoController, } private boolean applyPreferenceToPreview(int cameraId, String key, String value) { + if (!checkSessionAndBuilder(mCaptureSession[cameraId], mPreviewRequestBuilder[cameraId])) { + return false; + } boolean updatePreview = false; switch (key) { case SettingsManager.KEY_WHITE_BALANCE: @@ -3228,11 +3480,17 @@ public class CaptureModule implements CameraModule, PhotoController, updatePreview = true; applyFaceDetection(mPreviewRequestBuilder[cameraId]); break; + case SettingsManager.KEY_FOCUS_DISTANCE: + updatePreview = true; + applyFocusDistance(mPreviewRequestBuilder[cameraId], value); } return updatePreview; } private void applyZoomAndUpdate(int id) { + if (!checkSessionAndBuilder(mCaptureSession[id], mPreviewRequestBuilder[id])) { + return; + } applyZoom(mPreviewRequestBuilder[id], id); try { if(id == MONO_ID && !canStartMonoPreview()) { @@ -3242,7 +3500,7 @@ public class CaptureModule implements CameraModule, PhotoController, mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] .build(), mCaptureCallback, mCameraHandler); } - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } @@ -3277,8 +3535,9 @@ public class CaptureModule implements CameraModule, PhotoController, request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); return; } - if (mode != CaptureRequest.CONTROL_SCENE_MODE_DISABLED && mode != - SettingsManager.SCENE_MODE_DUAL_INT) { + if (mode != CaptureRequest.CONTROL_SCENE_MODE_DISABLED + && mode != SettingsManager.SCENE_MODE_DUAL_INT + && mode != SettingsManager.SCENE_MODE_PROMODE_INT) { request.set(CaptureRequest.CONTROL_SCENE_MODE, mode); request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_USE_SCENE_MODE); } else { @@ -3296,9 +3555,14 @@ public class CaptureModule implements CameraModule, PhotoController, private void applyIso(CaptureRequest.Builder request) { String value = mSettingsManager.getValue(SettingsManager.KEY_ISO); if (value == null) return; - if (value.equals("auto")) return; - int intValue = Integer.parseInt(value); - request.set(CaptureRequest.SENSOR_SENSITIVITY, intValue); + if (value.equals("auto")) { + request.set(SELECT_PRIORITY, null); + request.set(ISO_EXP, null); + } else { + long intValue = Integer.parseInt(value); + request.set(SELECT_PRIORITY, 0); + request.set(ISO_EXP, intValue); + } } private void applyColorEffect(CaptureRequest.Builder request) { @@ -3350,11 +3614,11 @@ public class CaptureModule implements CameraModule, PhotoController, } private void applyFaceDetection(CaptureRequest.Builder request) { - String value = mSettingsManager.getValue(SettingsManager.KEY_FACE_DETECTION); - if (value != null && value.equals("on")) { - request.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, - CaptureRequest.STATISTICS_FACE_DETECT_MODE_SIMPLE); - } + String value = mSettingsManager.getValue(SettingsManager.KEY_FACE_DETECTION); + if (value != null && value.equals("on")) { + request.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, + CaptureRequest.STATISTICS_FACE_DETECT_MODE_SIMPLE); + } } private void applyFlash(CaptureRequest.Builder request, int id) { @@ -3542,6 +3806,9 @@ public class CaptureModule implements CameraModule, PhotoController, case SettingsManager.KEY_FLASH_MODE: if (count == 0) restartSession(false); return; + case SettingsManager.KEY_SCENE_MODE: + restartAll(); + return; } if (isBackCamera()) { @@ -3564,30 +3831,39 @@ public class CaptureModule implements CameraModule, PhotoController, } if (updatePreviewBayer) { try { - mCaptureSession[BAYER_ID].setRepeatingRequest(mPreviewRequestBuilder[BAYER_ID] - .build(), mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + if (checkSessionAndBuilder(mCaptureSession[BAYER_ID], + mPreviewRequestBuilder[BAYER_ID])) { + mCaptureSession[BAYER_ID].setRepeatingRequest(mPreviewRequestBuilder[BAYER_ID] + .build(), mCaptureCallback, mCameraHandler); + } + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } if (updatePreviewMono) { try { - if(canStartMonoPreview()) { - mCaptureSession[MONO_ID].setRepeatingRequest(mPreviewRequestBuilder[MONO_ID] - .build(), mCaptureCallback, mCameraHandler); - } else { - mCaptureSession[MONO_ID].capture(mPreviewRequestBuilder[MONO_ID] - .build(), mCaptureCallback, mCameraHandler); + if (checkSessionAndBuilder(mCaptureSession[MONO_ID], + mPreviewRequestBuilder[MONO_ID])) { + if (canStartMonoPreview()) { + mCaptureSession[MONO_ID].setRepeatingRequest(mPreviewRequestBuilder[MONO_ID] + .build(), mCaptureCallback, mCameraHandler); + } else { + mCaptureSession[MONO_ID].capture(mPreviewRequestBuilder[MONO_ID] + .build(), mCaptureCallback, mCameraHandler); + } } - } catch (CameraAccessException e) { + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } if (updatePreviewFront) { try { - mCaptureSession[FRONT_ID].setRepeatingRequest(mPreviewRequestBuilder[FRONT_ID] - .build(), mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { + if (checkSessionAndBuilder(mCaptureSession[FRONT_ID], + mPreviewRequestBuilder[FRONT_ID])) { + mCaptureSession[FRONT_ID].setRepeatingRequest(mPreviewRequestBuilder[FRONT_ID] + .build(), mCaptureCallback, mCameraHandler); + } + } catch (CameraAccessException | IllegalStateException e) { e.printStackTrace(); } } @@ -3628,25 +3904,36 @@ public class CaptureModule implements CameraModule, PhotoController, } public void restartSession(boolean isSurfaceChanged) { + Log.d(TAG, "restartSession isSurfaceChanged = " + isSurfaceChanged); + if (isAllSessionClosed()) return; + + closeProcessors(); + closeSessions(); + if(isSurfaceChanged) { mUI.hideSurfaceView(); + mUI.showSurfaceView(); } - closeProcessors(); - closeSessions(); + initializeValues(); updatePreviewSize(); openProcessors(); - if(isSurfaceChanged) { - mUI.showSurfaceView(); - } else { - createSessions(); - } + createSessions(); if(isTrackingFocusSettingOn()) { mUI.resetTrackingFocus(); } + resetStateMachine(); } + private void resetStateMachine() { + for (int i = 0; i < MAX_NUM_CAM; i++) { + mState[i] = STATE_PREVIEW; + } + mUI.enableShutter(true); + } + + private Size getOptimalPreviewSize(Size pictureSize, Size[] prevSizes, int screenW, int screenH) { if (pictureSize.getWidth() <= screenH && pictureSize.getHeight() <= screenW) { @@ -3822,6 +4109,11 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void handleMessage(Message msg) { switch (msg.what) { + case CLEAR_SCREEN_DELAY: { + mActivity.getWindow().clearFlags( + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + break; + } case UPDATE_RECORD_TIME: { updateRecordingTime(); break; @@ -3835,6 +4127,18 @@ public class CaptureModule implements CameraModule, PhotoController, enableRecordingLocation(false); } + // from MediaRecorder.OnErrorListener + @Override + public void onError(MediaRecorder mr, int what, int extra) { + Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra); + stopRecordingVideo(getMainCameraId()); + mUI.showUIafterRecording(); + if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { + // We may have run out of space on the sdcard. + mActivity.updateStorageSpaceAndHint(); + } + } + // from MediaRecorder.OnInfoListener @Override public void onInfo(MediaRecorder mr, int what, int extra) { @@ -3863,4 +4167,109 @@ public class CaptureModule implements CameraModule, PhotoController, Storage.setSaveSDCard(mSettingsManager.getValue(SettingsManager .KEY_CAMERA_SAVEPATH).equals("1")); } + + private void deleteVideoFile(String fileName) { + Log.v(TAG, "Deleting video " + fileName); + File f = new File(fileName); + if (!f.delete()) { + Log.v(TAG, "Could not delete " + fileName); + } + } + + private void releaseMediaRecorder() { + Log.v(TAG, "Releasing media recorder."); + if (mMediaRecorder != null) { + cleanupEmptyFile(); + mMediaRecorder.reset(); + mMediaRecorder.release(); + mMediaRecorder = null; + } + mVideoFilename = null; + } + + private void cleanupEmptyFile() { + if (mVideoFilename != null) { + File f = new File(mVideoFilename); + if (f.length() == 0 && f.delete()) { + Log.v(TAG, "Empty video file deleted: " + mVideoFilename); + mVideoFilename = null; + } + } + } + + private void showToast(String tips) { + if (mToast == null) { + mToast = Toast.makeText(mActivity, tips, Toast.LENGTH_LONG); + } + mToast.setText(tips); + mToast.show(); + } + + private boolean isRecorderReady() { + if ((mStartRecPending == true || mStopRecPending == true)) + return false; + else + return true; + } + + /* + * Make sure we're not recording music playing in the background, ask the + * MediaPlaybackService to pause playback. + */ + private void requestAudioFocus() { + AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE); + // Send request to obtain audio focus. This will stop other + // music stream. + int result = am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { + Log.v(TAG, "Audio focus request failed"); + } + } + + private void releaseAudioFocus() { + AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE); + int result = am.abandonAudioFocus(null); + if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { + Log.v(TAG, "Audio focus release failed"); + } + } + + private boolean isVideoCaptureIntent() { + String action = mActivity.getIntent().getAction(); + return (MediaStore.ACTION_VIDEO_CAPTURE.equals(action)); + } + + private void resetScreenOn() { + mHandler.removeMessages(CLEAR_SCREEN_DELAY); + mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + private void keepScreenOnAwhile() { + mHandler.removeMessages(CLEAR_SCREEN_DELAY); + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + mHandler.sendEmptyMessageDelayed(CLEAR_SCREEN_DELAY, SCREEN_DELAY); + } + + private void keepScreenOn() { + mHandler.removeMessages(CLEAR_SCREEN_DELAY); + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + + private void setProModeVisible() { + String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + boolean promode = false; + if (scene != null) { + int mode = Integer.parseInt(scene); + if (mode == SettingsManager.SCENE_MODE_PROMODE_INT) { + promode = true; + } + } + mUI.initializeProMode(!mPaused && promode); + } + + boolean checkSessionAndBuilder(CameraCaptureSession session, CaptureRequest.Builder builder) { + return session != null && builder != null; + } } diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index 88460b865..1bb20e9f7 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -38,8 +38,10 @@ import android.renderscript.RenderScript; import android.renderscript.ScriptIntrinsicYuvToRGB; import android.renderscript.Type; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; @@ -49,12 +51,14 @@ import android.view.ViewGroup; import android.view.ViewPropertyAnimator; import android.view.Window; import android.view.WindowManager; +import android.widget.Button; +import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.RelativeLayout; import android.widget.SeekBar; import android.widget.TextView; - import com.android.camera.ui.AutoFitSurfaceView; import com.android.camera.ui.Camera2FaceView; import com.android.camera.ui.CameraControls; @@ -77,6 +81,7 @@ import org.codeaurora.snapcam.R; import java.util.List; import java.util.Locale; +import java.util.Map; public class CaptureUI implements FocusOverlayManager.FocusUI, PreviewGestures.SingleTapListener, @@ -184,15 +189,30 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private PauseButton mPauseButton; private RotateImageView mMuteButton; private ImageView mSeekbarToggleButton; + private View mProModeCloseButton; + private RotateLayout mSceneModeLabelRect; + private LinearLayout mSceneModeLabelView; + private TextView mSceneModeName; + private ImageView mSceneModeLabelCloseIcon; + private AlertDialog mSceneModeInstructionalDialog = null; int mPreviewWidth; int mPreviewHeight; private boolean mIsVideoUI = false; + private boolean mIsSceneModeLabelClose = false; private void previewUIReady() { if((mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid())) { mModule.onPreviewUIReady(); - mActivity.updateThumbnail(mThumbnail); + if (mIsVideoUI && mThumbnail != null) { + mThumbnail.setVisibility(View.INVISIBLE); + mThumbnail = null; + mActivity.updateThumbnail(mThumbnail); + } else if (!mIsVideoUI){ + if (mThumbnail == null) + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + mActivity.updateThumbnail(mThumbnail); + } } } @@ -208,7 +228,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, return mDisplaySize; } - public CaptureUI(CameraActivity activity, CaptureModule module, View parent) { + public CaptureUI(CameraActivity activity, final CaptureModule module, View parent) { mActivity = activity; mModule = module; mRootView = parent; @@ -278,15 +298,26 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mMakeupButton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { - toggleMakeup(); + if (module != null && !module.isAllSessionClosed()) { + toggleMakeup(); + updateMenus(); + } } }); setMakeupButtonIcon(); mFlashButton = (FlashToggleButton) mRootView.findViewById(R.id.flash_button); + mProModeCloseButton = mRootView.findViewById(R.id.promode_close_button); + mProModeCloseButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mSettingsManager.setValue(SettingsManager.KEY_SCENE_MODE, "" + SettingsManager.SCENE_MODE_AUTO_INT); + } + }); initFilterModeButton(); initSceneModeButton(); initSwitchCamera(); initFlashButton(); + updateMenus(); mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time); mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.recording_time_rect); @@ -316,6 +347,17 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, RotateImageView muteButton = (RotateImageView) mRootView.findViewById(R.id.mute_button); muteButton.setVisibility(View.GONE); + mSceneModeLabelRect = (RotateLayout)mRootView.findViewById(R.id.scene_mode_label_rect); + mSceneModeName = (TextView)mRootView.findViewById(R.id.scene_mode_label); + mSceneModeLabelCloseIcon = (ImageView)mRootView.findViewById(R.id.scene_mode_label_close); + mSceneModeLabelCloseIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mIsSceneModeLabelClose = true; + mSceneModeLabelRect.setVisibility(View.GONE); + } + }); + mCameraControls = (OneUICameraControls) mRootView.findViewById(R.id.camera_controls); mFaceView = (Camera2FaceView) mRootView.findViewById(R.id.face_view); @@ -359,7 +401,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mRenderOverlay.requestLayout(); mActivity.setPreviewGestures(mGestures); - ((ViewGroup)mRootView).removeView(mRecordingTimeRect); + mRecordingTimeRect.setVisibility(View.GONE); showFirstTimeHelp(); } @@ -367,8 +409,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, String value = mSettingsManager.getValue(SettingsManager.KEY_MAKEUP); if(value != null && !mIsVideoUI) { if(value.equals("0")) { - mSettingsManager.setValue(SettingsManager.KEY_MAKEUP, "10"); - mMakeupSeekBar.setProgress(10); + mSettingsManager.setValue(SettingsManager.KEY_MAKEUP, "50"); + mMakeupSeekBar.setProgress(50); mMakeupSeekBarLayout.setVisibility(View.VISIBLE); mSeekbarBody.setVisibility(View.VISIBLE); mSeekbarToggleButton.setImageResource(R.drawable.seekbar_hide); @@ -408,6 +450,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, initFilterModeButton(); initFlashButton(); setMakeupButtonIcon(); + showSceneModeLabel(); + updateMenus(); if(mModule.isTrackingFocusSettingOn()) { mTrackingFocusRenderer.setVisible(false); mTrackingFocusRenderer.setVisible(true); @@ -425,6 +469,12 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } + public void initializeProMode(boolean promode) { + mCameraControls.setProMode(promode); + if (promode) mVideoButton.setVisibility(View.INVISIBLE); + else mVideoButton.setVisibility(View.VISIBLE); + } + // called from onResume but only the first time public void initializeFirstTime() { // Initialize shutter button. @@ -477,6 +527,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mFilterLayout = null; } } + updateMenus(); } public void openSettingsMenu() { @@ -514,6 +565,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, public void initFlashButton() { mFlashButton.init(false); + enableView(mFlashButton, SettingsManager.KEY_FLASH_MODE); } public void initSceneModeButton() { @@ -538,16 +590,28 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, String value = mSettingsManager.getValue(SettingsManager.KEY_COLOR_EFFECT); if (value == null) return; + enableView(mFilterModeSwitcher, SettingsManager.KEY_COLOR_EFFECT); + mFilterModeSwitcher.setVisibility(View.VISIBLE); mFilterModeSwitcher.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addFilterMode(); adjustOrientation(); + updateMenus(); } }); } + private void enableView(View view, String key) { + Map<String, SettingsManager.Values> map = mSettingsManager.getValuesMap(); + SettingsManager.Values values = map.get(key); + if ( values != null ) { + boolean enabled = values.overriddenValue == null; + view.setEnabled(enabled); + } + } + public void showTimeLapseUI(boolean enable) { if (mTimeLapseLabel != null) { mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE); @@ -563,17 +627,128 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } mVideoButton.setImageResource(R.drawable.video_stop); mRecordingTimeView.setText(""); - ((ViewGroup)mRootView).addView(mRecordingTimeRect); + mRecordingTimeRect.setVisibility(View.VISIBLE); mMuteButton.setVisibility(View.VISIBLE); } else { mFlashButton.setVisibility(View.VISIBLE); mFlashButton.init(false); mVideoButton.setImageResource(R.drawable.video_capture); - ((ViewGroup)mRootView).removeView(mRecordingTimeRect); + mRecordingTimeRect.setVisibility(View.GONE); mMuteButton.setVisibility(View.INVISIBLE); } } + private boolean needShowInstructional() { + boolean needShow = true; + final SharedPreferences pref = mActivity.getSharedPreferences( + ComboPreferences.getGlobalSharedPreferencesName(mActivity), Context.MODE_PRIVATE); + int index = mSettingsManager.getValueIndex(SettingsManager.KEY_SCENE_MODE); + if ( index < 1 ) { + needShow = false; + }else{ + final String instructionalKey = SettingsManager.KEY_SCENE_MODE + "_" + index; + needShow = pref.getBoolean(instructionalKey, false) ? false : true; + } + + return needShow; + + } + + private void showSceneInstructionalDialog(int orientation) { + LayoutInflater inflater = + (LayoutInflater)mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.scene_mode_instructional, null); + + final int index = mSettingsManager.getValueIndex(SettingsManager.KEY_SCENE_MODE); + TextView name = (TextView)view.findViewById(R.id.scene_mode_name); + CharSequence sceneModeNameArray[] = + mSettingsManager.getEntries(SettingsManager.KEY_SCENE_MODE); + name.setText(sceneModeNameArray[index]); + + ImageView icon = (ImageView)view.findViewById(R.id.scene_mode_icon); + int[] resId = mSettingsManager.getResource(SettingsManager.KEY_SCEND_MODE_INSTRUCTIONAL, + SettingsManager.RESOURCE_TYPE_THUMBNAIL); + icon.setImageResource(resId[index]); + + TextView instructional = (TextView)view.findViewById(R.id.scene_mode_instructional); + CharSequence instructionalArray[] = + mSettingsManager.getEntries(SettingsManager.KEY_SCEND_MODE_INSTRUCTIONAL); + if ( instructionalArray[index].length() == 0 ) { + //For now, not all scene mode has instructional + return; + } + instructional.setText(instructionalArray[index]); + + final CheckBox remember = (CheckBox)view.findViewById(R.id.remember_selected); + Button ok = (Button)view.findViewById(R.id.scene_mode_instructional_ok); + ok.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + + if ( remember.isChecked()) { + final SharedPreferences pref = mActivity.getSharedPreferences( + ComboPreferences.getGlobalSharedPreferencesName(mActivity), + Context.MODE_PRIVATE); + + String instructionalKey = SettingsManager.KEY_SCENE_MODE + "_" + index; + SharedPreferences.Editor editor = pref.edit(); + editor.putBoolean(instructionalKey, true); + editor.commit(); + } + mSceneModeInstructionalDialog.dismiss(); + mSceneModeInstructionalDialog = null; + } + }); + + mSceneModeInstructionalDialog = + new AlertDialog.Builder(mActivity, AlertDialog.THEME_HOLO_LIGHT) + .setView(view).create(); + try { + mSceneModeInstructionalDialog.show(); + }catch(Exception e) { + e.printStackTrace(); + return; + } + if ( orientation != 0 ) { + rotationSceneModeInstructionalDialog(view, orientation); + } + } + + private int getScreenWidth() { + DisplayMetrics metric = new DisplayMetrics(); + mActivity.getWindowManager().getDefaultDisplay().getMetrics(metric); + return metric.widthPixels < metric.heightPixels ? metric.widthPixels : metric.heightPixels; + } + + private void rotationSceneModeInstructionalDialog(View view, int orientation) { + view.setRotation(-orientation); + int screenWidth = getScreenWidth(); + int dialogSize = screenWidth*9/10; + Window dialogWindow = mSceneModeInstructionalDialog.getWindow(); + WindowManager.LayoutParams lp = dialogWindow.getAttributes(); + dialogWindow.setGravity(Gravity.CENTER); + lp.width = lp.height = dialogSize; + dialogWindow.setAttributes(lp); + if ( orientation == 180 ) { + dialogSize -= 40; + RelativeLayout layout = (RelativeLayout)view.findViewById(R.id.mode_layout_rect); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(dialogSize, dialogSize); + layout.setLayoutParams(params); + } + } + + private void showSceneModeLabel() { + mIsSceneModeLabelClose = false; + int index = mSettingsManager.getValueIndex(SettingsManager.KEY_SCENE_MODE); + CharSequence sceneModeNameArray[] = mSettingsManager.getEntries(SettingsManager.KEY_SCENE_MODE); + if ( index > 0 && index < sceneModeNameArray.length ) { + mSceneModeName.setText(sceneModeNameArray[index]); + mSceneModeLabelRect.setVisibility(View.VISIBLE); + }else{ + mSceneModeLabelRect.setVisibility(View.GONE); + } + } + + public void resetTrackingFocus() { if(mModule.isTrackingFocusSettingOn()) { mTrackingFocusRenderer.setVisible(false); @@ -582,25 +757,26 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } public void hideUIwhileRecording() { - mCameraControls.setWillNotDraw(true); + mCameraControls.setVideoMode(true); mFrontBackSwitcher.setVisibility(View.INVISIBLE); mFilterModeSwitcher.setVisibility(View.INVISIBLE); mSceneModeSwitcher.setVisibility(View.INVISIBLE); - String value = mSettingsManager.getValue(SettingsManager.KEY_MAKEUP); if(value != null && value.equals("0")) { mMakeupButton.setVisibility(View.INVISIBLE); } mIsVideoUI = true; + mPauseButton.setVisibility(View.VISIBLE); } public void showUIafterRecording() { - mCameraControls.setWillNotDraw(false); + mCameraControls.setVideoMode(false); mFrontBackSwitcher.setVisibility(View.VISIBLE); mFilterModeSwitcher.setVisibility(View.VISIBLE); mSceneModeSwitcher.setVisibility(View.VISIBLE); mMakeupButton.setVisibility(View.VISIBLE); mIsVideoUI = false; + mPauseButton.setVisibility(View.INVISIBLE); } public void addFilterMode() { @@ -860,9 +1036,33 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, public void cleanUpMenus() { showUI(); + updateMenus(); mActivity.setSystemBarsVisibility(false); } + public void updateMenus() { + boolean enableMakeupMenu = true; + boolean enableFilterMenu = true; + boolean enableSceneMenu = true; + String makeupValue = mSettingsManager.getValue(SettingsManager.KEY_MAKEUP); + int colorEffect = mSettingsManager.getValueIndex(SettingsManager.KEY_COLOR_EFFECT); + String sceneMode = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (makeupValue != null && !makeupValue.equals("0")) { + enableSceneMenu = false; + enableFilterMenu = false; + } else if (colorEffect != 0 || mFilterMenuStatus == FILTER_MENU_ON){ + enableSceneMenu = false; + enableMakeupMenu = false; + }else if ( sceneMode != null && !sceneMode.equals("0")){ + enableMakeupMenu = false; + enableFilterMenu = false; + } + + mMakeupButton.setEnabled(enableMakeupMenu); + mFilterModeSwitcher.setEnabled(enableFilterMenu); + mSceneModeSwitcher.setEnabled(enableSceneMenu); + } + public boolean arePreviewControlsVisible() { return !mUIhidden; } @@ -879,6 +1079,15 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } + /** + * Enables or disables the video button. + */ + public void enableVideo(boolean enabled) { + if (mVideoButton != null) { + mVideoButton.setEnabled(enabled); + } + } + private boolean handleBackKeyOnMenu() { if (mFilterMenuStatus == FILTER_MENU_ON) { removeFilterMenu(true); @@ -1143,13 +1352,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } if (mRecordingTimeRect != null) { - if (orientation == 180) { - mRecordingTimeRect.setOrientation(0, false); - mRecordingTimeView.setRotation(180); - } else { - mRecordingTimeView.setRotation(0); - mRecordingTimeRect.setOrientation(orientation, false); - } + mRecordingTimeView.setRotation(-orientation); } if (mFaceView != null) { mFaceView.setDisplayRotation(orientation); @@ -1160,6 +1363,24 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, if (mZoomRenderer != null) { mZoomRenderer.setOrientation(orientation); } + + if ( mSceneModeLabelRect != null ) { + if (orientation == 180) { + mSceneModeName.setRotation(180); + mSceneModeLabelCloseIcon.setRotation(180); + mSceneModeLabelRect.setOrientation(0, false); + } else { + mSceneModeName.setRotation(0); + mSceneModeLabelCloseIcon.setRotation(0); + mSceneModeLabelRect.setOrientation(orientation, false); + } + } + + if ( mSceneModeInstructionalDialog != null && mSceneModeInstructionalDialog.isShowing()) { + mSceneModeInstructionalDialog.dismiss(); + mSceneModeInstructionalDialog = null; + showSceneInstructionalDialog(orientation); + } } public int getOrientation() { @@ -1282,6 +1503,23 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, @Override public void onSettingsChanged(List<SettingsManager.SettingState> settings) { + for( SettingsManager.SettingState state : settings) { + if (state.key.equals(SettingsManager.KEY_COLOR_EFFECT)) { + enableView(mFilterModeSwitcher, SettingsManager.KEY_COLOR_EFFECT); + } else if (state.key.equals(SettingsManager.KEY_SCENE_MODE)) { + String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if ( value.equals("104") ) {//panorama + mSceneModeLabelRect.setVisibility(View.GONE); + }else{ + if ( needShowInstructional() ) { + showSceneInstructionalDialog(mOrientation); + } + showSceneModeLabel(); + } + }else if(state.key.equals(SettingsManager.KEY_FLASH_MODE) ) { + enableView(mFlashButton, SettingsManager.KEY_FLASH_MODE); + } + } } public void startSelfieFlash() { @@ -1315,6 +1553,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } public void showSurfaceView() { + Log.d(TAG, "showSurfaceView"); mSurfaceView.getHolder().setFixedSize(mPreviewWidth, mPreviewHeight); mSurfaceView.setAspectRatio(mPreviewHeight, mPreviewWidth); mSurfaceView.setVisibility(View.VISIBLE); @@ -1359,4 +1598,9 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } + + public ImageView getVideoButton() { + return mVideoButton; + } + } diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java index 1979d1b5a..401289fc3 100644 --- a/src/com/android/camera/ComboPreferences.java +++ b/src/com/android/camera/ComboPreferences.java @@ -68,12 +68,12 @@ public class ComboPreferences implements } } - private static String getLocalSharedPreferencesName( + public static String getLocalSharedPreferencesName( Context context, int cameraId) { return context.getPackageName() + "_preferences_" + cameraId; } - private static String getGlobalSharedPreferencesName(Context context) { + public static String getGlobalSharedPreferencesName(Context context) { return context.getPackageName() + "_preferences_camera"; } diff --git a/src/com/android/camera/PanoCaptureModule.java b/src/com/android/camera/PanoCaptureModule.java index ff5715e20..c6045d1c3 100644 --- a/src/com/android/camera/PanoCaptureModule.java +++ b/src/com/android/camera/PanoCaptureModule.java @@ -271,10 +271,12 @@ public class PanoCaptureModule implements CameraModule, PhotoController { mCameraOpened = false; mSurfaceReady = false; mActivity = activity; - + SettingsManager settingsManager = SettingsManager.getInstance(); + settingsManager.init(); mUI = new PanoCaptureUI(activity, this, parent); mContentResolver = mActivity.getContentResolver(); mLocationManager = new LocationManager(mActivity, null); + } public void changePanoStatus(boolean newStatus, boolean isCancelling) { diff --git a/src/com/android/camera/PanoCaptureUI.java b/src/com/android/camera/PanoCaptureUI.java index 3ab43f915..5e9692908 100644 --- a/src/com/android/camera/PanoCaptureUI.java +++ b/src/com/android/camera/PanoCaptureUI.java @@ -24,16 +24,29 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.RectF; import android.hardware.Camera.Face; +import android.app.AlertDialog; +import android.content.Context; +import android.content.SharedPreferences; +import android.util.DisplayMetrics; import android.util.Log; import android.util.Size; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.SurfaceHolder; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; + +import android.view.Window; +import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; +import android.widget.Button; +import android.widget.CheckBox; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; import com.android.camera.ui.AutoFitSurfaceView; import com.android.camera.ui.CameraControls; @@ -41,6 +54,7 @@ import com.android.camera.ui.CameraRootView; import com.android.camera.ui.FocusIndicator; import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.PanoCaptureProcessView; +import com.android.camera.ui.RotateLayout; import com.android.camera.util.CameraUtil; import org.codeaurora.snapcam.R; @@ -60,6 +74,12 @@ public class PanoCaptureUI implements private ShutterButton mShutterButton; private ModuleSwitcher mSwitcher; private CameraControls mCameraControls; + private RotateLayout mSceneModeLabelRect; + private LinearLayout mSceneModeLabelView; + private TextView mSceneModeName; + private ImageView mSceneModeLabelCloseIcon; + private AlertDialog mSceneModeInstructionalDialog = null; + // Small indicators which show the camera settings in the viewfinder. private OnScreenIndicators mOnScreenIndicators; @@ -74,6 +94,7 @@ public class PanoCaptureUI implements private ImageView mThumbnail; private int mOrientation; + private boolean mIsSceneModeLabelClose = false; public void clearSurfaces() { mSurfaceHolder = null; @@ -193,12 +214,26 @@ public class PanoCaptureUI implements } }); + mSceneModeLabelRect = (RotateLayout)mRootView.findViewById(R.id.scene_mode_label_rect); + mSceneModeName = (TextView)mRootView.findViewById(R.id.scene_mode_label); + mSceneModeName.setText(R.string.pref_camera_scenemode_entry_panorama); + mSceneModeLabelCloseIcon = (ImageView)mRootView.findViewById(R.id.scene_mode_label_close); + mSceneModeLabelCloseIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mIsSceneModeLabelClose = true; + mSceneModeLabelRect.setVisibility(View.GONE); + } + }); initIndicators(); Point size = new Point(); mActivity.getWindowManager().getDefaultDisplay().getSize(size); calculateMargins(size); mCameraControls.setMargins(mTopMargin, mBottomMargin); + if ( needShowInstructional() ) { + showSceneInstructionalDialog(mOrientation); + } } private void calculateMargins(Point size) { @@ -381,6 +416,24 @@ public class PanoCaptureUI implements mOrientation = orientation; mCameraControls.setOrientation(orientation, animation); mPreviewProcessView.setOrientation(orientation); + + if ( mSceneModeLabelRect != null ) { + if (orientation == 180) { + mSceneModeName.setRotation(180); + mSceneModeLabelCloseIcon.setRotation(180); + mSceneModeLabelRect.setOrientation(0, false); + } else { + mSceneModeName.setRotation(0); + mSceneModeLabelCloseIcon.setRotation(0); + mSceneModeLabelRect.setOrientation(orientation, false); + } + } + + if ( mSceneModeInstructionalDialog != null && mSceneModeInstructionalDialog.isShowing()) { + mSceneModeInstructionalDialog.dismiss(); + mSceneModeInstructionalDialog = null; + showSceneInstructionalDialog(orientation); + } } public int getOrientation() { @@ -391,4 +444,83 @@ public class PanoCaptureUI implements public void onErrorListener(int error) { } + + private boolean needShowInstructional() { + final SharedPreferences pref = mActivity.getSharedPreferences( + ComboPreferences.getGlobalSharedPreferencesName(mActivity), Context.MODE_PRIVATE); + SettingsManager settingsManager = SettingsManager.getInstance(); + int index = settingsManager.getValueIndex(SettingsManager.KEY_SCENE_MODE); + final String instructionalKey = SettingsManager.KEY_SCENE_MODE + "_" + index; + return !pref.getBoolean(instructionalKey, false); + } + + private void showSceneInstructionalDialog(int orientation) { + LayoutInflater inflater = + (LayoutInflater)mActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflater.inflate(R.layout.scene_mode_instructional, null); + + TextView name = (TextView)view.findViewById(R.id.scene_mode_name); + name.setText(R.string.pref_camera_scenemode_entry_panorama); + + ImageView icon = (ImageView)view.findViewById(R.id.scene_mode_icon); + icon.setImageResource(R.drawable.ic_scene_mode_black_panorama); + + TextView instructional = (TextView)view.findViewById(R.id.scene_mode_instructional); + instructional.setText(R.string.pref_camera2_scene_mode_panorama_instructional_content); + + final CheckBox remember = (CheckBox)view.findViewById(R.id.remember_selected); + Button ok = (Button)view.findViewById(R.id.scene_mode_instructional_ok); + ok.setOnClickListener(new View.OnClickListener() { + public void onClick(View view) { + SharedPreferences pref = mActivity.getSharedPreferences( + ComboPreferences.getGlobalSharedPreferencesName(mActivity), + Context.MODE_PRIVATE); + int index = + SettingsManager.getInstance().getValueIndex(SettingsManager.KEY_SCENE_MODE); + String instructionalKey = SettingsManager.KEY_SCENE_MODE + "_" + index; + if ( remember.isChecked()) { + SharedPreferences.Editor editor = pref.edit(); + editor.putBoolean(instructionalKey, true); + editor.commit(); + } + mSceneModeInstructionalDialog.dismiss(); + mSceneModeInstructionalDialog = null; + } + }); + mSceneModeInstructionalDialog = + new AlertDialog.Builder(mActivity, AlertDialog.THEME_HOLO_LIGHT) + .setView(view).create(); + try { + mSceneModeInstructionalDialog.show(); + }catch(Exception e){ + e.printStackTrace(); + return; + } + if ( orientation != 0 ) { + rotationSceneModeInstructionalDialog(view, orientation); + } + } + + private int getScreenWidth() { + DisplayMetrics metric = new DisplayMetrics(); + mActivity.getWindowManager().getDefaultDisplay().getMetrics(metric); + return metric.widthPixels < metric.heightPixels ? metric.widthPixels : metric.heightPixels; + } + + private void rotationSceneModeInstructionalDialog(View view, int orientation) { + view.setRotation(-orientation); + int screenWidth = getScreenWidth(); + int dialogSize = screenWidth*9/10; + Window dialogWindow = mSceneModeInstructionalDialog.getWindow(); + WindowManager.LayoutParams lp = dialogWindow.getAttributes(); + dialogWindow.setGravity(Gravity.CENTER); + lp.width = lp.height = dialogSize; + dialogWindow.setAttributes(lp); + if ( orientation == 180 ) { + dialogSize -= 40; + RelativeLayout layout = (RelativeLayout)view.findViewById(R.id.mode_layout_rect); + FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(dialogSize, dialogSize); + layout.setLayoutParams(params); + } + } } diff --git a/src/com/android/camera/PauseButton.java b/src/com/android/camera/PauseButton.java index a785050fd..6511e0e08 100644..100755 --- a/src/com/android/camera/PauseButton.java +++ b/src/com/android/camera/PauseButton.java @@ -36,11 +36,13 @@ import android.view.MotionEvent; import android.view.View; import android.util.Log; +import com.android.camera.ui.RotateImageView; + /** * A button designed to be used for the on-screen recording * pausing-continue button. */ -public class PauseButton extends ImageView { +public class PauseButton extends RotateImageView { public interface OnPauseButtonListener { void onButtonPause(); diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index cfdd8af12..2d67fa9c0 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -629,7 +629,7 @@ public class PhotoModule private void locationFirstRun() { /* Do not prompt if the preference is already set, this is a secure * camera session, or the prompt has already been triggered. */ - if (RecordLocationPreference.isSet(mPreferences) || + if (RecordLocationPreference.isSet(mPreferences, CameraSettings.KEY_RECORD_LOCATION) || mActivity.isSecureCamera() || mLocationPromptTriggered) { return; } @@ -912,7 +912,8 @@ public class PhotoModule } // Initialize location service. - boolean recordLocation = RecordLocationPreference.get(mPreferences); + boolean recordLocation = RecordLocationPreference.get(mPreferences, + CameraSettings.KEY_RECORD_LOCATION); mLocationManager.recordLocation(recordLocation); mUI.initializeFirstTime(); @@ -943,7 +944,8 @@ public class PhotoModule // onResume. private void initializeSecondTime() { // Start location update if needed. - boolean recordLocation = RecordLocationPreference.get(mPreferences); + boolean recordLocation = RecordLocationPreference.get(mPreferences, + CameraSettings.KEY_RECORD_LOCATION); mLocationManager.recordLocation(recordLocation); MediaSaveService s = mActivity.getMediaSaveService(); if (s != null) { @@ -2360,7 +2362,7 @@ public class PhotoModule String zsl = mPreferences.getString(CameraSettings.KEY_ZSL, mActivity.getString(R.string.pref_camera_zsl_default)); mUI.overrideSettings(CameraSettings.KEY_ZSL, zsl); - mUI.startCountDown(seconds, isShutterSoundOn()); + mUI.startCountDown(seconds, playSound); } else { mSnapshotOnIdle = false; initiateSnap(); @@ -4673,7 +4675,8 @@ public class PhotoModule // ignore the events after "onPause()" if (mPaused) return; - boolean recordLocation = RecordLocationPreference.get(mPreferences); + boolean recordLocation = RecordLocationPreference.get(mPreferences, + CameraSettings.KEY_RECORD_LOCATION); mLocationManager.recordLocation(recordLocation); if(needRestart()){ Log.v(TAG, "Restarting Preview... Camera Mode Changed"); diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 250bfa603..442c65d94 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -728,7 +728,7 @@ public class PhotoUI implements PieListener, // make sure the correct value was found // otherwise use auto index mOnScreenIndicators.updateWBIndicator(wbIndex < 0 ? 2 : wbIndex); - boolean location = RecordLocationPreference.get(prefs); + boolean location = RecordLocationPreference.get(prefs, CameraSettings.KEY_RECORD_LOCATION); mOnScreenIndicators.updateLocationIndicator(location); } diff --git a/src/com/android/camera/RecordLocationPreference.java b/src/com/android/camera/RecordLocationPreference.java index b88f5e4ed..088ff663f 100644 --- a/src/com/android/camera/RecordLocationPreference.java +++ b/src/com/android/camera/RecordLocationPreference.java @@ -40,18 +40,17 @@ public class RecordLocationPreference extends IconListPreference { @Override public String getValue() { - return get(getSharedPreferences()) ? VALUE_ON : VALUE_OFF; + return get(getSharedPreferences(), getKey()) ? VALUE_ON : VALUE_OFF; } - public static boolean get(SharedPreferences pref) { - String value = pref.getString( - CameraSettings.KEY_RECORD_LOCATION, VALUE_NONE); + public static boolean get(SharedPreferences pref, String key) { + String value = pref.getString(key, VALUE_NONE); return VALUE_ON.equals(value); } - public static boolean isSet(SharedPreferences pref) { + public static boolean isSet(SharedPreferences pref, String key) { String value = pref.getString( - CameraSettings.KEY_RECORD_LOCATION, VALUE_NONE); + key, VALUE_NONE); return !VALUE_NONE.equals(value); } } diff --git a/src/com/android/camera/SettingsActivity.java b/src/com/android/camera/SettingsActivity.java index 503927904..e97073f58 100644 --- a/src/com/android/camera/SettingsActivity.java +++ b/src/com/android/camera/SettingsActivity.java @@ -89,6 +89,20 @@ public class SettingsActivity extends PreferenceActivity { } }; + private SettingsManager.Listener mListener = new SettingsManager.Listener(){ + @Override + public void onSettingsChanged(List<SettingsManager.SettingState> settings){ + Map<String, SettingsManager.Values> map = mSettingsManager.getValuesMap(); + for( SettingsManager.SettingState state : settings) { + SettingsManager.Values values = map.get(state.key); + boolean enabled = values.overriddenValue == null; + Preference pref = findPreference(state.key); + if (pref != null) { + pref.setEnabled(enabled); + } + } + } + }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -102,6 +116,7 @@ public class SettingsActivity extends PreferenceActivity { finish(); return; } + mSettingsManager.registerListener(mListener); addPreferencesFromResource(R.xml.setting_menu_preferences); mSharedPreferences = getPreferenceManager().getSharedPreferences(); @@ -207,12 +222,16 @@ public class SettingsActivity extends PreferenceActivity { private void updatePreferenceButton(String key) { Preference pref = findPreference(key); - if (pref != null && pref instanceof ListPreference) { - ListPreference pref2 = (ListPreference) pref; - if (pref2.getEntryValues().length == 1) { - pref2.setEnabled(false); - } else { - pref2.setEnabled(true); + if (pref != null ) { + if( pref instanceof ListPreference) { + ListPreference pref2 = (ListPreference) pref; + if (pref2.getEntryValues().length == 1) { + pref2.setEnabled(false); + } else { + pref2.setEnabled(true); + } + }else { + pref.setEnabled(false); } } } @@ -243,6 +262,12 @@ public class SettingsActivity extends PreferenceActivity { finish(); } + @Override + protected void onDestroy() { + super.onDestroy(); + mSettingsManager.unregisterListener(mListener); + } + private void setShowInLockScreen() { // Change the window flags so that secure camera can show when locked Window win = getWindow(); diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index f64d64a01..6adbcfb64 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -86,15 +86,17 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final int SCENE_MODE_NIGHT_INT = 5; // Custom-Scenemodes start from 100 - public static final int SCENE_MODE_DUAL_INT = 100; - public static final int SCENE_MODE_OPTIZOOM_INT = 101; - public static final int SCENE_MODE_UBIFOCUS_INT = 102; - public static final int SCENE_MODE_BESTPICTURE_INT = 103; - public static final int SCENE_MODE_PANORAMA_INT = 104; - public static final int SCENE_MODE_CHROMAFLASH_INT = 105; - public static final int SCENE_MODE_BLURBUSTER_INT = 106; - public static final int SCENE_MODE_SHARPSHOOTER_INT = 107; - public static final int SCENE_MODE_TRACKINGFOCUS_INT = 108; + public static final int SCENE_MODE_CUSTOM_START = 100; + public static final int SCENE_MODE_DUAL_INT = SCENE_MODE_CUSTOM_START; + public static final int SCENE_MODE_OPTIZOOM_INT = SCENE_MODE_CUSTOM_START + 1; + public static final int SCENE_MODE_UBIFOCUS_INT = SCENE_MODE_CUSTOM_START + 2; + public static final int SCENE_MODE_BESTPICTURE_INT = SCENE_MODE_CUSTOM_START + 3; + public static final int SCENE_MODE_PANORAMA_INT = SCENE_MODE_CUSTOM_START + 4; + public static final int SCENE_MODE_CHROMAFLASH_INT = SCENE_MODE_CUSTOM_START + 5; + public static final int SCENE_MODE_BLURBUSTER_INT = SCENE_MODE_CUSTOM_START + 6; + public static final int SCENE_MODE_SHARPSHOOTER_INT = SCENE_MODE_CUSTOM_START + 7; + public static final int SCENE_MODE_TRACKINGFOCUS_INT = SCENE_MODE_CUSTOM_START + 8; + public static final int SCENE_MODE_PROMODE_INT = SCENE_MODE_CUSTOM_START + 9; public static final String SCENE_MODE_DUAL_STRING = "100"; public static final String KEY_CAMERA_SAVEPATH = "pref_camera2_savepath_key"; public static final String KEY_RECORD_LOCATION = "pref_camera2_recordlocation_key"; @@ -110,6 +112,7 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final String KEY_FILTER_MODE = "pref_camera2_filter_mode_key"; public static final String KEY_COLOR_EFFECT = "pref_camera2_coloreffect_key"; public static final String KEY_SCENE_MODE = "pref_camera2_scenemode_key"; + public static final String KEY_SCEND_MODE_INSTRUCTIONAL = "pref_camera2_scenemode_instructional"; public static final String KEY_REDEYE_REDUCTION = "pref_camera2_redeyereduction_key"; public static final String KEY_CAMERA_ID = "pref_camera2_id_key"; public static final String KEY_PICTURE_SIZE = "pref_camera2_picturesize_key"; @@ -134,6 +137,7 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final String KEY_SHUTTER_SOUND = "pref_camera2_shutter_sound_key"; public static final String KEY_DEVELOPER_MENU = "pref_camera2_developer_menu_key"; public static final String KEY_RESTORE_DEFAULT = "pref_camera2_restore_default_key"; + public static final String KEY_FOCUS_DISTANCE = "pref_camera2_focus_distance_key"; private static final String TAG = "SnapCam_SettingsManager"; private static SettingsManager sInstance; @@ -419,12 +423,20 @@ public class SettingsManager implements ListMenu.SettingsListener { mListeners.add(listener); } + public void unregisterListener(Listener listener) { + mListeners.remove(listener); + } + private void notifyListeners(List<SettingState> changes) { for (Listener listener : mListeners) { listener.onSettingsChanged(changes); } } + public int getCurrentCameraId() { + return mCameraId; + } + public String getValue(String key) { Values values = mValuesMap.get(key); if (values == null) return null; @@ -439,6 +451,28 @@ public class SettingsManager implements ListMenu.SettingsListener { return pref.findIndexOfValue(value); } + private boolean setFocusValue(String key, float value) { + boolean result = false; + String prefName = ComboPreferences.getLocalSharedPreferencesName(mContext, mCameraId); + SharedPreferences sharedPreferences = mContext.getSharedPreferences(prefName, + Context.MODE_PRIVATE); + float prefValue = sharedPreferences.getFloat(key, 0.5f); + if (prefValue != value) { + SharedPreferences.Editor editor = sharedPreferences.edit(); + editor.putFloat(key, value); + editor.apply(); + result = true; + } + return result; + } + + public float getFocusValue(String key) { + String prefName = ComboPreferences.getLocalSharedPreferencesName(mContext, mCameraId); + SharedPreferences sharedPreferences = mContext.getSharedPreferences(prefName, + Context.MODE_PRIVATE); + return sharedPreferences.getFloat(key, 0.5f); + } + public boolean isOverriden(String key) { Values values = mValuesMap.get(key); return values.overriddenValue != null; @@ -467,6 +501,17 @@ public class SettingsManager implements ListMenu.SettingsListener { } } + public void setFocusDistance(String key, float value, float minFocus) { + boolean isSuccess = setFocusValue(key, value); + if (isSuccess) { + List<SettingState> list = new ArrayList<>(); + Values values = new Values("" + value * minFocus, null); + SettingState ss = new SettingState(KEY_FOCUS_DISTANCE, values); + list.add(ss); + notifyListeners(list); + } + } + private void updateMapAndNotify(ListPreference pref) { String key = pref.getKey(); List changed = checkDependencyAndUpdate(key); @@ -521,6 +566,9 @@ public class SettingsManager implements ListMenu.SettingsListener { ListPreference flashMode = mPreferenceGroup.findPreference(KEY_FLASH_MODE); ListPreference colorEffect = mPreferenceGroup.findPreference(KEY_COLOR_EFFECT); ListPreference sceneMode = mPreferenceGroup.findPreference(KEY_SCENE_MODE); + ListPreference sceneModeInstructional = + mPreferenceGroup.findPreference(KEY_SCEND_MODE_INSTRUCTIONAL); + ListPreference cameraIdPref = mPreferenceGroup.findPreference(KEY_CAMERA_ID); ListPreference pictureSize = mPreferenceGroup.findPreference(KEY_PICTURE_SIZE); ListPreference exposure = mPreferenceGroup.findPreference(KEY_EXPOSURE); @@ -560,6 +608,13 @@ public class SettingsManager implements ListMenu.SettingsListener { } } + if ( sceneModeInstructional != null ) { + if (filterUnsupportedOptions(sceneModeInstructional, + getSupportedSceneModes(cameraId)) ){ + mFilteredKeys.add(sceneModeInstructional.getKey()); + } + } + if (cameraIdPref != null) buildCameraId(); if (pictureSize != null) { @@ -996,6 +1051,11 @@ public class SettingsManager implements ListMenu.SettingsListener { return modes; } + public float getMinimumFocusDistance(int cameraId) { + return mCharacteristics.get(cameraId) + .get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + } + private List<String> getSupportedWhiteBalanceModes(int cameraId) { int[] whiteBalanceModes = mCharacteristics.get(cameraId).get(CameraCharacteristics .CONTROL_AWB_AVAILABLE_MODES); @@ -1020,6 +1080,7 @@ public class SettingsManager implements ListMenu.SettingsListener { if (BlurbusterFilter.isSupportedStatic()) modes.add(SCENE_MODE_BLURBUSTER_INT + ""); if (SharpshooterFilter.isSupportedStatic()) modes.add(SCENE_MODE_SHARPSHOOTER_INT + ""); if (TrackingFocusFrameListener.isSupportedStatic()) modes.add(SCENE_MODE_TRACKINGFOCUS_INT + ""); + modes.add("" + SCENE_MODE_PROMODE_INT); for (int mode : sceneModes) { modes.add("" + mode); } @@ -1040,6 +1101,11 @@ public class SettingsManager implements ListMenu.SettingsListener { return mCharacteristics.get(cameraId).get(CameraCharacteristics.FLASH_INFO_AVAILABLE); } + public StreamConfigurationMap getStreamConfigurationMap(int cameraId){ + return mCharacteristics.get(cameraId) + .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + } + public List<String> getSupportedColorEffects(int cameraId) { int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics .CONTROL_AVAILABLE_EFFECTS); diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index 21ad36fcf..e420addcb 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -1158,7 +1158,8 @@ public class VideoModule implements CameraModule, mOrientationManager.resume(); // Initialize location service. - boolean recordLocation = RecordLocationPreference.get(mPreferences); + boolean recordLocation = RecordLocationPreference.get(mPreferences, + CameraSettings.KEY_RECORD_LOCATION); mLocationManager.recordLocation(recordLocation); if (mPreviewing) { @@ -2798,7 +2799,8 @@ public class VideoModule implements CameraModule, // startPreview(). if (mCameraDevice == null) return; - boolean recordLocation = RecordLocationPreference.get(mPreferences); + boolean recordLocation = RecordLocationPreference.get(mPreferences, + CameraSettings.KEY_RECORD_LOCATION); mLocationManager.recordLocation(recordLocation); readVideoPreferences(); diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index c68d895ea..e853783a6 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -721,7 +721,7 @@ public class VideoUI implements PieRenderer.PieListener, public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) { mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode()); - boolean location = RecordLocationPreference.get(prefs); + boolean location = RecordLocationPreference.get(prefs, CameraSettings.KEY_RECORD_LOCATION); mOnScreenIndicators.updateLocationIndicator(location); } diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java index 7a3b114d1..2d4ec1419 100644 --- a/src/com/android/camera/WideAnglePanoramaModule.java +++ b/src/com/android/camera/WideAnglePanoramaModule.java @@ -1046,7 +1046,8 @@ public class WideAnglePanoramaModule mOrientationManager.resume(); // Initialize location service. - boolean recordLocation = RecordLocationPreference.get(mPreferences); + boolean recordLocation = RecordLocationPreference.get(mPreferences, + CameraSettings.KEY_RECORD_LOCATION); mLocationManager.recordLocation(recordLocation); mUI.initDisplayChangeListener(); UsageStatistics.onContentViewChanged( diff --git a/src/com/android/camera/imageprocessor/FrameProcessor.java b/src/com/android/camera/imageprocessor/FrameProcessor.java index a2ae18c1c..519d77458 100644..100755 --- a/src/com/android/camera/imageprocessor/FrameProcessor.java +++ b/src/com/android/camera/imageprocessor/FrameProcessor.java @@ -40,6 +40,7 @@ import android.renderscript.Allocation; import android.renderscript.Element; import android.renderscript.RenderScript; import android.renderscript.Type; +import android.util.Log; import android.util.Size; import android.view.Surface; import android.widget.Toast; @@ -155,10 +156,15 @@ public class FrameProcessor { mRsRotator.set_width(width); mRsRotator.set_height(height); mRsRotator.set_pad(stridePad); - if(mModule.getMainCameraCharacteristics() != null && - mModule.getMainCameraCharacteristics().get(CameraCharacteristics.SENSOR_ORIENTATION) == 270) { - mRsRotator.set_gFlip(true); + int degree = 90; + if(mModule.getMainCameraCharacteristics() != null) { + degree = mModule.getMainCameraCharacteristics(). + get(CameraCharacteristics.SENSOR_ORIENTATION); + if (mModule.getMainCameraId() == CaptureModule.FRONT_ID) { + degree = Math.abs(degree - 90); + } } + mRsRotator.set_degree(degree); mRsYuvToRGB.set_gIn(mProcessAllocation); mRsYuvToRGB.set_width(height); mRsYuvToRGB.set_height(width); diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java index 9039a9dce..b85d7f1ca 100644 --- a/src/com/android/camera/ui/CameraControls.java +++ b/src/com/android/camera/ui/CameraControls.java @@ -247,7 +247,6 @@ public class CameraControls extends RotatableLayout { } else { mHdrSwitcher.setEnabled(enable); } - mSceneModeSwitcher.setEnabled(enable); mPreview.setEnabled(enable); } diff --git a/src/com/android/camera/ui/CountDownView.java b/src/com/android/camera/ui/CountDownView.java index 6420fd2c3..7e1f28d52 100644..100755 --- a/src/com/android/camera/ui/CountDownView.java +++ b/src/com/android/camera/ui/CountDownView.java @@ -153,16 +153,21 @@ public class CountDownView extends FrameLayout { public void setOrientation(int orientation) { mRemainingSecondsView.setRotation(-orientation); - mCountDownTitle.setRotation(-orientation); + int width = getResources().getDisplayMetrics().widthPixels; + int height = mCountDownTitle.getMeasuredHeight(); + if (height == 0) { + measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED); + height = mCountDownTitle.getMeasuredHeight(); + } int dx = 0, dy = 0; switch (orientation) { case 90: - dy = (mCountDownTitle.getWidth() - mCountDownTitle.getHeight()) / 2; + dy = (width - height) / 2; dx = -dy; break; case 270: - dx = dy = (mCountDownTitle.getWidth() - mCountDownTitle.getHeight()) / 2; + dx = dy = (width - height) / 2; break; case 180: break; diff --git a/src/com/android/camera/ui/OneUICameraControls.java b/src/com/android/camera/ui/OneUICameraControls.java index 63e1b5b4d..3dce60d22 100644..100755 --- a/src/com/android/camera/ui/OneUICameraControls.java +++ b/src/com/android/camera/ui/OneUICameraControls.java @@ -24,24 +24,28 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; +import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; -import org.codeaurora.snapcam.R; - import com.android.camera.Storage; import com.android.camera.imageprocessor.filter.BeautificationFilter; +import org.codeaurora.snapcam.R; + public class OneUICameraControls extends RotatableLayout { private static final String TAG = "CAM_Controls"; private View mShutter; private View mVideoShutter; + private View mPauseButton; private View mFlashButton; private View mMute; private View mFrontBackSwitcher; @@ -53,6 +57,8 @@ public class OneUICameraControls extends RotatableLayout { private View mMakeupSeekBarLowText; private View mMakeupSeekBarHighText; private View mMakeupSeekBarLayout; + private ViewGroup mProModeLayout; + private View mProModeCloseButton; private ArrowTextView mRefocusToast; @@ -75,10 +81,31 @@ public class OneUICameraControls extends RotatableLayout { private int mWidth; private int mHeight; private boolean mVisible; + private boolean mIsVideoMode = false; + private int mBottomLargeSize; + private int mBottomSmallSize; + + private ProMode mProMode; + private ImageView mExposureIcon; + private ImageView mManualIcon; + private ImageView mWhiteBalanceIcon; + private ImageView mIsoIcon; + private TextView mExposureText; + private TextView mManualText; + private TextView mWhiteBalanceText; + private TextView mIsoText; + private boolean mProModeOn = false; + private LinearLayout mExposureLayout; + private LinearLayout mManualLayout; + private LinearLayout mWhiteBalanceLayout; + private LinearLayout mIsoLayout; + private RotateLayout mExposureRotateLayout; + private RotateLayout mManualRotateLayout; + private RotateLayout mWhiteBalanceRotateLayout; + private RotateLayout mIsoRotateLayout; public OneUICameraControls(Context context, AttributeSet attrs) { super(context, attrs); - mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); setWillNotDraw(false); @@ -103,6 +130,7 @@ public class OneUICameraControls extends RotatableLayout { super.onFinishInflate(); mShutter = findViewById(R.id.shutter_button); mVideoShutter = findViewById(R.id.video_button); + mPauseButton = findViewById(R.id.video_pause); mFrontBackSwitcher = findViewById(R.id.front_back_switcher); mTsMakeupSwitcher = findViewById(R.id.ts_makeup_switcher); mMakeupSeekBarLowText = findViewById(R.id.makeup_low_text); @@ -117,11 +145,92 @@ public class OneUICameraControls extends RotatableLayout { mFilterModeSwitcher = findViewById(R.id.filter_mode_switcher); mRemainingPhotos = (LinearLayout) findViewById(R.id.remaining_photos); mRemainingPhotosText = (TextView) findViewById(R.id.remaining_photos_text); + mProModeLayout = (ViewGroup) findViewById(R.id.pro_mode_layout); + mProModeCloseButton = findViewById(R.id.promode_close_button); + + mExposureIcon = (ImageView) findViewById(R.id.exposure); + mManualIcon = (ImageView) findViewById(R.id.manual); + mWhiteBalanceIcon = (ImageView) findViewById(R.id.white_balance); + mIsoIcon = (ImageView) findViewById(R.id.iso); + mExposureText = (TextView) findViewById(R.id.exposure_value); + mManualText = (TextView) findViewById(R.id.manual_value); + mWhiteBalanceText = (TextView) findViewById(R.id.white_balance_value); + mIsoText = (TextView) findViewById(R.id.iso_value); + mProMode = (ProMode) findViewById(R.id.promode_slider); + mProMode.initialize(this); + + mExposureLayout = (LinearLayout) findViewById(R.id.exposure_layout); + mManualLayout = (LinearLayout) findViewById(R.id.manual_layout); + mWhiteBalanceLayout = (LinearLayout) findViewById(R.id.white_balance_layout); + mIsoLayout = (LinearLayout) findViewById(R.id.iso_layout); + + mExposureRotateLayout = (RotateLayout) findViewById(R.id.exposure_rotate_layout); + mManualRotateLayout = (RotateLayout) findViewById(R.id.manual_rotate_layout); + mWhiteBalanceRotateLayout = (RotateLayout) findViewById(R.id.white_balance_rotate_layout); + mIsoRotateLayout = (RotateLayout) findViewById(R.id.iso_rotate_layout); + + mExposureLayout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.EXPOSURE_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mExposureIcon.setImageResource(R.drawable.icon_exposure_blue); + mProMode.setMode(ProMode.EXPOSURE_MODE); + } + } + }); + mManualLayout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.MANUAL_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mManualIcon.setImageResource(R.drawable.icon_manual_blue); + mProMode.setMode(ProMode.MANUAL_MODE); + } + } + }); + mWhiteBalanceLayout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.WHITE_BALANCE_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mWhiteBalanceIcon.setImageResource(R.drawable.icon_white_balance_blue); + mProMode.setMode(ProMode.WHITE_BALANCE_MODE); + } + } + }); + mIsoLayout.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.ISO_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mIsoIcon.setImageResource(R.drawable.icon_iso_blue); + mProMode.setMode(ProMode.ISO_MODE); + } + } + }); + mViews = new View[]{ mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, - mTsMakeupSwitcher, mFlashButton, mShutter, mPreview, mVideoShutter + mTsMakeupSwitcher, mFlashButton, mShutter, mPreview, mVideoShutter, + mPauseButton }; - + mBottomLargeSize = getResources().getDimensionPixelSize( + R.dimen.one_ui_bottom_large); + mBottomSmallSize = getResources().getDimensionPixelSize( + R.dimen.one_ui_bottom_small); if(!BeautificationFilter.isSupportedStatic()) { mTsMakeupSwitcher.setVisibility(View.GONE); mTsMakeupSwitcher = null; @@ -142,6 +251,14 @@ public class OneUICameraControls extends RotatableLayout { if(mMakeupSeekBar != null) { mMakeupSeekBar.setMinimumWidth(mWidth/2); } + + LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(mWidth/ 4,mWidth/4); + mExposureLayout.setLayoutParams(lp); + + mManualLayout.setLayoutParams(lp); + mWhiteBalanceLayout.setLayoutParams(lp); + mIsoLayout.setLayoutParams(lp); + initializeProMode(mProModeOn); } public boolean isControlRegion(int x, int y) { @@ -205,17 +322,34 @@ public class OneUICameraControls extends RotatableLayout { int rotation = getUnifiedRotation(); setLocation(mSceneModeSwitcher, true, 0); setLocation(mFilterModeSwitcher, true, 1); - setLocation(mFrontBackSwitcher, true, 2); - setLocation(mTsMakeupSwitcher, true, 3); - setLocation(mFlashButton, true, 4); - setLocation(mPreview, false, 0); - setLocation(mShutter, false, 2); - setLocation(mVideoShutter, false, 3.15f); + if (mIsVideoMode) { + setLocation(mMute, true, 2); + setLocation(mTsMakeupSwitcher, true, 3); + setLocation(mFlashButton, true, 4); + setLocation(mPauseButton, false, 3.15f); + setLocation(mShutter, false , 0.85f); + setLocation(mVideoShutter, false, 2); + } else { + setLocation(mFrontBackSwitcher, true, 2); + setLocation(mTsMakeupSwitcher, true, 3); + setLocation(mFlashButton, true, 4); + setLocation(mPreview, false, 0); + setLocation(mShutter, false, 2); + setLocation(mVideoShutter, false, 3.15f); + } setLocationCustomBottom(mMakeupSeekBarLayout, 0, 1); + setLocation(mProModeCloseButton, false, 4); layoutToast(mRefocusToast, w, h, rotation); } + private void setBottomButtionSize(View view, int width, int height) { + FrameLayout.LayoutParams layout = (FrameLayout.LayoutParams)view.getLayoutParams(); + layout.height = height; + layout.width = width; + view.setLayoutParams(layout); + } + private void layoutToast(final View v, int w, int h, int rotation) { int tw = v.getMeasuredWidth(); int th = v.getMeasuredHeight(); @@ -271,6 +405,17 @@ public class OneUICameraControls extends RotatableLayout { mVisible = true; } + public void setVideoMode(boolean videoMode) { + mIsVideoMode = videoMode; + if (mIsVideoMode) { + setBottomButtionSize(mVideoShutter, mBottomLargeSize, mBottomLargeSize); + setBottomButtionSize(mShutter, mBottomSmallSize, mBottomSmallSize); + } else { + setBottomButtionSize(mShutter, mBottomLargeSize, mBottomLargeSize); + setBottomButtionSize(mVideoShutter, mBottomSmallSize, mBottomSmallSize); + } + } + private void layoutRemaingPhotos() { int rl = mPreview.getLeft(); int rt = mPreview.getTop(); @@ -324,7 +469,7 @@ public class OneUICameraControls extends RotatableLayout { View[] views = { mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, mTsMakeupSwitcher, mFlashButton, mPreview, mMute, mShutter, mVideoShutter, - mMakeupSeekBarLowText, mMakeupSeekBarHighText + mMakeupSeekBarLowText, mMakeupSeekBarHighText, mPauseButton }; for (View v : views) { @@ -332,6 +477,11 @@ public class OneUICameraControls extends RotatableLayout { ((Rotatable) v).setOrientation(orientation, animation); } } + mExposureRotateLayout.setOrientation(orientation, animation); + mManualRotateLayout.setOrientation(orientation, animation); + mWhiteBalanceRotateLayout.setOrientation(orientation, animation); + mIsoRotateLayout.setOrientation(orientation, animation); + mProMode.setOrientation(orientation); layoutRemaingPhotos(); } @@ -377,4 +527,47 @@ public class OneUICameraControls extends RotatableLayout { mPath.lineTo(x1, y1); } } + + public void setProMode(boolean promode) { + mProModeOn = promode; + initializeProMode(mProModeOn); + resetProModeIcons(); + mProMode.reinit(); + } + + private void resetProModeIcons() { + mExposureIcon.setImageResource(R.drawable.icon_exposure); + mManualIcon.setImageResource(R.drawable.icon_manual); + mWhiteBalanceIcon.setImageResource(R.drawable.icon_white_balance); + mIsoIcon.setImageResource(R.drawable.icon_iso); + } + + public void initializeProMode(boolean promode) { + if (!promode) { + mProMode.setMode(ProMode.NO_MODE); + mProModeLayout.setVisibility(INVISIBLE); + mProModeCloseButton.setVisibility(INVISIBLE); + return; + } + mProModeLayout.setVisibility(VISIBLE); + mProModeCloseButton.setVisibility(VISIBLE); + mProModeLayout.setY(mHeight - mBottom - mProModeLayout.getHeight()); + } + + public void updateProModeText(int mode, String value) { + switch (mode) { + case ProMode.EXPOSURE_MODE: + mExposureText.setText(value); + break; + case ProMode.MANUAL_MODE: + mManualText.setText(value); + break; + case ProMode.WHITE_BALANCE_MODE: + mWhiteBalanceText.setText(value); + break; + case ProMode.ISO_MODE: + mIsoText.setText(value); + break; + } + } } diff --git a/src/com/android/camera/ui/PanoCaptureProcessView.java b/src/com/android/camera/ui/PanoCaptureProcessView.java index 2b37fd61e..95e9a095f 100644 --- a/src/com/android/camera/ui/PanoCaptureProcessView.java +++ b/src/com/android/camera/ui/PanoCaptureProcessView.java @@ -120,6 +120,7 @@ public class PanoCaptureProcessView extends View implements SensorEventListener private static final boolean DEBUG = false; //TODO: This has to be false before release private BitmapArrayOutputStream mBitmapStream; private static boolean mIsSupported = false; + private Object mBitmapStreamLock = new Object(); private boolean mIsFrameProcessing = false; enum PANO_STATUS { @@ -337,13 +338,15 @@ public class PanoCaptureProcessView extends View implements SensorEventListener public void onPause() { mSensorManager.unregisterListener(this, mRotationSensor); - if(mBitmapStream != null) { - try { - mBitmapStream.close(); - } catch (IOException e) { - //Ignore + synchronized (mBitmapStreamLock) { + if(mBitmapStream != null) { + try { + mBitmapStream.close(); + } catch (IOException e) { + //Ignore + } + mBitmapStream = null; } - mBitmapStream = null; } } @@ -510,13 +513,16 @@ public class PanoCaptureProcessView extends View implements SensorEventListener } private void doTask(BitmapTask bitmapTask) { - if(mBitmapStream == null) { - mBitmapStream = new BitmapArrayOutputStream(1024*1204); + int rtv = -1; + synchronized (mBitmapStreamLock) { + if(mBitmapStream == null) { + mBitmapStream = new BitmapArrayOutputStream(1024*1204); + } + mBitmapStream.reset(); + bitmapTask.bitmap.compress(Bitmap.CompressFormat.JPEG, 100, mBitmapStream); + rtv = callNativeProcessKeyFrame(mBitmapStream.toByteArray(), mBitmapStream.size(), + bitmapTask.x, bitmapTask.y, 0, bitmapTask.dir); } - mBitmapStream.reset(); - bitmapTask.bitmap.compress(Bitmap.CompressFormat.JPEG, 100, mBitmapStream); - int rtv = callNativeProcessKeyFrame(mBitmapStream.toByteArray(), mBitmapStream.size(), - bitmapTask.x, bitmapTask.y, 0, bitmapTask.dir); if(rtv < 0) { mShouldFinish = true; stopPano(false, mActivity.getResources().getString(R.string.panocapture_direction_is_changed)); diff --git a/src/com/android/camera/ui/ProMode.java b/src/com/android/camera/ui/ProMode.java new file mode 100644 index 000000000..b55d0c425 --- /dev/null +++ b/src/com/android/camera/ui/ProMode.java @@ -0,0 +1,378 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.camera.ui; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Path; +import android.graphics.PathMeasure; +import android.graphics.Point; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; + +import com.android.camera.SettingsManager; + +import org.codeaurora.snapcam.R; + +import java.util.ArrayList; + +public class ProMode extends View { + public static final int NO_MODE = -1; + public static final int EXPOSURE_MODE = 0; + public static final int MANUAL_MODE = 1; + public static final int WHITE_BALANCE_MODE = 2; + public static final int ISO_MODE = 3; + private static final int DRAG_Y_THRESHOLD = 100; + private static final int DRAG_X_THRESHOLD = 30; + private static final int BLUE = 0xff4693fb; + private static final int SELECTED_DOT_SIZE = 20; + private static final int DOT_SIZE = 10; + private static final int[] wbIcons = {R.drawable.auto, R.drawable.incandecent, + R.drawable.fluorescent, R.drawable.sunlight, R.drawable.cloudy}; + private static final int[] wbIconsBlue = {R.drawable.auto_blue, R.drawable.incandecent_blue, + R.drawable.fluorescent_blue, R.drawable.sunlight_blue, R.drawable.cloudy_blue}; + private static final int WB_ICON_SIZE = 80; + private PathMeasure mCurveMeasure; + private int mCurveLeft; + private int mCurveRight; + private float mSlider = -1; + private Paint mPaint = new Paint(); + private int mNums; + private int mIndex; + private Point[] mPoints; + private float mClickThreshold; + private int mStride; + private SettingsManager mSettingsManager; + private int mMode = NO_MODE; + private Context mContext; + private ViewGroup mParent; + private float minFocus; + private OneUICameraControls mUI; + private int mWidth; + private int mHeight; + private int mCurveY; + private ArrayList<View> mAddedViews; + private float curveCoordinate[] = new float[2]; + private Path mCurvePath = new Path(); + private int mCurveHeight; + private int mOrientation; + + public ProMode(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + mPaint.setStrokeWidth(7f); + mSettingsManager = SettingsManager.getInstance(); + } + + private void init() { + init(EXPOSURE_MODE); + init(WHITE_BALANCE_MODE); + init(ISO_MODE); + mUI.updateProModeText(MANUAL_MODE, "Manual"); + } + + private void init(int mode) { + String key = getKey(mode); + if (key == null) return; + int index = mSettingsManager.getValueIndex(key); + CharSequence[] cc = mSettingsManager.getEntries(key); + mUI.updateProModeText(mode, cc[index].toString()); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + if (mMode != NO_MODE) { + mPaint.setColor(Color.WHITE); + mPaint.setStyle(Paint.Style.STROKE); + canvas.drawPath(mCurvePath, mPaint); + } + mPaint.setStyle(Paint.Style.FILL); + if (mMode == MANUAL_MODE) { + mPaint.setColor(Color.WHITE); + canvas.drawCircle(mCurveLeft, mCurveY, DOT_SIZE, mPaint); + canvas.drawCircle(mCurveRight, mCurveY, DOT_SIZE, mPaint); + mPaint.setColor(BLUE); + if (mSlider >= 0f) { + mCurveMeasure.getPosTan(mCurveMeasure.getLength() * mSlider, curveCoordinate, null); + canvas.drawCircle(curveCoordinate[0], curveCoordinate[1], SELECTED_DOT_SIZE, + mPaint); + } + } else { + for (int i = 0; i < mNums; i++) { + if (i == mIndex) { + mPaint.setColor(BLUE); + canvas.drawCircle(mPoints[i].x, mPoints[i].y, SELECTED_DOT_SIZE, mPaint); + } else { + mPaint.setColor(Color.WHITE); + canvas.drawCircle(mPoints[i].x, mPoints[i].y, DOT_SIZE, mPaint); + } + } + } + } + + public void initialize(OneUICameraControls ui) { + mParent = (ViewGroup) getParent(); + mUI = ui; + init(); + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + super.onLayout(changed, left, top, right, bottom); + mWidth = right - left; + mHeight = bottom - top; + + mCurveLeft = mWidth / 10; + mCurveRight = mWidth - mCurveLeft; + mCurveHeight = mWidth / 7; + mCurveY = (int) (mHeight * 0.67); + + float cx = (mCurveLeft + mCurveRight) / 2; + mCurvePath.reset(); + mCurvePath.moveTo(mCurveLeft, mCurveY); + mCurvePath.quadTo(cx, mCurveY - mCurveHeight, mCurveRight, mCurveY); + mCurveMeasure = new PathMeasure(mCurvePath, false); + } + + public void reinit() { + init(); + } + + public void setOrientation(int orientation) { + mOrientation = orientation; + if (mAddedViews != null) { + int rotation = mOrientation; + if (rotation == 90 || rotation == 270) rotation += 180; + rotation %= 360; + for (View v : mAddedViews) { + v.setRotation(rotation); + } + } + } + + public int getMode() { + return mMode; + } + + public void setMode(int mode) { + mMode = mode; + removeViews(); + if (mMode == NO_MODE) { + setVisibility(INVISIBLE); + return; + } else { + setVisibility(VISIBLE); + } + mIndex = -1; + String key = currentKey(); + if (mMode == MANUAL_MODE) { + minFocus = mSettingsManager + .getMinimumFocusDistance(mSettingsManager.getCurrentCameraId()); + float value = mSettingsManager.getFocusValue(SettingsManager.KEY_FOCUS_DISTANCE); + setSlider(value); + int stride = mCurveRight - mCurveLeft; + for (int i = 0; i < 2; i++) { + TextView v = new TextView(mContext); + String s = "infinity"; + if (i == 1) s = "macro"; + v.setText(s); + v.setTextColor(Color.WHITE); + v.measure(0, 0); + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(v.getMeasuredWidth(), + v.getMeasuredHeight()); + v.setLayoutParams(lp); + v.setX(mCurveLeft + i * stride - v.getMeasuredWidth() / 2); + v.setY(mCurveY - 2 * v.getMeasuredHeight()); + mParent.addView(v); + mAddedViews.add(v); + } + } else { + if (key == null) return; + CharSequence[] cc = mSettingsManager.getEntries(key); + int length = mSettingsManager.getEntryValues(key).length; + int index = mSettingsManager.getValueIndex(key); + updateSlider(length); + + for (int i = 0; i < length; i++) { + View v; + if (mMode == WHITE_BALANCE_MODE) { + v = new ImageView(mContext); + ((ImageView) v).setImageResource(wbIcons[i]); + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams( + WB_ICON_SIZE, WB_ICON_SIZE); + v.setLayoutParams(lp); + v.setX(mPoints[i].x - WB_ICON_SIZE / 2); + v.setY(mPoints[i].y - 2 * WB_ICON_SIZE); + } else { + v = new TextView(mContext); + ((TextView) v).setText(cc[i]); + ((TextView) v).setTextColor(Color.WHITE); + v.measure(0, 0); + ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(v.getMeasuredWidth(), + v.getMeasuredHeight()); + v.setLayoutParams(lp); + v.setX(mPoints[i].x - v.getMeasuredWidth() / 2); + v.setY(mPoints[i].y - 2 * v.getMeasuredHeight()); + } + + + mParent.addView(v); + mAddedViews.add(v); + } + setIndex(index, true); + } + setOrientation(mOrientation); + } + + private String getKey(int mode) { + switch (mode) { + case EXPOSURE_MODE: + return SettingsManager.KEY_EXPOSURE; + case WHITE_BALANCE_MODE: + return SettingsManager.KEY_WHITE_BALANCE; + case ISO_MODE: + return SettingsManager.KEY_ISO; + } + return null; + } + + private String currentKey() { + return getKey(mMode); + } + + private void updateSlider(int n) { + mNums = n; + mStride = (mCurveRight - mCurveLeft) / (mNums - 1); + mClickThreshold = mStride * 0.45f; + mPoints = new Point[mNums]; + + float slide = 1f / (mNums - 1); + for (int i = 0; i < mNums; i++) { + mCurveMeasure.getPosTan(mCurveMeasure.getLength() * (slide * i), curveCoordinate, null); + mPoints[i] = new Point((int) curveCoordinate[0], (int) curveCoordinate[1]); + } + + invalidate(); + } + + public void setSlider(float slider) { + mSlider = slider; + mSettingsManager.setFocusDistance(SettingsManager.KEY_FOCUS_DISTANCE, mSlider, minFocus); + mUI.updateProModeText(mMode, "Manual"); + invalidate(); + } + + private void setIndex(int index, boolean force) { + if (mIndex == index && !force) return; + if (mIndex != -1) { + View v = mAddedViews.get(mIndex); + if (v instanceof TextView) { + ((TextView) v).setTextColor(Color.WHITE); + } else if (v instanceof ImageView) { + if (mMode == WHITE_BALANCE_MODE) { + ((ImageView) v).setImageResource(wbIcons[mIndex]); + } + } + } + + mIndex = index; + String key = currentKey(); + View v = mAddedViews.get(mIndex); + if (v instanceof TextView) { + ((TextView) v).setTextColor(BLUE); + } else if (v instanceof ImageView) { + if (mMode == WHITE_BALANCE_MODE) { + ((ImageView) v).setImageResource(wbIconsBlue[mIndex]); + } + } + if (key != null) mSettingsManager.setValueIndex(key, mIndex); + CharSequence[] cc = mSettingsManager.getEntries(key); + mUI.updateProModeText(mMode, cc[mIndex].toString()); + invalidate(); + } + + private void removeViews() { + ViewGroup vg = (ViewGroup) getParent(); + if (mAddedViews != null) { + for (int i = 0; i < mAddedViews.size(); i++) { + vg.removeView(mAddedViews.get(i)); + } + } + mAddedViews = new ArrayList<View>(); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (mMode == MANUAL_MODE) { + float slider = getSlider(event.getX(), event.getY()); + if (slider >= 0) { + setSlider(slider); + } + } else { + int idx = findButton(event.getX(), event.getY()); + if (idx != -1) { + setIndex(idx, false); + } + return true; + } + return true; + } + + private int findButton(float x, float y) { + for (int i = 0; i < mNums; i++) { + float xdiff = Math.abs(mPoints[i].x - x); + float ydiff = Math.abs(mPoints[i].y - y); + float dist = xdiff * xdiff + ydiff * ydiff; + if (dist < mClickThreshold * mClickThreshold) return i; + } + return -1; + } + + private float getSlider(float x, float y) { + if (x > mCurveLeft - DRAG_X_THRESHOLD && x < mCurveRight + DRAG_X_THRESHOLD + && y > mCurveY - mCurveHeight - DRAG_Y_THRESHOLD + && y < mCurveY + DRAG_Y_THRESHOLD) { + if (x < mCurveLeft) x = mCurveLeft; + if (x > mCurveRight) x = mCurveRight; + return (x - mCurveLeft) / (mCurveRight - mCurveLeft); + } else { + return -1; + } + } + +} diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index 838f8d950..cb414bac0 100644 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java @@ -71,9 +71,22 @@ import java.text.SimpleDateFormat; import java.util.Comparator; import java.util.Date; import java.util.List; +import java.util.ArrayList; import java.util.Locale; +import android.util.Range; import java.util.StringTokenizer; +import com.android.camera.SettingsManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.impl.CameraMetadataNative; +import android.hardware.camera2.utils.SurfaceUtils; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + /** * Collection of utility functions used in this package. */ @@ -1275,4 +1288,85 @@ public class CameraUtil { (long) rhs.getWidth() * rhs.getHeight()); } } + + public static List<CaptureRequest> createHighSpeedRequestList(CaptureRequest request + ,int cameraId) throws CameraAccessException { + if (request == null) { + throw new IllegalArgumentException("Input capture request must not be null"); + } + Collection<Surface> outputSurfaces = request.getTargets(); + Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE); + + StreamConfigurationMap config = + SettingsManager.getInstance().getStreamConfigurationMap(cameraId); + SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config); + + // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize + // the preview frame rate, should use maxBatch size for that high speed stream + // configuration. We choose the former for now. + int requestListSize = fpsRange.getUpper() / 30; + List<CaptureRequest> requestList = new ArrayList<CaptureRequest>(); + + // Prepare the Request builders: need carry over the request controls. + // First, create a request builder that will only include preview or recording target. + CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy()); + // Note that after this step, the requestMetadata is mutated (swapped) and can not be used + // for next request builder creation. + CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder( + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + singleTargetRequestBuilder.setTag(cameraId); + + // Overwrite the capture intent to make sure a good value is set. + Iterator<Surface> iterator = outputSurfaces.iterator(); + Surface firstSurface = iterator.next(); + Surface secondSurface = null; + if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(firstSurface)) { + singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW); + } else { + // Video only, or preview + video + singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + } + singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); + + // Second, Create a request builder that will include both preview and recording targets. + CaptureRequest.Builder doubleTargetRequestBuilder = null; + if (outputSurfaces.size() == 2) { + // Have to create a new copy, the original one was mutated after a new + // CaptureRequest.Builder creation. + requestMetadata = new CameraMetadataNative(request.getNativeCopy()); + doubleTargetRequestBuilder = new CaptureRequest.Builder( + requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE); + doubleTargetRequestBuilder.setTag(cameraId); + doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT, + CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD); + doubleTargetRequestBuilder.addTarget(firstSurface); + secondSurface = iterator.next(); + doubleTargetRequestBuilder.addTarget(secondSurface); + doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true); + // Make sure singleTargetRequestBuilder contains only recording surface for + // preview + recording case. + Surface recordingSurface = firstSurface; + if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) { + recordingSurface = secondSurface; + } + singleTargetRequestBuilder.addTarget(recordingSurface); + } else { + // Single output case: either recording or preview. + singleTargetRequestBuilder.addTarget(firstSurface); + } + + // Generate the final request list. + for (int i = 0; i < requestListSize; i++) { + if (i == 0 && doubleTargetRequestBuilder != null) { + // First request should be recording + preview request + requestList.add(doubleTargetRequestBuilder.build()); + } else { + requestList.add(singleTargetRequestBuilder.build()); + } + } + + return Collections.unmodifiableList(requestList); + } } diff --git a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java index 595df8122..944bf0800 100644..100755 --- a/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java +++ b/src/org/codeaurora/snapcam/filter/ClearSightImageProcessor.java @@ -52,6 +52,7 @@ import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.InputConfiguration; import android.hardware.camera2.params.StreamConfigurationMap; @@ -623,18 +624,26 @@ public class ClearSightImageProcessor { ReprocessableImage bayer = mBayerFrames.peek(); ReprocessableImage mono = mMonoFrames.peek(); + long bayerTsSOF = bayer.mCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP); + long bayerTsEOF = bayerTsSOF + bayer.mCaptureResult.get( + CaptureResult.SENSOR_EXPOSURE_TIME); + long monoTsSOF = mono.mCaptureResult.get(CaptureResult.SENSOR_TIMESTAMP); + long monoTsEOF = monoTsSOF + mono.mCaptureResult.get( + CaptureResult.SENSOR_EXPOSURE_TIME); + + Log.d(TAG, - "checkForValidFramePair - bayer ts: " - + bayer.mImage.getTimestamp() + " mono ts: " - + mono.mImage.getTimestamp()); + "checkForValidFramePair - bayer ts SOF: " + + bayerTsSOF + ", EOF: " + bayerTsEOF + + ", mono ts SOF: " + monoTsSOF + ", EOF: " + monoTsEOF); Log.d(TAG, - "checkForValidFramePair - difference: " - + Math.abs(bayer.mImage.getTimestamp() - - mono.mImage.getTimestamp())); + "checkForValidFramePair - difference SOF: " + + Math.abs(bayerTsSOF - monoTsSOF) + + ", EOF: " + Math.abs(bayerTsEOF - monoTsEOF)); // if timestamps are within threshold, keep frames - if (Math.abs(bayer.mImage.getTimestamp() - - mono.mImage.getTimestamp()) > mTimestampThresholdNs) { - if(bayer.mImage.getTimestamp() > mono.mImage.getTimestamp()) { + if ((Math.abs(bayerTsSOF - monoTsSOF) > mTimestampThresholdNs) && + (Math.abs(bayerTsEOF - monoTsEOF) > mTimestampThresholdNs)) { + if(bayerTsSOF > monoTsSOF) { Log.d(TAG, "checkForValidFramePair - toss mono"); // no match, toss mono = mMonoFrames.poll(); @@ -903,6 +912,12 @@ public class ClearSightImageProcessor { mImageEncodeHandler.obtainMessage(MSG_START_CAPTURE).sendToTarget(); short encodeRequest = 0; + /* In same case, timeout will reset ClearSightNativeEngine object, so fields + in the object is not initial, need to return and skip process. + */ + if (ClearSightNativeEngine.getInstance().getReferenceImage(true) == null) { + return; + } long csTs = ClearSightNativeEngine.getInstance().getReferenceImage(true).getTimestamp(); CaptureRequest.Builder csRequest = createEncodeReprocRequest( ClearSightNativeEngine.getInstance().getReferenceResult(true), CAM_TYPE_BAYER); @@ -1028,6 +1043,8 @@ public class ClearSightImageProcessor { }, null); } catch (CameraAccessException e) { e.printStackTrace(); + } catch (IllegalStateException e1) { + e1.printStackTrace(); } } } |