diff options
author | Linux Build Service Account <lnxbuild@localhost> | 2016-08-22 06:10:10 -0600 |
---|---|---|
committer | Linux Build Service Account <lnxbuild@localhost> | 2016-08-22 06:10:10 -0600 |
commit | 050fe78517519b689ce5adff1cf2c074bcbd186e (patch) | |
tree | b0fe6dd4a41a8c2fb38651c468657b235f6d796d | |
parent | 7df580fbc33fd58794bbe7d7bf7b193208fe76c0 (diff) | |
parent | 6bd73c5658538182923109b1423481720b269402 (diff) | |
download | android_packages_apps_Snap-050fe78517519b689ce5adff1cf2c074bcbd186e.tar.gz android_packages_apps_Snap-050fe78517519b689ce5adff1cf2c074bcbd186e.tar.bz2 android_packages_apps_Snap-050fe78517519b689ce5adff1cf2c074bcbd186e.zip |
Promotion of camera.lnx.1.0-00118.
CRs Change ID Subject
--------------------------------------------------------------------------------------------------------------
1050663 I2e6845c9e25481106f51772b5d3dfd0a971a2be7 SnapdragonCamera: Selfie Mirror mode
1046143 Ieab39f4916d07be8cda5179a15c1d0f3af2472bb SnapdragonCamera: set flash off when stillmore is on
1042288 Ida4961d03668ac09e3fc35bff2d645e4d755eb10 SnapdragonCamera: reset the snapshot status when switch
1050663 Ifa0f544e80d161eaef743d611c712d8db48e5631 SnapdragonCamera: Bestpicture scene filter
1050663 I82bd9931168b831bf1c819935c47113f37d7a188 SnapdragonCamera: StillMore to camera2
1019338 Ibc40297817ebfca04e19097bd463f78aec4d75cd SnapdragonCamera: Fix crash when switching between camer
1050099 I730ecc8ba3e7ee41da4e7ef2604e147c35a79c1a SnapdragonCamera: Fix video capture bandwith issue
1050106 Ib718497efdff13115c95011d08020474dc929573 SnapdragonCamera: Allow persist flag to hardcode preview
1040876 I67be2627b493edcbc329146eb816a15bddecab23 SnapdragonCamera: Fix face circle disappears for few fra
1050117 I624f28428415b8dde7da6e4908c26dd59bb3fbdb SnapdragonCamera: Add shutter sound to camera2
1043599 I7407700befad925e73ca8ce9eaa01fc7d3de1ab0 SnapdragonCamera: Fix circles not drawn first time switc
1045405 Ib0dd7e63c167ead0fd734382d3ad4fdc7a64f8cc SnapdragonCamera: Add High Framerate Video capture
1028467 Ib90a3002e2e6815b5d6f7aea8fdf73aafbe527ee SnapdragonCamera: Remove swipe right to open menu
1045131 Ibb721fc36cd4ef2d3a0623ae0ba7fec468a8b377 SnapdragonCamera: Fix camera preview stretched
1047924 I2c403d2cd78453cc8afcda374969dac87a6becf6 SnapdragonCamera: fix invoke camera failture caused by p
1050663 Id5ea22a837cd658c3278160a21c74b519f1bc212 SnapdragonCamera: Shutter sound menu
1019192 Ifd8720b0a01273f402847ed4f6681678a366e13f SnapdragonCamera: Expose the Video time lapse option
1047161 I2ee7033d7854bb15fb9eabe215aaf063b440aee4 SnapdragonCamera: Add selfie flash to camera2
Change-Id: I8cfe4e829a9d2b74cf876eb7a6a4be6a048b6058
CRs-Fixed: 1045405, 1040876, 1050117, 1050106, 1019338, 1028467, 1043599, 1042288, 1019192, 1046143, 1045131, 1047161, 1047924, 1050099, 1050663
48 files changed, 1876 insertions, 143 deletions
diff --git a/Android.mk b/Android.mk index 5077a1c06..2f9723815 100644 --- a/Android.mk +++ b/Android.mk @@ -5,6 +5,7 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13 +LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 LOCAL_STATIC_JAVA_LIBRARIES += xmp_toolkit LOCAL_SRC_FILES := $(call all-java-files-under, src) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 37ce25f8b..6ec0df351 100755 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -54,18 +54,6 @@ android:taskAffinity="com.android.camera.CameraActivity" android:theme="@style/Theme.Camera" android:windowSoftInputMode="stateAlwaysHidden|adjustPan" > - <intent-filter> - <action android:name="android.media.action.IMAGE_CAPTURE" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - <intent-filter> - <action android:name="android.media.action.STILL_IMAGE_CAMERA" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.DEFAULT" /> - </intent-filter> <meta-data android:name="com.android.keyguard.layout" @@ -95,13 +83,33 @@ </intent-filter> </activity-alias> + <activity-alias + android:name="com.android.camera.PhotoCamera" + android:icon="@mipmap/ic_launcher_camera" + android:label="@string/snapcam_app_name" + android:launchMode="singleTop" + android:targetActivity="com.android.camera.PermissionsActivity"> + <intent-filter> + <action android:name="android.media.action.IMAGE_CAPTURE" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <intent-filter> + <action android:name="android.media.action.STILL_IMAGE_CAMERA" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity-alias> + <!-- Video camera and capture use the Camcorder label and icon. --> <activity-alias android:name="com.android.camera.VideoCamera" android:icon="@mipmap/ic_launcher_video_camera" android:label="@string/video_camera_label" android:launchMode="singleTop" - android:targetActivity="com.android.camera.CameraActivity"> + android:targetActivity="com.android.camera.PermissionsActivity"> <intent-filter> <action android:name="android.media.action.VIDEO_CAMERA" /> <category android:name="android.intent.category.DEFAULT" /> @@ -164,6 +172,11 @@ android:configChanges="keyboardHidden|orientation|screenSize"> </activity> + <activity + android:name="com.android.camera.BestpictureActivity" + android:configChanges="keyboardHidden|orientation|screenSize"> + </activity> + <receiver android:name="com.android.camera.DisableCameraReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> diff --git a/assets/dependency.json b/assets/dependency.json index 390adcde4..39f9f91b0 100644 --- a/assets/dependency.json +++ b/assets/dependency.json @@ -53,5 +53,12 @@ "off":{}, "on": {"pref_camera2_mpo_key":"on"} + }, + "pref_camera2_video_time_lapse_frame_interval_key": + { + "default": + {"pref_camera2_hfr_key":"off"} + , + "0":{} } } diff --git a/jni/image_util_jni.cpp b/jni/image_util_jni.cpp index 2297f9164..a4779b3d7 100644 --- a/jni/image_util_jni.cpp +++ b/jni/image_util_jni.cpp @@ -44,6 +44,8 @@ JNIEXPORT jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nat jint imageWidth, jint imageHeight, jint degree, jobjectArray outBuf); JNIEXPORT jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeNV21toRgb( JNIEnv *env, jobject thiz, jobjectArray yvuBuf, jobjectArray rgbBuf, jint width, jint height); +JNIEXPORT jint JNICALL Java_com_android_camera_imageprocessor_PostProcessor_nativeFlipVerticalNV21( + JNIEnv* env, jobject thiz, jbyteArray yvuBytes, jint width, jint height); #ifdef __cplusplus } #endif @@ -150,4 +152,32 @@ jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeNV21toR } } return 0; +} + +jint JNICALL Java_com_android_camera_imageprocessor_PostProcessor_nativeFlipVerticalNV21( + JNIEnv* env, jobject thiz, jbyteArray yvuBytes, jint width, jint height) +{ + jbyte* imageDataNV21Array = env->GetByteArrayElements(yvuBytes, NULL); + uint8_t *buf = (uint8_t *)imageDataNV21Array; + int ysize = width * height; + uint8_t temp1, temp2; + for(int x=0; x < width; x++) { + for(int y=0; y < height/2; y++) { + temp1 = buf[y*width + x]; + buf[y*width + x] = buf[(height-1-y)*width + x]; + buf[(height-1-y)*width + x] = temp1; + } + } + for(int x=0; x < width; x+=2) { + for(int y=0; y < height/4; y++) { + temp1 = buf[ysize + y*width + x]; + temp2 = buf[ysize + y*width + x + 1]; + buf[ysize + y*width + x] = buf[ysize + (height/2-1-y)*width + x]; + buf[ysize + y*width + x + 1] = buf[ysize + (height/2-1-y)*width + x + 1]; + buf[ysize + (height/2-1-y)*width + x] = temp1; + buf[ysize + (height/2-1-y)*width + x + 1] = temp2; + } + } + env->ReleaseByteArrayElements(yvuBytes, imageDataNV21Array, JNI_ABORT); + return 0; }
\ No newline at end of file diff --git a/res/drawable-hdpi/ic_settings_selfiemirror.png b/res/drawable-hdpi/ic_settings_selfiemirror.png Binary files differnew file mode 100644 index 000000000..c6d0f8be7 --- /dev/null +++ b/res/drawable-hdpi/ic_settings_selfiemirror.png diff --git a/res/drawable-hdpi/ic_settings_shuttersound.png b/res/drawable-hdpi/ic_settings_shuttersound.png Binary files differnew file mode 100644 index 000000000..ba1e42321 --- /dev/null +++ b/res/drawable-hdpi/ic_settings_shuttersound.png diff --git a/res/drawable-hdpi/pick_the_best_photo.png b/res/drawable-hdpi/pick_the_best_photo.png Binary files differnew file mode 100644 index 000000000..f0b892136 --- /dev/null +++ b/res/drawable-hdpi/pick_the_best_photo.png diff --git a/res/drawable-hdpi/pick_the_best_photo_selected.png b/res/drawable-hdpi/pick_the_best_photo_selected.png Binary files differnew file mode 100644 index 000000000..d2b5f1508 --- /dev/null +++ b/res/drawable-hdpi/pick_the_best_photo_selected.png diff --git a/res/drawable-hdpi/pick_the_best_photo_unselected.png b/res/drawable-hdpi/pick_the_best_photo_unselected.png Binary files differnew file mode 100644 index 000000000..13bdec3e3 --- /dev/null +++ b/res/drawable-hdpi/pick_the_best_photo_unselected.png diff --git a/res/drawable-xhdpi/ic_settings_selfiemirror.png b/res/drawable-xhdpi/ic_settings_selfiemirror.png Binary files differnew file mode 100644 index 000000000..975448e94 --- /dev/null +++ b/res/drawable-xhdpi/ic_settings_selfiemirror.png diff --git a/res/drawable-xhdpi/pick_the_best_photo.png b/res/drawable-xhdpi/pick_the_best_photo.png Binary files differnew file mode 100644 index 000000000..629af4a09 --- /dev/null +++ b/res/drawable-xhdpi/pick_the_best_photo.png diff --git a/res/drawable-xhdpi/pick_the_best_photo_selected.png b/res/drawable-xhdpi/pick_the_best_photo_selected.png Binary files differnew file mode 100644 index 000000000..92f11cb17 --- /dev/null +++ b/res/drawable-xhdpi/pick_the_best_photo_selected.png diff --git a/res/drawable-xhdpi/pick_the_best_photo_unselected.png b/res/drawable-xhdpi/pick_the_best_photo_unselected.png Binary files differnew file mode 100644 index 000000000..a558c7a1f --- /dev/null +++ b/res/drawable-xhdpi/pick_the_best_photo_unselected.png diff --git a/res/drawable-xxhdpi/ic_settings_selfiemirror.png b/res/drawable-xxhdpi/ic_settings_selfiemirror.png Binary files differnew file mode 100644 index 000000000..b49b6fa4a --- /dev/null +++ b/res/drawable-xxhdpi/ic_settings_selfiemirror.png diff --git a/res/drawable-xxhdpi/pick_the_best_photo.png b/res/drawable-xxhdpi/pick_the_best_photo.png Binary files differnew file mode 100644 index 000000000..2374b1848 --- /dev/null +++ b/res/drawable-xxhdpi/pick_the_best_photo.png diff --git a/res/drawable-xxhdpi/pick_the_best_photo_selected.png b/res/drawable-xxhdpi/pick_the_best_photo_selected.png Binary files differnew file mode 100644 index 000000000..522e9f2ba --- /dev/null +++ b/res/drawable-xxhdpi/pick_the_best_photo_selected.png diff --git a/res/drawable-xxhdpi/pick_the_best_photo_unselected.png b/res/drawable-xxhdpi/pick_the_best_photo_unselected.png Binary files differnew file mode 100644 index 000000000..98e18b07e --- /dev/null +++ b/res/drawable-xxhdpi/pick_the_best_photo_unselected.png diff --git a/res/layout/bestpicture_editor.xml b/res/layout/bestpicture_editor.xml new file mode 100644 index 000000000..6c35c27a1 --- /dev/null +++ b/res/layout/bestpicture_editor.xml @@ -0,0 +1,69 @@ +<?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. +--> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <android.support.v4.view.ViewPager + android:id="@+id/bestpicture_pager" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_gravity="center" /> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="50dp" + android:background="#b2191919"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentRight="true"> + <TextView + android:id="@+id/bestpicture_done" + android:text="@string/bestpicture_done" + android:textColor="#ffffff" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center"/> + </LinearLayout> + </RelativeLayout> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="50dp" + android:layout_gravity="bottom" + android:background="#b2191919"> + <com.android.camera.ui.DotsView + android:id="@+id/dots_view" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_centerInParent="true" /> + </RelativeLayout> +</FrameLayout> diff --git a/res/layout/bestpicture_page.xml b/res/layout/bestpicture_page.xml new file mode 100644 index 000000000..d6b6022c8 --- /dev/null +++ b/res/layout/bestpicture_page.xml @@ -0,0 +1,58 @@ +<?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. +--> +<FrameLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <ImageView + android:id="@+id/image_view" + android:adjustViewBounds="true" + android:scaleType="fitXY" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" /> + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="match_parent" + android:id="@+id/click_view"> + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="150dp" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true"> + <ImageView + android:id="@+id/picture_select" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="top" + android:layout_marginRight="20dp"/> + </LinearLayout> + </RelativeLayout> +</FrameLayout> diff --git a/res/layout/capture_module.xml b/res/layout/capture_module.xml index dd7d3cb1b..41757c535 100644 --- a/res/layout/capture_module.xml +++ b/res/layout/capture_module.xml @@ -27,7 +27,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_gravity="center"> - + <include layout="@layout/selfie_flash_view" /> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml index 37a33ec17..1f590fc32 100644 --- a/res/values/camera2arrays.xml +++ b/res/values/camera2arrays.xml @@ -133,6 +133,7 @@ <item>8</item> <item>9</item> <item>-1</item> + <item>103</item> </string-array> <!-- Camera Preferences Scene Mode dialog box entries --> @@ -153,6 +154,7 @@ <item>@string/pref_camera_scenemode_entry_beach</item> <item>@string/pref_camera_scenemode_entry_snow</item> <item>@string/pref_camera_scenemode_entry_asd</item> + <item>@string/pref_camera_scenemode_entry_bestpicture</item> </string-array> <array name="pref_camera2_scenemode_thumbnails" translatable="false"> @@ -172,6 +174,7 @@ <item>@drawable/ic_scene_mode_beach</item> <item>@drawable/ic_scene_mode_snow</item> <item>@drawable/ic_scene_mode_smartauto</item> + <item>@drawable/pick_the_best_photo</item> </array> <string-array name="pref_camera2_whitebalance_entryvalues" translatable="false"> @@ -826,4 +829,14 @@ for time lapse recording --> <item>@string/pref_camera2_videosnap_value_enable</item> <item>@string/pref_camera2_videosnap_value_disable</item> </string-array> + + <string-array name="pref_camera2_shutter_sound_entries" translatable="true"> + <item>@string/pref_camera2_shutter_sound_entry_on</item> + <item>@string/pref_camera2_shutter_sound_entry_off</item> + </string-array> + + <string-array name="pref_camera2_shutter_sound_entryvalues" translatable="false"> + <item>@string/pref_camera2_shutter_sound_value_on</item> + <item>@string/pref_camera2_shutter_sound_value_off</item> + </string-array> </resources> diff --git a/res/values/qcomarrays.xml b/res/values/qcomarrays.xml index 7574992b9..5a7f7a309 100644 --- a/res/values/qcomarrays.xml +++ b/res/values/qcomarrays.xml @@ -587,6 +587,25 @@ <item>disable</item> <item>enable</item> </string-array> + + <string-array name="pref_camera_selfiemirror_entries" translatable="false"> + <item>@string/pref_camera_selfiemirror_entry_disable</item> + <item>@string/pref_camera_selfiemirror_entry_enable</item> + </string-array> + <string-array name="pref_camera_selfiemirror_entryvalues" translatable="false"> + <item>disable</item> + <item>enable</item> + </string-array> + + <string-array name="pref_camera_shuttersound_entries" translatable="false"> + <item>@string/pref_camera_shuttersound_entry_disable</item> + <item>@string/pref_camera_shuttersound_entry_enable</item> + </string-array> + <string-array name="pref_camera_shuttersound_entryvalues" translatable="false"> + <item>disable</item> + <item>enable</item> + </string-array> + <!-- Camera Preferences Selectable Zone AF dialog box entries --> <string-array name="pref_camera_selectablezoneaf_entries" translatable="false"> <item>@string/pref_camera_selectablezoneaf_entry_auto</item> diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml index 0fb750590..330cb8979 100644 --- a/res/values/qcomstrings.xml +++ b/res/values/qcomstrings.xml @@ -322,6 +322,17 @@ <!-- Settings menu, redeye reduction choices --> <string name="pref_camera_redeyereduction_entry_enable">Enable</string> <string name="pref_camera_redeyereduction_entry_disable">Disable</string> + + <string name="pref_camera_selfiemirror_default" translatable="false">disable</string> + <string name="pref_camera_selfiemirror_title" translatable="true">Selfie Mirror</string> + <string name="pref_camera_selfiemirror_entry_enable">Enable</string> + <string name="pref_camera_selfiemirror_entry_disable">Disable</string> + + <string name="pref_camera_shuttersound_default" translatable="false">enable</string> + <string name="pref_camera_shuttersound_title" translatable="true">Shutter Sound</string> + <string name="pref_camera_shuttersound_entry_enable">Enable</string> + <string name="pref_camera_shuttersound_entry_disable">Disable</string> + <!-- Default mce setting. Do not translate. --> <string name="pref_camera_mce_default">enable</string> @@ -1012,5 +1023,16 @@ <string name="pref_camera2_videosnap_entry_enable" translatable="true">Enable</string> <string name="pref_camera2_videosnap_entry_disable" translatable="true">Disable</string> <string name="pref_camera2_trackingfocus_title" translatable="true">Tracking Focus</string> + <string name="pref_camera_scenemode_entry_bestpicture" translatable="true">BestPicture</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> + + <string name="pref_camera2_shutter_sound_default" translatable="true">on</string> + <string name="pref_camera2_shutter_sound_value_on" translatable="true">on</string> + <string name="pref_camera2_shutter_sound_value_off" translatable="true">off</string> + + <string name="pref_camera2_shutter_sound_entry_on" translatable="true">On</string> + <string name="pref_camera2_shutter_sound_entry_off" translatable="true">Off</string> + <string name="pref_camera2_shutter_sound_title" translatable="true">Shutter Sound</string> </resources> diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml index 7e024b298..742c09d33 100644 --- a/res/xml/camera_preferences.xml +++ b/res/xml/camera_preferences.xml @@ -285,6 +285,20 @@ camera:entries="@array/pref_camera_redeyereduction_entries" camera:singleIcon="@drawable/ic_settings_redeye" camera:entryValues="@array/pref_camera_redeyereduction_entryvalues" /> + <IconListPreference + camera:key="pref_camera_selfiemirror_key" + camera:defaultValue="@string/pref_camera_selfiemirror_default" + camera:title="@string/pref_camera_selfiemirror_title" + camera:entries="@array/pref_camera_selfiemirror_entries" + camera:singleIcon="@drawable/ic_settings_selfiemirror" + camera:entryValues="@array/pref_camera_selfiemirror_entryvalues" /> + <IconListPreference + camera:key="pref_camera_shuttersound_key" + camera:defaultValue="@string/pref_camera_shuttersound_default" + camera:title="@string/pref_camera_shuttersound_title" + camera:entries="@array/pref_camera_shuttersound_entries" + camera:singleIcon="@drawable/ic_settings_shuttersound" + camera:entryValues="@array/pref_camera_shuttersound_entryvalues" /> <ListPreference camera:key="pref_camera_selectablezoneaf_key" camera:defaultValue="@string/pref_camera_selectablezoneaf_default" diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml index 2f0849442..dae2c5f7d 100644 --- a/res/xml/capture_preferences.xml +++ b/res/xml/capture_preferences.xml @@ -280,10 +280,34 @@ camera:entryValues="@array/pref_camera2_videosnap_entryvalues" camera:key="pref_camera2_videosnap_key" camera:title="@string/pref_camera2_videosnap_title" /> + <ListPreference camera:defaultValue="off" camera:key="pref_camera2_trackingfocus_key" camera:entries="@array/pref_camera2_trackingfocus_entries" camera:entryValues="@array/pref_camera2_trackingfocus_entryvalues" camera:title="@string/pref_camera2_trackingfocus_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_hfr_default" + camera:entries="@array/pref_camera_hfr_entries" + camera:entryValues="@array/pref_camera_hfr_entryvalues" + camera:key="pref_camera2_hfr_key" + camera:singleIcon="@drawable/ic_settings_fps" + camera:title="@string/pref_camera_hfr_title" /> + + <IconListPreference + camera:defaultValue="@string/pref_selfie_flash_default" + camera:entries="@array/pref_selfie_flash_entries" + camera:entryValues="@array/pref_selfie_flash_entryvalues" + camera:key="pref_selfie_flash_key" + camera:singleIcon="@drawable/ic_settings_flash" + camera:title="@string/pref_selfie_flash_title" /> + + <ListPreference + camera:defaultValue="@string/pref_camera2_shutter_sound_default" + camera:entries="@array/pref_camera2_shutter_sound_entries" + camera:entryValues="@array/pref_camera2_shutter_sound_entryvalues" + camera:key="pref_camera2_shutter_sound_key" + camera:title="@string/pref_camera2_shutter_sound_title" /> </PreferenceGroup> diff --git a/src/com/android/camera/BestpictureActivity.java b/src/com/android/camera/BestpictureActivity.java new file mode 100644 index 000000000..bb519e74f --- /dev/null +++ b/src/com/android/camera/BestpictureActivity.java @@ -0,0 +1,348 @@ +/* + * 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; + +import android.app.ProgressDialog; +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; +import android.graphics.Point; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.v13.app.FragmentStatePagerAdapter; +import android.support.v4.view.PagerAdapter; +import android.support.v4.view.ViewPager; +import android.util.Log; +import android.view.Display; +import android.view.Menu; +import android.view.View; +import android.support.v4.app.FragmentActivity; +import android.widget.Toast; + +import com.android.camera.exif.ExifInterface; +import com.android.camera.ui.DotsView; +import com.android.camera.ui.DotsViewItem; +import com.android.camera.ui.RotateTextToast; +import com.android.camera.util.CameraUtil; + +import org.codeaurora.snapcam.R; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.RandomAccessFile; + +public class BestpictureActivity extends FragmentActivity{ + private static final String TAG = "BestpictureActivity"; + public static final String[] NAMES = { + "00", "01", "02", "03", "04", "05", "06", "07", "08", "09" + }; + + public static final int NUM_IMAGES = 10; + + private ViewPager mImagePager; + private PagerAdapter mImagePagerAdapter; + private int mWidth; + private int mHeight; + private String mFilesPath; + private ProgressDialog mProgressDialog; + private BestpictureActivity mActivity; + private DotsView mDotsView; + private ImageItems mImageItems; + private ImageLoadingThread mLoadingThread; + private PhotoModule.NamedImages mNamedImages; + private Uri mPlaceHolderUri; + public static int BESTPICTURE_ACTIVITY_CODE = 11; + + static class ImageItems implements Parcelable, DotsViewItem { + private Bitmap[] mBitmap; + private boolean[] mChosen; + private BestpictureActivity mActivity; + + public ImageItems(BestpictureActivity activity) { + mBitmap = new Bitmap[NUM_IMAGES]; + mChosen = new boolean[NUM_IMAGES]; + for (int i = 0; i < mChosen.length; i++) { + if (i == 0) { + mChosen[i] = true; + } else { + mChosen[i] = false; + } + } + mActivity = activity; + } + + @Override + public int describeContents() { + return 0; + } + + public Bitmap getBitmap(int index) { + return mBitmap[index]; + } + + public void setBitmap(int index, Bitmap bitmap) { + mBitmap[index] = bitmap; + } + + @Override + public int getTotalItemNums() { + return NUM_IMAGES; + } + + public boolean isChosen(int index) { + return mChosen[index]; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + } + + public void toggleImageSelection(int num) { + mChosen[num] = !mChosen[num]; + boolean isChosen = false; + for(int i=0; i < mChosen.length; i++) { + isChosen |= mChosen[i]; + } + if(!isChosen) { + mChosen[num] = true; + RotateTextToast.makeText(mActivity, + mActivity.getResources().getString(R.string.bestpicture_at_least_one_picture), + Toast.LENGTH_SHORT).show(); + } + mActivity.mDotsView.update(); + } + } + + @Override + public void onCreate(Bundle state) { + super.onCreate(state); + mActivity = this; + mFilesPath = getFilesDir()+"/Bestpicture"; + setContentView(R.layout.bestpicture_editor); + Display display = getWindowManager().getDefaultDisplay(); + Point size = new Point(); + display.getSize(size); + mWidth = size.x/2; + mHeight = size.y/2; + mNamedImages = new PhotoModule.NamedImages(); + + mImageItems = new ImageItems(mActivity); + mDotsView = (DotsView) findViewById(R.id.dots_view); + mDotsView.setItems(mImageItems); + mPlaceHolderUri = getIntent().getData(); + + mImagePager = (ViewPager) findViewById(R.id.bestpicture_pager); + mImagePagerAdapter = new ImagePagerAdapter(getFragmentManager()); + mImagePager.setAdapter(mImagePagerAdapter); + mImagePager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + mDotsView.update(position, positionOffset); + } + + @Override + public void onPageSelected(int position) { + } + }); + findViewById(R.id.bestpicture_done).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View v) { + int index = -1; + for(int i=0; i < mImageItems.mChosen.length; i++) { + if (mImageItems.mChosen[i]) { + if(index != -1) { + new SaveImageTask().execute(mFilesPath + "/" + NAMES[i] + ".jpg"); + } else { + index = i; + saveForground(mFilesPath + "/" + NAMES[i] + ".jpg"); + } + } + } + setResult(RESULT_OK, new Intent()); + finish(); + } + }); + } + + private class ImageLoadingThread extends Thread { + public void run() { + showProgressDialog(); + for(int i=0; i < NUM_IMAGES; i++) { + String path = mFilesPath + "/" + BestpictureActivity.NAMES[i] + ".jpg"; + final BitmapFactory.Options o = new BitmapFactory.Options(); + o.inJustDecodeBounds = true; + BitmapFactory.decodeFile(path, o); + ExifInterface exif = new ExifInterface(); + int orientation = 0; + try { + exif.readExif(path); + orientation = Exif.getOrientation(exif); + } catch (IOException e) { + } + int h = o.outHeight; + int w = o.outWidth; + int sample = 1; + if (h > mHeight || w > mWidth) { + while (h / sample / 2 > mHeight && w / sample / 2 > mWidth) { + sample *= 2; + } + } + + o.inJustDecodeBounds = false; + o.inSampleSize = sample; + Bitmap bitmap = BitmapFactory.decodeFile(path, o); + if (orientation != 0) { + Matrix matrix = new Matrix(); + matrix.setRotate(orientation); + bitmap = Bitmap.createBitmap(bitmap, 0, 0, + bitmap.getWidth(), bitmap.getHeight(), matrix, false); + } + mImageItems.setBitmap(i, bitmap); + } + dismissProgressDialog(); + } + } + + @Override + public void onResume() { + super.onResume(); + if (mLoadingThread == null) { + mLoadingThread = new ImageLoadingThread(); + mLoadingThread.start(); + } + } + + @Override + public void onPause() { + super.onPause(); + } + + private void showProgressDialog() { + mActivity.runOnUiThread(new Runnable() { + public void run() { + mProgressDialog = ProgressDialog.show(mActivity, "", "Processing...", true, false); + mProgressDialog.show(); + } + }); + } + + private void dismissProgressDialog() { + mActivity.runOnUiThread(new Runnable() { + public void run() { + if (mProgressDialog != null && mProgressDialog.isShowing()) { + mProgressDialog.dismiss(); + mProgressDialog = null; + } + } + }); + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + return true; + } + + private class ImagePagerAdapter extends FragmentStatePagerAdapter { + public ImagePagerAdapter(android.app.FragmentManager manager) { + super(manager); + } + + @Override + public android.app.Fragment getItem(int imageNum) { + while(mImageItems.getBitmap(imageNum) == null) { + try { + Thread.sleep(5); + } catch (Exception e) { + } + } + return BestpictureFragment.create(imageNum, mImageItems); + } + + @Override + public int getCount() { + return NUM_IMAGES; + } + } + + private void saveForground(String path) { + long captureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(captureStartTime); + PhotoModule.NamedImages.NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + String outPath = mPlaceHolderUri.getPath(); + try { + FileOutputStream out = new FileOutputStream(outPath); + FileInputStream in = new FileInputStream(path); + byte[] buf = new byte[4096]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + out.close(); + } catch (Exception e) { + } + } + + private class SaveImageTask extends AsyncTask<String, Void, Void> { + protected Void doInBackground(String... path) { + long captureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(captureStartTime); + PhotoModule.NamedImages.NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + String outPath = Storage.generateFilepath(title, "jpeg"); + try { + FileOutputStream out = new FileOutputStream(outPath); + FileInputStream in = new FileInputStream(path[0]); + byte[] buf = new byte[4096]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + in.close(); + out.close(); + } catch (Exception e) { + } + Uri uri = Uri.fromFile(new File(outPath)); + Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri); + sendBroadcast(intent); + return null; + } + + protected void onPostExecute(Void v) { + } + } +} diff --git a/src/com/android/camera/BestpictureFragment.java b/src/com/android/camera/BestpictureFragment.java new file mode 100644 index 000000000..6dc4ebd59 --- /dev/null +++ b/src/com/android/camera/BestpictureFragment.java @@ -0,0 +1,93 @@ +/* + * 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; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import org.codeaurora.snapcam.R; + +public class BestpictureFragment extends Fragment { + public static final String PARAM_IMAGE_NUM = "image_num"; + private static final String TAG = "BestpictureFilter"; + private int mImageNum; + private ImageView mImageView; + private ImageView mPictureSelectButton; + private BestpictureActivity.ImageItems mImageItems; + + public static BestpictureFragment create(int imageNum, BestpictureActivity.ImageItems items) { + BestpictureFragment fragment = new BestpictureFragment(); + Bundle args = new Bundle(); + args.putInt(PARAM_IMAGE_NUM, imageNum); + args.putParcelable("imageItems", items); + fragment.setArguments(args); + return fragment; + } + + public BestpictureFragment() { + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mImageNum = getArguments().getInt(PARAM_IMAGE_NUM); + mImageItems = getArguments().getParcelable("imageItems"); + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + ViewGroup rootView = (ViewGroup) inflater + .inflate(R.layout.bestpicture_page, container, false); + mImageView = (ImageView) rootView.findViewById(R.id.image_view); + mPictureSelectButton = (ImageView) rootView.findViewById(R.id.picture_select); + initSelectButton(); + mImageView.setImageBitmap(mImageItems.getBitmap(mImageNum)); + rootView.findViewById(R.id.picture_select).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(final View v) { + mImageItems.toggleImageSelection(mImageNum); + initSelectButton(); + } + }); + return rootView; + } + + private void initSelectButton() { + if(mImageItems.isChosen(mImageNum)) { + mPictureSelectButton.setBackground(getResources().getDrawable(R.drawable.pick_the_best_photo_selected, null)); + } else { + mPictureSelectButton.setBackground(getResources().getDrawable(R.drawable.pick_the_best_photo_unselected, null)); + } + } +}
\ No newline at end of file diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 5666331fa..e5c24fff8 100755 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -16,6 +16,8 @@ package com.android.camera; +import android.os.Parcel; +import android.os.Parcelable; import android.view.Display; import android.graphics.Point; import android.Manifest; @@ -67,7 +69,6 @@ import android.os.PowerManager.WakeLock; import android.os.SystemProperties; import android.preference.PreferenceManager; import android.provider.MediaStore; -import android.provider.Settings; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -224,6 +225,7 @@ public class CameraActivity extends Activity private FrameLayout mPreviewContentLayout; private boolean mPaused = true; private boolean mHasCriticalPermissions; + private boolean mForceReleaseCamera = false; private Uri[] mNfcPushUris = new Uri[1]; @@ -1642,6 +1644,13 @@ public class CameraActivity extends Activity if(resultCode == RESULT_OK) { mCaptureModule.setRefocusLastTaken(false); } + } else if (requestCode == BestpictureActivity.BESTPICTURE_ACTIVITY_CODE) { + if(resultCode == RESULT_OK) { + byte[] jpeg = data.getByteArrayExtra("thumbnail"); + if(jpeg != null) { + updateThumbnail(jpeg); + } + } } else { super.onActivityResult(requestCode, resultCode, data); } @@ -1672,8 +1681,7 @@ public class CameraActivity extends Activity } else { mHasCriticalPermissions = false; } - if ((checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) || - !mHasCriticalPermissions) { + if (!mHasCriticalPermissions) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); boolean isRequestShown = prefs.getBoolean(CameraSettings.KEY_REQUEST_PERMISSION, false); if(!isRequestShown || !mHasCriticalPermissions) { @@ -1903,9 +1911,9 @@ public class CameraActivity extends Activity switch (requestCode) { case PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION: { // If request is cancelled, the result arrays are empty. - mCurrentModule.waitingLocationPermissionResult(false); + mCurrentModule.waitingLocationPermissionResult(false); if (grantResults.length > 0 - && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Log.v(TAG, "Location permission is granted"); mCurrentModule.enableRecordingLocation(true); } else { @@ -1917,11 +1925,17 @@ public class CameraActivity extends Activity } } + public boolean isForceReleaseCamera() { + return mForceReleaseCamera; + } + @Override public void onModuleSelected(int moduleIndex) { boolean cam2on = SettingsManager.getInstance().isCamera2On(); - if (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX) + mForceReleaseCamera = cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX; + if (mForceReleaseCamera) { moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; + } if (mCurrentModuleIndex == moduleIndex) { if (mCurrentModuleIndex != ModuleSwitcher.CAPTURE_MODULE_INDEX) { return; @@ -1932,6 +1946,7 @@ public class CameraActivity extends Activity setModuleFromIndex(moduleIndex); openModule(mCurrentModule); + mForceReleaseCamera = false; mCurrentModule.onOrientationChanged(mLastRawOrientation); if (mMediaSaveService != null) { mCurrentModule.onMediaSaveServiceConnected(mMediaSaveService); diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index f700d2187..b09d147bf 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -100,6 +100,8 @@ public class CameraSettings { public static final String KEY_DENOISE = "pref_camera_denoise_key"; public static final String KEY_BRIGHTNESS = "pref_camera_brightness_key"; public static final String KEY_REDEYE_REDUCTION = "pref_camera_redeyereduction_key"; + public static final String KEY_SELFIE_MIRROR = "pref_camera_selfiemirror_key"; + public static final String KEY_SHUTTER_SOUND = "pref_camera_shuttersound_key"; public static final String KEY_CDS_MODE = "pref_camera_cds_mode_key"; public static final String KEY_VIDEO_CDS_MODE = "pref_camera_video_cds_mode_key"; public static final String KEY_TNR_MODE = "pref_camera_tnr_mode_key"; diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 4edf3ab9e..1a7811000 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -35,6 +35,7 @@ import android.graphics.RectF; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraManager; import android.hardware.camera2.CameraMetadata; @@ -51,6 +52,7 @@ import android.media.CamcorderProfile; import android.media.CameraProfile; import android.media.Image; import android.media.ImageReader; +import android.media.MediaActionSound; import android.media.MediaMetadataRetriever; import android.media.MediaRecorder; import android.net.Uri; @@ -62,13 +64,12 @@ import android.os.Message; import android.os.SystemClock; import android.provider.MediaStore; import android.util.Log; +import android.util.Range; import android.util.Size; -import android.view.Display; import android.view.KeyEvent; import android.view.OrientationEventListener; import android.view.Surface; import android.view.SurfaceHolder; -import android.view.SurfaceView; import android.view.View; import android.widget.Toast; @@ -80,6 +81,7 @@ import com.android.camera.imageprocessor.FrameProcessor; import com.android.camera.PhotoModule.NamedImages; import com.android.camera.PhotoModule.NamedImages.NamedEntity; import com.android.camera.imageprocessor.filter.SharpshooterFilter; +import com.android.camera.imageprocessor.filter.StillmoreFilter; import com.android.camera.ui.CountDownView; import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.RotateTextToast; @@ -157,8 +159,6 @@ public class CaptureModule implements CameraModule, PhotoController, // we can change it based on memory status or other requirements. private static final int LONGSHOT_CANCEL_THRESHOLD = 40 * 1024 * 1024; - private static final int MAX_IMAGE_NUM = 8; - MeteringRectangle[][] mAFRegions = new MeteringRectangle[MAX_NUM_CAM][]; MeteringRectangle[][] mAERegions = new MeteringRectangle[MAX_NUM_CAM][]; CaptureRequest.Key<Byte> BayerMonoLinkEnableKey = @@ -251,6 +251,7 @@ public class CaptureModule implements CameraModule, PhotoController, private CameraCaptureSession mCurrentSession; private Size mPreviewSize; private Size mPictureSize; + private Size mVideoPreviewSize; private Size mVideoSize; private Size mVideoSnapshotSize; @@ -271,6 +272,30 @@ public class CaptureModule implements CameraModule, PhotoController, private long mRecordingTotalTime; private boolean mRecordingTimeCountsDown = false; private ImageReader mVideoSnapshotImageReader; + private Range mHighSpeedFPSRange; + private boolean mHighSpeedCapture = false; + private boolean mHighSpeedCaptureSlowMode = false; //HFR + private int mHighSpeedCaptureRate; + + private static final int SELFIE_FLASH_DURATION = 680; + + private MediaActionSound mSound; + + private class SelfieThread extends Thread { + public void run() { + try { + Thread.sleep(SELFIE_FLASH_DURATION); + mActivity.runOnUiThread(new Runnable() { + public void run() { + takePicture(); + } + }); + } catch(InterruptedException e) { + } + selfieThread = null; + } + } + private SelfieThread selfieThread; private class MediaSaveNotifyThread extends Thread { private Uri uri; @@ -1039,6 +1064,7 @@ public class CaptureModule implements CameraModule, PhotoController, warningToast("Camera is not ready yet to take a picture."); return; } + checkAndPlayShutterSound(id); final boolean csEnabled = isClearSightOn(); CaptureRequest.Builder captureBuilder; @@ -1165,6 +1191,7 @@ public class CaptureModule implements CameraModule, PhotoController, warningToast("Camera is not ready yet to take a video snapshot."); return; } + checkAndPlayShutterSound(id); CaptureRequest.Builder captureBuilder = mCameraDevice[id].createCaptureRequest(CameraDevice.TEMPLATE_VIDEO_SNAPSHOT); @@ -1265,7 +1292,7 @@ public class CaptureModule implements CameraModule, PhotoController, } else { // No Clearsight mImageReader[i] = ImageReader.newInstance(mPictureSize.getWidth(), - mPictureSize.getHeight(), imageFormat, MAX_IMAGE_NUM); + mPictureSize.getHeight(), imageFormat, PostProcessor.MAX_REQUIRED_IMAGE_NUM); if((mPostProcessor.isFilterOn() || getFrameFilters().size() != 0) && i == getMainCameraId()) { mImageReader[i].setOnImageAvailableListener(mPostProcessor, mImageAvailableHandler); @@ -1315,7 +1342,7 @@ public class CaptureModule implements CameraModule, PhotoController, mVideoSnapshotImageReader.close(); } mVideoSnapshotImageReader = ImageReader.newInstance(mVideoSnapshotSize.getWidth(), - mVideoSnapshotSize.getHeight(), ImageFormat.JPEG, MAX_IMAGE_NUM); + mVideoSnapshotSize.getHeight(), ImageFormat.JPEG, mPostProcessor.MAX_REQUIRED_IMAGE_NUM); mVideoSnapshotImageReader.setOnImageAvailableListener( new ImageReader.OnImageAvailableListener() { @Override @@ -1371,6 +1398,7 @@ public class CaptureModule implements CameraModule, PhotoController, mActivity.runOnUiThread(new Runnable() { @Override public void run() { + mUI.stopSelfieFlash(); mUI.enableShutter(true); } }); @@ -1600,6 +1628,14 @@ public class CaptureModule implements CameraModule, PhotoController, if (mIsRecordingVideo) { stopRecordingVideo(getMainCameraId()); } + if (mSound != null) { + mSound.release(); + mSound = null; + } + if (selfieThread != null) { + selfieThread.interrupt(); + } + mUI.stopSelfieFlash(); } @Override @@ -1664,6 +1700,10 @@ public class CaptureModule implements CameraModule, PhotoController, return PostProcessor.FILTER_SHARPSHOOTER; } else if (mode == SettingsManager.SCENE_MODE_UBIFOCUS_INT) { return PostProcessor.FILTER_UBIFOCUS; + } else if (mode == SettingsManager.SCENE_MODE_AUTO_INT && StillmoreFilter.isSupportedStatic()) { + return PostProcessor.FILTER_STILLMORE; + } else if (mode == SettingsManager.SCENE_MODE_BESTPICTURE_INT) { + return PostProcessor.FILTER_BESTPICTURE; } return PostProcessor.FILTER_NONE; } @@ -1677,14 +1717,57 @@ public class CaptureModule implements CameraModule, PhotoController, updateMaxVideoDuration(); } + private void updatePreviewSize() { + int preview_resolution = PersistUtil.getCameraPreviewSize(); + int width = mPreviewSize.getWidth(); + int height = mPreviewSize.getHeight(); + switch (preview_resolution) { + case 1: { + width = 640; + height = 480; + Log.v(TAG, "Preview resolution hardcoded to 640x480"); + break; + } + case 2: { + width = 720; + height = 480; + Log.v(TAG, "Preview resolution hardcoded to 720x480"); + break; + } + case 3: { + width = 1280; + height = 720; + Log.v(TAG, "Preview resolution hardcoded to 1280x720"); + break; + } + case 4: { + width = 1920; + height = 1080; + Log.v(TAG, "Preview resolution hardcoded to 1920x1080"); + break; + } + default: { + Log.v(TAG, "Preview resolution as per Snapshot aspect ratio"); + break; + } + } + mPreviewSize = new Size(width, height); + mUI.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); + } + @Override public void onResumeAfterSuper() { Log.d(TAG, "onResume " + getCameraMode()); initializeValues(); - mUI.setPreviewSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());; + updatePreviewSize(); mUI.showSurfaceView(); mUI.setSwitcherIndex(); mCameraIdList = new ArrayList<>(); + + if (mSound == null) { + mSound = new MediaActionSound(); + } + if(mPostProcessor != null) { String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); if (scene != null) { @@ -1937,10 +2020,27 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void checkSelfieFlashAndTakePicture() { + String value = mSettingsManager.getValue(SettingsManager.KEY_SELFIE_FLASH); + if (value == null) { + takePicture(); + return; + } + if (value.equals("on") && getMainCameraId() == FRONT_ID) { + mUI.startSelfieFlash(); + if (selfieThread == null) { + selfieThread = new SelfieThread(); + selfieThread.start(); + } + } else { + takePicture(); + } + } + @Override public void onCountDownFinished() { mUI.showUIAfterCountDown(); - takePicture(); + checkSelfieFlashAndTakePicture(); } @Override @@ -2094,6 +2194,11 @@ public class CaptureModule implements CameraModule, PhotoController, private void updateVideoSize() { String videoSize = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_QUALITY); mVideoSize = parsePictureSize(videoSize); + Point screenSize = new Point(); + mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize); + Size[] prevSizes = mSettingsManager.getSupportedOutputSize(getMainCameraId(), + MediaRecorder.class); + mVideoPreviewSize = getOptimalPreviewSize(mVideoSize, prevSizes, screenSize.x, screenSize.y); } private void updateVideoSnapshotSize() { @@ -2132,7 +2237,8 @@ public class CaptureModule implements CameraModule, PhotoController, mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; closePreviewSession(); mFrameProcessor.onClose(); - boolean changed = mUI.setPreviewSize(mVideoSize.getWidth(), mVideoSize.getHeight()); + boolean changed = mUI.setPreviewSize(mVideoPreviewSize.getWidth(), + mVideoPreviewSize.getHeight()); if (changed) { mUI.hideSurfaceView(); mUI.showSurfaceView(); @@ -2146,46 +2252,96 @@ public class CaptureModule implements CameraModule, PhotoController, List<Surface> surfaces = new ArrayList<>(); Surface surface = getPreviewSurfaceForSession(cameraId); - mFrameProcessor.init(mVideoSize); + if(mFrameProcessor.isFrameFilterEnabled()) { + mFrameProcessor.init(mVideoSize); mActivity.runOnUiThread(new Runnable() { public void run() { mUI.getSurfaceHolder().setFixedSize(mVideoSize.getHeight(), mVideoSize.getWidth()); } }); + mFrameProcessor.setOutputSurface(surface); + mFrameProcessor.setVideoOutputSurface(mMediaRecorder.getSurface()); + addPreviewSurface(mPreviewBuilder, surfaces, cameraId); + } else { + surfaces.add(surface); + mPreviewBuilder.addTarget(surface); + surfaces.add(mMediaRecorder.getSurface()); + mPreviewBuilder.addTarget(mMediaRecorder.getSurface()); } - mFrameProcessor.setOutputSurface(surface); - mFrameProcessor.setVideoOutputSurface(mMediaRecorder.getSurface()); - addPreviewSurface(mPreviewBuilder, surfaces, cameraId); - surfaces.add(mVideoSnapshotImageReader.getSurface()); - mCameraDevice[cameraId].createCaptureSession(surfaces, new CameraCaptureSession - .StateCallback() { + if (!mHighSpeedCapture) surfaces.add(mVideoSnapshotImageReader.getSurface()); + else mPreviewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, mHighSpeedFPSRange); - @Override - public void onConfigured(CameraCaptureSession cameraCaptureSession) { - Log.d(TAG, "StartRecordingVideo session onConfigured"); - mCurrentSession = cameraCaptureSession; - try { - setUpVideoCaptureRequestBuilder(mPreviewBuilder); - mCurrentSession.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler); - } catch (CameraAccessException e) { - e.printStackTrace(); + if (!mHighSpeedCapture) { + mCameraDevice[cameraId].createCaptureSession(surfaces, new CameraCaptureSession + .StateCallback() { + + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + Log.d(TAG, "StartRecordingVideo session onConfigured"); + mCurrentSession = cameraCaptureSession; + try { + setUpVideoCaptureRequestBuilder(mPreviewBuilder); + mCurrentSession.setRepeatingRequest(mPreviewBuilder.build(), null, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mMediaRecorder.start(); + mUI.clearFocus(); + mUI.resetPauseButton(); + mRecordingTotalTime = 0L; + mRecordingStartTime = SystemClock.uptimeMillis(); + mUI.showRecordingUI(true); + updateRecordingTime(); } - mMediaRecorder.start(); - mUI.clearFocus(); - mUI.resetPauseButton(); - mRecordingTotalTime = 0L; - mRecordingStartTime = SystemClock.uptimeMillis(); - mUI.showRecordingUI(true); - updateRecordingTime(); - } - @Override - public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { - Toast.makeText(mActivity, "Video Failed", Toast.LENGTH_SHORT).show(); - } - }, null); + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Video Failed", Toast.LENGTH_SHORT).show(); + } + }, null); + } else { + mCameraDevice[cameraId].createConstrainedHighSpeedCaptureSession(surfaces, new + CameraConstrainedHighSpeedCaptureSession.StateCallback() { + + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + mCurrentSession = cameraCaptureSession; + CameraConstrainedHighSpeedCaptureSession session = + (CameraConstrainedHighSpeedCaptureSession) mCurrentSession; + try { + List list = session + .createHighSpeedRequestList(mPreviewBuilder.build()); + session.setRepeatingBurst(list, null, mCameraHandler); + } catch (CameraAccessException e) { + Log.e(TAG, "Failed to start high speed video recording " + + e.getMessage()); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + Log.e(TAG, "Failed to start high speed video recording " + + e.getMessage()); + e.printStackTrace(); + } catch (IllegalStateException e) { + Log.e(TAG, "Failed to start high speed video recording " + + e.getMessage()); + e.printStackTrace(); + } + mMediaRecorder.start(); + mUI.clearFocus(); + mUI.resetPauseButton(); + mRecordingTotalTime = 0L; + mRecordingStartTime = SystemClock.uptimeMillis(); + mUI.showRecordingUI(true); + updateRecordingTime(); + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Failed", Toast.LENGTH_SHORT).show(); + } + }, null); + } } catch (CameraAccessException e) { e.printStackTrace(); } catch (IOException e) { @@ -2203,6 +2359,19 @@ public class CaptureModule implements CameraModule, PhotoController, mUI.showTimeLapseUI(mCaptureTimeLapse); } + private void updateHFRSetting() { + String value = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_HIGH_FRAME_RATE); + if (value == null) return; + if (value.equals("off")) { + mHighSpeedCapture = false; + } else { + mHighSpeedCapture = true; + String mode = value.substring(0, 3); + mHighSpeedCaptureSlowMode = mode.equals("hsr"); + mHighSpeedCaptureRate = Integer.parseInt(value.substring(3)); + } + } + private void setUpVideoCaptureRequestBuilder(CaptureRequest.Builder builder) { builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); builder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest @@ -2426,8 +2595,11 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "setUpMediaRecorder"); String videoSize = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_QUALITY); int size = CameraSettings.VIDEO_QUALITY_TABLE.get(videoSize); - if (mCaptureTimeLapse) + if (mCaptureTimeLapse) { size = CameraSettings.getTimeLapseQualityFor(size); + } + updateHFRSetting(); + boolean hfr = mHighSpeedCapture && !mHighSpeedCaptureSlowMode; mProfile = CamcorderProfile.get(cameraId, size); int videoEncoder = SettingTranslation @@ -2437,7 +2609,7 @@ public class CaptureModule implements CameraModule, PhotoController, int outputFormat = MediaRecorder.OutputFormat.MPEG_4; - if (!mCaptureTimeLapse) { + if (!mCaptureTimeLapse && !hfr) { mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); } mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); @@ -2454,7 +2626,7 @@ public class CaptureModule implements CameraModule, PhotoController, mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); } mMediaRecorder.setVideoEncoder(videoEncoder); - if (!mCaptureTimeLapse) { + if (!mCaptureTimeLapse && !hfr) { mMediaRecorder.setAudioEncodingBitRate(mProfile.audioBitRate); mMediaRecorder.setAudioChannels(mProfile.audioChannels); mMediaRecorder.setAudioSamplingRate(mProfile.audioSampleRate); @@ -2464,7 +2636,21 @@ public class CaptureModule implements CameraModule, PhotoController, if (mCaptureTimeLapse) { double fps = 1000 / (double) mTimeBetweenTimeLapseFrameCaptureMs; mMediaRecorder.setCaptureRate(fps); + } else if (mHighSpeedCapture) { + mHighSpeedFPSRange = new Range(mHighSpeedCaptureRate, mHighSpeedCaptureRate); + int fps = (int) mHighSpeedFPSRange.getUpper(); + mMediaRecorder.setCaptureRate(fps); + if (mHighSpeedCaptureSlowMode) { + mMediaRecorder.setVideoFrameRate(30); + } else { + mMediaRecorder.setVideoFrameRate(fps); + } + + int scaledBitrate = mProfile.videoBitRate * fps / mProfile.videoFrameRate; + Log.i(TAG, "Scaled Video bitrate : " + scaledBitrate); + mMediaRecorder.setVideoEncodingBitRate(scaledBitrate); } + Location loc = mLocationManager.getCurrentLocation(); if (loc != null) { mMediaRecorder.setLocation((float) loc.getLatitude(), @@ -2520,7 +2706,7 @@ public class CaptureModule implements CameraModule, PhotoController, warningToast("It's still busy processing previous scene mode request."); return; } - takePicture(); + checkSelfieFlashAndTakePicture(); } } } @@ -2839,6 +3025,15 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void checkAndPlayShutterSound(int id) { + if (id == getMainCameraId()) { + String value = mSettingsManager.getValue(SettingsManager.KEY_SHUTTER_SOUND); + if (value != null && value.equals("on")) { + mSound.play(MediaActionSound.SHUTTER_CLICK); + } + } + } + private Surface getPreviewSurfaceForSession(int id) { if (isBackCamera()) { if (getCameraMode() == DUAL_MODE && id == MONO_ID) { diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index dadbb3144..cf3ca6d3a 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -44,6 +44,7 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.ViewGroup; import android.view.ViewPropertyAnimator; +import android.view.Window; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; @@ -67,6 +68,7 @@ import com.android.camera.ui.RenderOverlay; import com.android.camera.ui.RotateImageView; import com.android.camera.ui.RotateLayout; import com.android.camera.ui.RotateTextToast; +import com.android.camera.ui.SelfieFlashView; import com.android.camera.ui.TrackingFocusRenderer; import com.android.camera.ui.ZoomRenderer; import com.android.camera.util.CameraUtil; @@ -100,6 +102,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private static final int ANIMATION_DURATION = 300; private static final int CLICK_THRESHOLD = 200; String[] mSettingKeys = new String[]{ + SettingsManager.KEY_SELFIE_FLASH, SettingsManager.KEY_FLASH_MODE, SettingsManager.KEY_RECORD_LOCATION, SettingsManager.KEY_PICTURE_SIZE, @@ -111,12 +114,15 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_WHITE_BALANCE, SettingsManager.KEY_CAMERA2, SettingsManager.KEY_FACE_DETECTION, + SettingsManager.KEY_VIDEO_HIGH_FRAME_RATE, SettingsManager.KEY_VIDEO_FLASH_MODE, SettingsManager.KEY_VIDEO_DURATION, SettingsManager.KEY_VIDEO_QUALITY, SettingsManager.KEY_TRACKINGFOCUS, - SettingsManager.KEY_MAKEUP - }; + SettingsManager.KEY_MAKEUP, + SettingsManager.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, + SettingsManager.KEY_SHUTTER_SOUND + }; String[] mDeveloperKeys = new String[]{ SettingsManager.KEY_REDEYE_REDUCTION, SettingsManager.KEY_MONO_ONLY, @@ -127,7 +133,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_DIS, SettingsManager.KEY_VIDEO_ENCODER, SettingsManager.KEY_AUDIO_ENCODER, - SettingsManager.KEY_VIDEO_TIME_LAPSE_FRAME_INTERVAL, SettingsManager.KEY_VIDEO_ROTATION, SettingsManager.KEY_AUTO_VIDEOSNAP_SIZE }; @@ -155,6 +160,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private ImageView mThumbnail; private Camera2FaceView mFaceView; private Point mDisplaySize = new Point(); + private SelfieFlashView mSelfieView; + private float mScreenBrightness = 0.0f; private SurfaceHolder.Callback callbackMono = new SurfaceHolder.Callback() { // SurfaceHolder callbacks @@ -1673,6 +1680,32 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } + public void startSelfieFlash() { + if (mSelfieView == null) + mSelfieView = (SelfieFlashView) (mRootView.findViewById(R.id.selfie_flash)); + mSelfieView.bringToFront(); + mSelfieView.open(); + mScreenBrightness = setScreenBrightness(1F); + } + + public void stopSelfieFlash() { + if (mSelfieView == null) + mSelfieView = (SelfieFlashView) (mRootView.findViewById(R.id.selfie_flash)); + mSelfieView.close(); + if (mScreenBrightness != 0.0f) + setScreenBrightness(mScreenBrightness); + } + + private float setScreenBrightness(float brightness) { + float originalBrightness; + Window window = mActivity.getWindow(); + WindowManager.LayoutParams layout = window.getAttributes(); + originalBrightness = layout.screenBrightness; + layout.screenBrightness = brightness; + window.setAttributes(layout); + return originalBrightness; + } + public void hideSurfaceView() { mSurfaceView.setVisibility(View.INVISIBLE); } diff --git a/src/com/android/camera/PermissionsActivity.java b/src/com/android/camera/PermissionsActivity.java index c5e43e56d..5ebe72075 100644 --- a/src/com/android/camera/PermissionsActivity.java +++ b/src/com/android/camera/PermissionsActivity.java @@ -33,16 +33,20 @@ public class PermissionsActivity extends Activity { private boolean mFlagHasMicrophonePermission; private boolean mFlagHasStoragePermission; private boolean mCriticalPermissionDenied; + private Intent mIntent; + private boolean mIsReturnResult; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + mIntent = getIntent(); + mIsReturnResult = false; } @Override protected void onResume() { super.onResume(); - if (!mCriticalPermissionDenied) { + if (!mCriticalPermissionDenied && !mIsReturnResult) { mNumPermissionsToRequest = 0; checkPermissions(); } else { @@ -160,9 +164,16 @@ public class PermissionsActivity extends Activity { } private void handlePermissionsSuccess() { - Intent intent = new Intent(this, CameraActivity.class); - startActivity(intent); - finish(); + if (mIntent != null) { + mIsReturnResult = true; + mIntent.setClass(this, CameraActivity.class); + startActivityForResult(mIntent, 1); + } else { + mIsReturnResult = false; + Intent intent = new Intent(this, CameraActivity.class); + startActivity(intent); + finish(); + } } private void handlePermissionsFailure() { @@ -187,4 +198,11 @@ public class PermissionsActivity extends Activity { }) .show(); } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + setResult(resultCode, data); + finish(); + } } diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java index c2f9a538b..dca66d1cb 100755 --- a/src/com/android/camera/PhotoMenu.java +++ b/src/com/android/camera/PhotoMenu.java @@ -191,7 +191,9 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_EXPOSURE, CameraSettings.KEY_WHITE_BALANCE, CameraSettings.KEY_QC_CHROMA_FLASH, - CameraSettings.KEY_REDEYE_REDUCTION + CameraSettings.KEY_REDEYE_REDUCTION, + CameraSettings.KEY_SELFIE_MIRROR, + CameraSettings.KEY_SHUTTER_SOUND }; mOtherKeys2 = new String[] { @@ -234,6 +236,8 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_MANUAL_EXPOSURE, CameraSettings.KEY_MANUAL_WB, CameraSettings.KEY_MANUAL_FOCUS, + CameraSettings.KEY_SELFIE_MIRROR, + CameraSettings.KEY_SHUTTER_SOUND, SettingsManager.KEY_CAMERA2 }; @@ -1478,6 +1482,12 @@ public class PhotoMenu extends MenuController mActivity.getString(R.string.pref_camera_coloreffect_default)); } + String stillMoreOn = mActivity.getString(R.string. + pref_camera_advanced_feature_value_stillmore_on); + if (same(pref, CameraSettings.KEY_ADVANCED_FEATURES, stillMoreOn)) { + setPreference(CameraSettings.KEY_FLASH_MODE, Parameters.FLASH_MODE_OFF); + } + ListPreference autoHdrPref = mPreferenceGroup.findPreference(CameraSettings.KEY_AUTO_HDR); if (autoHdrPref != null && autoHdrPref.getValue().equalsIgnoreCase("enable")) { mHdrSwitcher.setVisibility(View.GONE); diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 3a7e41976..279c903d4 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -24,7 +24,10 @@ import android.content.Intent; import android.content.SharedPreferences.Editor; import android.content.res.Configuration; import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Matrix; import android.graphics.Rect; +import android.graphics.drawable.BitmapDrawable; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; @@ -47,6 +50,7 @@ import android.os.Message; import android.os.MessageQueue; import android.os.SystemClock; import android.provider.MediaStore; +import android.util.DisplayMetrics; import android.util.Log; import android.view.KeyEvent; import android.view.OrientationEventListener; @@ -88,6 +92,7 @@ import android.text.TextUtils; import com.android.internal.util.MemInfoReader; import android.app.ActivityManager; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -720,6 +725,7 @@ public class PhotoModule Log.v(TAG, "Start to switch camera. id=" + mPendingSwitchCameraId); mCameraId = mPendingSwitchCameraId; mPendingSwitchCameraId = -1; + mSnapshotOnIdle = false; setCameraId(mCameraId); // from onPause @@ -1259,6 +1265,19 @@ public class PhotoModule } } + private byte[] flipJpeg(byte[] jpegData) { + Bitmap srcBitmap = BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length); + Matrix m = new Matrix(); + m.preScale(-1, 1); + Bitmap dstBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(), srcBitmap.getHeight(), m, false); + dstBitmap.setDensity(DisplayMetrics.DENSITY_DEFAULT); + int size = dstBitmap.getWidth() * dstBitmap.getHeight(); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(size); + dstBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outStream); + + return outStream.toByteArray(); + } + private final class JpegPictureCallback implements CameraPictureCallback { Location mLocation; @@ -1268,7 +1287,7 @@ public class PhotoModule } @Override - public void onPictureTaken(final byte [] jpegData, CameraProxy camera) { + public void onPictureTaken(byte [] jpegData, CameraProxy camera) { mUI.stopSelfieFlash(); mUI.enableShutter(true); if (mUI.isPreviewCoverVisible()) { @@ -1360,6 +1379,16 @@ public class PhotoModule if (!mRefocus || (mRefocus && mReceivedSnapNum == 7)) { ExifInterface exif = Exif.getExif(jpegData); int orientation = Exif.getOrientation(exif); + if(mCameraId == CameraHolder.instance().getFrontCameraId()) { + IconListPreference selfieMirrorPref = (IconListPreference) mPreferenceGroup + .findPreference(CameraSettings.KEY_SELFIE_MIRROR); + if (selfieMirrorPref != null && selfieMirrorPref.getValue() != null && + selfieMirrorPref.getValue().equalsIgnoreCase("enable")) { + jpegData = flipJpeg(jpegData); + exif = Exif.getExif(jpegData); + exif.addOrientationTag(orientation); + } + } if (!mIsImageCaptureIntent) { // Burst snapshot. Generate new image name. if (mReceivedSnapNum > 1) { @@ -1689,7 +1718,12 @@ public class PhotoModule new JpegPictureCallback(loc)); } } else { - mCameraDevice.enableShutterSound(!mRefocus); + if (!isShutterSoundOn()) { + mCameraDevice.enableShutterSound(false); + } else { + mCameraDevice.enableShutterSound(!mRefocus); + } + mCameraDevice.takePicture(mHandler, new ShutterCallback(!animateBefore), mRawPictureCallback, mPostViewPictureCallback, @@ -1972,8 +2006,16 @@ public class PhotoModule mUI.overrideSettings(CameraSettings.KEY_FLASH_MODE, flashMode); } - if(mCameraId != CameraHolder.instance().getFrontCameraId()) + if(mCameraId != CameraHolder.instance().getFrontCameraId()) { CameraSettings.removePreferenceFromScreen(mPreferenceGroup, CameraSettings.KEY_SELFIE_FLASH); + CameraSettings.removePreferenceFromScreen(mPreferenceGroup, CameraSettings.KEY_SELFIE_MIRROR); + } else { + ListPreference prefSelfieMirror = mPreferenceGroup.findPreference(CameraSettings.KEY_SELFIE_MIRROR); + if(prefSelfieMirror != null && prefSelfieMirror.getValue() != null + && prefSelfieMirror.getValue().equalsIgnoreCase("enable")) { + mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, "off"); + } + } } private void overrideCameraSettings(final String flashMode, @@ -2288,14 +2330,23 @@ 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, playSound); - + mUI.startCountDown(seconds, isShutterSoundOn()); } else { mSnapshotOnIdle = false; initiateSnap(); } } + private boolean isShutterSoundOn() { + IconListPreference shutterSoundPref = (IconListPreference) mPreferenceGroup + .findPreference(CameraSettings.KEY_SHUTTER_SOUND); + if (shutterSoundPref != null && shutterSoundPref.getValue() != null && + shutterSoundPref.getValue().equalsIgnoreCase("disable")) { + return false; + } + return true; + } + private void initiateSnap() { if(mPreferences.getString(CameraSettings.KEY_SELFIE_FLASH, @@ -2792,7 +2843,7 @@ public class PhotoModule mCameraDevice.setFaceDetectionCallback(null, null); mCameraDevice.setErrorCallback(null); - if (mActivity.isSecureCamera()) { + if (mActivity.isSecureCamera() || mActivity.isForceReleaseCamera()) { // Blocks until camera is actually released. CameraHolder.instance().strongRelease(); } else { @@ -3002,7 +3053,7 @@ public class PhotoModule mParameters.set(CameraSettings.KEY_QC_MULTI_TOUCH_FOCUS, multiTouchFocus); } if (CameraUtil.isSupported(stillMore, - CameraSettings.getSupportedStillMoreModes(mParameters))) { + CameraSettings.getSupportedStillMoreModes(mParameters))) { mParameters.set(CameraSettings.KEY_QC_STILL_MORE, stillMore); } } diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index fd4b18186..fd848c623 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -458,6 +458,7 @@ public class PhotoUI implements PieListener, } mSurfaceView.setLayoutParams(lp); + mRootView.requestLayout(); if (mFaceView != null) { mFaceView.setLayoutParams(lp); } @@ -1252,7 +1253,10 @@ public class PhotoUI implements PieListener, @Override public void clearFocus() { - FocusIndicator indicator = getFocusIndicator(); + FocusIndicator indicator = mPieRenderer; + if (hasFaces()) { + mFaceView.showStart(); + } if (indicator != null) indicator.clear(); } @@ -1295,6 +1299,9 @@ public class PhotoUI implements PieListener, mFaceView.setVisibility(View.VISIBLE); mFaceView.setDisplayOrientation(orientation); mFaceView.setMirror(mirror); + LayoutParams layoutParams = new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + mFaceView.setLayoutParams(layoutParams); mFaceView.resume(); } diff --git a/src/com/android/camera/PreviewGestures.java b/src/com/android/camera/PreviewGestures.java index 48bfb7e58..0ac6ce01c 100644 --- a/src/com/android/camera/PreviewGestures.java +++ b/src/com/android/camera/PreviewGestures.java @@ -87,29 +87,8 @@ public class PreviewGestures return false; } if (mZoomOnly || mMode == MODE_ZOOM) return false; - int deltaX = (int) (e1.getX() - e2.getX()); - int deltaY = (int) (e1.getY() - e2.getY()); - - int orientation = 0; - if (mPhotoMenu != null) - orientation = mPhotoMenu.getOrientation(); - else if (mVideoMenu != null) - orientation = mVideoMenu.getOrientation(); - else if (mCaptureUI != null) - orientation = mCaptureUI.getOrientation(); - - if (isLeftSwipe(orientation, deltaX, deltaY)) { - waitUntilNextDown = true; - if (mPhotoMenu != null && !mPhotoMenu.isMenuBeingShown()) - mPhotoMenu.openFirstLevel(); - else if (mVideoMenu != null && !mVideoMenu.isMenuBeingShown()) - mVideoMenu.openFirstLevel(); - else if (mCaptureUI != null && !mCaptureUI.isMenuBeingShown()) - mCaptureUI.showSettingMenu(); - return true; - } else { - return onSingleTapUp(e2); - } + + return onSingleTapUp(e2); } private boolean isLeftSwipe(int orientation, int deltaX, int deltaY) { diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index 7e2e3bf6a..c8f3f8c7b 100644 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -45,6 +45,7 @@ import android.util.Rational; import android.util.Size; import com.android.camera.imageprocessor.filter.BeautificationFilter; +import com.android.camera.imageprocessor.filter.BestpictureFilter; import com.android.camera.imageprocessor.filter.OptizoomFilter; import com.android.camera.imageprocessor.filter.TrackingFocusFrameListener; import com.android.camera.imageprocessor.filter.UbifocusFilter; @@ -69,12 +70,15 @@ import java.util.Set; public class SettingsManager implements ListMenu.SettingsListener { public static final int RESOURCE_TYPE_THUMBNAIL = 0; public static final int RESOURCE_TYPE_LARGEICON = 1; + + public static final int SCENE_MODE_AUTO_INT = 0; 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 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"; @@ -111,6 +115,9 @@ public class SettingsManager implements ListMenu.SettingsListener { "pref_camera2_video_time_lapse_frame_interval_key"; public static final String KEY_FACE_DETECTION = "pref_camera2_facedetection_key"; public static final String KEY_AUTO_VIDEOSNAP_SIZE = "pref_camera2_videosnap_key"; + public static final String KEY_VIDEO_HIGH_FRAME_RATE = "pref_camera2_hfr_key"; + public static final String KEY_SELFIE_FLASH = "pref_selfie_flash_key"; + public static final String KEY_SHUTTER_SOUND = "pref_camera2_shutter_sound_key"; private static final String TAG = "SnapCam_SettingsManager"; private static SettingsManager sInstance; @@ -124,6 +131,7 @@ public class SettingsManager implements ListMenu.SettingsListener { private boolean mIsMonoCameraPresent = false; private boolean mIsFrontCameraPresent = false; private JSONObject mDependency; + private int mCameraId; private SettingsManager(Context context) { mListeners = new ArrayList<>(); @@ -197,6 +205,7 @@ public class SettingsManager implements ListMenu.SettingsListener { @Override public void onSettingChanged(ListPreference pref) { String key = pref.getKey(); + if (pref.getKey().equals(KEY_VIDEO_QUALITY)) buildHFR(); List changed = checkDependencyAndUpdate(key); if (changed == null) return; notifyListeners(changed); @@ -215,6 +224,7 @@ public class SettingsManager implements ListMenu.SettingsListener { private void setLocalIdAndInitialize(int cameraId) { mPreferences.setLocalId(mContext, cameraId); + mCameraId = cameraId; CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); PreferenceInflater inflater = new PreferenceInflater(mContext); @@ -501,6 +511,7 @@ public class SettingsManager implements ListMenu.SettingsListener { ListPreference faceDetection = mPreferenceGroup.findPreference(KEY_FACE_DETECTION); ListPreference makeup = mPreferenceGroup.findPreference(KEY_MAKEUP); ListPreference trackingfocus = mPreferenceGroup.findPreference(KEY_TRACKINGFOCUS); + ListPreference hfr = mPreferenceGroup.findPreference(KEY_VIDEO_HIGH_FRAME_RATE); if (whiteBalance != null) { CameraSettings.filterUnsupportedOptions(mPreferenceGroup, @@ -587,6 +598,14 @@ public class SettingsManager implements ListMenu.SettingsListener { if (!TrackingFocusFrameListener.isSupportedStatic()) removePreference(mPreferenceGroup, KEY_TRACKINGFOCUS); } + + if (hfr != null) { + buildHFR(); + } + + if (!mIsFrontCameraPresent || !isFacingFront(mCameraId)) { + removePreference(mPreferenceGroup, KEY_SELFIE_FLASH); + } } private void buildExposureCompensation(int cameraId) { @@ -649,6 +668,86 @@ public class SettingsManager implements ListMenu.SettingsListener { cameraIdPref.setEntries(entries); } + private void buildHFR() { + ListPreference hfrPref = mPreferenceGroup.findPreference(KEY_VIDEO_HIGH_FRAME_RATE); + + + Size[] highSpeedVideoSize = getSupportedHighSpeedVideoSize(mCameraId); + if (highSpeedVideoSize.length == 0) { + CharSequence[] entryValues = new CharSequence[1]; + CharSequence[] entries = new CharSequence[1]; + entryValues[0] = "off"; + entries[0] = "off"; + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + hfrPref.setValueIndex(0); + return; + } + + ListPreference videoQuality = mPreferenceGroup.findPreference(KEY_VIDEO_QUALITY); + String video = videoQuality.getValue(); + int x = video.indexOf('x'); + Size videoSize = new Size(Integer.parseInt(video.substring(0, x)), + Integer.parseInt(video.substring(x + 1))); + + boolean found = false; + for (Size s : highSpeedVideoSize) { + if (videoSize.equals(s)) { + found = true; + break; + } + } + + if (!found) { + CharSequence[] entryValues = new CharSequence[1]; + CharSequence[] entries = new CharSequence[1]; + entryValues[0] = "off"; + entries[0] = "Off"; + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + hfrPref.setValueIndex(0); + return; + } + + Range[] range = getSupportedHighSpeedVideoFPSRange(mCameraId, highSpeedVideoSize[0]); + ArrayList<Range> list = new ArrayList<>(); + for (Range r : range) { + if (r.getLower() == r.getUpper()) { + list.add(r); + } + } + + if (list.size() == 0) { + CharSequence[] entryValues = new CharSequence[1]; + CharSequence[] entries = new CharSequence[1]; + entryValues[0] = "off"; + entries[0] = "Off"; + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + hfrPref.setValueIndex(0); + return; + } + + CharSequence[] entryValues = new CharSequence[list.size() * 2 + 1]; + CharSequence[] entries = new CharSequence[list.size() * 2 + 1]; + entryValues[0] = "off"; + entries[0] = "Off"; + int i = 1; + for (Range r : list) { + entries[i] = "HFR " + r.getLower(); + entryValues[i] = "hfr" + r.getLower(); + i++; + } + for (Range r : list) { + entries[i] = "HSR " + r.getLower(); + entryValues[i] = "hsr" + r.getLower(); + i++; + } + + hfrPref.setEntryValues(entryValues); + hfrPref.setEntries(entries); + } + private boolean removePreference(PreferenceGroup group, String key) { for (int i = 0, n = group.size(); i < n; i++) { CameraPreference child = group.get(i); @@ -812,6 +911,18 @@ public class SettingsManager implements ListMenu.SettingsListener { return res; } + public Size[] getSupportedHighSpeedVideoSize(int cameraId) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return map.getHighSpeedVideoSizes(); + } + + public Range[] getSupportedHighSpeedVideoFPSRange(int cameraId, Size videoSize) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + return map.getHighSpeedVideoFpsRangesFor(videoSize); + } + private List<String> getSupportedRedeyeReduction(int cameraId) { int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics .CONTROL_AE_AVAILABLE_MODES); @@ -844,6 +955,7 @@ public class SettingsManager implements ListMenu.SettingsListener { if (mIsMonoCameraPresent) modes.add(SCENE_MODE_DUAL_STRING); // need special case handle for dual mode if (OptizoomFilter.isSupportedStatic()) modes.add(SCENE_MODE_OPTIZOOM_INT + ""); if (UbifocusFilter.isSupportedStatic() && cameraId == CaptureModule.BAYER_ID) modes.add(SCENE_MODE_UBIFOCUS_INT + ""); + if (BestpictureFilter.isSupportedStatic() && cameraId == CaptureModule.BAYER_ID) modes.add(SCENE_MODE_BESTPICTURE_INT + ""); for (int mode : sceneModes) { modes.add("" + mode); } @@ -978,6 +1090,7 @@ public class SettingsManager implements ListMenu.SettingsListener { JSONObject dependencyMap = getDependencyMapForKey(key); if (dependencyMap == null) return null; if (!dependencyMap.has(value)) value = "default"; + if (!dependencyMap.has(value)) return null; value = getDependencyKey(dependencyMap, value); try { return dependencyMap.getJSONObject(value); diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index a93cff611..b0699bb08 100755 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -1268,7 +1268,11 @@ public class VideoModule implements CameraModule, mCameraDevice.setZoomChangeListener(null); mCameraDevice.setErrorCallback(null); mCameraDevice.setFaceDetectionCallback(null, null); - CameraHolder.instance().release(); + if (mActivity.isForceReleaseCamera()) { + CameraHolder.instance().strongRelease(); + } else { + CameraHolder.instance().release(); + } mCameraDevice = null; mPreviewing = false; mSnapshotInProgress = false; diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index cf93feeac..41b9cfd89 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -1250,6 +1250,9 @@ public class VideoUI implements PieRenderer.PieListener, mFaceView.setVisibility(View.VISIBLE); mFaceView.setDisplayOrientation(orientation); mFaceView.setMirror(mirror); + LayoutParams layoutParams = new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + mFaceView.setLayoutParams(layoutParams); mFaceView.resume(); } diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java index 210fe3600..8d7243c5c 100644 --- a/src/com/android/camera/WideAnglePanoramaModule.java +++ b/src/com/android/camera/WideAnglePanoramaModule.java @@ -380,7 +380,11 @@ public class WideAnglePanoramaModule private void releaseCamera() { if (mCameraDevice != null) { - CameraHolder.instance().release(); + if (mActivity.isForceReleaseCamera()) { + CameraHolder.instance().strongRelease(); + } else { + CameraHolder.instance().release(); + } mCameraDevice.setErrorCallback(null); mCameraDevice = null; mCameraState = PREVIEW_STOPPED; diff --git a/src/com/android/camera/imageprocessor/FrameProcessor.java b/src/com/android/camera/imageprocessor/FrameProcessor.java index 74db08b31..4bee19194 100644 --- a/src/com/android/camera/imageprocessor/FrameProcessor.java +++ b/src/com/android/camera/imageprocessor/FrameProcessor.java @@ -125,7 +125,7 @@ public class FrameProcessor { mOutingHandler = new Handler(mOutingThread.getLooper()); } - if(mListeningThread == null) { + if (mListeningThread == null) { mListeningThread = new HandlerThread("FrameListeningThread"); mListeningThread.start(); mListeningHandler = new Handler(mListeningThread.getLooper()); @@ -256,10 +256,10 @@ public class FrameProcessor { } catch (InterruptedException e) { } } - for(ImageFilter filter : mPreviewFilters) { + for (ImageFilter filter : mPreviewFilters) { filter.deinit(); } - for(ImageFilter filter : mFinalFilters) { + for (ImageFilter filter : mFinalFilters) { filter.deinit(); } } @@ -275,14 +275,14 @@ public class FrameProcessor { public List<Surface> getInputSurfaces() { List<Surface> surfaces = new ArrayList<Surface>(); - if(mPreviewFilters.size() == 0 && mFinalFilters.size() == 0) { + if (mPreviewFilters.size() == 0 && mFinalFilters.size() == 0) { surfaces.add(mSurfaceAsItIs); - if(mIsVideoOn) { + if (mIsVideoOn) { surfaces.add(mVideoSurfaceAsItIs); } } else if (mFinalFilters.size() == 0) { surfaces.add(mSurfaceAsItIs); - if(mIsVideoOn) { + if (mIsVideoOn) { surfaces.add(mVideoSurfaceAsItIs); } surfaces.add(getReaderSurface()); @@ -293,7 +293,7 @@ public class FrameProcessor { } public boolean isFrameFilterEnabled() { - if(mFinalFilters.size() == 0) { + if (mFinalFilters.size() == 0) { return false; } return true; @@ -301,13 +301,13 @@ public class FrameProcessor { public void setOutputSurface(Surface surface) { mSurfaceAsItIs = surface; - if(mFinalFilters.size() != 0) { + if (mFinalFilters.size() != 0) { mOutputAllocation.setSurface(surface); } } public void setVideoOutputSurface(Surface surface) { - if(surface == null) { + if (surface == null) { synchronized (mAllocationLock) { if (mVideoOutputAllocation != null) { mVideoOutputAllocation.destroy(); @@ -319,7 +319,7 @@ public class FrameProcessor { } mVideoSurfaceAsItIs = surface; mIsVideoOn = true; - if(mFinalFilters.size() != 0) { + if (mFinalFilters.size() != 0) { synchronized (mAllocationLock) { if (mVideoOutputAllocation == null) { Type.Builder rgbTypeBuilder = new Type.Builder(mRs, Element.RGBA_8888(mRs)); @@ -346,32 +346,32 @@ public class FrameProcessor { @Override public void onImageAvailable(ImageReader reader) { synchronized (mAllocationLock) { - if(mOutputAllocation == null) { + if (mOutputAllocation == null) { return; } try { Image image = reader.acquireLatestImage(); - if(image == null) { + if (image == null) { return; } - if(!mIsActive) { + if (!mIsActive) { image.close(); return; } mIsAllocationEverUsed = true; ByteBuffer bY = image.getPlanes()[0].getBuffer(); ByteBuffer bVU = image.getPlanes()[2].getBuffer(); - if(yvuBytes == null || width != mSize.getWidth() || height != mSize.getHeight()) { + if (yvuBytes == null || width != mSize.getWidth() || height != mSize.getHeight()) { stride = image.getPlanes()[0].getRowStride(); width = mSize.getWidth(); height = mSize.getHeight(); ySize = stride * mSize.getHeight(); - yvuBytes = new byte[ySize*3/2]; + yvuBytes = new byte[ySize * 3 / 2]; } boolean needToFeedSurface = false; //Start processing yvu buf for (ImageFilter filter : mPreviewFilters) { - if(filter.isFrameListener()) { + if (filter.isFrameListener()) { if (mListeningTask.setParam(filter, bY, bVU, mSize.getWidth(), mSize.getHeight(), stride)) { mListeningHandler.post(mListeningTask); } @@ -384,7 +384,7 @@ public class FrameProcessor { bVU.rewind(); } //End processing yvu buf - if(needToFeedSurface) { + if (needToFeedSurface) { bY.get(yvuBytes, 0, bY.remaining()); bVU.get(yvuBytes, ySize, bVU.remaining()); mOutingHandler.post(this); @@ -398,17 +398,17 @@ public class FrameProcessor { @Override public void run() { synchronized (mAllocationLock) { - if(!mIsActive) { + if (!mIsActive) { return; } - if(mInputAllocation == null) { + if (mInputAllocation == null) { createAllocation(stride, height, stride - width); } mInputAllocation.copyFrom(yvuBytes); mRsRotator.forEach_rotate90andMerge(mInputAllocation); mRsYuvToRGB.forEach_nv21ToRgb(mOutputAllocation); mOutputAllocation.ioSend(); - if(mVideoOutputAllocation != null) { + if (mVideoOutputAllocation != null) { mVideoOutputAllocation.copyFrom(mOutputAllocation); mVideoOutputAllocation.ioSend(); } @@ -425,7 +425,7 @@ public class FrameProcessor { Semaphore mMutureLock = new Semaphore(1); public boolean setParam(ImageFilter filter, ByteBuffer bY, ByteBuffer bVU, int width, int height, int stride) { - if(!mIsActive) { + if (!mIsActive) { return false; } if (!mMutureLock.tryAcquire()) { @@ -467,13 +467,5 @@ public class FrameProcessor { } } } - - private native int nativeRotateNV21(ByteBuffer inBuf, int imageWidth, int imageHeight, int degree, ByteBuffer outBuf); - - private native int nativeNV21toRgb(ByteBuffer yvuBuf, ByteBuffer rgbBuf, int width, int height); - - static { - System.loadLibrary("jni_imageutil"); - } } diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java index 8c0d2ff88..295707afd 100644 --- a/src/com/android/camera/imageprocessor/PostProcessor.java +++ b/src/com/android/camera/imageprocessor/PostProcessor.java @@ -51,8 +51,10 @@ import com.android.camera.MediaSaveService; import com.android.camera.PhotoModule; import com.android.camera.SettingsManager; import com.android.camera.exif.ExifInterface; +import com.android.camera.imageprocessor.filter.BestpictureFilter; import com.android.camera.imageprocessor.filter.OptizoomFilter; import com.android.camera.imageprocessor.filter.SharpshooterFilter; +import com.android.camera.imageprocessor.filter.StillmoreFilter; import com.android.camera.imageprocessor.filter.UbifocusFilter; import com.android.camera.ui.RotateTextToast; @@ -76,8 +78,12 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ public static final int FILTER_OPTIZOOM = 1; public static final int FILTER_SHARPSHOOTER = 2; public static final int FILTER_UBIFOCUS = 3; - public static final int FILTER_MAX = 4; + public static final int FILTER_STILLMORE = 4; + public static final int FILTER_BESTPICTURE = 5; + public static final int FILTER_MAX = 6; + //Max image is now Bestpicture filter with 10 + public static final int MAX_REQUIRED_IMAGE_NUM = 10; private int mCurrentNumImage = 0; private ImageFilter mFilter; private int mFilterIndex; @@ -285,6 +291,12 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ case FILTER_UBIFOCUS: mFilter = new UbifocusFilter(mController, mActivity); break; + case FILTER_STILLMORE: + mFilter = new StillmoreFilter(mController); + break; + case FILTER_BESTPICTURE: + mFilter = new BestpictureFilter(mController, mActivity); + break; } } @@ -435,10 +447,12 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ } } } - //Start processing FrameProcessor filter as well - for (ImageFilter filter : mController.getFrameFilters()) { - filter.init(resultImage.width, resultImage.height, resultImage.stride, resultImage.stride); - filter.addImage(resultImage.outBuffer, null, 0, new Boolean(false)); + if(resultImage != null) { + //Start processing FrameProcessor filter as well + for (ImageFilter filter : mController.getFrameFilters()) { + filter.init(resultImage.width, resultImage.height, resultImage.stride, resultImage.stride); + filter.addImage(resultImage.outBuffer, null, 0, new Boolean(false)); + } } //End processing FrameProessor filter clear(); @@ -446,10 +460,11 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ if(mWatchdog != null) { mWatchdog.stopMonitor(); } - if((resultImage.outRoi.left + resultImage.outRoi.width() > resultImage.width) || + if(resultImage == null || + (resultImage.outRoi.left + resultImage.outRoi.width() > resultImage.width) || (resultImage.outRoi.top + resultImage.outRoi.height() > resultImage.height) ) { - Log.e(TAG, "Processed outRoi is not within picture range"); + Log.d(TAG, "Result image is not valid."); } else { if(mFilter != null && DEBUG_FILTER) { bytes = nv21ToJpeg(mDebugResultImage, mOrientation); @@ -488,5 +503,9 @@ public class PostProcessor implements ImageReader.OnImageAvailableListener{ } } + private native int nativeFlipVerticalNV21(byte[] buf, int stride, int height); + static { + System.loadLibrary("jni_imageutil"); + } } diff --git a/src/com/android/camera/imageprocessor/filter/BestpictureFilter.java b/src/com/android/camera/imageprocessor/filter/BestpictureFilter.java new file mode 100644 index 000000000..638d037ce --- /dev/null +++ b/src/com/android/camera/imageprocessor/filter/BestpictureFilter.java @@ -0,0 +1,252 @@ +/* +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.imageprocessor.filter; + +import android.content.Intent; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.graphics.YuvImage; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.net.Uri; +import android.os.Bundle; +import android.os.Handler; +import android.util.Log; + +import com.android.camera.BestpictureActivity; +import com.android.camera.CameraActivity; +import com.android.camera.CaptureModule; +import com.android.camera.MediaSaveService; +import com.android.camera.PhotoModule; +import com.android.camera.imageprocessor.PostProcessor; +import com.android.camera.util.CameraUtil; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class BestpictureFilter implements ImageFilter { + public static final int NUM_REQUIRED_IMAGE = 10; + private int mWidth; + private int mHeight; + private int mStrideY; + private int mStrideVU; + private static String TAG = "BestpictureFilter"; + private static final boolean DEBUG = false; + private static boolean mIsSupported = true; + private CaptureModule mModule; + private CameraActivity mActivity; + private int mOrientation = 0; + final String[] NAMES = {"00.jpg", "01.jpg", "02.jpg", "03.jpg", + "04.jpg", "05.jpg", "06.jpg", "07.jpg", "08.jpg" + ,"09.jpg"}; + + private final static int TIME_DELAY = 50; + private int mSavedCount = 0; + private PhotoModule.NamedImages mNamedImages; + private ByteBuffer mBY; + private ByteBuffer mBVU; + + private static void Log(String msg) { + if (DEBUG) { + Log.d(TAG, msg); + } + } + + public BestpictureFilter(CaptureModule module, CameraActivity activity) { + mModule = module; + mActivity = activity; + mNamedImages = new PhotoModule.NamedImages(); + } + + @Override + public List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder) { + List<CaptureRequest> list = new ArrayList<CaptureRequest>(); + for(int i=0; i < NUM_REQUIRED_IMAGE; i++) { + list.add(builder.build()); + } + return list; + } + + @Override + public String getStringName() { + return "BestpictureFilter"; + } + + @Override + public int getNumRequiredImage() { + return NUM_REQUIRED_IMAGE; + } + + @Override + public void init(int width, int height, int strideY, int strideVU) { + Log("init"); + mWidth = width/2*2; + mHeight = height/2*2; + mStrideY = strideY/2*2; + mStrideVU = strideVU / 2 * 2; + Log("width: " + mWidth + " height: " + mHeight + " strideY: " + mStrideY + " strideVU: " + mStrideVU); + } + + @Override + public void deinit() { + Log("deinit"); + } + + @Override + public void addImage(final ByteBuffer bY, final ByteBuffer bVU, final int imageNum, Object param) { + Log("addImage"); + if(imageNum == 0) { + mOrientation = CameraUtil.getJpegRotation(mModule.getMainCameraId(), mModule.getDisplayOrientation()); + mSavedCount = 0; + mBY = bY; + mBVU = bVU; + } + new Thread() { + public void run() { + saveToPrivateFile(imageNum, nv21ToJpeg(bY, bVU, new Rect(0, 0, mWidth, mHeight), mOrientation)); + mSavedCount++; + } + }.start(); + } + + @Override + public ResultImage processImage() { + long captureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(captureStartTime); + PhotoModule.NamedImages.NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + long date = (name == null) ? -1 : name.date; + mActivity.getMediaSaveService().addImage( + nv21ToJpeg(mBY, mBVU, new Rect(0, 0, mWidth, mHeight), mOrientation), title, date, null, mWidth, mHeight, + mOrientation, null, new MediaSaveService.OnMediaSavedListener() { + @Override + public void onMediaSaved(Uri uri) { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } + startBestpictureActivity(uri); + } + } + , mActivity.getContentResolver(), "jpeg"); + while(mSavedCount < NUM_REQUIRED_IMAGE) { + try { + Thread.sleep(1); + } catch (Exception e) { + } + } + return null; + } + + private void startBestpictureActivity(Uri uri) { + Intent intent = new Intent(); + intent.setData(uri); + intent.setClass(mActivity, BestpictureActivity.class); + mActivity.startActivityForResult(intent, BestpictureActivity.BESTPICTURE_ACTIVITY_CODE); + } + + @Override + public boolean isSupported() { + return mIsSupported; + } + + @Override + public boolean isFrameListener() { + return false; + } + + @Override + public boolean isManualMode() { + return false; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) throws CameraAccessException { + for(int i=0; i < NUM_REQUIRED_IMAGE; i++) { + captureSession.capture(builder.build(), callback, handler); + try { + Thread.sleep(TIME_DELAY); + } catch (InterruptedException e) { + } + } + } + + public static boolean isSupportedStatic() { + return mIsSupported; + } + + private byte[] nv21ToJpeg(ByteBuffer bY, ByteBuffer bVU, Rect roi, int orientation) { + ByteBuffer buf = ByteBuffer.allocate(mStrideY*mHeight*3/2); + buf.put(bY); + bY.rewind(); + if(bVU != null) { + buf.put(bVU); + bVU.rewind(); + } + BitmapOutputStream bos = new BitmapOutputStream(1024); + YuvImage im = new YuvImage(buf.array(), ImageFormat.NV21, + mWidth, mHeight, new int[]{mStrideY, mStrideVU}); + im.compressToJpeg(roi, 50, bos); + byte[] bytes = bos.getArray(); + bytes = PostProcessor.addExifTags(bytes, orientation); + return bytes; + } + + private class BitmapOutputStream extends ByteArrayOutputStream { + public BitmapOutputStream(int size) { + super(size); + } + + public byte[] getArray() { + return buf; + } + } + + private void saveToPrivateFile(final int index, final byte[] bytes) { + String filesPath = mActivity.getFilesDir()+"/Bestpicture"; + File file = new File(filesPath); + if(!file.exists()) { + file.mkdir(); + } + file = new File(filesPath+"/"+NAMES[index]); + try { + FileOutputStream out = new FileOutputStream(file); + out.write(bytes, 0, bytes.length); + out.close(); + } catch (Exception e) { + } + } +} diff --git a/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java b/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java new file mode 100644 index 000000000..e5c2c03d9 --- /dev/null +++ b/src/com/android/camera/imageprocessor/filter/StillmoreFilter.java @@ -0,0 +1,175 @@ +/* +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.imageprocessor.filter; + +import android.graphics.Rect; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.os.Handler; +import android.util.Log; +import android.util.Range; + +import com.android.camera.CaptureModule; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class StillmoreFilter implements ImageFilter{ + public static final int NUM_REQUIRED_IMAGE = 5; + private int mWidth; + private int mHeight; + private int mStrideY; + private int mStrideVU; + private static String TAG = "StillmoreFilter"; + private static final boolean DEBUG = false; + private static boolean mIsSupported = false; + private ByteBuffer mOutBuf; + private CaptureModule mModule; + private int mSenseValue = 0; + private long mExpoTime; + + private static void Log(String msg) { + if(DEBUG) { + Log.d(TAG, msg); + } + } + + public StillmoreFilter(CaptureModule module) { + mModule = module; + } + + @Override + public List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder) { + mExpoTime = (mModule.getPreviewCaptureResult().get(CaptureResult.SENSOR_EXPOSURE_TIME)); + mSenseValue = (mModule.getPreviewCaptureResult().get(CaptureResult.SENSOR_SENSITIVITY)).intValue(); + + List<CaptureRequest> list = new ArrayList<CaptureRequest>(); + for(int i=0; i < NUM_REQUIRED_IMAGE; i++) { + list.add(builder.build()); + } + return list; + } + + @Override + public String getStringName() { + return "StillmoreFilter"; + } + + @Override + public int getNumRequiredImage() { + return NUM_REQUIRED_IMAGE; + } + + @Override + public void init(int width, int height, int strideY, int strideVU) { + Log("init"); + mWidth = width/2*2; + mHeight = height/2*2; + mStrideY = strideY/2*2; + mStrideVU = strideVU/2*2; + mOutBuf = ByteBuffer.allocate(mStrideY*mHeight*3/2); + Log("width: "+mWidth+" height: "+mHeight+" strideY: "+mStrideY+" strideVU: "+mStrideVU); + nativeInit(mWidth, mHeight, mStrideY, mStrideVU, + 0, 0, mWidth, mHeight, NUM_REQUIRED_IMAGE); + } + + @Override + public void deinit() { + Log("deinit"); + mOutBuf = null; + nativeDeinit(); + } + + @Override + public void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object param) { + Log("addImage"); + int yActualSize = bY.remaining(); + int vuActualSize = bVU.remaining(); + int status = nativeAddImage(bY, bVU, yActualSize, vuActualSize, imageNum); + if(status != 0) { + Log.e(TAG, "Fail to add image"); + } + } + + @Override + public ResultImage processImage() { + Log("processImage "); + int[] roi = new int[4]; + int status = nativeProcessImage(mOutBuf.array(), (int) (mExpoTime / 1000000), mSenseValue, roi); + Log("processImage done"); + if(status < 0) { //In failure case, library will return the first image as it is. + Log.w(TAG, "Fail to process the image."); + } + return new ResultImage(mOutBuf, new Rect(roi[0], roi[1], roi[0]+roi[2], roi[1] + roi[3]), mWidth, mHeight, mStrideY); + } + + @Override + public boolean isSupported() { + return mIsSupported; + } + + @Override + public boolean isFrameListener() { + return false; + } + + @Override + public boolean isManualMode() { + return false; + } + + @Override + public void manualCapture(CaptureRequest.Builder builder, CameraCaptureSession captureSession, + CameraCaptureSession.CaptureCallback callback, Handler handler) { + + } + + public static boolean isSupportedStatic() { + return mIsSupported; + } + + private native int nativeInit(int width, int height, int yStride, int vuStride, + int roiX, int roiY, int roiW, int roiH, int numImages); + private native int nativeDeinit(); + private native int nativeAddImage(ByteBuffer yB, ByteBuffer vuB, int ySize, int vuSize, int imageNum); + private native int nativeProcessImage(byte[] buffer, int expoTime, int isoValue, int[] roi); + + static { + try { + System.loadLibrary("jni_stillmore"); + mIsSupported = true; + }catch(UnsatisfiedLinkError e) { + Log.d(TAG, e.toString()); + mIsSupported = false; + } + } +} diff --git a/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java b/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java index b410e01b4..c68b5091e 100644 --- a/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java +++ b/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java @@ -63,7 +63,7 @@ public class UbifocusFilter implements ImageFilter { private int mStrideY; private int mStrideVU; private static String TAG = "UbifocusFilter"; - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private static final int FOCUS_ADJUST_TIME_OUT = 200; private static final int META_BYTES_SIZE = 25; private int temp; @@ -76,6 +76,8 @@ public class UbifocusFilter implements ImageFilter { final String[] NAMES = {"00.jpg", "01.jpg", "02.jpg", "03.jpg", "04.jpg", "DepthMapImage.y", "AllFocusImage.jpg"}; + private int mSavedCount = 0; + private static void Log(String msg) { if (DEBUG) { Log.d(TAG, msg); @@ -122,18 +124,24 @@ public class UbifocusFilter implements ImageFilter { } @Override - public void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object param) { + public void addImage(final ByteBuffer bY, final ByteBuffer bVU, final int imageNum, Object param) { Log("addImage"); if(imageNum == 0) { mModule.setRefocusLastTaken(false); mOrientation = CameraUtil.getJpegRotation(mModule.getMainCameraId(), mModule.getDisplayOrientation()); + mSavedCount = 0; } - saveToPrivateFile(imageNum, nv21ToJpeg(bY, bVU, new Rect(0, 0, mWidth, mHeight), mOrientation)); int yActualSize = bY.remaining(); int vuActualSize = bVU.remaining(); if(nativeAddImage(bY, bVU, yActualSize, vuActualSize, imageNum) < 0) { Log.e(TAG, "Fail to add image"); } + new Thread() { + public void run() { + saveToPrivateFile(imageNum, nv21ToJpeg(bY, bVU, new Rect(0, 0, mWidth, mHeight), mOrientation)); + mSavedCount++; + } + }.start(); } @Override @@ -151,7 +159,12 @@ public class UbifocusFilter implements ImageFilter { saveToPrivateFile(NAMES.length - 1, nv21ToJpeg(mOutBuf, null, new Rect(roi[0], roi[1], roi[0] + roi[2], roi[1] + roi[3]), mOrientation)); mModule.setRefocusLastTaken(true); } - + while(mSavedCount < NUM_REQUIRED_IMAGE) { + try { + Thread.sleep(1); + } catch (Exception e) { + } + } ResultImage result = new ResultImage(mOutBuf, new Rect(roi[0], roi[1], roi[0]+roi[2], roi[1] + roi[3]), mWidth, mHeight, mStrideY); Log("processImage done"); return result; diff --git a/src/com/android/camera/ui/DotsView.java b/src/com/android/camera/ui/DotsView.java new file mode 100644 index 000000000..f08b96713 --- /dev/null +++ b/src/com/android/camera/ui/DotsView.java @@ -0,0 +1,98 @@ +/* + * 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.util.AttributeSet; +import android.view.View; + +public class DotsView extends View { + private Paint mTargetPaint; + private int mPosition; + private float mPositionOffset; + private DotsViewItem mItems; + + public DotsView(Context context, AttributeSet attrs) { + super(context, attrs); + mTargetPaint = new Paint(); + mTargetPaint.setColor(Color.WHITE); + mTargetPaint.setStyle(Paint.Style.FILL_AND_STROKE); + } + + public void update(int position, float positionOffest) { + mPosition = position; + mPositionOffset = positionOffest; + invalidate(); + } + + public void setItems(DotsViewItem item) { + mItems = item; + } + + public void update() { + invalidate(); + } + + @Override + public void onDraw(Canvas canvas) { + if(mItems == null) { + return; + } + + int dx = canvas.getWidth()/(mItems.getTotalItemNums()+1); + int dy = canvas.getHeight()/(mItems.getTotalItemNums()+1); + int y = canvas.getHeight()/2; + float radius = Math.min(dx, dy)/2f; + for(int i=0; i < mItems.getTotalItemNums(); i++) { + if(i-1 == mPosition && mPositionOffset > 0f) { + drawDot(canvas, (i + 1) * dx, y, radius + radius * mPositionOffset, mItems.isChosen(i)); + } else if(i+1 == mPosition && mPositionOffset < 0f) { + drawDot(canvas, (i + 1) * dx, y, radius - radius*mPositionOffset, mItems.isChosen(i)); + } else if(i == mPosition) { + drawDot(canvas, (i + 1) * dx, y, radius + radius * (1 - Math.abs(mPositionOffset)), mItems.isChosen(i)); + } else { + drawDot(canvas, (i + 1) * dx, y, radius, mItems.isChosen(i)); + } + } + } + + private void drawDot(Canvas canvas, float cx, float cy, float radius, boolean isChosen) { + if(isChosen) { + mTargetPaint.setStyle(Paint.Style.FILL_AND_STROKE); + canvas.drawCircle(cx, cy, radius, mTargetPaint); + } else { + mTargetPaint.setStyle(Paint.Style.STROKE); + canvas.drawCircle(cx, cy, radius, mTargetPaint); + } + } +} diff --git a/src/com/android/camera/ui/DotsViewItem.java b/src/com/android/camera/ui/DotsViewItem.java new file mode 100644 index 000000000..eb342938c --- /dev/null +++ b/src/com/android/camera/ui/DotsViewItem.java @@ -0,0 +1,35 @@ +/* + * 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; + +public interface DotsViewItem { + int getTotalItemNums(); + boolean isChosen(int index); +} diff --git a/src/com/android/camera/util/PersistUtil.java b/src/com/android/camera/util/PersistUtil.java index c9a5a474a..a39abaa82 100644 --- a/src/com/android/camera/util/PersistUtil.java +++ b/src/com/android/camera/util/PersistUtil.java @@ -35,6 +35,7 @@ public class PersistUtil { private static final String PERSIST_MEMORY_LIMIT = "persist.camera.perf.memlimit"; private static final String PERSIST_SKIP_MEMORY_CHECK = "persist.camera.perf.skip_memck"; private static final String PERSIST_LONGSHOT_SHOT_LIMIT = "persist.camera.longshot.shotnum"; + private static final String PERSIST_CAMERA_PREVIEW_SIZE = "persist.camera.preview.size"; public static int getMemoryLimit() { return SystemProperties.getInt(PERSIST_MEMORY_LIMIT, 60); @@ -48,4 +49,7 @@ public class PersistUtil { return SystemProperties.getInt(PERSIST_LONGSHOT_SHOT_LIMIT, 20); } + public static int getCameraPreviewSize() { + return SystemProperties.getInt(PERSIST_CAMERA_PREVIEW_SIZE, 0); + } } |