diff options
43 files changed, 5454 insertions, 1711 deletions
diff --git a/Android.mk b/Android.mk index f230c0742..de980e692 100644 --- a/Android.mk +++ b/Android.mk @@ -7,15 +7,13 @@ LOCAL_MODULE_TAGS := optional LOCAL_STATIC_JAVA_LIBRARIES := android-support-v13 LOCAL_STATIC_JAVA_LIBRARIES += xmp_toolkit - LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_SRC_FILES += $(call all-java-files-under, src_pd) LOCAL_SRC_FILES += $(call all-java-files-under, src_pd_gcam) +LOCAL_SRC_FILES += $(call all-renderscript-files-under, rs) LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res -LOCAL_CERTIFICATE := platform - include $(LOCAL_PATH)/version.mk LOCAL_AAPT_FLAGS := \ --auto-add-overlay \ @@ -25,6 +23,7 @@ LOCAL_AAPT_FLAGS := \ LOCAL_PACKAGE_NAME := SnapdragonCamera #LOCAL_SDK_VERSION := current +LOCAL_RENDERSCRIPT_TARGET_API := 23 LOCAL_OVERRIDES_PACKAGES := Camera2 @@ -34,9 +33,9 @@ LOCAL_PROGUARD_FLAG_FILES := proguard.flags # the libraries in the APK, otherwise just put them in /system/lib and # leave them out of the APK ifneq (,$(TARGET_BUILD_APPS)) - LOCAL_JNI_SHARED_LIBRARIES := libjni_snapcammosaic libjni_snapcamtinyplanet + LOCAL_JNI_SHARED_LIBRARIES := libjni_snapcammosaic libjni_snapcamtinyplanet libjni_imageutil else - LOCAL_REQUIRED_MODULES := libjni_snapcammosaic libjni_snapcamtinyplanet + LOCAL_REQUIRED_MODULES := libjni_snapcammosaic libjni_snapcamtinyplanet libjni_imageutil endif include $(BUILD_PACKAGE) diff --git a/jni/Android.mk b/jni/Android.mk index c94a8075d..de2abb6b8 100644 --- a/jni/Android.mk +++ b/jni/Android.mk @@ -63,3 +63,13 @@ LOCAL_ARM_MODE := arm include $(BUILD_SHARED_LIBRARY) +# ImageUtilForCamera2 with beautification +include $(CLEAR_VARS) +LOCAL_LDFLAGS := -llog +LOCAL_SDK_VERSION := 9 +LOCAL_MODULE := libjni_imageutil +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := image_util_jni.cpp +LOCAL_CFLAGS += -ffast-math -O3 -funroll-loops +include $(BUILD_SHARED_LIBRARY) + diff --git a/jni/image_util_jni.cpp b/jni/image_util_jni.cpp new file mode 100644 index 000000000..2297f9164 --- /dev/null +++ b/jni/image_util_jni.cpp @@ -0,0 +1,153 @@ +/* +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. +*/ + +#include <jni.h> +#include <assert.h> +#include <stdlib.h> + +#ifdef __ANDROID__ +#include "android/log.h" +#define printf(...) __android_log_print( ANDROID_LOG_ERROR, "ImageUtil", __VA_ARGS__ ) +#endif + +#ifdef __cplusplus +extern "C" { +#endif +JNIEXPORT jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeRotateNV21 + (JNIEnv* env, jobject thiz, jobjectArray inBuf, + 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); +#ifdef __cplusplus +} +#endif + +typedef unsigned char uint8_t; + +void rotateBufAndMerge(uint8_t *in_buf, jint imageWidth, jint imageHeight, jint degree, uint8_t *out_buf) +{ + if(degree == 90) { + int i = 0; + for (int x = 0; x < imageWidth; x++) { + for (int y = imageHeight - 1; y >= 0; y--) { + int offset = y * imageWidth + x; + out_buf[i] = in_buf[offset]; + i++; + } + } + i = imageWidth * imageHeight; + for (int x = 0; x < imageWidth; x += 2) { + for (int y = imageHeight / 2 - 1; y >= 0; y--) { + int offset = imageWidth*imageHeight + y * imageWidth + x; + out_buf[i] = in_buf[offset]; + i++; + out_buf[i] = in_buf[offset + 1]; + i++; + } + } + } else if(degree == 270) { + int i = 0; + for (int x = imageWidth - 1; x >= 0; x--) { + for (int y = 0; y < imageHeight; y++) { + int offset = y * imageWidth + x; + out_buf[i] = in_buf[offset]; + i++; + } + } + i = imageWidth * imageHeight; + for (int x = imageWidth - 2; x >= 0; x-=2) { + for (int y = 0; y < imageHeight/2; y++) { + int offset = imageWidth*imageHeight + y * imageWidth + x; + out_buf[i] = in_buf[offset]; + i++; + out_buf[i] = in_buf[offset + 1]; + i++; + } + } + } else if(degree == 180) { + int i = 0; + for (int y = imageHeight - 1; y >= 0; y--) { + for (int x = imageWidth - 1; x >= 0 ; x--) { + int offset = y * imageWidth + x; + out_buf[i] = in_buf[offset]; + i++; + } + } + i = imageWidth * imageHeight; + for (int y = imageHeight/2 - 1; y >= 0; y--) { + for (int x = imageWidth - 2; x >= 0 ; x-=2) { + int offset = imageWidth*imageHeight + y * imageWidth + x; + out_buf[i] = in_buf[offset]; + i++; + out_buf[i] = in_buf[offset + 1]; + i++; + } + } + } +} + +jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeRotateNV21( + JNIEnv* env, jobject thiz, jobjectArray inBuf, + jint imageWidth, jint imageHeight, jint degree, jobjectArray outBuf) +{ + uint8_t *in_buf = (uint8_t *)env->GetDirectBufferAddress(inBuf); + uint8_t *out_buf = (uint8_t *)env->GetDirectBufferAddress(outBuf); + rotateBufAndMerge(in_buf, imageWidth, imageHeight, degree, out_buf); + + return 0; +} + +jint JNICALL Java_com_android_camera_imageprocessor_FrameProcessor_nativeNV21toRgb( + JNIEnv* env, jobject thiz, jobjectArray yvuBuf, jobjectArray rgbBuf, jint width, jint height) +{ + uint8_t *in_buf = (uint8_t *)env->GetDirectBufferAddress(yvuBuf); + uint8_t *rgb_buf = (uint8_t *)env->GetDirectBufferAddress(rgbBuf); + int ysize = width * height; + int y_value; + int i, v, u, r, g, b; + for(int x=0; x < width; x++) { + for(int y=0; y < height; y++) { + y_value = (in_buf[y*width+x] & 0xFF); + i = ysize + (x/2*2) + ((y/2) * width); + v = (in_buf[i] & 0xFF) - 128; + u = (in_buf[i + 1] & 0xFF) - 128; + r = (int)(1.164f * y_value + 1.596f * v); + g = (int)(1.164f * y_value - 0.813f * v - 0.391f * u); + b = (int)(1.164f * y_value + 2.018f * u); + r = r > 255 ? 255 : r < 0 ? 0 : r; + g = g > 255 ? 255 : g < 0 ? 0 : g; + b = b > 255 ? 255 : b < 0 ? 0 : b; + rgb_buf[(y*width + x) * 4 + 3] = (uint8_t)(0xFF); + rgb_buf[(y*width + x) * 4 + 2] = (uint8_t)(b & 0xFF); + rgb_buf[(y*width + x) * 4 + 1] = (uint8_t)(g & 0xFF); + rgb_buf[(y*width + x) * 4 + 0] = (uint8_t)(r & 0xFF); + } + } + return 0; +}
\ No newline at end of file diff --git a/proguard.flags b/proguard.flags index 10fd77b6b..9d477262a 100644 --- a/proguard.flags +++ b/proguard.flags @@ -45,3 +45,15 @@ *** closeSilently(...); } +-keepclasseswithmembernames class * { + native <methods>; +} + +-keep class * { + public <methods>; + public <fields>; + private <methods>; + private <fields>; +} + +-keep class android.renderscript.** { *; } diff --git a/res/layout/capture_module.xml b/res/layout/capture_module.xml index a68af5e08..d91b49a97 100644 --- a/res/layout/capture_module.xml +++ b/res/layout/capture_module.xml @@ -36,7 +36,8 @@ <com.android.camera.ui.AutoFitSurfaceView android:id="@+id/mdp_preview_content" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:layout_gravity="center" /> <com.android.camera.ui.AutoFitSurfaceView android:id="@+id/mdp_preview_content2" diff --git a/res/layout/video_module.xml b/res/layout/video_module.xml index 20d7be1d2..2456caf82 100644 --- a/res/layout/video_module.xml +++ b/res/layout/video_module.xml @@ -70,8 +70,8 @@ <com.android.camera.PauseButton android:id="@+id/video_pause" android:layout_height="wrap_content" android:layout_width="wrap_content" - android:layout_marginLeft="70dp" - android:padding="23dp" + android:layout_marginLeft="50dp" + android:padding="38dp" android:src="@drawable/btn_pause_recording"/> <include layout="@layout/viewfinder_labels_video" android:id="@+id/labels" /> diff --git a/res/values/attrs.xml b/res/values/attrs.xml index b9c04f290..a243e5fa6 100644 --- a/res/values/attrs.xml +++ b/res/values/attrs.xml @@ -30,6 +30,7 @@ <attr name="entryValues" format="reference" /> <attr name="entries" format="reference" /> <attr name="labelList" format="reference" /> + <attr name="dependencyList" format="reference" /> </declare-styleable> <declare-styleable name="IconIndicator"> <attr name="icons" format="reference" /> diff --git a/res/values/camera2arrays.xml b/res/values/camera2arrays.xml new file mode 100644 index 000000000..49f37e139 --- /dev/null +++ b/res/values/camera2arrays.xml @@ -0,0 +1,574 @@ +<?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. +--> + +<resources> + <string-array name="pref_camera2_camera2_entries" translatable="true"> + <item>@string/pref_camera2_camera2_entry_disable</item> + <item>@string/pref_camera2_camera2_entry_enable</item> + </string-array> + + <string-array name="pref_camera2_camera2_entryvalues" translatable="false"> + <item>@string/pref_camera2_camera2_value_disable</item> + <item>@string/pref_camera2_camera2_value_enable</item> + </string-array> + + <string-array name="pref_camera2_mono_only_entries" translatable="true"> + <item>On</item> + <item>Off</item> + </string-array> + + <string-array name="pref_camera2_mono_only_entryvalues" translatable="false"> + <item>on</item> + <item>off</item> + </string-array> + + <string-array name="pref_camera2_initial_camera_entries" translatable="true"> + <item>Bayer</item> + <item>Mono</item> + <item>Front</item> + </string-array> + + <string-array name="pref_camera2_initial_camera_entryvalues" translatable="false"> + <item>bayer</item> + <item>mono</item> + <item>front</item> + </string-array> + + <string-array name="pref_camera2_makeup_entries" translatable="true"> + <item>On</item> + <item>Off</item> + </string-array> + + <string-array name="pref_camera2_makeup_entryvalues" translatable="false"> + <item>on</item> + <item>off</item> + </string-array> + + <string-array name="pref_camera2_mono_preview_entries" translatable="true"> + <item>@string/pref_camera2_mono_preview_entry_on</item> + <item>@string/pref_camera2_mono_preview_entry_off</item> + </string-array> + + <string-array name="pref_camera2_mono_preview_entryvalues" translatable="false"> + <item>@string/pref_camera2_mono_preview_value_on</item> + <item>@string/pref_camera2_mono_preview_value_off</item> + </string-array> + + <string-array name="pref_camera2_clearsight_entries" translatable="true"> + <item>@string/pref_camera2_clearsight_entry_off</item> + <item>@string/pref_camera2_clearsight_entry_on</item> + </string-array> + + <string-array name="pref_camera2_clearsight_entryvalues" translatable="false"> + <item>@string/pref_camera2_clearsight_value_off</item> + <item>@string/pref_camera2_clearsight_value_on</item> + </string-array> + + <!-- Refer to CONTROL_SCENE_MODE of Camera2 API for values + -1 refers to ones not supported in Camera2 API + 0 is special case added for auto (meaning off) + 100 is for dual mode (Custom-Scenemodes start from 100) + --> + <string-array name="pref_camera2_scenemode_entryvalues" translatable="false"> + <item>0</item> + <item>100</item> + <item>18</item> + <item>-1</item> + <item>101</item> + <item>3</item> + <item>4</item> + <item>13</item> + <item>-1</item> + <item>-1</item> + <item>15</item> + <item>10</item> + <item>5</item> + <item>8</item> + <item>9</item> + <item>-1</item> + </string-array> + + <!-- Camera Preferences Scene Mode dialog box entries --> + <string-array name="pref_camera2_scenemode_entries" translatable="false"> + <item>@string/pref_camera_scenemode_entry_auto</item> + <item>Dual</item> + <item>@string/pref_camera_scenemode_entry_hdr</item> + <item>@string/pref_camera_scenemode_entry_refocus</item> + <item>@string/pref_camera_scenemode_entry_optizoom</item> + <item>@string/pref_camera_scenemode_entry_portrait</item> + <item>@string/pref_camera_scenemode_entry_landscape</item> + <item>@string/pref_camera_scenemode_entry_sports</item> + <item>@string/pref_camera_scenemode_entry_flowers</item> + <item>@string/pref_camera_scenemode_entry_backlight</item> + <item>@string/pref_camera_scenemode_entry_candlelight</item> + <item>@string/pref_camera_scenemode_entry_sunset</item> + <item>@string/pref_camera_scenemode_entry_night</item> + <item>@string/pref_camera_scenemode_entry_beach</item> + <item>@string/pref_camera_scenemode_entry_snow</item> + <item>@string/pref_camera_scenemode_entry_asd</item> + </string-array> + + <array name="pref_camera2_scenemode_thumbnails" translatable="false"> + <item>@drawable/ic_scene_mode_auto</item> + <item>@drawable/ic_scene_mode_hdr</item> + <item>@drawable/ic_scene_mode_hdr</item> + <item>@drawable/ic_scene_mode_refocus</item> + <item>@drawable/ic_scene_mode_optizoom</item> + <item>@drawable/ic_scene_mode_portrait</item> + <item>@drawable/ic_scene_mode_landscape</item> + <item>@drawable/ic_scene_mode_sports</item> + <item>@drawable/ic_scene_mode_flower</item> + <item>@drawable/ic_scene_mode_backlight</item> + <item>@drawable/ic_scene_mode_candlelight</item> + <item>@drawable/ic_scene_mode_sunset</item> + <item>@drawable/ic_scene_mode_night</item> + <item>@drawable/ic_scene_mode_beach</item> + <item>@drawable/ic_scene_mode_snow</item> + <item>@drawable/ic_scene_mode_smartauto</item> + </array> + + <string-array name="pref_camera2_whitebalance_entryvalues" translatable="false"> + <item>1</item> + <item>2</item> + <item>4</item> + <item>5</item> + <item>6</item> + </string-array> + + <!-- Camera Preferences White Balance dialog box entries --> + <string-array name="pref_camera2_whitebalance_entries" translatable="false"> + <item>@string/pref_camera_whitebalance_entry_auto</item> + <item>@string/pref_camera_whitebalance_entry_incandescent</item> + <item>@string/pref_camera_whitebalance_entry_fluorescent</item> + <item>@string/pref_camera_whitebalance_entry_daylight</item> + <item>@string/pref_camera_whitebalance_entry_cloudy</item> + </string-array> + + <string-array name="pref_camera2_whitebalance_labels" translatable="false"> + <item>@string/pref_camera_whitebalance_label_auto</item> + <item>@string/pref_camera_whitebalance_label_incandescent</item> + <item>@string/pref_camera_whitebalance_label_fluorescent</item> + <item>@string/pref_camera_whitebalance_label_daylight</item> + <item>@string/pref_camera_whitebalance_label_cloudy</item> + </string-array> + + <array name="pref_camera2_whitebalance_icons" translatable="false"> + <item>@drawable/ic_wb_auto</item> + <item>@drawable/ic_wb_incandescent</item> + <item>@drawable/ic_wb_fluorescent</item> + <item>@drawable/ic_wb_sunlight</item> + <item>@drawable/ic_wb_cloudy</item> + </array> + + <array name="pref_camera2_whitebalance_largeicons" translatable="false"> + <item>@drawable/ic_wb_incandescent</item> + <item>@drawable/ic_wb_fluorescent</item> + <item>@drawable/ic_wb_auto</item> + <item>@drawable/ic_wb_sunlight</item> + <item>@drawable/ic_wb_cloudy</item> + </array> + + <!-- Refer to CONTROL_EFFECT_MODE of Camera2 API for values + -1 refers to ones not supported in Camera2 API + --> + <string-array name="pref_camera2_coloreffect_entryvalues" translatable="false"> + <item>0</item> + <item>1</item> + <item>4</item> + <item>2</item> + <item>3</item> + <item>5</item> + <item>8</item> + <item>-1</item> + <item>-1</item> + <item>-1</item> + <item>-1</item> + <item>-1</item> + <item>-1</item> + <item>-1</item> + <item>-1</item> + </string-array> + + <string-array name="pref_camera2_coloreffect_icons" translatable="false"> + <item>@drawable/ic_settings_filter</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + <item>@drawable/ic_settings_filter_on</item> + </string-array> + + <!-- Camera Preferences Color effect dialog box entries --> + <string-array name="pref_camera2_coloreffect_entries" translatable="false"> + <item>@string/pref_camera_coloreffect_entry_none</item> + <item>@string/pref_camera_coloreffect_entry_mono</item> + <item>@string/pref_camera_coloreffect_entry_sepia</item> + <item>@string/pref_camera_coloreffect_entry_negative</item> + <item>@string/pref_camera_coloreffect_entry_solarize</item> + <item>@string/pref_camera_coloreffect_entry_posterize</item> + <item>@string/pref_camera_coloreffect_entry_aqua</item> + <item>@string/pref_camera_coloreffect_entry_emboss</item> + <item>@string/pref_camera_coloreffect_entry_sketch</item> + <item>@string/pref_camera_coloreffect_entry_neon</item> + <item>@string/pref_camera_coloreffect_entry_pastel</item> + <item>@string/pref_camera_coloreffect_entry_mosaic</item> + <item>@string/pref_camera_coloreffect_entry_redtint</item> + <item>@string/pref_camera_coloreffect_entry_bluetint</item> + <item>@string/pref_camera_coloreffect_entry_greentint</item> + </string-array> + + <array name="pref_camera2_coloreffect_thumbnails" translatable="false"> + <item>@drawable/thumb_filter_nofilter</item> + <item>@drawable/thumb_filter_monochrome</item> + <item>@drawable/thumb_filter_sepia</item> + <item>@drawable/thumb_filter_negative</item> + <item>@drawable/thumb_filter_solarize</item> + <item>@drawable/thumb_filter_posterize</item> + <item>@drawable/thumb_filter_aqua</item> + <item>@drawable/thumb_filter_emboss</item> + <item>@drawable/thumb_filter_sketch</item> + <item>@drawable/thumb_filter_neon</item> + <item>0</item> + <item>0</item> + <item>0</item> + <item>0</item> + <item>0</item> + </array> + + <!-- Camera Preferences flash mode dialog box entries --> + <string-array name="pref_camera2_flashmode_entries" translatable="false"> + <item>@string/pref_camera_flashmode_entry_off</item> + <item>@string/pref_camera_flashmode_entry_auto</item> + <item>@string/pref_camera_flashmode_entry_on</item> + </string-array> + + <string-array name="pref_camera2_flashmode_labels" translatable="false"> + <item>@string/pref_camera_flashmode_label_off</item> + <item>@string/pref_camera_flashmode_label_auto</item> + <item>@string/pref_camera_flashmode_label_on</item> + </string-array> + + <string-array name="pref_camera2_flashmode_entryvalues" translatable="false"> + <item>1</item> + <item>2</item> + <item>3</item> + </string-array> + + <array name="pref_camera2_flashmode_icons" translatable="false"> + <item>@drawable/ic_flash_off_holo_light</item> + <item>@drawable/ic_flash_auto_holo_light</item> + <item>@drawable/ic_flash_on_holo_light</item> + </array> + + <array name="pref_camera2_flashmode_largeicons" translatable="false"> + <item>@drawable/ic_flash_off_holo_light</item> + <item>@drawable/ic_flash_auto_holo_light</item> + <item>@drawable/ic_flash_on_holo_light</item> + </array> + + <!-- Camera Preference save path entries --> + <string-array name="pref_camera2_savepath_entries" translatable="false"> + <item>@string/pref_camera_savepath_entry_0</item> + <item>@string/pref_camera_savepath_entry_1</item> + </string-array> + + <string-array name="pref_camera2_savepath_entryvalues" translatable="false"> + <item>0</item> + <item>1</item> + </string-array> + + <!-- Camera Preferences Picture size dialog box entries --> + <string-array name="pref_camera2_picturesize_entries" translatable="false"> + <item>@string/pref_camera_picturesize_entry_24mp</item> + <item>@string/pref_camera_picturesize_entry_21mp</item> + <item>@string/pref_camera_picturesize_entry_16mp</item> + <item>@string/pref_camera_picturesize_entry_16mp_wide</item> + <item>@string/pref_camera_picturesize_entry_13mp</item> + <item>@string/pref_camera_picturesize_entry_12mp</item> + <item>@string/pref_camera_picturesize_entry_8mp</item> + <item>@string/pref_camera_picturesize_entry_8mp</item> + <item>@string/pref_camera_picturesize_entry_square</item> + <item>@string/pref_camera_picturesize_entry_5mp</item> + <item>@string/pref_camera_picturesize_entry_5mp</item> + <item>@string/pref_camera_picturesize_entry_5mp</item> + <item>@string/pref_camera_picturesize_entry_4mp_wide</item> + <item>@string/pref_camera_picturesize_entry_3mp</item> + <item>@string/pref_camera_picturesize_entry_3mp</item> + <item>@string/pref_camera_picturesize_entry_1920x1080</item> + <item>@string/pref_camera_picturesize_entry_2mp</item> + <item>@string/pref_camera_picturesize_entry_2mp_wide</item> + <item>@string/pref_camera_picturesize_entry_1_5mp</item> + <item>@string/pref_camera_picturesize_entry_1_3mp</item> + <item>@string/pref_camera_picturesize_entry_1280x768</item> + <item>@string/pref_camera_picturesize_entry_1280x720</item> + <item>@string/pref_camera_picturesize_entry_1mp</item> + <item>@string/pref_camera_picturesize_entry_800x600</item> + <item>@string/pref_camera_picturesize_entry_800x480</item> + <item>@string/pref_camera_picturesize_entry_960x720</item> + <item>@string/pref_camera_picturesize_entry_720x480</item> + <item>@string/pref_camera_picturesize_entry_vga</item> + <item>@string/pref_camera_picturesize_entry_352x288</item> + <item>@string/pref_camera_picturesize_entry_qvga</item> + <item>@string/pref_camera_picturesize_entry_176x144</item> + </string-array> + <!-- When launching the camera app first time, we will set the picture + size to the first one in the list that is also supported by the + driver --> + <string-array name="pref_camera2_picturesize_entryvalues" translatable="false"> + <item>5656x4242</item> + <item>5344x4008</item> + <item>4608x3456</item> + <item>5312x2988</item> + <item>4160x3120</item> + <item>4000x3000</item> + <item>3840x2160</item> + <item>3264x2448</item> + <item>2976x2976</item> + <item>2592x1944</item> + <item>2592x1936</item> + <item>2560x1920</item> + <item>2688x1512</item> + <item>2048x1536</item> + <item>2048x1520</item> + <item>1920x1080</item> + <item>1600x1200</item> + <item>1920x1088</item> + <item>1440x1080</item> + <item>1280x960</item> + <item>1280x768</item> + <item>1280x720</item> + <item>1024x768</item> + <item>800x600</item> + <item>800x480</item> + <item>960x720</item> + <item>720x480</item> + <item>640x480</item> + <item>352x288</item> + <item>320x240</item> + <item>176x144</item> + </string-array> + + <!-- Camera Preferences focus mode dialog box entries --> + <string-array name="pref_camera2_focusmode_entries" translatable="false"> + <item>@string/pref_camera_focusmode_entry_auto</item> + <item>@string/pref_camera_focusmode_entry_infinity</item> + <item>@string/pref_camera_focusmode_entry_macro</item> + <item>@string/pref_camera_focusmode_entry_normal</item> + <item>@string/pref_camera_focusmode_entry_continuous</item> + </string-array> + + <string-array name="pref_camera2_focusmode_entryvalues" translatable="false"> + <item>auto</item> + <item>infinity</item> + <item>macro</item> + <item>normal</item> + <item>continuous-picture</item> + </string-array> + + <string-array name="pref_camera2_focusmode_labels" translatable="false"> + <item>@string/pref_camera_focusmode_label_auto</item> + <item>@string/pref_camera_focusmode_label_infinity</item> + <item>@string/pref_camera_focusmode_label_macro</item> + </string-array> + + <string-array name="pref_camera2_recordlocation_entryvalues" translatable="false"> + <item>off</item> + <item>on</item> + </string-array> + + <array name="pref_camera2_recordlocation_entries" translatable="false"> + <item>@string/setting_off</item> + <item>@string/setting_on</item> + </array> + + <array name="pref_camera2_recordlocation_labels" translatable="false"> + <item>@string/pref_camera_location_label</item> + <item>@string/pref_camera_location_label</item> + </array> + + <array name="pref_camera2_recordlocation_icons" translatable="false"> + <item>@drawable/ic_location_off</item> + <item>@drawable/ic_location</item> + </array> + + <array name="pref_camera2_recordlocation_largeicons" translatable="false"> + <item>@drawable/ic_location_off</item> + <item>@drawable/ic_location</item> + </array> + + <array name="pref_camera2_id_entries" translatable="false"> + <item>@string/pref_camera_id_entry_back</item> + <item>@string/pref_camera_id_entry_front</item> + </array> + + <array name="pref_camera2_id_labels" translatable="false"> + <item>@string/pref_camera_id_label_back</item> + <item>@string/pref_camera_id_label_front</item> + </array> + + <array name="pref_camera2_id_icons" translatable="false"> + <item>@drawable/ic_switch_back</item> + <item>@drawable/ic_switch_front</item> + </array> + + <array name="pref_camera2_id_largeicons" translatable="false"> + <item>@drawable/ic_switch_back</item> + <item>@drawable/ic_switch_front</item> + </array> + + <string-array name="pref_camera2_timer_sound_entries" translatable="false"> + <item>@string/setting_off</item> + <item>@string/setting_on</item> + </string-array> + + <string-array name="pref_camera2_timer_sound_entryvalues" translatable="false"> + <item>@string/setting_off_value</item> + <item>@string/setting_on_value</item> + </string-array> + + <!-- Icons for exposure compensation --> + <array name="pref_camera2_exposure_icons" translatable="false"> + <item>@drawable/ic_exposure_n3</item> + <item>@drawable/ic_exposure_n2</item> + <item>@drawable/ic_exposure_n1</item> + <item>@drawable/ic_exposure_0</item> + <item>@drawable/ic_exposure_p1</item> + <item>@drawable/ic_exposure_p2</item> + <item>@drawable/ic_exposure_p3</item> + </array> + + <!-- Labels for Countdown timer --> + <string-array name="pref_camera2_countdown_labels"> + <item>@string/pref_camera_countdown_label_off</item> + <item>@string/pref_camera_countdown_label_one</item> + <item>@string/pref_camera_countdown_label_three</item> + <item>@string/pref_camera_countdown_label_ten</item> + <item>@string/pref_camera_countdown_label_fifteen</item> + </string-array> + + <!-- Camera Preferences JPEG quality dialog box entries --> + <string-array name="pref_camera2_jpegquality_entries" translatable="false"> + <item>@string/pref_camera_jpegquality_entry_0</item> + <item>@string/pref_camera_jpegquality_entry_1</item> + <item>@string/pref_camera_jpegquality_entry_2</item> + </string-array> + + <string-array name="pref_camera2_jpegquality_entryvalues" translatable="false"> + <item>55</item> + <item>85</item> + <item>100</item> + </string-array> + + <!-- Rough estimates of jpeg compression ratio corresponding to qualities defined above. --> + <integer-array name="pref_camera2_jpegquality_compression_ratio"> + <item>48</item> + <item>20</item> + <item>6</item> + </integer-array> + + <!-- Camera Preferences ISO dialog box entries --> + <string-array name="pref_camera2_iso_entries"> + <item>@string/pref_camera_iso_entry_auto</item> + <item>@string/pref_camera_iso_entry_iso100</item> + <item>@string/pref_camera_iso_entry_iso200</item> + <item>@string/pref_camera_iso_entry_iso400</item> + <item>@string/pref_camera_iso_entry_iso800</item> + <item>@string/pref_camera_iso_entry_iso1600</item> + </string-array> + + <!-- Do not localize entryvalues --> + <string-array name="pref_camera2_iso_entryvalues"> + <item>auto</item> + <item>100</item> + <item>200</item> + <item>400</item> + <item>800</item> + <item>1600</item> + </string-array> + + <!-- Camera Preferences Auto Exposure dialog box entries --> + <string-array name="pref_camera2_autoexposure_entries"> + <item>@string/pref_camera_autoexposure_entry_frameaverage</item> + <item>@string/pref_camera_autoexposure_entry_centerweighted</item> + <item>@string/pref_camera_autoexposure_entry_spotmetering</item> + </string-array> + + <!-- Do not localize entryvalues --> + <string-array name="pref_camera2_autoexposure_entryvalues"> + <item>@string/pref_camera_autoexposure_value_frameaverage</item> + <item>@string/pref_camera_autoexposure_value_centerweighted</item> + <item>@string/pref_camera_autoexposure_value_spotmetering</item> + </string-array> + + <!-- Camera Preferences Redeye Reduction dialog box entries --> + <string-array name="pref_camera2_redeyereduction_entries" translatable="false"> + <item>@string/pref_camera_redeyereduction_entry_disable</item> + <item>@string/pref_camera_redeyereduction_entry_enable</item> + </string-array> + + <string-array name="pref_camera2_redeyereduction_entryvalues" translatable="false"> + <item>disable</item> + <item>enable</item> + </string-array> + + <!-- Camera Preferences Long Shot dialog box entries --> + <string-array name="pref_camera2_longshot_entries" translatable="false"> + <item>@string/setting_off</item> + <item>@string/setting_on</item> + </string-array> + + <string-array name="pref_camera2_longshot_entryvalues" translatable="false"> + <item>@string/setting_off_value</item> + <item>@string/setting_on_value</item> + </string-array> + + <string-array name="pref_camera2_filter_mode_entries" translatable="false"> + <item>@string/pref_camera_filter_mode_entry_off</item> + <item>@string/pref_camera_filter_mode_entry_on</item> + </string-array> + <string-array name="pref_camera2_filter_mode_entryvalues" translatable="false"> + <item>Off</item> + <item>On</item> + </string-array> + + <string-array name="pref_camera2_filter_mode_icons" translatable="false"> + <item>@drawable/ic_settings_filter</item> + <item>@drawable/ic_settings_filter_on</item> + </string-array> + +</resources> diff --git a/res/values/qcomarrays.xml b/res/values/qcomarrays.xml index d3218bdaf..7574992b9 100644 --- a/res/values/qcomarrays.xml +++ b/res/values/qcomarrays.xml @@ -833,55 +833,15 @@ </string-array> <string-array name="pref_camera_instant_capture_entries" translatable="true"> - <item>@string/pref_camera_instant_capture_entry_enable</item> + <item>@string/pref_camera_instant_capture_entry_aggressive_aec</item> + <item>@string/pref_camera_instant_capture_entry_fast_aec</item> <item>@string/pref_camera_instant_capture_entry_disable</item> </string-array> <string-array name="pref_camera_instant_capture_entry_values" translatable="false"> - <item>@string/pref_camera_instant_capture_value_enable</item> + <item>@string/pref_camera_instant_capture_value_aggressive_aec</item> + <item>@string/pref_camera_instant_capture_value_fast_aec</item> <item>@string/pref_camera_instant_capture_value_disable</item> </string-array> - - <string-array name="pref_camera_camera2_entries" translatable="true"> - <item>@string/pref_camera_camera2_entry_enable</item> - <item>@string/pref_camera_camera2_entry_disable</item> - </string-array> - - <string-array name="pref_camera_camera2_entryvalues" translatable="false"> - <item>@string/pref_camera_camera2_value_enable</item> - <item>@string/pref_camera_camera2_value_disable</item> - </string-array> - - <string-array name="pref_camera_dual_camera_entries" translatable="true"> - <item>@string/pref_camera_dual_camera_entry_dual</item> - <item>@string/pref_camera_dual_camera_entry_bayer</item> - <item>@string/pref_camera_dual_camera_entry_mono</item> - </string-array> - - <string-array name="pref_camera_dual_camera_entryvalues" translatable="false"> - <item>@string/pref_camera_dual_camera_value_dual</item> - <item>@string/pref_camera_dual_camera_value_bayer</item> - <item>@string/pref_camera_dual_camera_value_mono</item> - </string-array> - - <string-array name="pref_camera_mono_preview_entries" translatable="true"> - <item>@string/pref_camera_mono_preview_entry_on</item> - <item>@string/pref_camera_mono_preview_entry_off</item> - </string-array> - - <string-array name="pref_camera_mono_preview_entryvalues" translatable="false"> - <item>@string/pref_camera_mono_preview_value_on</item> - <item>@string/pref_camera_mono_preview_value_off</item> - </string-array> - - <string-array name="pref_camera_clearsight_entries" translatable="true"> - <item>@string/pref_camera_clearsight_entry_on</item> - <item>@string/pref_camera_clearsight_entry_off</item> - </string-array> - - <string-array name="pref_camera_clearsight_entryvalues" translatable="false"> - <item>@string/pref_camera_clearsight_value_on</item> - <item>@string/pref_camera_clearsight_value_off</item> - </string-array> </resources> diff --git a/res/values/qcomstrings.xml b/res/values/qcomstrings.xml index ea90cf72a..a808ba354 100644 --- a/res/values/qcomstrings.xml +++ b/res/values/qcomstrings.xml @@ -202,12 +202,14 @@ <!-- Instant Capture entry --> <string name="pref_camera_instant_capture_title" translatable="true">Instant Capture</string> - <string name="pref_camera_instant_capture_entry_enable" translatable="true">Enable</string> + <string name="pref_camera_instant_capture_entry_aggressive_aec" translatable="true">Aggressive AEC</string> + <string name="pref_camera_instant_capture_entry_fast_aec" translatable="true">Fast AEC</string> <string name="pref_camera_instant_capture_entry_disable" translatable="true">Disable</string> <!-- Instant Capture entry values --> <string name="pref_camera_instant_capture_default" translatable="false">0</string> - <string name="pref_camera_instant_capture_value_enable" translatable="false">1</string> + <string name="pref_camera_instant_capture_value_aggressive_aec" translatable="false">1</string> + <string name="pref_camera_instant_capture_value_fast_aec" translatable="false">2</string> <string name="pref_camera_instant_capture_value_disable" translatable="false">0</string> <!-- Settings screen, ZSL location dialog choices --> @@ -773,6 +775,8 @@ <string name="pref_camera_picturesize_entry_1280x720">HD720</string> <string name="pref_camera_picturesize_entry_800x600">SVGA</string> <string name="pref_camera_picturesize_entry_800x480">WVGA</string> + <string name="pref_camera_picturesize_entry_960x720">960 x 720</string> + <string name="pref_camera_picturesize_entry_720x480">720 x 480</string> <string name="pref_camera_picturesize_entry_352x288">CIF</string> <string name="pref_camera_picturesize_entry_176x144">QCIF</string> @@ -945,41 +949,38 @@ <string name="help_menu_switcher_2" translatable="true">between</string> <string name="help_menu_switcher_3" translatable="true">camera, video, and panorama</string> - <string name="pref_camera_camera2_title">Camera2 Mode</string> - <string name="pref_camera_camera2_default">disable</string> - <string name="pref_camera_camera2_entry_enable">Enable</string> - <string name="pref_camera_camera2_entry_disable">Disable</string> + <string name="pref_camera2_camera2_title" translatable="true">Camera2 Mode</string> + <string name="pref_camera2_camera2_entry_enable" translatable="true">Enable</string> + <string name="pref_camera2_camera2_entry_disable" translatable="true">Disable</string> + <string name="pref_camera2_camera2_default" translatable="false">disable</string> + <string name="pref_camera2_camera2_value_enable" translatable="false">enable</string> + <string name="pref_camera2_camera2_value_disable" translatable="false">disable</string> - <string name="pref_camera_camera2_value_enable">enable</string> - <string name="pref_camera_camera2_value_disable">disable</string> + <string name="pref_camera2_mono_only_title" translatable="true">Mono Only</string> + <string name="pref_camera2_mono_only_default" translatable="false">off</string> - <string name="pref_camera_dual_camera_title">Dual Camera Mode</string> - <string name="pref_camera_dual_camera_default">dual</string> - <string name="pref_camera_dual_camera_entry_dual">Dual-camera Linked</string> - <string name="pref_camera_dual_camera_entry_bayer">Single Bayer Camera</string> - <string name="pref_camera_dual_camera_entry_mono">Single Mono Camera</string> + <string name="pref_camera2_mono_preview_title" translatable="true">Mono Preview</string> + <string name="pref_camera2_mono_preview_entry_on" translatable="true">On</string> + <string name="pref_camera2_mono_preview_entry_off" translatable="true">Off</string> + <string name="pref_camera2_mono_preview_default" translatable="false">off</string> + <string name="pref_camera2_mono_preview_value_on" translatable="false">on</string> + <string name="pref_camera2_mono_preview_value_off" translatable="false">off</string> - <string name="pref_camera_dual_camera_value_dual">dual</string> - <string name="pref_camera_dual_camera_value_bayer">bayer</string> - <string name="pref_camera_dual_camera_value_mono">mono</string> + <string name="pref_camera2_clearsight_title" translatable="true">ClearSight</string> + <string name="pref_camera2_clearsight_default" translatable="false">on</string> + <string name="pref_camera2_clearsight_entry_on" translatable="false">On</string> + <string name="pref_camera2_clearsight_entry_off" translatable="false">Off</string> - <string name="pref_camera_mono_preview_title">Mono Preview</string> - <string name="pref_camera_mono_preview_default">off</string> - <string name="pref_camera_mono_preview_entry_on">On</string> - <string name="pref_camera_mono_preview_entry_off">Off</string> - - <string name="pref_camera_mono_preview_value_on">on</string> - <string name="pref_camera_mono_preview_value_off">off</string> - - <string name="pref_camera_clearsight_title">ClearSight</string> - <string name="pref_camera_clearsight_default" translatable="false">off</string> - <string name="pref_camera_clearsight_entry_on">On</string> - <string name="pref_camera_clearsight_entry_off">Off</string> - - <string name="pref_camera_clearsight_value_on" translatable="false">on</string> - <string name="pref_camera_clearsight_value_off" translatable="false">off</string> + <string name="pref_camera2_clearsight_value_on" translatable="false">on</string> + <string name="pref_camera2_clearsight_value_off" translatable="false">off</string> <string name="clearsight_capture_success">ClearSight capture successful</string> <string name="clearsight_capture_fail">ClearSight capture failed</string> + + <string name="pref_camera2_scenemode_default" translatable="false">0</string> + <string name="pref_camera2_whitebalance_default" translatable="false">1</string> + <string name="pref_camera2_coloreffect_default" translatable="false">0</string> + <string name="pref_camera2_flashmode_default" translatable="false">2</string> + <string name="pref_camera2_makeup_title" translatable="true">Makeup</string> </resources> diff --git a/res/values/styles.xml b/res/values/styles.xml index bb13cc5c8..4a7284874 100644 --- a/res/values/styles.xml +++ b/res/values/styles.xml @@ -129,7 +129,7 @@ <item name="android:layout_width">wrap_content</item> <item name="android:layout_height">wrap_content</item> <item name="android:singleLine">true</item> - <item name="android:layout_marginTop">23dp</item> + <item name="android:layout_marginTop">38dp</item> <item name="android:paddingLeft">15dp</item> <item name="android:paddingRight">15dp</item> <item name="android:paddingTop">3dp</item> diff --git a/res/xml/camera_preferences.xml b/res/xml/camera_preferences.xml index 5fafb855b..7e024b298 100644 --- a/res/xml/camera_preferences.xml +++ b/res/xml/camera_preferences.xml @@ -367,31 +367,9 @@ camera:entryValues="@array/pref_camera_instant_capture_entry_values" /> <ListPreference - camera:key="pref_camera_camera2_key" - camera:defaultValue="@string/pref_camera_camera2_default" - camera:title="@string/pref_camera_camera2_title" - camera:entries="@array/pref_camera_camera2_entries" - camera:entryValues="@array/pref_camera_camera2_entryvalues" /> - - <ListPreference - camera:key="pref_camera_dual_camera_key" - camera:defaultValue="@string/pref_camera_dual_camera_default" - camera:title="@string/pref_camera_dual_camera_title" - camera:entries="@array/pref_camera_dual_camera_entries" - camera:entryValues="@array/pref_camera_dual_camera_entryvalues" /> - - <ListPreference - camera:defaultValue="@string/pref_camera_mono_preview_default" - camera:entries="@array/pref_camera_mono_preview_entries" - camera:entryValues="@array/pref_camera_mono_preview_entryvalues" - camera:key="pref_camera_mono_preview_key" - camera:title="@string/pref_camera_mono_preview_title" /> - - <ListPreference - camera:defaultValue="@string/pref_camera_clearsight_default" - camera:entries="@array/pref_camera_clearsight_entries" - camera:entryValues="@array/pref_camera_clearsight_entryvalues" - camera:key="pref_camera_clearsight_key" - camera:title="@string/pref_camera_clearsight_title" /> - + camera:key="pref_camera2_camera2_key" + camera:defaultValue="@string/pref_camera2_camera2_default" + camera:title="@string/pref_camera2_camera2_title" + camera:entries="@array/pref_camera2_camera2_entries" + camera:entryValues="@array/pref_camera2_camera2_entryvalues" /> </PreferenceGroup> diff --git a/res/xml/capture_preferences.xml b/res/xml/capture_preferences.xml new file mode 100644 index 000000000..5e9235e34 --- /dev/null +++ b/res/xml/capture_preferences.xml @@ -0,0 +1,193 @@ +<?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. +--> + +<PreferenceGroup + xmlns:camera="http://schemas.android.com/apk/res/org.codeaurora.snapcam" + camera:title="@string/pref_camera_settings_category"> + <IconListPreference + camera:defaultValue="@string/pref_camera2_flashmode_default" + camera:entries="@array/pref_camera2_flashmode_entries" + camera:entryValues="@array/pref_camera2_flashmode_entryvalues" + camera:icons="@array/pref_camera2_flashmode_icons" + camera:key="pref_camera2_flashmode_key" + camera:labelList="@array/pref_camera2_flashmode_labels" + camera:largeIcons="@array/pref_camera2_flashmode_largeicons" + camera:singleIcon="@drawable/ic_settings_flash" + camera:title="@string/pref_camera_flashmode_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera2_whitebalance_default" + camera:entries="@array/pref_camera2_whitebalance_entries" + camera:entryValues="@array/pref_camera2_whitebalance_entryvalues" + camera:icons="@array/pref_camera2_whitebalance_icons" + camera:key="pref_camera2_whitebalance_key" + camera:labelList="@array/pref_camera2_whitebalance_labels" + camera:largeIcons="@array/pref_camera2_whitebalance_largeicons" + camera:singleIcon="@drawable/ic_settings_lightsource" + camera:title="@string/pref_camera_whitebalance_title"/> + + <RecordLocationPreference + camera:defaultValue="@string/pref_camera_recordlocation_default" + camera:entries="@array/pref_camera2_recordlocation_entries" + camera:entryValues="@array/pref_camera2_recordlocation_entryvalues" + camera:icons="@array/pref_camera2_recordlocation_icons" + camera:key="pref_camera2_recordlocation_key" + camera:labelList="@array/pref_camera2_recordlocation_labels" + camera:largeIcons="@array/pref_camera2_recordlocation_largeicons" + camera:singleIcon="@drawable/ic_settings_location" + camera:title="@string/pref_camera_recordlocation_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_jpegquality_default" + camera:entries="@array/pref_camera2_jpegquality_entries" + camera:entryValues="@array/pref_camera2_jpegquality_entryvalues" + camera:key="pref_camera2_jpegquality_key" + camera:singleIcon="@drawable/ic_settings_quality" + camera:title="@string/pref_camera_jpegquality_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_savepath_default" + camera:entries="@array/pref_camera2_savepath_entries" + camera:entryValues="@array/pref_camera2_savepath_entryvalues" + camera:key="pref_camera2_savepath_key" + camera:singleIcon="@drawable/ic_settings_storage" + camera:title="@string/pref_camera_savepath_title"/> + + <ListPreference + camera:defaultValue="@string/pref_camera2_camera2_default" + camera:entries="@array/pref_camera2_camera2_entries" + camera:entryValues="@array/pref_camera2_camera2_entryvalues" + camera:key="pref_camera2_camera2_key" + camera:title="@string/pref_camera2_camera2_title"/> + + <ListPreference + camera:defaultValue="@string/pref_camera2_mono_only_default" + camera:entries="@array/pref_camera2_mono_only_entries" + camera:entryValues="@array/pref_camera2_mono_only_entryvalues" + camera:key="pref_camera2_mono_only_key" + camera:title="@string/pref_camera2_mono_only_title"/> + + <ListPreference + camera:defaultValue="@string/pref_camera2_mono_preview_default" + camera:entries="@array/pref_camera2_mono_preview_entries" + camera:entryValues="@array/pref_camera2_mono_preview_entryvalues" + camera:key="pref_camera2_mono_preview_key" + camera:title="@string/pref_camera2_mono_preview_title"/> + + <ListPreference + camera:defaultValue="@string/pref_camera2_clearsight_default" + camera:entries="@array/pref_camera2_clearsight_entries" + camera:entryValues="@array/pref_camera2_clearsight_entryvalues" + camera:key="pref_camera2_clearsight_key" + camera:title="@string/pref_camera2_clearsight_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera2_coloreffect_default" + camera:entries="@array/pref_camera2_coloreffect_entries" + camera:entryValues="@array/pref_camera2_coloreffect_entryvalues" + camera:key="pref_camera2_coloreffect_key" + camera:largeIcons="@array/pref_camera2_coloreffect_icons" + camera:singleIcon="@drawable/ic_settings_filter" + camera:thumbnails="@array/pref_camera2_coloreffect_thumbnails" + camera:title="@string/pref_camera_coloreffect_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera2_scenemode_default" + camera:entries="@array/pref_camera2_scenemode_entries" + camera:entryValues="@array/pref_camera2_scenemode_entryvalues" + camera:key="pref_camera2_scenemode_key" + camera:singleIcon="@drawable/ic_settings_scenemode" + camera:thumbnails="@array/pref_camera2_scenemode_thumbnails" + camera:title="@string/pref_camera_scenemode_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_redeyereduction_default" + camera:entries="@array/pref_camera2_redeyereduction_entries" + camera:entryValues="@array/pref_camera2_redeyereduction_entryvalues" + camera:key="pref_camera2_redeyereduction_key" + camera:singleIcon="@drawable/ic_settings_redeye" + camera:title="@string/pref_camera_redeyereduction_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_id_default" + camera:entries="@array/pref_camera2_id_entries" + camera:icons="@array/pref_camera2_id_icons" + camera:key="pref_camera2_id_key" + camera:labelList="@array/pref_camera2_id_labels" + camera:largeIcons="@array/pref_camera2_id_largeicons" + camera:title="@string/pref_camera_id_title"/> + + <IconListPreference + camera:entries="@array/pref_camera2_picturesize_entries" + camera:entryValues="@array/pref_camera2_picturesize_entryvalues" + camera:key="pref_camera2_picturesize_key" + camera:singleIcon="@drawable/ic_settings_picturesize" + camera:title="@string/pref_camera_picturesize_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_exposure_default" + camera:key="pref_camera2_exposure_key" + camera:singleIcon="@drawable/ic_settings_exposure" + camera:title="@string/pref_exposure_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_iso_default" + camera:entries="@array/pref_camera2_iso_entries" + camera:entryValues="@array/pref_camera2_iso_entryvalues" + camera:key="pref_camera2_iso_key" + camera:singleIcon="@drawable/ic_settings_iso" + camera:title="@string/pref_camera_iso_title"/> + + <IconListPreference + camera:defaultValue="@string/pref_camera_longshot_default" + camera:entries="@array/pref_camera2_longshot_entries" + camera:entryValues="@array/pref_camera2_longshot_entryvalues" + camera:key="pref_camera2_longshot_key" + camera:singleIcon="@drawable/ic_settings_continuous" + camera:title="@string/pref_camera_longshot_title"/> + + <CountDownTimerPreference + camera:defaultValue="@string/pref_camera_timer_default" + camera:key="pref_camera2_timer_key" + camera:singleIcon="@drawable/ic_settings_countdowntimer" + camera:title="@string/pref_camera_timer_title"/> + + <ListPreference + camera:defaultValue="0" + camera:key="pref_camera2_initial_camera_key" + camera:entries="@array/pref_camera2_initial_camera_entries" + camera:entryValues="@array/pref_camera2_initial_camera_entryvalues"/> + + <ListPreference + camera:defaultValue="off" + camera:key="pref_camera2_makeup_key" + camera:entries="@array/pref_camera2_makeup_entries" + camera:entryValues="@array/pref_camera2_makeup_entryvalues" + camera:title="@string/pref_camera2_makeup_title"/> +</PreferenceGroup> diff --git a/rs/YuvToRgb.rs b/rs/YuvToRgb.rs new file mode 100644 index 000000000..25771c5c7 --- /dev/null +++ b/rs/YuvToRgb.rs @@ -0,0 +1,58 @@ +/* +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. +*/ +#pragma version(1) +#pragma rs java_package_name(com.android.camera.imageprocessor) +#pragma rs_fp_relaxed + +rs_allocation gIn; +uint32_t width; +uint32_t height; + +uchar4 __attribute__((kernel)) nv21ToRgb(uint32_t x, uint32_t y) { + uint32_t ySize = width*height; + uint32_t index = ySize + (x/2*2) + ((y/2) * width); + int yV = (int)(rsGetElementAt_uchar(gIn, x + y*width) & 0xFF); + int vV = (int)(rsGetElementAt_uchar(gIn, index) & 0xFF ) -128; + int uV = (int)(rsGetElementAt_uchar(gIn, index+1) & 0xFF ) -128; + + int r = (int) (1.164f * yV + 1.596f * vV ); + int g = (int) (1.164f * yV - 0.813f * vV - 0.391f * uV); + int b = (int) (1.164f * yV + 2.018f * uV ); + + r = r>255? 255 : r<0 ? 0 : r; + g = g>255? 255 : g<0 ? 0 : g; + b = b>255? 255 : b<0 ? 0 : b; + uchar4 res4; + res4.r = (uchar)(r & 0xFF); + res4.g = (uchar)(g & 0xFF); + res4.b = (uchar)(b & 0xFF); + res4.a = 0xFF; + + return res4; +}
\ No newline at end of file diff --git a/rs/rotator.rs b/rs/rotator.rs new file mode 100644 index 000000000..cd9da4396 --- /dev/null +++ b/rs/rotator.rs @@ -0,0 +1,51 @@ +/* +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. +*/ +#pragma version(1) +#pragma rs java_package_name(com.android.camera.imageprocessor) +#pragma rs_fp_relaxed + +rs_allocation gOut; +rs_allocation gIn; +uint32_t width; +uint32_t height; + +uchar __attribute__((kernel)) rotate90andMerge(uint32_t x, uint32_t y) { + uchar yValue = rsGetElementAt_uchar(gIn, x + y*width); + rsSetElementAt_uchar(gOut, yValue, x*height + height - 1 - y); + + if(x%2 == 0 && y%2==1) { + uint32_t ySize = width*height; + uint32_t index = ySize + x + ((y/2) * width); + uchar vValue = rsGetElementAt_uchar(gIn, index); + uchar uValue = rsGetElementAt_uchar(gIn, index + 1); + rsSetElementAt_uchar(gOut, vValue, ySize + x/2*height + height - 1 - y); + rsSetElementAt_uchar(gOut, uValue, ySize + x/2*height + height - 1 - y - 1); + } + return (uchar)0; +}
\ No newline at end of file diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index f6e832e45..74bcf5d3d 100644 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -161,8 +161,6 @@ public class CameraActivity extends Activity /** Whether onResume should reset the view to the preview. */ private boolean mResetToPreviewOnResume = true; - public static boolean CAMERA_2_ON = false; - // Supported operations at FilmStripView. Different data has different // set of supported operations. private static final int SUPPORT_DELETE = 1 << 0; @@ -243,6 +241,7 @@ public class CameraActivity extends Activity public static int SETTING_LIST_WIDTH_2 = 250; private ImageView mThumbnail; + private UpdateThumbnailTask mUpdateThumbnailTask; private CircularDrawable mThumbnailDrawable; // FilmStripView.setDataAdapter fires 2 onDataLoaded calls before any data is actually loaded // Keep track of data request here to avoid creating useless UpdateThumbnailTask. @@ -708,7 +707,9 @@ public class CameraActivity extends Activity } public void updateThumbnail(final byte[] jpegData) { - (new UpdateThumbnailTask(jpegData, false)).execute(); + if (mUpdateThumbnailTask != null) mUpdateThumbnailTask.cancel(true); + mUpdateThumbnailTask = new UpdateThumbnailTask(jpegData, false); + mUpdateThumbnailTask.execute(); } public void updateThumbnail(final Bitmap bitmap) { @@ -1417,6 +1418,8 @@ public class CameraActivity extends Activity getWindow().requestFeature(Window.FEATURE_ACTION_BAR); + SettingsManager.createInstance(this); + LayoutInflater inflater = getLayoutInflater(); View rootLayout = inflater.inflate(R.layout.camera, null, false); mCameraRootFrame = (FrameLayout)rootLayout.findViewById(R.id.camera_root_frame); @@ -1451,16 +1454,15 @@ public class CameraActivity extends Activity moduleIndex = ModuleSwitcher.PHOTO_MODULE_INDEX; } } - SharedPreferences pref = PreferenceManager - .getDefaultSharedPreferences(this); - CAMERA_2_ON = pref.getBoolean(CameraSettings.KEY_CAMERA2, false); - if (CAMERA_2_ON && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX) + + boolean cam2on = SettingsManager.getInstance().isCamera2On(); + if (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX) moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; mOrientationListener = new MyOrientationEventListener(this); - setModuleFromIndex(moduleIndex); - setContentView(R.layout.camera_filmstrip); + mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); + setModuleFromIndex(moduleIndex); mActionBar = getActionBar(); mActionBar.addOnMenuVisibilityListener(this); @@ -1490,7 +1492,7 @@ public class CameraActivity extends Activity new CameraDataAdapter(new ColorDrawable( getResources().getColor(R.color.photo_placeholder))), mCameraPreviewData); - mFilmStripView = (FilmStripView) findViewById(R.id.filmstrip_view); + mFilmStripView.setViewGap( getResources().getDimensionPixelSize(R.dimen.camera_film_strip_gap)); mPanoramaViewHelper = new PanoramaViewHelper(this); @@ -1864,7 +1866,9 @@ public class CameraActivity extends Activity @Override public void onModuleSelected(int moduleIndex) { - if (moduleIndex == 0 && CAMERA_2_ON) moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; + boolean cam2on = SettingsManager.getInstance().isCamera2On(); + if (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX) + moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; if (mCurrentModuleIndex == moduleIndex) { if (mCurrentModuleIndex != ModuleSwitcher.CAPTURE_MODULE_INDEX) { return; @@ -1932,6 +1936,8 @@ public class CameraActivity extends Activity if(mCaptureModule == null) { mCaptureModule = new CaptureModule(); mCaptureModule.init(this, mCameraCaptureModuleRootView); + } else { + mCaptureModule.reinit(); } mCurrentModule = mCaptureModule; mCameraCaptureModuleRootView.setVisibility(View.VISIBLE); diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index 4393c6225..7a1cf8d74 100644 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -167,6 +167,7 @@ public class CameraSettings { public static final String KEY_QC_SEE_MORE_MODE = "see-more"; public static final String KEY_QC_NOISE_REDUCTION_MODE = "noise-reduction-mode"; public static final String KEY_QC_INSTANT_CAPTURE = "instant-capture"; + public static final String KEY_QC_INSTANT_CAPTURE_VALUES = "instant-capture-values"; public static final String KEY_INTERNAL_PREVIEW_RESTART = "internal-restart"; public static final String KEY_QC_ZSL_HDR_SUPPORTED = "zsl-hdr-supported"; @@ -246,11 +247,6 @@ public class CameraSettings { public static final String KEY_TS_MAKEUP_LEVEL_WHITEN = "pref_camera_tsmakeup_whiten"; public static final String KEY_TS_MAKEUP_LEVEL_CLEAN = "pref_camera_tsmakeup_clean"; - public static final String KEY_CAMERA2 = "pref_camera_camera2_key"; - public static final String KEY_DUAL_CAMERA = "pref_camera_dual_camera_key"; - public static final String KEY_MONO_PREVIEW = "pref_camera_mono_preview_key"; - public static final String KEY_CLEARSIGHT = "pref_camera_clearsight_key"; - public static final String KEY_REFOCUS_PROMPT = "refocus-prompt"; public static final String KEY_SHOW_MENU_HELP = "help_menu"; @@ -284,7 +280,13 @@ public class CameraSettings { //video encoders VIDEO_ENCODER_TABLE.put(MediaRecorder.VideoEncoder.H263, "h263"); VIDEO_ENCODER_TABLE.put(MediaRecorder.VideoEncoder.H264, "h264"); - VIDEO_ENCODER_TABLE.put(MediaRecorder.VideoEncoder.HEVC, "h265"); + int h265 = ApiHelper.getIntFieldIfExists(MediaRecorder.VideoEncoder.class, + "HEVC", null, MediaRecorder.VideoEncoder.DEFAULT); + if (h265 == MediaRecorder.VideoEncoder.DEFAULT) { + h265 = ApiHelper.getIntFieldIfExists(MediaRecorder.VideoEncoder.class, + "H265", null, MediaRecorder.VideoEncoder.DEFAULT); + } + VIDEO_ENCODER_TABLE.put(h265, "h265"); VIDEO_ENCODER_TABLE.put(MediaRecorder.VideoEncoder.MPEG_4_SP, "m4v"); //video qualities @@ -1037,7 +1039,7 @@ public class CameraSettings { return false; } - private void filterUnsupportedOptions(PreferenceGroup group, + public static void filterUnsupportedOptions(PreferenceGroup group, ListPreference pref, List<String> supported) { // Remove the preference if the parameter is not supported or there is @@ -1066,7 +1068,7 @@ public class CameraSettings { resetIfInvalid(pref); } - private void resetIfInvalid(ListPreference pref) { + private static void resetIfInvalid(ListPreference pref) { // Set the value to the first entry if it is invalid. String value = pref.getValue(); if (pref.findIndexOfValue(value) == NOT_FOUND) { @@ -1173,6 +1175,11 @@ public class CameraSettings { return Integer.parseInt(pref.getString(KEY_CAMERA_ID, rearCameraId)); } + public static int getInitialCameraId(SharedPreferences pref) { + String value = pref.getString(SettingsManager.KEY_INITIAL_CAMERA, "0"); + return Integer.parseInt(value); + } + public static void writePreferredCameraId(SharedPreferences pref, int cameraId) { Editor editor = pref.edit(); @@ -1404,14 +1411,11 @@ public class CameraSettings { public static boolean isInstantCaptureSupported(Parameters params) { boolean ret = false; if (null != params) { - // TODO: need to uncomment this code once get parameter - // is supported - //String val = params.get(KEY_QC_INSTANT_CAPTURE); - //if (null != val) { + String val = params.get(KEY_QC_INSTANT_CAPTURE_VALUES); + if (null != val) { ret = true; - //} + } } return ret; } - } diff --git a/src/com/android/camera/CaptureMenu.java b/src/com/android/camera/CaptureMenu.java deleted file mode 100644 index 8b8f5c357..000000000 --- a/src/com/android/camera/CaptureMenu.java +++ /dev/null @@ -1,571 +0,0 @@ -/* - * Copyright (c) 2016, The Linux Foundation. All rights reserved. - * Not a Contribution. - * - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Rect; -import android.preference.PreferenceManager; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewPropertyAnimator; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.Toast; - -import com.android.camera.ui.CameraControls; -import com.android.camera.ui.CountdownTimerPopup; -import com.android.camera.ui.ListMenu; -import com.android.camera.ui.ListSubMenu; -import com.android.camera.ui.ModuleSwitcher; -import com.android.camera.ui.RotateTextToast; - -import org.codeaurora.snapcam.R; - -import java.util.Locale; - -public class CaptureMenu extends MenuController - implements ListMenu.Listener, - CountdownTimerPopup.Listener, - ListSubMenu.Listener { - private static final int POPUP_NONE = 0; - private static final int POPUP_FIRST_LEVEL = 1; - private static final int POPUP_SECOND_LEVEL = 2; - private static final int POPUP_IN_ANIMATION_SLIDE = 3; - private static final int POPUP_IN_ANIMATION_FADE = 4; - private static final int DEVELOPER_MENU_TOUCH_COUNT = 10; - private static final int ANIMATION_DURATION = 300; - private static final String TAG = "SnapCam_CaptureMenu"; - private String[] mOtherKeys1; - private String[] mOtherKeys2; - private ListMenu mListMenu; - private CaptureUI mUI; - private int mPopupStatus; - private ListSubMenu mListSubMenu; - private CameraActivity mActivity; - private int privateCounter = 0; - - public CaptureMenu(CameraActivity activity, CaptureUI ui) { - super(activity); - mUI = ui; - mActivity = activity; - } - - // Return true if the preference has the specified key but not the value. - private static boolean notSame(ListPreference pref, String key, String value) { - return (key.equals(pref.getKey()) && !value.equals(pref.getValue())); - } - - // Return true if the preference has the specified key and the value. - private static boolean same(ListPreference pref, String key, String value) { - return (key.equals(pref.getKey()) && value.equals(pref.getValue())); - } - - public void initialize(PreferenceGroup group) { - super.initialize(group); - mListSubMenu = null; - mListMenu = null; - mPopupStatus = POPUP_NONE; - - mOtherKeys1 = new String[]{ - CameraSettings.KEY_FLASH_MODE, - CameraSettings.KEY_RECORD_LOCATION, - CameraSettings.KEY_JPEG_QUALITY, - CameraSettings.KEY_CAMERA_SAVEPATH, - CameraSettings.KEY_WHITE_BALANCE, - CameraSettings.KEY_CAMERA2, - CameraSettings.KEY_DUAL_CAMERA, - CameraSettings.KEY_CLEARSIGHT - }; - - //Todo: 2nd string to contain only developer settings - mOtherKeys2 = new String[]{ - CameraSettings.KEY_FLASH_MODE, - CameraSettings.KEY_RECORD_LOCATION, - CameraSettings.KEY_JPEG_QUALITY, - CameraSettings.KEY_CAMERA_SAVEPATH, - CameraSettings.KEY_WHITE_BALANCE, - CameraSettings.KEY_CAMERA2, - CameraSettings.KEY_DUAL_CAMERA, - CameraSettings.KEY_CLEARSIGHT, - CameraSettings.KEY_MONO_PREVIEW - }; - - } - - @Override - // Hit when an item in a popup gets selected - public void onListPrefChanged(ListPreference pref) { - onSettingChanged(pref); - closeView(); - } - - public boolean handleBackKey() { - if (mPopupStatus == POPUP_NONE) - return false; - if (mPopupStatus == POPUP_FIRST_LEVEL) { - animateSlideOut(mListMenu, 1); - } else if (mPopupStatus == POPUP_SECOND_LEVEL) { - animateFadeOut(mListSubMenu, 2); - ((ListMenu) mListMenu).resetHighlight(); - } - return true; - } - - public void tryToCloseSubList() { - if (mListMenu != null) - ((ListMenu) mListMenu).resetHighlight(); - - if (mPopupStatus == POPUP_SECOND_LEVEL) { - mUI.dismissLevel2(); - mPopupStatus = POPUP_FIRST_LEVEL; - } - } - - private void animateFadeOut(final ListView v, final int level) { - if (v == null || mPopupStatus == POPUP_IN_ANIMATION_FADE) - return; - mPopupStatus = POPUP_IN_ANIMATION_FADE; - - ViewPropertyAnimator vp = v.animate(); - vp.alpha(0f).setDuration(ANIMATION_DURATION); - vp.setListener(new AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - if (level == 1) { - mUI.dismissLevel1(); - initializePopup(); - mPopupStatus = POPUP_NONE; - mUI.cleanupListview(); - } else if (level == 2) { - mUI.dismissLevel2(); - mPopupStatus = POPUP_FIRST_LEVEL; - } - } - - @Override - public void onAnimationCancel(Animator animation) { - if (level == 1) { - mUI.dismissLevel1(); - initializePopup(); - mPopupStatus = POPUP_NONE; - mUI.cleanupListview(); - } else if (level == 2) { - mUI.dismissLevel2(); - mPopupStatus = POPUP_FIRST_LEVEL; - } - - } - }); - vp.start(); - } - - private void animateSlideOut(final ListView v, final int level) { - if (v == null || mPopupStatus == POPUP_IN_ANIMATION_SLIDE) - return; - mPopupStatus = POPUP_IN_ANIMATION_SLIDE; - - ViewPropertyAnimator vp = v.animate(); - if (View.LAYOUT_DIRECTION_RTL == TextUtils - .getLayoutDirectionFromLocale(Locale.getDefault())) { - switch (mUI.getOrientation()) { - case 0: - vp.translationXBy(v.getWidth()); - break; - case 90: - vp.translationYBy(-2 * v.getHeight()); - break; - case 180: - vp.translationXBy(-2 * v.getWidth()); - break; - case 270: - vp.translationYBy(v.getHeight()); - break; - } - } else { - switch (mUI.getOrientation()) { - case 0: - vp.translationXBy(-v.getWidth()); - break; - case 90: - vp.translationYBy(2 * v.getHeight()); - break; - case 180: - vp.translationXBy(2 * v.getWidth()); - break; - case 270: - vp.translationYBy(-v.getHeight()); - break; - } - } - vp.setListener(new AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationRepeat(Animator animation) { - - } - - @Override - public void onAnimationEnd(Animator animation) { - if (level == 1) { - mUI.dismissLevel1(); - initializePopup(); - mPopupStatus = POPUP_NONE; - mUI.cleanupListview(); - } else if (level == 2) { - mUI.dismissLevel2(); - mPopupStatus = POPUP_FIRST_LEVEL; - } - } - - @Override - public void onAnimationCancel(Animator animation) { - if (level == 1) { - mUI.dismissLevel1(); - initializePopup(); - mPopupStatus = POPUP_NONE; - mUI.cleanupListview(); - } else if (level == 2) { - mUI.dismissLevel2(); - mPopupStatus = POPUP_FIRST_LEVEL; - } - - } - }); - vp.setDuration(ANIMATION_DURATION).start(); - } - - public void animateFadeIn(final ListView v) { - ViewPropertyAnimator vp = v.animate(); - vp.alpha(0.85f).setDuration(ANIMATION_DURATION); - vp.start(); - } - - public void animateSlideIn(final View v, int delta, boolean forcePortrait) { - int orientation = mUI.getOrientation(); - if (!forcePortrait) - orientation = 0; - - ViewPropertyAnimator vp = v.animate(); - float dest; - if (View.LAYOUT_DIRECTION_RTL == TextUtils - .getLayoutDirectionFromLocale(Locale.getDefault())) { - switch (orientation) { - case 0: - dest = v.getX(); - v.setX(-(dest - delta)); - vp.translationX(dest); - break; - case 90: - dest = v.getY(); - v.setY(-(dest + delta)); - vp.translationY(dest); - break; - case 180: - dest = v.getX(); - v.setX(-(dest + delta)); - vp.translationX(dest); - break; - case 270: - dest = v.getY(); - v.setY(-(dest - delta)); - vp.translationY(dest); - break; - } - } else { - switch (orientation) { - case 0: - dest = v.getX(); - v.setX(dest - delta); - vp.translationX(dest); - break; - case 90: - dest = v.getY(); - v.setY(dest + delta); - vp.translationY(dest); - break; - case 180: - dest = v.getX(); - v.setX(dest + delta); - vp.translationX(dest); - break; - case 270: - dest = v.getY(); - v.setY(dest - delta); - vp.translationY(dest); - break; - } - } - vp.setDuration(ANIMATION_DURATION).start(); - } - - public boolean isOverMenu(MotionEvent ev) { - if (mPopupStatus == POPUP_NONE - || mPopupStatus == POPUP_IN_ANIMATION_SLIDE - || mPopupStatus == POPUP_IN_ANIMATION_FADE) - return false; - if (mUI.getMenuLayout() == null) - return false; - Rect rec = new Rect(); - mUI.getMenuLayout().getChildAt(0).getHitRect(rec); - return rec.contains((int) ev.getX(), (int) ev.getY()); - } - - public boolean isOverPreviewMenu(MotionEvent ev) { - return false; - } - - public boolean isMenuBeingShown() { - return mPopupStatus != POPUP_NONE; - } - - public boolean isMenuBeingAnimated() { - return mPopupStatus == POPUP_IN_ANIMATION_SLIDE || mPopupStatus == POPUP_IN_ANIMATION_FADE; - } - - public boolean isPreviewMenuBeingShown() { - return false; - } - - public boolean isPreviewMenuBeingAnimated() { - return false; - } - - public boolean sendTouchToPreviewMenu(MotionEvent ev) { - return mUI.sendTouchToPreviewMenu(ev); - } - - public boolean sendTouchToMenu(MotionEvent ev) { - return mUI.sendTouchToMenu(ev); - } - - @Override - public void overrideSettings(final String... keyvalues) { - super.overrideSettings(keyvalues); - if ((mListMenu == null)) - initializePopup(); - mListMenu.overrideSettings(keyvalues); - } - - protected void initializePopup() { - LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - ListMenu listMenu = (ListMenu) inflater.inflate( - R.layout.list_menu, null, false); - - listMenu.setSettingChangedListener(this); - - String[] keys = mOtherKeys1; - if (mActivity.isDeveloperMenuEnabled()) - keys = mOtherKeys2; - listMenu.initialize(mPreferenceGroup, keys); - mListMenu = listMenu; - - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_DUAL_CAMERA); - if (!pref.getValue().equals("dual")) { - setPreference(CameraSettings.KEY_MONO_PREVIEW, "off"); - mListMenu.setPreferenceEnabled(CameraSettings.KEY_MONO_PREVIEW, false); - setPreference(CameraSettings.KEY_CLEARSIGHT, "off"); - mListMenu.setPreferenceEnabled(CameraSettings.KEY_CLEARSIGHT, false); - } - - if (mListener != null) { - mListener.onSharedPreferenceChanged(); - } - } - - public void initSwitchItem(final String prefKey, View switcher) { - final IconListPreference pref = - (IconListPreference) mPreferenceGroup.findPreference(prefKey); - if (pref == null) - return; - - int[] iconIds = pref.getLargeIconIds(); - int resid = -1; - int index = pref.findIndexOfValue(pref.getValue()); - if (!pref.getUseSingleIcon() && iconIds != null) { - // Each entry has a corresponding icon. - resid = iconIds[index]; - } else { - // The preference only has a single icon to represent it. - resid = pref.getSingleIcon(); - } - ((ImageView) switcher).setImageResource(resid); - switcher.setVisibility(View.VISIBLE); - mPreferences.add(pref); - mPreferenceMap.put(pref, switcher); - switcher.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - IconListPreference pref = (IconListPreference) mPreferenceGroup - .findPreference(prefKey); - if (pref == null) - return; - int index = pref.findIndexOfValue(pref.getValue()); - CharSequence[] values = pref.getEntryValues(); - index = (index + 1) % values.length; - pref.setValueIndex(index); - ((ImageView) v).setImageResource( - ((IconListPreference) pref).getLargeIconIds()[index]); - if (prefKey.equals(CameraSettings.KEY_CAMERA_ID)) - mListener.onCameraPickerClicked(index); - reloadPreference(pref); - onSettingChanged(pref); - } - }); - } - - public void openFirstLevel() { - if (isMenuBeingShown() || CameraControls.isAnimating()) { - return; - } - if (mListMenu == null || mPopupStatus != POPUP_FIRST_LEVEL) { - initializePopup(); - mPopupStatus = POPUP_FIRST_LEVEL; - } - mUI.showPopup(mListMenu, 1, true); - } - - @Override - // Hit when an item in the first-level popup gets selected, then bring up - // the second-level popup - public void onPreferenceClicked(ListPreference pref) { - onPreferenceClicked(pref, 0); - } - - public void onPreferenceClicked(ListPreference pref, int y) { - if (!mActivity.isDeveloperMenuEnabled()) { - if (pref.getKey().equals(CameraSettings.KEY_REDEYE_REDUCTION)) { - privateCounter++; - if (privateCounter >= DEVELOPER_MENU_TOUCH_COUNT) { - mActivity.enableDeveloperMenu(); - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(mActivity); - prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, true).apply(); - RotateTextToast.makeText(mActivity, - "Camera developer option is enabled now", Toast.LENGTH_SHORT).show(); - } - } else { - privateCounter = 0; - } - } - - LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - ListSubMenu basic = (ListSubMenu) inflater.inflate( - R.layout.list_sub_menu, null, false); - basic.initialize(pref, y); - basic.setSettingChangedListener(this); - basic.setAlpha(0f); - mListSubMenu = basic; - mUI.removeLevel2(); - if (mPopupStatus == POPUP_SECOND_LEVEL) { - mUI.showPopup(mListSubMenu, 2, false); - } else { - mUI.showPopup(mListSubMenu, 2, true); - } - mPopupStatus = POPUP_SECOND_LEVEL; - } - - public void onListMenuTouched() { - mUI.removeLevel2(); - } - - public void removeAllView() { - if (mUI != null) - mUI.removeLevel2(); - - if (mListMenu != null) { - mUI.dismissLevel1(); - mPopupStatus = POPUP_NONE; - } - mUI.cleanupListview(); - } - - public void closeView() { - if (mUI != null) - mUI.removeLevel2(); - - if (mListMenu != null && mPopupStatus != POPUP_NONE) - animateSlideOut(mListMenu, 1); - } - - public void setPreference(String key, String value) { - ListPreference pref = mPreferenceGroup.findPreference(key); - if (pref != null && !value.equals(pref.getValue())) { - pref.setValue(value); - reloadPreferences(); - } - } - - @Override - public void onSettingChanged(ListPreference pref) { - super.onSettingChanged(pref); - String key = pref.getKey(); - String value = pref.getValue(); - Log.d(TAG, "" + key + " " + value); - //Todo: restructure by using switch and create function for each case - if (key.equals(CameraSettings.KEY_CAMERA2)) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(mActivity); - if (value.equals("enable")) { - prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, true).apply(); - CameraActivity.CAMERA_2_ON = true; - mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX); - } else if (value.equals("disable")) { - prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, false).apply(); - CameraActivity.CAMERA_2_ON = false; - mActivity.onModuleSelected(ModuleSwitcher.PHOTO_MODULE_INDEX); - } - } else if (key.equals(CameraSettings.KEY_DUAL_CAMERA)) { - boolean changeMode = CaptureModule.setMode(value); - if (changeMode) mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX); - } else if (key.equals(CameraSettings.KEY_MONO_PREVIEW)) { - if (value.equals("on")) { - } else if (value.equals("off")) { - } - } else if (key.equals(CameraSettings.KEY_CLEARSIGHT)) { - // restart module to re-create sessions and callbacks - mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX); - } - } - - public int getOrientation() { - return mUI == null ? 0 : mUI.getOrientation(); - } -} diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 90e1794ac..0f65f21c3 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -19,30 +19,17 @@ package com.android.camera; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -import org.codeaurora.snapcam.R; -import org.codeaurora.snapcam.filter.ClearSightImageProcessor; -import org.codeaurora.snapcam.filter.ClearSightNativeEngine.ClearsightImage; - +import android.app.ActivityManager; +import android.app.AlertDialog; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; +import android.graphics.Camera; import android.graphics.ImageFormat; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.YuvImage; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; @@ -53,12 +40,14 @@ import android.hardware.camera2.CaptureFailure; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.Face; import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.CameraProfile; import android.media.Image; import android.media.ImageReader; import android.net.Uri; +import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -66,6 +55,7 @@ import android.os.Message; import android.util.Log; import android.util.Size; import android.util.SparseIntArray; +import android.view.Display; import android.view.KeyEvent; import android.view.OrientationEventListener; import android.view.Surface; @@ -73,16 +63,42 @@ import android.view.SurfaceHolder; import android.view.View; import android.widget.Toast; +import com.android.camera.imageprocessor.filter.ImageFilter; +import com.android.camera.imageprocessor.PostProcessor; +import com.android.camera.imageprocessor.FrameProcessor; import com.android.camera.PhotoModule.NamedImages; import com.android.camera.PhotoModule.NamedImages.NamedEntity; +import com.android.camera.ui.CountDownView; +import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.RotateTextToast; import com.android.camera.util.CameraUtil; +import com.android.camera.util.PersistUtil; +import com.android.internal.util.MemInfoReader; + +import org.codeaurora.snapcam.R; +import org.codeaurora.snapcam.filter.ClearSightImageProcessor; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; public class CaptureModule implements CameraModule, PhotoController, - MediaSaveService.Listener, ClearSightImageProcessor.Callback { + MediaSaveService.Listener, ClearSightImageProcessor.Callback, + SettingsManager.Listener, CountDownView.OnCountDownFinishedListener { public static final int DUAL_MODE = 0; public static final int BAYER_MODE = 1; public static final int MONO_MODE = 2; + public static final int BAYER_ID = 0; + public static int MONO_ID = 1; + public static int FRONT_ID = 1; + private static final int BACK_MODE = 0; + private static final int FRONT_MODE = 1; private static final int CANCEL_TOUCH_FOCUS_DELAY = 3000; private static final int OPEN_CAMERA = 0; private static final int CANCEL_TOUCH_FOCUS = 1; @@ -117,11 +133,12 @@ public class CaptureModule implements CameraModule, PhotoController, * Camera state: Waiting for the touch-to-focus to converge. */ private static final int STATE_WAITING_TOUCH_FOCUS = 5; - //Todo: Read ids from the device dynamically - private static final int BAYER_ID = 0; - private static final int MONO_ID = 1; private static final String TAG = "SnapCam_CaptureModule"; - private static int MODE = DUAL_MODE; + + // Used for check memory status for longshot mode + // Currently, this cancel threshold selection is based on test experiments, + // we can change it based on memory status or other requirements. + private static final int LONGSHOT_CANCEL_THRESHOLD = 40 * 1024 * 1024; static { ORIENTATIONS.append(Surface.ROTATION_0, 90); @@ -129,6 +146,7 @@ public class CaptureModule implements CameraModule, PhotoController, ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } + private static final int MAX_IMAGE_NUM = 8; MeteringRectangle[][] mAFRegions = new MeteringRectangle[MAX_NUM_CAM][]; CaptureRequest.Key<Byte> BayerMonoLinkEnableKey = @@ -140,7 +158,9 @@ public class CaptureModule implements CameraModule, PhotoController, CaptureRequest.Key<Integer> BayerMonoLinkSessionIdKey = new CaptureRequest.Key<>("org.codeaurora.qcamera3.dualcam_link_meta_data" + ".related_camera_id", Integer.class); - + public static CameraCharacteristics.Key<Byte> MetaDataMonoOnlyKey = + new CameraCharacteristics.Key<>("org.codeaurora.qcamera3.sensor_meta_data.is_mono_only", + Byte.class); private boolean[] mTakingPicture = new boolean[MAX_NUM_CAM]; private int mControlAFMode = CameraMetadata.CONTROL_AF_MODE_CONTINUOUS_PICTURE; private int mLastResultAFState = -1; @@ -149,7 +169,6 @@ public class CaptureModule implements CameraModule, PhotoController, // The degrees of the device rotated clockwise from its natural orientation. private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; private int mJpegQuality; - private Map<String, String> mSettings = new HashMap<String, String>(); private boolean mFirstTimeInitialized; private boolean mInitialized = false; private boolean mIsLinked = false; @@ -161,13 +180,14 @@ public class CaptureModule implements CameraModule, PhotoController, private String[] mCameraId = new String[MAX_NUM_CAM]; private CaptureUI mUI; private CameraActivity mActivity; - private PreferenceGroup mPreferenceGroup; - private ComboPreferences mPreferences; - private CameraCharacteristics[] mCharacteristics = new CameraCharacteristics[MAX_NUM_CAM]; - private List<Integer> mCharacteristicsIndex; + private List<Integer> mCameraIdList; private float mZoomValue = 1f; private FocusStateListener mFocusStateListener; private LocationManager mLocationManager; + private SettingsManager mSettingsManager; + private long SECONDARY_SERVER_MEM; + private boolean mLongshotActive = false; + /** * A {@link CameraCaptureSession } for camera preview. */ @@ -182,6 +202,12 @@ public class CaptureModule implements CameraModule, PhotoController, /** * A {@link Handler} for running tasks in the background. */ + private PostProcessor mPostProcessor; + private FrameProcessor mFrameProcessor; + private Size mFrameProcPreviewOutputSize; + private Face[] mPreviewFaces = null; + private Face[] mStickyFaces = null; + private Rect mBayerCameraRegion; private Handler mCameraHandler; private Handler mImageAvailableHandler; private Handler mCaptureCallbackHandler; @@ -192,16 +218,70 @@ public class CaptureModule implements CameraModule, PhotoController, private ImageReader[] mImageReader = new ImageReader[MAX_NUM_CAM]; private NamedImages mNamedImages; private ContentResolver mContentResolver; + private byte[] mLastJpegData; + private int mJpegFileSizeEstimation; + private boolean mFirstPreviewLoaded; + private int[] mPrecaptureRequestHashCode = new int[MAX_NUM_CAM]; + private int[] mLockRequestHashCode = new int[MAX_NUM_CAM]; + + private class MediaSaveNotifyThread extends Thread { + private Uri uri; + + public MediaSaveNotifyThread(Uri uri) { + this.uri = uri; + } + + public void setUri(Uri uri) { + this.uri = uri; + } + + public void run() { + while (mLongshotActive) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + mActivity.runOnUiThread(new Runnable() { + public void run() { + if (uri != null) + mActivity.notifyNewMedia(uri); + mActivity.updateStorageSpaceAndHint(); + if (mLastJpegData != null) mActivity.updateThumbnail(mLastJpegData); + } + }); + mediaSaveNotifyThread = null; + } + } + + public void updateThumbnailJpegData(byte[] jpegData) { + mLastJpegData = jpegData; + } + + private MediaSaveNotifyThread mediaSaveNotifyThread; private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = new MediaSaveService.OnMediaSavedListener() { @Override public void onMediaSaved(Uri uri) { - if (uri != null) { - mActivity.notifyNewMedia(uri); + if (mLongshotActive) { + if (mediaSaveNotifyThread == null) { + mediaSaveNotifyThread = new MediaSaveNotifyThread(uri); + mediaSaveNotifyThread.start(); + } else + mediaSaveNotifyThread.setUri(uri); + } else { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } + if (mLastJpegData != null) mActivity.updateThumbnail(mLastJpegData); } } }; + public MediaSaveService.OnMediaSavedListener getMediaSavedListener() { + return mOnMediaSavedListener; + } + static abstract class ImageAvailableListener implements ImageReader.OnImageAvailableListener { int mCamId; @@ -233,6 +313,20 @@ public class CaptureModule implements CameraModule, PhotoController, * camera. */ private Semaphore mCameraOpenCloseLock = new Semaphore(1); + + + public Face[] getPreviewFaces() { + return mPreviewFaces; + } + + public Face[] getStickyFaces() { + return mStickyFaces; + } + + public Rect getCameraRegion() { + return mBayerCameraRegion; + } + /** * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. */ @@ -242,6 +336,22 @@ public class CaptureModule implements CameraModule, PhotoController, private void process(CaptureResult result) { int id = (int) result.getRequest().getTag(); + if (!mFirstPreviewLoaded) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.hidePreviewCover(); + } + }); + mFirstPreviewLoaded = true; + } + + Face[] faces = result.get(CaptureResult.STATISTICS_FACES); + mPreviewFaces = faces; + if(faces != null && faces.length != 0) { + mStickyFaces = faces; + } + switch (mState[id]) { case STATE_PREVIEW: { break; @@ -254,10 +364,12 @@ public class CaptureModule implements CameraModule, PhotoController, if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState || CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED == afState || - CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState) { + CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED == afState || + (mLockRequestHashCode[id] == result.getRequest().hashCode() && + afState == CaptureResult.CONTROL_AF_STATE_INACTIVE)) { // CONTROL_AE_STATE can be null on some devices if (aeState == null || (aeState == CaptureResult - .CONTROL_AE_STATE_CONVERGED) && isFlashOff()) { + .CONTROL_AE_STATE_CONVERGED) && isFlashOff(id)) { mState[id] = STATE_PICTURE_TAKEN; captureStillPicture(id); } else { @@ -274,7 +386,8 @@ public class CaptureModule implements CameraModule, PhotoController, aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE || aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) { - mState[id] = STATE_WAITING_NON_PRECAPTURE; + if (mPrecaptureRequestHashCode[id] == result.getRequest().hashCode()) + mState[id] = STATE_WAITING_NON_PRECAPTURE; } break; } @@ -311,52 +424,7 @@ public class CaptureModule implements CameraModule, PhotoController, process(result); } }; - private final CameraPreference.OnPreferenceChangedListener prefListener = new - CameraPreference.OnPreferenceChangedListener() { - @Override - public void onSharedPreferenceChanged(ListPreference pref) { - if (mPaused) return; - if (CameraSettings.KEY_CAMERA_SAVEPATH.equals(pref.getKey())) { - Storage.setSaveSDCard( - mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0") - .equals("1")); - mActivity.updateStorageSpaceAndHint(); - } - - switch (MODE) { - case BAYER_MODE: - applyPreference(0, pref); - break; - case MONO_MODE: - applyPreference(1, pref); - break; - case DUAL_MODE: - applyPreference(0, pref); - applyPreference(1, pref); - } - mUI.overrideSettings(pref.getKey(), null); - } - - @Override - public void onSharedPreferenceChanged() { - if (mPaused) return; - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); - mLocationManager.recordLocation(recordLocation); - } - - @Override - public void onRestorePreferencesClicked() { - } - - @Override - public void onOverriddenPreferencesClicked() { - } - @Override - public void onCameraPickerClicked(int cameraId) { - } - }; private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override @@ -367,7 +435,7 @@ public class CaptureModule implements CameraModule, PhotoController, if (mPaused) { return; } - if (MODE == DUAL_MODE && id == BAYER_ID) { + if (isBackCamera() && getCameraMode() == DUAL_MODE && id == BAYER_ID) { Message msg = mCameraHandler.obtainMessage(OPEN_CAMERA, MONO_ID); mCameraHandler.sendMessage(msg); } @@ -376,8 +444,7 @@ public class CaptureModule implements CameraModule, PhotoController, mActivity.runOnUiThread(new Runnable() { @Override public void run() { - mUI.onCameraOpened(mCharacteristics, mCharacteristicsIndex, - mPreferenceGroup, prefListener); + mUI.onCameraOpened(mCameraIdList); } }); } @@ -399,7 +466,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onError(CameraDevice cameraDevice, int error) { int id = Integer.parseInt(cameraDevice.getId()); - Log.d(TAG, "onError " + id + error); + Log.e(TAG, "onError " + id + " " + error); cameraDevice.close(); mCameraDevice[id] = null; mCameraOpenCloseLock.release(); @@ -418,23 +485,32 @@ public class CaptureModule implements CameraModule, PhotoController, }; - public static boolean setMode(String value) { - int mode = DUAL_MODE; - switch (value) { - case "dual": - mode = DUAL_MODE; + private boolean isMonoPreviewOn() { + String value = mSettingsManager.getValue(SettingsManager.KEY_MONO_PREVIEW); + if (value == null) return false; + if (value.equals("on")) return true; + else return false; + } - break; - case "bayer": - mode = BAYER_MODE; - break; - case "mono": - mode = MONO_MODE; - break; - } - if (MODE == mode) return false; - MODE = mode; - return true; + private boolean isBackCamera() { + String value = mSettingsManager.getValue(SettingsManager.KEY_CAMERA_ID); + if (value == null) return true; + if (Integer.parseInt(value) == BAYER_ID) return true; + return false; + } + + private int getCameraMode() { + String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (value != null && value.equals(SettingsManager.SCENE_MODE_DUAL_STRING)) return DUAL_MODE; + value = mSettingsManager.getValue(SettingsManager.KEY_MONO_ONLY); + if (value == null || !value.equals("on")) return BAYER_MODE; + return MONO_MODE; + } + + private boolean isClearSightOn() { + String value = mSettingsManager.getValue(SettingsManager.KEY_CLEARSIGHT); + if (value == null) return false; + return isBackCamera() && getCameraMode() == DUAL_MODE && value.equals("on"); } public static int getQualityNumber(String jpegQuality) { @@ -471,8 +547,7 @@ public class CaptureModule implements CameraModule, PhotoController, //Todo: test record location. Jack to provide instructions // Initialize location service. - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); + boolean recordLocation = getRecordLocation(); mLocationManager.recordLocation(recordLocation); mUI.initializeFirstTime(); @@ -489,90 +564,113 @@ public class CaptureModule implements CameraModule, PhotoController, private void initializeSecondTime() { // Start location update if needed. - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); + boolean recordLocation = getRecordLocation(); mLocationManager.recordLocation(recordLocation); MediaSaveService s = mActivity.getMediaSaveService(); if (s != null) { s.setListener(this); } mNamedImages = new NamedImages(); - mUI.initializeSecondTime(); + } + + public ArrayList<ImageFilter> getFrameFilters() { + if(mFrameProcessor == null) { + return new ArrayList<ImageFilter>(); + } else { + return mFrameProcessor.getFrameFilters(); + } + } + + private void applyFaceDetect(CaptureRequest.Builder builder, int id) { + if(id == getMainCameraId()) { + builder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, + CameraMetadata.STATISTICS_FACE_DETECT_MODE_SIMPLE); + } } private void createSession(final int id) { if (mPaused || !mCameraOpened[id] || !mSurfaceReady) return; + Log.d(TAG, "createSession " + id); List<Surface> list = new LinkedList<Surface>(); - mUI.hidePreviewCover(); try { - Surface surface; - if (id == BAYER_ID || (id == MONO_ID && MODE == MONO_MODE)) { - SurfaceHolder sh = mUI.getSurfaceHolder(); - if (sh == null) { - return; - } - surface = sh.getSurface(); - } else { - SurfaceHolder sh = mUI.getSurfaceHolder2(); - if (sh == null) { - return; - } - surface = sh.getSurface(); - } + Surface surface = getPreviewSurfaceForSession(id); // We set up a CaptureRequest.Builder with the output Surface. mPreviewRequestBuilder[id] = mCameraDevice[id].createCaptureRequest(CameraDevice .TEMPLATE_PREVIEW); mPreviewRequestBuilder[id].setTag(id); - mPreviewRequestBuilder[id].addTarget(surface); CameraCaptureSession.StateCallback captureSessionCallback = new CameraCaptureSession.StateCallback() { - @Override - public void onConfigured(CameraCaptureSession cameraCaptureSession) { - // The camera is already closed - if (mPaused || null == mCameraDevice[id]) { - return; - } - // When the session is ready, we start displaying the preview. - mCaptureSession[id] = cameraCaptureSession; - initializePreviewConfiguration(id); - try { - if (MODE == DUAL_MODE) { - linkBayerMono(id); - mIsLinked = true; + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + if (mPaused || null == mCameraDevice[id]) { + return; + } + // When the session is ready, we start displaying the preview. + mCaptureSession[id] = cameraCaptureSession; + initializePreviewConfiguration(id); + try { + if (isBackCamera() && getCameraMode() == DUAL_MODE) { + linkBayerMono(id); + mIsLinked = true; + } + // Finally, we start displaying the camera preview. + mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] + .build(), mCaptureCallback, mCameraHandler); + if (isClearSightOn()) { + ClearSightImageProcessor.getInstance().onCaptureSessionConfigured(id == BAYER_ID, cameraCaptureSession); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } } - // Finally, we start displaying the camera preview. - mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id] - .build(), mCaptureCallback, mCameraHandler); - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CLEARSIGHT); - if(pref.getValue().equals(mActivity.getString(R.string.pref_camera_clearsight_value_on))) { - ClearSightImageProcessor.getInstance().onCaptureSessionConfigured(id == BAYER_ID, cameraCaptureSession); + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Log.e(TAG, "cameracapturesession - onConfigureFailed"); + new AlertDialog.Builder(mActivity) + .setTitle("Camera Initialization Failed") + .setMessage("Closing SnapdragonCamera") + .setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + closeCamera(); + mActivity.finish(); + } + }) + .setCancelable(false) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); } - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } - - @Override - public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { - Log.d(TAG, "cameracapturesession - onConfigureFailed"); - } - @Override - public void onClosed(CameraCaptureSession session) { - Log.d(TAG, "cameracapturesession - onClosed"); - } - }; - - list.add(surface); + @Override + public void onClosed(CameraCaptureSession session) { + Log.d(TAG, "cameracapturesession - onClosed"); + } + }; - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CLEARSIGHT); - if(pref.getValue().equals(mActivity.getString(R.string.pref_camera_clearsight_value_on))) { + if(isClearSightOn()) { + mPreviewRequestBuilder[id].addTarget(surface); + list.add(surface); ClearSightImageProcessor.getInstance().createCaptureSession( - id==BAYER_ID, mCameraDevice[id], list, captureSessionCallback); + id == BAYER_ID, mCameraDevice[id], list, captureSessionCallback); + } else if (id == getMainCameraId()) { + if(mFrameProcessor.isFrameFilterEnabled()) { + mFrameProcessor.init(mFrameProcPreviewOutputSize); + mActivity.runOnUiThread(new Runnable() { + public void run() { + mUI.getSurfaceHolder().setFixedSize(mFrameProcPreviewOutputSize.getHeight(), mFrameProcPreviewOutputSize.getWidth()); + } + }); + } + mFrameProcessor.setOutputSurface(surface); + mPreviewRequestBuilder[id].addTarget(mFrameProcessor.getInputSurface()); + list.add(mFrameProcessor.getInputSurface()); + list.add(mImageReader[id].getSurface()); + mCameraDevice[id].createCaptureSession(list, captureSessionCallback, null); } else { + mPreviewRequestBuilder[id].addTarget(surface); + list.add(surface); list.add(mImageReader[id].getSurface()); // Here, we create a CameraCaptureSession for camera preview. mCameraDevice[id].createCaptureSession(list, captureSessionCallback, null); @@ -593,8 +691,24 @@ public class CaptureModule implements CameraModule, PhotoController, } } + public void reinit() { + setCurrentMode(); + mSettingsManager.reinit(getMainCameraId()); + } + + public boolean getRecordLocation() { + String value = mSettingsManager.getValue(SettingsManager.KEY_RECORD_LOCATION); + if (value == null) value = RecordLocationPreference.VALUE_NONE; + return RecordLocationPreference.VALUE_ON.equals(value); + } + @Override public void init(CameraActivity activity, View parent) { + mActivity = activity; + mSettingsManager = SettingsManager.getInstance(); + mSettingsManager.registerListener(this); + mSettingsManager.init(); + mFirstPreviewLoaded = false; Log.d(TAG, "init"); for (int i = 0; i < MAX_NUM_CAM; i++) { mCameraOpened[i] = false; @@ -602,32 +716,22 @@ public class CaptureModule implements CameraModule, PhotoController, } mSurfaceReady = false; - mActivity = activity; for (int i = 0; i < MAX_NUM_CAM; i++) { mState[i] = STATE_PREVIEW; } - mPreferences = new ComboPreferences(mActivity); - CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal(), activity); - - mPreferences.setLocalId(mActivity, BAYER_ID); - CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); - - PreferenceInflater inflater = new PreferenceInflater(mActivity); - PreferenceGroup group = - (PreferenceGroup) inflater.inflate(R.xml.camera_preferences); - mPreferenceGroup = group; - - ListPreference pref = group.findPreference(CameraSettings.KEY_DUAL_CAMERA); - setMode(pref.getValue()); + mPostProcessor = new PostProcessor(mActivity, this); + mFrameProcessor = new FrameProcessor(mActivity, this); + setCurrentMode(); mContentResolver = mActivity.getContentResolver(); mUI = new CaptureUI(activity, this, parent); mUI.initializeControlByIntent(); + mFocusStateListener = new FocusStateListener(mUI); mLocationManager = new LocationManager(mActivity, mUI); - Storage.setSaveSDCard( - mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); + Storage.setSaveSDCard(mSettingsManager.getValue(SettingsManager + .KEY_CAMERA_SAVEPATH).equals("1")); } /** @@ -635,17 +739,22 @@ public class CaptureModule implements CameraModule, PhotoController, */ private void takePicture() { Log.d(TAG, "takePicture"); - switch (MODE) { - case DUAL_MODE: - lockFocus(BAYER_ID); - lockFocus(MONO_ID); - break; - case BAYER_MODE: - lockFocus(BAYER_ID); - break; - case MONO_MODE: - lockFocus(MONO_ID); - break; + mUI.enableShutter(false); + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + lockFocus(BAYER_ID); + lockFocus(MONO_ID); + break; + case BAYER_MODE: + lockFocus(BAYER_ID); + break; + case MONO_MODE: + lockFocus(MONO_ID); + break; + } + } else { + lockFocus(FRONT_ID); } } @@ -653,7 +762,12 @@ public class CaptureModule implements CameraModule, PhotoController, * Lock the focus as the first step for a still image capture. */ private void lockFocus(int id) { + if (mActivity == null || mCameraDevice[id] == null) { + warningToast("Camera is not ready yet to take a picture."); + return; + } Log.d(TAG, "lockFocus " + id); + mTakingPicture[id] = true; if (mState[id] == STATE_WAITING_TOUCH_FOCUS) { mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, id); @@ -667,14 +781,11 @@ public class CaptureModule implements CameraModule, PhotoController, builder.setTag(id); builder.addTarget(getPreviewSurface(id)); - builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); - builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); - builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); - applyWhiteBalance(builder); - applyZoom(builder, id); - applyAFRegions(builder, id); + applySettingsForLockFocus(builder, id); + CaptureRequest request = builder.build(); + mLockRequestHashCode[id] = request.hashCode(); mState[id] = STATE_WAITING_LOCK; - mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); + mCaptureSession[id].capture(request, mCaptureCallback, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); } @@ -682,6 +793,10 @@ public class CaptureModule implements CameraModule, PhotoController, private void autoFocusTrigger(int id) { Log.d(TAG, "autoFocusTrigger " + id); + if (null == mActivity || null == mCameraDevice[id]) { + warningToast("Camera is not ready yet to take a picture."); + return; + } try { CaptureRequest.Builder builder = mCameraDevice[id].createCaptureRequest(CameraDevice .TEMPLATE_PREVIEW); @@ -689,14 +804,7 @@ public class CaptureModule implements CameraModule, PhotoController, builder.addTarget(getPreviewSurface(id)); mControlAFMode = CaptureRequest.CONTROL_AF_MODE_AUTO; - builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); - builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); - builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest - .CONTROL_AF_TRIGGER_START); - - applyWhiteBalance(builder); - applyZoom(builder, id); - applyAFRegions(builder, id); + applySettingsForAutoFocus(builder, id); mState[id] = STATE_WAITING_TOUCH_FOCUS; mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); setAFModeToPreview(id, mControlAFMode); @@ -729,20 +837,15 @@ public class CaptureModule implements CameraModule, PhotoController, } } - /** - * Capture a still picture. This method should be called when we get a response in - * {@link #mCaptureCallback} from both {@link #lockFocus()}. - */ private void captureStillPicture(final int id) { Log.d(TAG, "captureStillPicture " + id); try { if (null == mActivity || null == mCameraDevice[id]) { + warningToast("Camera is not ready yet to take a picture."); return; } - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CLEARSIGHT); - final boolean csEnabled = pref.getValue().equals( - mActivity.getString(R.string.pref_camera_clearsight_value_on)); + final boolean csEnabled = isClearSightOn(); CaptureRequest.Builder captureBuilder; if(csEnabled) { @@ -754,32 +857,32 @@ public class CaptureModule implements CameraModule, PhotoController, // Orientation int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); - captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, CameraUtil.getJpegRotation(id, rotation)); captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); captureBuilder.addTarget(getPreviewSurface(id)); captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); - applyCaptureSettings(captureBuilder, id); + applySettingsForCapture(captureBuilder, id); if(csEnabled) { ClearSightImageProcessor.getInstance().capture( id==BAYER_ID, mCaptureSession[id], captureBuilder, mCaptureCallbackHandler); - } else { + } else if(id == getMainCameraId() && mPostProcessor.isFilterOn()) { captureBuilder.addTarget(mImageReader[id].getSurface()); - - mCaptureSession[id].capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() { + List<CaptureRequest> captureList = mPostProcessor.setRequiredImages(captureBuilder); + mCaptureSession[id].captureBurst(captureList, new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, - CaptureRequest request, - TotalCaptureResult result) { + CaptureRequest request, + TotalCaptureResult result) { Log.d(TAG, "captureStillPicture onCaptureCompleted: " + id); } @Override public void onCaptureFailed(CameraCaptureSession session, - CaptureRequest request, - CaptureFailure result) { + CaptureRequest request, + CaptureFailure result) { Log.d(TAG, "captureStillPicture onCaptureFailed: " + id); } @@ -790,6 +893,83 @@ public class CaptureModule implements CameraModule, PhotoController, unlockFocus(id); } }, mCaptureCallbackHandler); + } else { + captureBuilder.addTarget(mImageReader[id].getSurface()); + mCaptureSession[id].stopRepeating(); + + if (mLongshotActive) { + Log.d(TAG, "captureStillPicture capture longshot " + id); + List<CaptureRequest> burstList = new ArrayList<>(); + for (int i = 0; i < PersistUtil.getLongshotShotLimit(); i++) { + burstList.add(captureBuilder.build()); + } + mCaptureSession[id].captureBurst(burstList, new + CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, "captureStillPicture Longshot onCaptureCompleted: " + id); + if (mLongshotActive) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.doShutterAnimation(); + } + }); + } + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, "captureStillPicture Longshot onCaptureFailed: " + id); + if (mLongshotActive) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.doShutterAnimation(); + } + }); + } + } + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, "captureStillPicture Longshot onCaptureSequenceCompleted: " + id); + mLongshotActive = false; + unlockFocus(id); + } + }, mCaptureCallbackHandler); + } else { + mCaptureSession[id].capture(captureBuilder.build(), + new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, "captureStillPicture onCaptureCompleted: " + id); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, "captureStillPicture onCaptureFailed: " + id); + } + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, "captureStillPicture onCaptureSequenceCompleted: " + id); + unlockFocus(id); + } + }, mCaptureCallbackHandler); + } } } catch (CameraAccessException e) { Log.d(TAG, "Capture still picture has failed"); @@ -808,92 +988,129 @@ public class CaptureModule implements CameraModule, PhotoController, .TEMPLATE_PREVIEW); builder.setTag(id); builder.addTarget(getPreviewSurface(id)); - builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); - builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); - builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, - CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); - // Applying flash only to capture does not work. Need to apply flash here. - applyFlash(builder); - applyWhiteBalance(builder); - applyZoom(builder, id); + applySettingsForPrecapture(builder, id); + CaptureRequest request = builder.build(); + mPrecaptureRequestHashCode[id] = request.hashCode(); mState[id] = STATE_WAITING_PRECAPTURE; - mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); + mCaptureSession[id].capture(request, mCaptureCallback, mCameraHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } + private void determineFrameProcPreviewOutputSize(List<Size> sizeList, float targetRatio) { + Display display = mActivity.getWindowManager().getDefaultDisplay(); + Point ds = new Point(); + display.getSize(ds); + int i=0, j=0, width, height; + float ratio; + for(; i < sizeList.size(); i++) { + width = sizeList.get(i).getHeight(); + height = sizeList.get(i).getWidth(); + ratio = (float)height/width; + if(ds.x >= width || ds.y >= height) { + if(j == 0) { + j = i; + } + if(ratio < targetRatio + 0.2f && ratio > targetRatio - 0.2f) { + break; + } + } + } + if(i == sizeList.size()) { + if(j != 0) { + mFrameProcPreviewOutputSize = sizeList.get(j); + } else { + mFrameProcPreviewOutputSize = sizeList.get(sizeList.size()-1); + } + } else { + mFrameProcPreviewOutputSize = sizeList.get(i); + } + } /** * Sets up member variables related to camera. * * @param width The width of available size for camera preview * @param height The height of available size for camera preview */ - private void setUpCameraOutputs() { + private void setUpCameraOutputs(int imageFormat) { + Log.d(TAG, "setUpCameraOutputs"); CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); try { String[] cameraIdList = manager.getCameraIdList(); - for (int i = 0; i < cameraIdList.length; i++) { String cameraId = cameraIdList[i]; - CameraCharacteristics characteristics - = manager.getCameraCharacteristics(cameraId); - mCharacteristics[i] = characteristics; - mCharacteristicsIndex.add(i); + + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + if (isInMode(i)) + mCameraIdList.add(i); + if(i == getMainCameraId()) { + mBayerCameraRegion = characteristics.get(CameraCharacteristics + .SENSOR_INFO_ACTIVE_ARRAY_SIZE); + } StreamConfigurationMap map = characteristics.get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (map == null) { continue; } - Boolean available = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE); - Log.d(TAG, "flash : " + (available == null ? false : available)); mCameraId[i] = cameraId; - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CLEARSIGHT); - if(pref.getValue().equals(mActivity.getString(R.string.pref_camera_clearsight_value_on))) { - Size largest = Collections.max( - Arrays.asList(map.getOutputSizes(ImageFormat.YUV_420_888)), - new CompareSizesByArea()); - ClearSightImageProcessor.getInstance().init( - largest.getWidth(), largest.getHeight(), mActivity, mOnMediaSavedListener); + String pictureSize = mSettingsManager.getValue(SettingsManager + .KEY_PICTURE_SIZE); + + Size size = parsePictureSize(pictureSize); + + if (i == getMainCameraId()) { + Point screenSize = new Point(); + mActivity.getWindowManager().getDefaultDisplay().getSize(screenSize); + Size[] prevSizes = map.getOutputSizes(imageFormat); + mFrameProcPreviewOutputSize = getOptimalPreviewSize(size, prevSizes, screenSize.x, + screenSize.y); + mUI.setPreviewSize(mFrameProcPreviewOutputSize.getWidth(), mFrameProcPreviewOutputSize.getHeight()); + } + if (isClearSightOn()) { + ClearSightImageProcessor.getInstance().init(size.getWidth(), size.getHeight(), + mActivity, mOnMediaSavedListener); ClearSightImageProcessor.getInstance().setCallback(this); } else { // No Clearsight - // For still image captures, we use the largest available size. - Size largest = Collections.max( - Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), - new CompareSizesByArea()); - - mImageReader[i] = ImageReader.newInstance(largest.getWidth(), largest.getHeight(), - ImageFormat.JPEG, 3); - mImageReader[i].setOnImageAvailableListener(new ImageAvailableListener(i) { - @Override - public void onImageAvailable(ImageReader reader) { - Log.d(TAG, "image available for cam: " + mCamId); - Image image = reader.acquireNextImage(); - mCaptureStartTime = System.currentTimeMillis(); - mNamedImages.nameNewImage(mCaptureStartTime); - NamedEntity name = mNamedImages.getNextNameEntity(); - String title = (name == null) ? null : name.title; - long date = (name == null) ? -1 : name.date; - - ByteBuffer buffer = image.getPlanes()[0].getBuffer(); - byte[] bytes = new byte[buffer.remaining()]; - buffer.get(bytes); - - mActivity.getMediaSaveService().addImage(bytes, title, date, - null, image.getWidth(), image.getHeight(), 0, null, - mOnMediaSavedListener, mContentResolver, "jpeg"); - image.close(); - } - }, mImageAvailableHandler); + mImageReader[i] = ImageReader.newInstance(size.getWidth(), size.getHeight(), imageFormat, MAX_IMAGE_NUM); + if(mPostProcessor.isFilterOn() && i == getMainCameraId()) { + mImageReader[i].setOnImageAvailableListener(mPostProcessor, mImageAvailableHandler); +// if(mFrameProcessor.isFrameFilterEnabled()) { +// determineFrameProcPreviewOutputSize(Arrays.asList(map.getOutputSizes(imageFormat)), +// (float) size.getWidth() / (float) size.getHeight()); +// } + } else { + mImageReader[i].setOnImageAvailableListener(new ImageAvailableListener(i) { + @Override + public void onImageAvailable(ImageReader reader) { + Log.d(TAG, "image available for cam: " + mCamId); + Image image = reader.acquireNextImage(); + mCaptureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(mCaptureStartTime); + NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + long date = (name == null) ? -1 : name.date; + + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + mLastJpegData = bytes; + buffer.get(bytes); + + mActivity.getMediaSaveService().addImage(bytes, title, date, + null, image.getWidth(), image.getHeight(), 0, null, + mOnMediaSavedListener, mContentResolver, "jpeg"); + image.close(); + } + }, mImageAvailableHandler); + } } + } - mAutoFocusSupported = CameraUtil.isAutoFocusSupported(mCharacteristics, - mCharacteristicsIndex); + mAutoFocusSupported = mSettingsManager.isAutoFocusSupported(mCameraIdList); } catch (CameraAccessException e) { e.printStackTrace(); - } catch (NullPointerException e) { } } @@ -909,14 +1126,7 @@ public class CaptureModule implements CameraModule, PhotoController, builder.setTag(id); builder.addTarget(getPreviewSurface(id)); - builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest - .CONTROL_MODE_AUTO); - builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); - builder.set(CaptureRequest.CONTROL_AF_TRIGGER, - CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); - //Todo: Create applyCommonSettings function for settings applied everytime - applyWhiteBalance(builder); - applyZoom(builder, id); + applySettingsForUnlockFocus(builder, id); mCaptureSession[id].capture(builder.build(), mCaptureCallback, mCameraHandler); mState[id] = STATE_PREVIEW; mControlAFMode = CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE; @@ -924,11 +1134,24 @@ public class CaptureModule implements CameraModule, PhotoController, mCaptureSession[id].setRepeatingRequest(mPreviewRequestBuilder[id].build(), mCaptureCallback, mCameraHandler); mTakingPicture[id] = false; + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.enableShutter(true); + } + }); } catch (CameraAccessException e) { e.printStackTrace(); } } + private Size parsePictureSize(String value) { + int indexX = value.indexOf('x'); + int width = Integer.parseInt(value.substring(0, indexX)); + int height = Integer.parseInt(value.substring(indexX + 1)); + return new Size(width, height); + } + /** * Closes the current {@link CameraDevice}. */ @@ -936,6 +1159,12 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "closeCamera"); try { mCameraOpenCloseLock.acquire(); + if(mPostProcessor != null) { + mPostProcessor.onClose(); + } + if(mFrameProcessor != null) { + mFrameProcessor.onClose(); + } for (int i = 0; i < MAX_NUM_CAM; i++) { if (null != mCaptureSession[i]) { if (mIsLinked) { @@ -947,7 +1176,6 @@ public class CaptureModule implements CameraModule, PhotoController, e.printStackTrace(); } } - mCaptureSession[i].close(); mCaptureSession[i] = null; } @@ -977,6 +1205,53 @@ public class CaptureModule implements CameraModule, PhotoController, } } + private void applySettingsForLockFocus(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); + applyAFRegions(builder, id); + applyCommonSettings(builder, id); + applyFaceDetect(builder, id); + } + + private void applySettingsForCapture(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + applyJpegQuality(builder); + applyCommonSettings(builder, id); + } + + private void applySettingsForPrecapture(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, + CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); + applyCommonSettings(builder, id); + applyFaceDetect(builder, id); + } + + private void applySettingsForUnlockFocus(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AF_TRIGGER, + CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); + applyCommonSettings(builder, id); + applyFaceDetect(builder, id); + } + + private void applySettingsForAutoFocus(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest + .CONTROL_AF_TRIGGER_START); + applyAFRegions(builder, id); + applyCommonSettings(builder, id); + applyFaceDetect(builder, id); + } + + private void applyCommonSettings(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); + builder.set(CaptureRequest.CONTROL_AF_MODE, mControlAFMode); + applyFlash(builder, id); + applyWhiteBalance(builder); + applyExposure(builder); + applyIso(builder); + applyColorEffect(builder); + applySceneMode(builder); + applyZoom(builder, id); + } + /** * Starts a background thread and its {@link Handler}. */ @@ -1058,13 +1333,14 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onPauseAfterSuper() { Log.d(TAG, "onPause"); - mUI.showPreviewCover(); if (mLocationManager != null) mLocationManager.recordLocation(false); - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CLEARSIGHT); - if(pref.getValue().equals(mActivity.getString(R.string.pref_camera_clearsight_value_on))) { + if(isClearSightOn()) { ClearSightImageProcessor.getInstance().close(); } closeCamera(); + mUI.showPreviewCover(); + mUI.hideSurfaceView(); + mFirstPreviewLoaded = false; stopBackgroundThread(); mUI.onPause(); } @@ -1074,33 +1350,83 @@ public class CaptureModule implements CameraModule, PhotoController, mPaused = false; } + private void setCurrentMode() { + mCurrentMode = isBackCamera() ? getCameraMode() : FRONT_MODE; + } + + private ArrayList<Integer> getFrameProcFilterId() { + String scene = mSettingsManager.getValue(SettingsManager.KEY_MAKEUP); + ArrayList<Integer> filters = new ArrayList<Integer>(); + if(scene != null && scene.equalsIgnoreCase("on")) { + filters.add(FrameProcessor.FILTER_MAKEUP); + } + + return filters; + } + + private int getPostProcFilterId() { + String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (scene != null) { + int mode = Integer.parseInt(scene); + if (mode == SettingsManager.SCENE_MODE_OPTIZOOM_INT) + return PostProcessor.FILTER_OPTIZOOM; + } + return PostProcessor.FILTER_NONE; + } + + private boolean isPostProcFilter(String value) { + if(value.equalsIgnoreCase(SettingsManager.SCENE_MODE_OPTIZOOM_INT+"")) { + return true; + } + return false; + } + @Override public void onResumeAfterSuper() { - Log.d(TAG, "onResume " + MODE); - mCharacteristicsIndex = new ArrayList<>(); + Log.d(TAG, "onResume " + getCameraMode()); + mUI.showSurfaceView(); + mUI.setSwitcherIndex(); + mCameraIdList = new ArrayList<>(); + if(mPostProcessor != null) { + Log.d(TAG, "Chosen postproc filter id : "+getPostProcFilterId()); + mPostProcessor.onOpen(getPostProcFilterId()); + } + if(mFrameProcessor != null) { + mFrameProcessor.onOpen(getFrameProcFilterId()); + } + if(mPostProcessor.isFilterOn()) { + setUpCameraOutputs(ImageFormat.YUV_420_888); + } else { + setUpCameraOutputs(ImageFormat.JPEG); + } startBackgroundThread(); - setUpCameraOutputs(); - readInitialValues(); Message msg = Message.obtain(); msg.what = OPEN_CAMERA; - switch (MODE) { - case DUAL_MODE: - case BAYER_MODE: - msg.obj = BAYER_ID; - mCameraHandler.sendMessage(msg); - break; - case MONO_MODE: - msg.what = OPEN_CAMERA; - msg.obj = MONO_ID; - mCameraHandler.sendMessage(msg); - break; + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + case BAYER_MODE: + msg.obj = BAYER_ID; + mCameraHandler.sendMessage(msg); + break; + case MONO_MODE: + msg.what = OPEN_CAMERA; + msg.obj = MONO_ID; + mCameraHandler.sendMessage(msg); + break; + } + } else { + msg.obj = FRONT_ID; + mCameraHandler.sendMessage(msg); } if (!mFirstTimeInitialized) { initializeFirstTime(); } else { initializeSecondTime(); } - mUI.hidePreviewCover(); + mUI.reInitUI(); + mActivity.updateStorageSpaceAndHint(); + estimateJpegFileSize(); mUI.enableShutter(true); } @@ -1148,18 +1474,38 @@ public class CaptureModule implements CameraModule, PhotoController, public void onZoomChanged(float requestedZoom) { mZoomValue = requestedZoom; - switch (MODE) { - case DUAL_MODE: - applyZoomAndUpdate(BAYER_ID); - applyZoomAndUpdate(MONO_ID); - break; - case BAYER_MODE: - applyZoomAndUpdate(BAYER_ID); - break; - case MONO_MODE: - applyZoomAndUpdate(MONO_ID); - break; + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + applyZoomAndUpdate(BAYER_ID); + applyZoomAndUpdate(MONO_ID); + break; + case BAYER_MODE: + applyZoomAndUpdate(BAYER_ID); + break; + case MONO_MODE: + applyZoomAndUpdate(MONO_ID); + break; + } + } else { + applyZoomAndUpdate(FRONT_ID); + } + } + + private boolean isInMode(int cameraId) { + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + return cameraId == BAYER_ID || cameraId == MONO_ID; + case BAYER_MODE: + return cameraId == BAYER_ID; + case MONO_MODE: + return cameraId == MONO_ID; + } + } else { + return cameraId == FRONT_ID; } + return false; } @Override @@ -1211,41 +1557,64 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "onSingleTapUp " + x + " " + y); mUI.setFocusPosition(x, y); mUI.onFocusStarted(); - switch (MODE) { - case DUAL_MODE: - triggerFocusAtPoint(x, y, BAYER_ID); - triggerFocusAtPoint(x, y, MONO_ID); - break; - case BAYER_MODE: - triggerFocusAtPoint(x, y, BAYER_ID); - break; - case MONO_MODE: - triggerFocusAtPoint(x, y, MONO_ID); - break; + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + triggerFocusAtPoint(x, y, BAYER_ID); + triggerFocusAtPoint(x, y, MONO_ID); + break; + case BAYER_MODE: + triggerFocusAtPoint(x, y, BAYER_ID); + break; + case MONO_MODE: + triggerFocusAtPoint(x, y, MONO_ID); + break; + } + } else { + triggerFocusAtPoint(x, y, FRONT_ID); } } private int getMainCameraId() { - switch (MODE) { - case DUAL_MODE: - case BAYER_MODE: - return BAYER_ID; - case MONO_MODE: - return MONO_ID; + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + case BAYER_MODE: + return BAYER_ID; + case MONO_MODE: + return MONO_ID; + } + return 0; + } else { + return FRONT_ID; } - return 0; } - private boolean isTouchToFocusAllowed() { + public boolean isTakingPicture() { for (int i = 0; i < mTakingPicture.length; i++) { - if (mTakingPicture[i]) return false; + if (mTakingPicture[i]) return true; } + return false; + } + + private boolean isTouchToFocusAllowed() { + if (isTakingPicture() || isSceneModeOn()) return false; return true; } + private boolean isSceneModeOn() { + String scene = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (scene == null) return false; + int mode = Integer.parseInt(scene); + if (mode != SettingsManager.SCENE_MODE_DUAL_INT && mode != CaptureRequest + .CONTROL_SCENE_MODE_DISABLED) return true; + return false; + } + @Override public void onCountDownFinished() { - + mUI.showUIAfterCountDown(); + takePicture(); } @Override @@ -1265,16 +1634,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void enableRecordingLocation(boolean enable) { - Log.d(TAG, "CaptureModule enableRecordingLocation " + enable); - setLocationPreference(enable ? RecordLocationPreference.VALUE_ON - : RecordLocationPreference.VALUE_OFF); - } - private void setLocationPreference(String value) { - mPreferences.edit() - .putString(CameraSettings.KEY_RECORD_LOCATION, value) - .apply(); - prefListener.onSharedPreferenceChanged(); } @Override @@ -1284,23 +1644,26 @@ public class CaptureModule implements CameraModule, PhotoController, } Log.d(TAG, "onPreviewUIReady"); mSurfaceReady = true; - switch (MODE) { - case DUAL_MODE: - createSession(BAYER_ID); - createSession(MONO_ID); - break; - case BAYER_MODE: - createSession(BAYER_ID); - break; - case MONO_MODE: - createSession(MONO_ID); - break; + if (isBackCamera()) { + switch (getCameraMode()) { + case DUAL_MODE: + createSession(BAYER_ID); + createSession(MONO_ID); + break; + case BAYER_MODE: + createSession(BAYER_ID); + break; + case MONO_MODE: + createSession(MONO_ID); + break; + } + } else { + createSession(FRONT_ID); } } @Override public void onPreviewUIDestroyed() { - } @Override @@ -1332,6 +1695,7 @@ public class CaptureModule implements CameraModule, PhotoController, int oldOrientation = mOrientation; mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); if (oldOrientation != mOrientation) { + mUI.onOrientationChanged(); mUI.setOrientation(mOrientation, true); } } @@ -1345,8 +1709,7 @@ public class CaptureModule implements CameraModule, PhotoController, public void onMediaSaveServiceConnected(MediaSaveService s) { if (mFirstTimeInitialized) { s.setListener(this); - ListPreference pref = mPreferenceGroup.findPreference(CameraSettings.KEY_CLEARSIGHT); - if(pref.getValue().equals(mActivity.getString(R.string.pref_camera_clearsight_value_on))) { + if (isClearSightOn()) { ClearSightImageProcessor.getInstance().setMediaSaveService(s); } } @@ -1364,65 +1727,165 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onSwitchSavePath() { - if (mUI.mMenuInitialized) { - mUI.setPreference(CameraSettings.KEY_CAMERA_SAVEPATH, "1"); - } else { - mPreferences.edit() - .putString(CameraSettings.KEY_CAMERA_SAVEPATH, "1") - .apply(); - } + mSettingsManager.setValue(SettingsManager.KEY_CAMERA_SAVEPATH, "1"); RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard, Toast.LENGTH_SHORT).show(); } @Override public void onShutterButtonFocus(boolean pressed) { - + if (!pressed && mLongshotActive) { + Log.d(TAG, "Longshot button up"); + } } @Override public void onShutterButtonClick() { - takePicture(); + String timer = mSettingsManager.getValue(SettingsManager.KEY_TIMER); + + int seconds = Integer.parseInt(timer); + // When shutter button is pressed, check whether the previous countdown is + // finished. If not, cancel the previous countdown and start a new one. + if (mUI.isCountingDown()) { + mUI.cancelCountDown(); + } + if (seconds > 0) { + mUI.startCountDown(seconds, true); + } else { + if(mPostProcessor.isFilterOn() && mPostProcessor.isItBusy()) { + warningToast("It's still busy processing previous scene mode request."); + return; + } + takePicture(); + } + } + + private void warningToast(final String msg) { + mActivity.runOnUiThread(new Runnable() { + public void run() { + RotateTextToast.makeText(mActivity, msg, + Toast.LENGTH_SHORT).show(); + } + }); + } @Override public void onShutterButtonLongClick() { + if (isBackCamera() && getCameraMode() == DUAL_MODE) return; + + String longshot = mSettingsManager.getValue(SettingsManager.KEY_LONGSHOT); + if (longshot.equals("on")) { + //Cancel the previous countdown when long press shutter button for longshot. + if (mUI.isCountingDown()) { + mUI.cancelCountDown(); + } + //check whether current memory is enough for longshot. + mActivity.updateStorageSpaceAndHint(); + + long storageSpace = mActivity.getStorageSpaceBytes(); + int mLongShotCaptureCountLimit = PersistUtil.getLongshotShotLimit(); + if (storageSpace <= Storage.LOW_STORAGE_THRESHOLD_BYTES + mLongShotCaptureCountLimit + * mJpegFileSizeEstimation) { + Log.i(TAG, "Not enough space or storage not ready. remaining=" + storageSpace); + return; + } + + if (isLongshotNeedCancel()) { + mLongshotActive = false; + return; + } + + Log.d(TAG, "Start Longshot"); + mLongshotActive = true; + takePicture(); + } } - private boolean isFlashOff() { - return readSetting(CameraSettings.KEY_FLASH_MODE).equals("off"); + private void estimateJpegFileSize() { + String quality = mSettingsManager.getValue(SettingsManager + .KEY_JPEG_QUALITY); + int[] ratios = mActivity.getResources().getIntArray(R.array.jpegquality_compression_ratio); + String[] qualities = mActivity.getResources().getStringArray( + R.array.pref_camera_jpegquality_entryvalues); + int ratio = 0; + for (int i = ratios.length - 1; i >= 0; --i) { + if (qualities[i].equals(quality)) { + ratio = ratios[i]; + break; + } + } + String pictureSize = mSettingsManager.getValue(SettingsManager + .KEY_PICTURE_SIZE); + + Size size = parsePictureSize(pictureSize); + if (ratio == 0) { + Log.d(TAG, "mJpegFileSizeEstimation 0"); + } else { + mJpegFileSizeEstimation = size.getWidth() * size.getHeight() * 3 / ratio; + Log.d(TAG, "mJpegFileSizeEstimation " + mJpegFileSizeEstimation); + } + + } + + private boolean isLongshotNeedCancel() { + if (PersistUtil.getSkipMemoryCheck()) { + return false; + } + + if (Storage.getAvailableSpace() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { + Log.w(TAG, "current storage is full"); + return true; + } + if (SECONDARY_SERVER_MEM == 0) { + ActivityManager am = (ActivityManager) mActivity.getSystemService( + Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); + am.getMemoryInfo(memInfo); + SECONDARY_SERVER_MEM = memInfo.secondaryServerThreshold; + } + + long totalMemory = Runtime.getRuntime().totalMemory(); + long maxMemory = Runtime.getRuntime().maxMemory(); + long remainMemory = maxMemory - totalMemory; + + MemInfoReader reader = new MemInfoReader(); + reader.readMemInfo(); + long[] info = reader.getRawInfo(); + long availMem = (info[Debug.MEMINFO_FREE] + info[Debug.MEMINFO_CACHED]) * 1024; + + if (availMem <= SECONDARY_SERVER_MEM || remainMemory <= LONGSHOT_CANCEL_THRESHOLD) { + Log.e(TAG, "cancel longshot: free=" + info[Debug.MEMINFO_FREE] * 1024 + + " cached=" + info[Debug.MEMINFO_CACHED] * 1024 + + " threshold=" + SECONDARY_SERVER_MEM); + RotateTextToast.makeText(mActivity, R.string.msg_cancel_longshot_for_limited_memory, + Toast.LENGTH_SHORT).show(); + return true; + } + + return false; + } + + private boolean isFlashOff(int id) { + if (!mSettingsManager.isFlashSupported(id)) return true; + return mSettingsManager.getValue(SettingsManager.KEY_FLASH_MODE).equals("1"); } private void initializePreviewConfiguration(int id) { - mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_MODE, CaptureRequest - .CONTROL_MODE_AUTO); - mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest - .CONTROL_AF_MODE_CONTINUOUS_PICTURE); mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest .CONTROL_AF_TRIGGER_IDLE); - mPreviewRequestBuilder[id].set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest - .CONTROL_AE_MODE_ON); - mPreviewRequestBuilder[id].set(CaptureRequest.FLASH_MODE, CaptureRequest - .FLASH_MODE_OFF); - applyWhiteBalance(mPreviewRequestBuilder[id]); - applyZoom(mPreviewRequestBuilder[id], id); + applyCommonSettings(mPreviewRequestBuilder[id], id); + applyFaceDetect(mPreviewRequestBuilder[id], id); } - private void readInitialValues() { - for (int i = 0; i < mPreferenceGroup.size(); i++) { - CameraPreference pref = mPreferenceGroup.get(i); - if (pref instanceof ListPreference) { - ListPreference listPref = (ListPreference) pref; - storeSetting(listPref.getKey(), listPref.getValue()); - } - } + public float getZoomValue() { + return mZoomValue; } public Rect cropRegionForZoom(int id) { Log.d(TAG, "cropRegionForZoom " + id); - Rect activeRegion = mCharacteristics[id].get(CameraCharacteristics - .SENSOR_INFO_ACTIVE_ARRAY_SIZE); + Rect activeRegion = mSettingsManager.getSensorActiveArraySize(id); Rect cropRegion = new Rect(); int xCenter = activeRegion.width() / 2; @@ -1438,36 +1901,36 @@ public class CaptureModule implements CameraModule, PhotoController, request.set(CaptureRequest.SCALER_CROP_REGION, cropRegionForZoom(id)); } - private void applyCaptureSettings(CaptureRequest.Builder request, int id) { - applyFlash(request); - applyWhiteBalance(request); - applyJpegQuality(request); - applyZoom(request, id); - } - - private void applyPreference(int cameraId, ListPreference pref) { + private boolean applyPreferenceToPreview(int cameraId, String key, String value) { boolean updatePreview = false; - String key = pref.getKey(); - String value = pref.getValue(); - storeSetting(key, value); switch (key) { - //Todo: CreateUISettings file and add UI preference settings to there - case CameraSettings.KEY_JPEG_QUALITY: - mJpegQuality = getQualityNumber(value); - break; - case CameraSettings.KEY_WHITE_BALANCE: + case SettingsManager.KEY_WHITE_BALANCE: updatePreview = true; applyWhiteBalance(mPreviewRequestBuilder[cameraId]); break; + case SettingsManager.KEY_COLOR_EFFECT: + updatePreview = true; + applyColorEffect(mPreviewRequestBuilder[cameraId]); + break; + case SettingsManager.KEY_SCENE_MODE: + updatePreview = true; + applySceneMode(mPreviewRequestBuilder[cameraId]); + break; + case SettingsManager.KEY_EXPOSURE: + updatePreview = true; + applyExposure(mPreviewRequestBuilder[cameraId]); + break; + case SettingsManager.KEY_FLASH_MODE: + updatePreview = true; + applyFlash(mPreviewRequestBuilder[cameraId], cameraId); + break; + case SettingsManager.KEY_ISO: + updatePreview = true; + applyIso(mPreviewRequestBuilder[cameraId]); + break; } - if (updatePreview) { - try { - mCaptureSession[cameraId].setRepeatingRequest(mPreviewRequestBuilder[cameraId] - .build(), mCaptureCallback, mCameraHandler); - } catch (CameraAccessException e) { - e.printStackTrace(); - } - } + applyFaceDetect(mPreviewRequestBuilder[cameraId], cameraId); + return updatePreview; } private void applyZoomAndUpdate(int id) { @@ -1480,14 +1943,6 @@ public class CaptureModule implements CameraModule, PhotoController, } } - private void storeSetting(String key, String value) { - mSettings.put(key, value); - } - - private String readSetting(String key) { - return mSettings.get(key); - } - private void applyJpegQuality(CaptureRequest.Builder request) { request.set(CaptureRequest.JPEG_QUALITY, (byte) mJpegQuality); } @@ -1500,64 +1955,101 @@ public class CaptureModule implements CameraModule, PhotoController, } } - private void applyWhiteBalance(CaptureRequest.Builder request) { - String value = readSetting(CameraSettings.KEY_WHITE_BALANCE); - switch (value) { - case "incandescent": - request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest - .CONTROL_AWB_MODE_INCANDESCENT); - break; - case "fluorescent": - request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest - .CONTROL_AWB_MODE_FLUORESCENT); - break; - case "auto": - request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest - .CONTROL_AWB_MODE_AUTO); - break; - case "daylight": - request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest - .CONTROL_AWB_MODE_DAYLIGHT); - break; - case "cloudy-daylight": - request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest - .CONTROL_AWB_MODE_CLOUDY_DAYLIGHT); - break; + private void applySceneMode(CaptureRequest.Builder request) { + String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (value == null) return; + int mode = Integer.parseInt(value); + if (mode != CaptureRequest.CONTROL_SCENE_MODE_DISABLED && mode != + SettingsManager.SCENE_MODE_DUAL_INT) { + request.set(CaptureRequest.CONTROL_SCENE_MODE, mode); + request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_USE_SCENE_MODE); + } else { + request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); } } - private Surface getPreviewSurface(int id) { - if (MODE == DUAL_MODE && id == MONO_ID) { - return mUI.getSurfaceHolder2().getSurface(); - } else { - return mUI.getSurfaceHolder().getSurface(); - } + private void applyExposure(CaptureRequest.Builder request) { + String value = mSettingsManager.getValue(SettingsManager.KEY_EXPOSURE); + if (value == null) return; + int intValue = Integer.parseInt(value); + request.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, intValue); + } + + private void applyIso(CaptureRequest.Builder request) { + String value = mSettingsManager.getValue(SettingsManager.KEY_ISO); + if (value == null) return; + if (value.equals("auto")) return; + int intValue = Integer.parseInt(value); + request.set(CaptureRequest.SENSOR_SENSITIVITY, intValue); + } + + private void applyColorEffect(CaptureRequest.Builder request) { + String value = mSettingsManager.getValue(SettingsManager.KEY_COLOR_EFFECT); + if (value == null) return; + int mode = Integer.parseInt(value); + request.set(CaptureRequest.CONTROL_EFFECT_MODE, mode); + } + + private void applyWhiteBalance(CaptureRequest.Builder request) { + String value = mSettingsManager.getValue(SettingsManager.KEY_WHITE_BALANCE); + if (value == null) return; + int mode = Integer.parseInt(value); + request.set(CaptureRequest.CONTROL_AWB_MODE, mode); } private void applyFlash(CaptureRequest.Builder request, String value) { - switch (value) { - case "on": - request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest - .CONTROL_AE_MODE_ON_ALWAYS_FLASH); + int mode = Integer.parseInt(value); + String redeye = mSettingsManager.getValue(SettingsManager.KEY_REDEYE_REDUCTION); + request.set(CaptureRequest.CONTROL_AE_MODE, mode); + switch (mode) { + case CaptureRequest.CONTROL_AE_MODE_ON_ALWAYS_FLASH: request.set(CaptureRequest.FLASH_MODE, CaptureRequest .FLASH_MODE_SINGLE); break; - case "auto": - request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest - .CONTROL_AE_MODE_ON_AUTO_FLASH); - break; - case "off": - request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest - .CONTROL_AE_MODE_ON); + case CaptureRequest.CONTROL_AE_MODE_ON: request.set(CaptureRequest.FLASH_MODE, CaptureRequest .FLASH_MODE_OFF); break; + case CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH: + if (redeye.equals("disable")) { + request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest + .CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE); + } + break; } } - private void applyFlash(CaptureRequest.Builder request) { - String value = readSetting(CameraSettings.KEY_FLASH_MODE); - applyFlash(request, value); + private void applyFlash(CaptureRequest.Builder request, int id) { + if (mSettingsManager.isFlashSupported(id)) { + String value = mSettingsManager.getValue(SettingsManager.KEY_FLASH_MODE); + applyFlash(request, value); + } else { + request.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF); + } + } + + private Surface getPreviewSurface(int id) { + if (isBackCamera()) { + if (getCameraMode() == DUAL_MODE && id == MONO_ID) { + return mUI.getSurfaceHolder2().getSurface(); + } else { + return mFrameProcessor.getInputSurface(); + } + } else { + return mFrameProcessor.getInputSurface(); + } + } + + private Surface getPreviewSurfaceForSession(int id) { + if (isBackCamera()) { + if (getCameraMode() == DUAL_MODE && id == MONO_ID) { + return mUI.getSurfaceHolder2().getSurface(); + } else { + return mUI.getSurfaceHolder().getSurface(); + } + } else { + return mUI.getSurfaceHolder().getSurface(); + } } @Override @@ -1572,12 +2064,7 @@ public class CaptureModule implements CameraModule, PhotoController, public void triggerFocusAtPoint(float x, float y, int id) { Log.d(TAG, "triggerFocusAtPoint " + x + " " + y + " " + id); - Point p; - if (id == getMainCameraId()) { - p = mUI.getSurfaceViewSize(); - } else { - p = mUI.getSurfaceView2Size(); - } + Point p = mUI.getSurfaceViewSize(); int width = p.x; int height = p.y; x = x / width; @@ -1622,6 +2109,144 @@ public class CaptureModule implements CameraModule, PhotoController, mLastResultAFState = resultAFState; } + @Override + public void onSettingsChanged(List<SettingsManager.SettingState> settings) { + if (mPaused) return; + boolean updatePreviewBayer = false; + boolean updatePreviewMono = false; + boolean updatePreviewFront = false; + int count = 0; + for (SettingsManager.SettingState settingState : settings) { + String key = settingState.key; + SettingsManager.Values values = settingState.values; + String value; + if (values.overriddenValue != null) { + value = values.overriddenValue; + } else { + value = values.value; + } + switch (key) { + case SettingsManager.KEY_CAMERA_SAVEPATH: + Storage.setSaveSDCard(value.equals("1")); + mActivity.updateStorageSpaceAndHint(); + continue; + case SettingsManager.KEY_JPEG_QUALITY: + mJpegQuality = getQualityNumber(value); + estimateJpegFileSize(); + continue; + case SettingsManager.KEY_CAMERA2: + switchCameraMode(value); + return; + case SettingsManager.KEY_CAMERA_ID: + case SettingsManager.KEY_MONO_ONLY: + case SettingsManager.KEY_CLEARSIGHT: + case SettingsManager.KEY_PICTURE_SIZE: + case SettingsManager.KEY_MONO_PREVIEW: + if (count == 0) restart(); + return; + case SettingsManager.KEY_MAKEUP: + restart(); + return; + case SettingsManager.KEY_SCENE_MODE: + if (count == 0 && checkNeedToRestart(value)) { + restart(); + return; + } + break; + } + + if (isBackCamera()) { + switch (getCameraMode()) { + case BAYER_MODE: + updatePreviewBayer |= applyPreferenceToPreview(BAYER_ID, key, value); + break; + case MONO_MODE: + updatePreviewMono |= applyPreferenceToPreview(MONO_ID, key, value); + break; + case DUAL_MODE: + updatePreviewBayer |= applyPreferenceToPreview(BAYER_ID, key, value); + updatePreviewMono |= applyPreferenceToPreview(MONO_ID, key, value); + break; + } + } else { + updatePreviewFront |= applyPreferenceToPreview(FRONT_ID, key, value); + } + count++; + } + if (updatePreviewBayer) { + try { + mCaptureSession[BAYER_ID].setRepeatingRequest(mPreviewRequestBuilder[BAYER_ID] + .build(), mCaptureCallback, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + if (updatePreviewMono) { + try { + mCaptureSession[MONO_ID].setRepeatingRequest(mPreviewRequestBuilder[MONO_ID] + .build(), mCaptureCallback, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + if (updatePreviewFront) { + try { + mCaptureSession[FRONT_ID].setRepeatingRequest(mPreviewRequestBuilder[FRONT_ID] + .build(), mCaptureCallback, mCameraHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + } + + private int mCurrentMode; + + private boolean checkNeedToRestart(String value) { + mPostProcessor.setFilter(PostProcessor.FILTER_NONE); + if (isPostProcFilter(value)) + return true; + if (value.equals(SettingsManager.SCENE_MODE_DUAL_STRING) && mCurrentMode != DUAL_MODE) + return true; + if (!value.equals(SettingsManager.SCENE_MODE_DUAL_STRING) && mCurrentMode == DUAL_MODE) + return true; + return false; + } + + private void restart() { + reinit(); + onPauseBeforeSuper(); + onPauseAfterSuper(); + onResumeBeforeSuper(); + onResumeAfterSuper(); + } + + private void switchCameraMode(String value) { + if (value.equals("enable")) { + mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX); + } else { + mActivity.onModuleSelected(ModuleSwitcher.PHOTO_MODULE_INDEX); + } + } + + private Size getOptimalPreviewSize(Size pictureSize, Size[] prevSizes, int screenW, int + screenH) { + Size optimal = prevSizes[0]; + float ratio = (float) pictureSize.getWidth() / pictureSize.getHeight(); + for (Size prevSize: prevSizes) { + float prevRatio = (float) prevSize.getWidth() / prevSize.getHeight(); + if (Math.abs(prevRatio - ratio) < 0.01) { + // flip w and h + if (prevSize.getWidth() <= screenH && prevSize.getHeight() <= screenW && + prevSize.getWidth() <= pictureSize.getWidth() && prevSize.getHeight() <= pictureSize.getHeight()) { + return prevSize; + } else { + optimal = prevSize; + } + } + } + return optimal; + } + /** * Compares two {@code Size}s based on their areas. */ diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index a6accae7b..5b8ecad6e 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -19,37 +19,36 @@ package com.android.camera; -import android.content.res.Configuration; -import android.graphics.Matrix; +import android.animation.Animator; +import android.content.Context; +import android.content.res.Resources; import android.graphics.Point; -import android.graphics.RectF; import android.graphics.drawable.AnimationDrawable; import android.hardware.Camera.Face; -import android.hardware.camera2.CameraCharacteristics; +import android.text.TextUtils; import android.util.Log; +import android.view.Display; import android.view.Gravity; +import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.SurfaceHolder; -import android.view.SurfaceView; import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnLayoutChangeListener; import android.view.ViewGroup; +import android.view.ViewPropertyAnimator; +import android.view.WindowManager; import android.widget.FrameLayout; -import android.widget.FrameLayout.LayoutParams; +import android.widget.ImageView; import android.widget.LinearLayout; -import android.widget.ListView; -import android.widget.PopupWindow; +import android.widget.TextView; -import com.android.camera.CameraPreference.OnPreferenceChangedListener; -import com.android.camera.FocusOverlayManager.FocusUI; +import com.android.camera.ui.AutoFitSurfaceView; import com.android.camera.ui.CameraControls; -import com.android.camera.ui.CameraRootView; +import com.android.camera.ui.CountDownView; import com.android.camera.ui.FocusIndicator; +import com.android.camera.ui.ListMenu; import com.android.camera.ui.ListSubMenu; import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.PieRenderer; -import com.android.camera.ui.PieRenderer.PieListener; import com.android.camera.ui.RenderOverlay; import com.android.camera.ui.RotateImageView; import com.android.camera.ui.RotateLayout; @@ -60,150 +59,165 @@ import com.android.camera.util.CameraUtil; import org.codeaurora.snapcam.R; import java.util.List; +import java.util.Locale; -public class CaptureUI implements PieListener, +public class CaptureUI implements FocusOverlayManager.FocusUI, PreviewGestures.SingleTapListener, - FocusUI, - SurfaceHolder.Callback, LocationManager.Listener, - CameraRootView.MyDisplayListener, - CameraManager.CameraFaceDetectionCallback { - + CameraManager.CameraFaceDetectionCallback, + SettingsManager.Listener, + ListMenu.Listener, + ListSubMenu.Listener { + private static final int HIGHLIGHT_COLOR = 0xff33b5e5; private static final String TAG = "SnapCam_CaptureUI"; - public boolean mMenuInitialized = false; - private boolean surface1created = false; - private boolean surface2created = false; + private static final int SETTING_MENU_NONE = 0; + private static final int SETTING_MENU_IN_ANIMATION = 1; + private static final int SETTING_MENU_ON = 2; + private static final int SETTING_MENU_LEVEL_ONE = 0; + private static final int SETTING_MENU_LEVEL_TWO = 1; + private static final int SCENE_AND_FILTER_MENU_NONE = 0; + private static final int SCENE_AND_FILTER_MENU_IN_ANIMATION = 1; + private static final int SCENE_AND_FILTER_MENU_ON = 2; + private static final int MODE_FILTER = 0; + private static final int MODE_SCENE = 1; + private static final int ANIMATION_DURATION = 300; + private static final int CLICK_THRESHOLD = 200; + String[] mSettingKeys = new String[]{ + SettingsManager.KEY_FLASH_MODE, + SettingsManager.KEY_RECORD_LOCATION, + SettingsManager.KEY_PICTURE_SIZE, + SettingsManager.KEY_JPEG_QUALITY, + SettingsManager.KEY_TIMER, + SettingsManager.KEY_CAMERA_SAVEPATH, + SettingsManager.KEY_LONGSHOT, + SettingsManager.KEY_EXPOSURE, + SettingsManager.KEY_WHITE_BALANCE, + SettingsManager.KEY_CAMERA2, + SettingsManager.KEY_MAKEUP + }; + String[] mDeveloperKeys = new String[]{ + SettingsManager.KEY_MONO_ONLY, + SettingsManager.KEY_CLEARSIGHT, + SettingsManager.KEY_MONO_PREVIEW + }; private CameraActivity mActivity; - private PhotoController mController; - private PreviewGestures mGestures; - private View mRootView; + private View mPreviewCover; + private CaptureModule mModule; + private AutoFitSurfaceView mSurfaceView; + private AutoFitSurfaceView mSurfaceView2; private SurfaceHolder mSurfaceHolder; private SurfaceHolder mSurfaceHolder2; + private int mOrientation; + private RotateLayout mMenuLayout; + private RotateLayout mSubMenuLayout; + private int mSettingMenuState; + private int mSettingMenuLevel; + private int mSceneAndFilterMenuStatus; + private int mSceneAndFilterMenuMode; + private ListMenu mSettingMenu; + private ListSubMenu mSettingSubMenu; + private PreviewGestures mGestures; + private boolean mUIhidden = false; + private SettingsManager mSettingsManager; + + private ImageView mThumbnail; + private SurfaceHolder.Callback callback = new SurfaceHolder.Callback() { // SurfaceHolder callbacks @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.v(TAG, "surfaceChanged: width =" + width + ", height = " + height); } @Override public void surfaceCreated(SurfaceHolder holder) { - mSurfaceHolder2 = holder; - if (surface1created) mController.onPreviewUIReady(); - surface2created = true; + Log.v(TAG, "surfaceCreated"); + mSurfaceHolder = holder; + mModule.onPreviewUIReady(); + mActivity.updateThumbnail(mThumbnail); } @Override public void surfaceDestroyed(SurfaceHolder holder) { - mSurfaceHolder2 = null; - surface2created = false; + Log.v(TAG, "surfaceDestroyed"); + mSurfaceHolder = null; + mModule.onPreviewUIDestroyed(); } }; - private PopupWindow mPopup; + private ShutterButton mShutterButton; private RenderOverlay mRenderOverlay; private View mMenuButton; - private CaptureMenu mMenu; private ModuleSwitcher mSwitcher; + private CountDownView mCountDownView; private CameraControls mCameraControls; - // Small indicators which show the camera settings in the viewfinder. - private OnScreenIndicators mOnScreenIndicators; private PieRenderer mPieRenderer; private ZoomRenderer mZoomRenderer; - private int mPreviewWidth = 0; - private int mPreviewHeight = 0; - private int mOriginalPreviewWidth = 0; - private int mOriginalPreviewHeight = 0; - private float mSurfaceTextureUncroppedWidth; - private float mSurfaceTextureUncroppedHeight; - - private SurfaceView mSurfaceView = null; - private SurfaceView mSurfaceView2 = null; - private Matrix mMatrix = null; - private boolean mAspectRatioResize; - - private boolean mOrientationResize; - private boolean mPrevOrientationResize; - private View mPreviewCover; - private RotateLayout mMenuLayout; - private RotateLayout mSubMenuLayout; - private LinearLayout mPreviewMenuLayout; - private boolean mUIhidden = false; - private int mPreviewOrientation = -1; private int mScreenRatio = CameraUtil.RATIO_UNKNOWN; private int mTopMargin = 0; private int mBottomMargin = 0; + private LinearLayout mSceneAndFilterLayout; + private int mSceneAndFilterMenuSize; - private int mOrientation; - private OnLayoutChangeListener mLayoutListener = new OnLayoutChangeListener() { + private View mFilterModeSwitcher; + private View mSceneModeSwitcher; + private View mFrontBackSwitcher; + + private SurfaceHolder.Callback callback2 = new SurfaceHolder.Callback() { + + // SurfaceHolder callbacks @Override - public void onLayoutChange(View v, int left, int top, int right, - int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - int width = right - left; - int height = bottom - top; - - int orientation = mActivity.getResources().getConfiguration().orientation; - if ((orientation == Configuration.ORIENTATION_PORTRAIT && width > height) - || (orientation == Configuration.ORIENTATION_LANDSCAPE && width < height)) { - // The screen has rotated; swap SurfaceView width & height - // to ensure correct preview - int oldWidth = width; - width = height; - height = oldWidth; - Log.d(TAG, "Swapping SurfaceView width & height dimensions"); - if (mOriginalPreviewWidth != 0 && mOriginalPreviewHeight != 0) { - int temp = mOriginalPreviewWidth; - mOriginalPreviewWidth = mOriginalPreviewHeight; - mOriginalPreviewHeight = temp; - } - } + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + } - if (mPreviewWidth != width || mPreviewHeight != height - || (mOrientationResize != mPrevOrientationResize) - || mAspectRatioResize) { - if (mOriginalPreviewWidth == 0) mOriginalPreviewWidth = width; - if (mOriginalPreviewHeight == 0) mOriginalPreviewHeight = height; - mPreviewWidth = width; - mPreviewHeight = height; - setTransformMatrix(mPreviewWidth, mPreviewHeight); - mController.onScreenSizeChanged((int) mSurfaceTextureUncroppedWidth, - (int) mSurfaceTextureUncroppedHeight); - mAspectRatioResize = false; - } + @Override + public void surfaceCreated(SurfaceHolder holder) { + mSurfaceHolder2 = holder; + } - if (mMenu != null) - mMenu.tryToCloseSubList(); + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + mSurfaceHolder2 = null; } }; - public CaptureUI(CameraActivity activity, PhotoController controller, View parent) { + public CaptureUI(CameraActivity activity, CaptureModule module, View parent) { mActivity = activity; - mController = controller; + mModule = module; mRootView = parent; + mSettingsManager = SettingsManager.getInstance(); + mSettingsManager.registerListener(this); mActivity.getLayoutInflater().inflate(R.layout.capture_module, (ViewGroup) mRootView, true); mPreviewCover = mRootView.findViewById(R.id.preview_cover); // display the view - mSurfaceView = (SurfaceView) mRootView.findViewById(R.id.mdp_preview_content); - mSurfaceView2 = (SurfaceView) mRootView.findViewById(R.id.mdp_preview_content2); + mSurfaceView = (AutoFitSurfaceView) mRootView.findViewById(R.id.mdp_preview_content); + mSurfaceView2 = (AutoFitSurfaceView) mRootView.findViewById(R.id.mdp_preview_content2); mSurfaceView2.setZOrderMediaOverlay(true); mSurfaceHolder = mSurfaceView.getHolder(); - mSurfaceHolder.addCallback(this); + mSurfaceHolder.addCallback(callback); mSurfaceHolder2 = mSurfaceView2.getHolder(); - mSurfaceHolder2.addCallback(callback); - Log.v(TAG, "Using mdp_preview_content (MDP path)"); + mSurfaceHolder2.addCallback(callback2); mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); + mFilterModeSwitcher = mRootView.findViewById(R.id.filter_mode_switcher); + mSceneModeSwitcher = mRootView.findViewById(R.id.scene_mode_switcher); + mFrontBackSwitcher = mRootView.findViewById(R.id.front_back_switcher); + initFilterModeButton(); + initSceneModeButton(); + initSwitchCamera(); + mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher); mSwitcher.setCurrentIndex(ModuleSwitcher.PHOTO_MODULE_INDEX); mSwitcher.setSwitchListener(mActivity); - mSwitcher.setOnClickListener(new OnClickListener() { + mSwitcher.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - if (mController.getCameraState() == PhotoController.LONGSHOT) { + if (mModule.getCameraState() == PhotoController.LONGSHOT) { return; } mSwitcher.showPopup(); @@ -217,10 +231,6 @@ public class CaptureUI implements PieListener, mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls); - initIndicators(); - mOrientationResize = false; - mPrevOrientationResize = false; - Point size = new Point(); mActivity.getWindowManager().getDefaultDisplay().getSize(size); mScreenRatio = CameraUtil.determineRatio(size.x, size.y); @@ -232,68 +242,12 @@ public class CaptureUI implements PieListener, mBottomMargin = l / 4 - mTopMargin; } mCameraControls.setMargins(mTopMargin, mBottomMargin); - } - - private void setTransformMatrix(int width, int height) { - mMatrix = mSurfaceView.getMatrix(); - - // Calculate the new preview rectangle. - RectF previewRect = new RectF(0, 0, width, height); - mMatrix.mapRect(previewRect); - mController.onPreviewRectChanged(CameraUtil.rectFToRect(previewRect)); - } - - // SurfaceHolder callbacks - @Override - public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { - Log.v(TAG, "surfaceChanged: width =" + width + ", height = " + height); - } - @Override - public void surfaceCreated(SurfaceHolder holder) { - Log.v(TAG, "surfaceCreated"); - mSurfaceHolder = holder; - if (surface2created) mController.onPreviewUIReady(); - surface1created = true; - if (mPreviewWidth != 0 && mPreviewHeight != 0) { - // Re-apply transform matrix for new surface texture - setTransformMatrix(mPreviewWidth, mPreviewHeight); - } - } - - @Override - public void surfaceDestroyed(SurfaceHolder holder) { - Log.v(TAG, "surfaceDestroyed"); - mSurfaceHolder = null; - surface1created = false; - mController.onPreviewUIDestroyed(); - } - - public View getRootView() { - return mRootView; - } - - private void initIndicators() { - mOnScreenIndicators = new OnScreenIndicators(mActivity, - mRootView.findViewById(R.id.on_screen_indicators)); - } - - public void onCameraOpened(CameraCharacteristics[] characteristics, - List<Integer> characteristicsIndex, PreferenceGroup prefGroup, - OnPreferenceChangedListener listener) { if (mPieRenderer == null) { mPieRenderer = new PieRenderer(mActivity); - mPieRenderer.setPieListener(this); mRenderOverlay.addRenderer(mPieRenderer); } - if (mMenu == null) { - mMenu = new CaptureMenu(mActivity, this); - mMenu.setListener(listener); - } - mMenu.initialize(prefGroup); - mMenuInitialized = true; - if (mZoomRenderer == null) { mZoomRenderer = new ZoomRenderer(mActivity); mRenderOverlay.addRenderer(mZoomRenderer); @@ -304,209 +258,686 @@ public class CaptureUI implements PieListener, mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer); mRenderOverlay.setGestures(mGestures); } - mGestures.setCaptureMenu(mMenu); - mGestures.setZoomEnabled(CameraUtil.isZoomSupported(characteristics, characteristicsIndex)); mGestures.setRenderOverlay(mRenderOverlay); mRenderOverlay.requestLayout(); - initializeZoom(characteristics, characteristicsIndex); mActivity.setPreviewGestures(mGestures); } - public void initializeControlByIntent() { - mMenuButton = mRootView.findViewById(R.id.menu); - mMenuButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - if (mMenu != null) { - mMenu.openFirstLevel(); - } - } - }); - } - - public void hideUI() { - mSwitcher.closePopup(); - if (mUIhidden) - return; - mUIhidden = true; - mCameraControls.hideUI(); + public void onCameraOpened(List<Integer> cameraIds) { + mGestures.setCaptureUI(this); + mGestures.setZoomEnabled(mSettingsManager.isZoomSupported(cameraIds)); + initializeZoom(cameraIds); } - public void showUI() { - if (!mUIhidden || (mMenu != null && mMenu.isMenuBeingShown())) - return; - mUIhidden = false; - mCameraControls.showUI(); + public ViewGroup getSceneAndFilterLayout() { + return mSceneAndFilterLayout; } - public boolean arePreviewControlsVisible() { - return !mUIhidden; + public void reInitUI() { + initializeSettingMenu(); + initSceneModeButton(); + initFilterModeButton(); } // called from onResume but only the first time public void initializeFirstTime() { // Initialize shutter button. mShutterButton.setImageResource(R.drawable.shutter_button_anim); - mShutterButton.setOnClickListener(new OnClickListener() { + mShutterButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!CameraControls.isAnimating()) doShutterAnimation(); } }); - mShutterButton.setOnShutterButtonListener(mController); + mShutterButton.setOnShutterButtonListener(mModule); mShutterButton.setVisibility(View.VISIBLE); } - // called from onResume every other time - public void initializeSecondTime() { - if (mMenu != null) { - mMenu.reloadPreferences(); + public void initializeZoom(List<Integer> ids) { + if (!mSettingsManager.isZoomSupported(ids) || (mZoomRenderer == null)) + return; + + Float zoomMax = mSettingsManager.getMaxZoom(ids); + mZoomRenderer.setZoomMax(zoomMax); + mZoomRenderer.setZoom(1f); + mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener()); + } + + public void enableGestures(boolean enable) { + if (mGestures != null) { + mGestures.setEnabled(enable); } } - public void doShutterAnimation() { - AnimationDrawable frameAnimation = (AnimationDrawable) mShutterButton.getDrawable(); - frameAnimation.stop(); - frameAnimation.start(); + public boolean isPreviewMenuBeingShown() { + return mSceneAndFilterMenuStatus == SCENE_AND_FILTER_MENU_ON; } - public void initializeZoom(CameraCharacteristics[] characteristics, - List<Integer> characteristicsIndex) { - if ((characteristics == null) || !CameraUtil.isZoomSupported(characteristics, - characteristicsIndex) || (mZoomRenderer == null)) - return; - if (mZoomRenderer != null) { - float zoomMax = Float.MAX_VALUE; - for (int i = 0; i < characteristicsIndex.size(); i++) { - zoomMax = Math.min(characteristics[characteristicsIndex.get(i)].get - (CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM), zoomMax); + public void removeSceneAndFilterMenu(boolean animate) { + if (animate) { + animateSlideOut(mSceneAndFilterLayout); + } else { + mSceneAndFilterMenuStatus = SCENE_AND_FILTER_MENU_NONE; + if (mSceneAndFilterLayout != null) { + ((ViewGroup) mRootView).removeView(mSceneAndFilterLayout); + mSceneAndFilterLayout = null; } - mZoomRenderer.setZoomMax(zoomMax); - mZoomRenderer.setZoom(1f); - mZoomRenderer.setOnZoomChangeListener(new ZoomChangeListener()); } } - @Override - public void showGpsOnScreenIndicator(boolean hasSignal) { + public void initSwitchCamera() { + mFrontBackSwitcher.setVisibility(View.INVISIBLE); + String value = mSettingsManager.getValue(SettingsManager.KEY_CAMERA_ID); + if (value == null) + return; + + int[] largeIcons = mSettingsManager.getResource(SettingsManager.KEY_CAMERA_ID, + SettingsManager.RESOURCE_TYPE_LARGEICON); + ((ImageView) mFrontBackSwitcher).setImageResource(largeIcons[mSettingsManager + .getValueIndex(SettingsManager.KEY_CAMERA_ID)]); + mFrontBackSwitcher.setVisibility(View.VISIBLE); + mFrontBackSwitcher.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + String value = mSettingsManager.getValue(SettingsManager.KEY_CAMERA_ID); + if (value == null) + return; + + int index = mSettingsManager.getValueIndex(SettingsManager.KEY_CAMERA_ID); + CharSequence[] entries = mSettingsManager.getEntries(SettingsManager.KEY_CAMERA_ID); + index = (index + 1) % entries.length; + mSettingsManager.setValueIndex(SettingsManager.KEY_CAMERA_ID, index); + int[] largeIcons = mSettingsManager.getResource(SettingsManager.KEY_CAMERA_ID, + SettingsManager.RESOURCE_TYPE_LARGEICON); + ((ImageView) v).setImageResource(largeIcons[index]); + } + }); } - @Override - public void hideGpsOnScreenIndicator() { + public void initSceneModeButton() { + mSceneModeSwitcher.setVisibility(View.INVISIBLE); + String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (value == null) return; + updateSceneModeIcon(); + mSceneModeSwitcher.setVisibility(View.VISIBLE); + mSceneModeSwitcher.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + addSceneMode(); + ViewGroup menuLayout = getSceneAndFilterLayout(); + if (menuLayout != null) { + View view = menuLayout.getChildAt(0); + adjustOrientation(); + animateSlideIn(view, mSceneAndFilterMenuSize, false); + } + } + }); } - public void overrideSettings(final String... keyvalues) { - if (mMenu == null) - return; - mMenu.overrideSettings(keyvalues); + public void initFilterModeButton() { + mFilterModeSwitcher.setVisibility(View.INVISIBLE); + String value = mSettingsManager.getValue(SettingsManager.KEY_COLOR_EFFECT); + if (value == null) return; + changeFilterModeControlIcon(value); + + updateFilterModeIcon(!mSettingsManager.isOverriden(SettingsManager.KEY_COLOR_EFFECT)); + mFilterModeSwitcher.setVisibility(View.VISIBLE); + mFilterModeSwitcher.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + addFilterMode(); + ViewGroup menuLayout = getSceneAndFilterLayout(); + if (menuLayout != null) { + View view = getSceneAndFilterLayout().getChildAt(0); + adjustOrientation(); + animateSlideIn(view, mSceneAndFilterMenuSize, false); + } + } + }); } - public void enableGestures(boolean enable) { - if (mGestures != null) { - mGestures.setEnabled(enable); + public void setSwitcherIndex() { + mSwitcher.setCurrentIndex(ModuleSwitcher.PHOTO_MODULE_INDEX); + } + + public void addSceneMode() { + String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); + if (value == null) return; + + int rotation = CameraUtil.getDisplayRotation(mActivity); + boolean mIsDefaultToPortrait = CameraUtil.isDefaultToPortrait(mActivity); + if (!mIsDefaultToPortrait) { + rotation = (rotation + 90) % 360; + } + WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + + CharSequence[] entries = mSettingsManager.getEntries(SettingsManager.KEY_SCENE_MODE); + + int[] thumbnails = mSettingsManager.getResource(SettingsManager.KEY_SCENE_MODE, + SettingsManager.RESOURCE_TYPE_THUMBNAIL); + Resources r = mActivity.getResources(); + int height = (int) (r.getDimension(R.dimen.scene_mode_height) + 2 + * r.getDimension(R.dimen.scene_mode_padding) + 1); + int width = (int) (r.getDimension(R.dimen.scene_mode_width) + 2 + * r.getDimension(R.dimen.scene_mode_padding) + 1); + + int gridRes; + boolean portrait = (rotation == 0) || (rotation == 180); + int size = height; + if (portrait) { + gridRes = R.layout.vertical_grid; + size = width; + } else { + gridRes = R.layout.horiz_grid; + } + mSceneAndFilterMenuSize = size; + hideUI(); + + LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + FrameLayout gridOuterLayout = (FrameLayout) inflater.inflate( + gridRes, null, false); + + removeSceneAndFilterMenu(false); + mSceneAndFilterMenuStatus = SCENE_AND_FILTER_MENU_ON; + mSceneAndFilterMenuMode = MODE_SCENE; + mSceneAndFilterLayout = new LinearLayout(mActivity); + ViewGroup.LayoutParams params = null; + if (portrait) { + params = new ViewGroup.LayoutParams(size, FrameLayout.LayoutParams.MATCH_PARENT); + mSceneAndFilterLayout.setLayoutParams(params); + ((ViewGroup) mRootView).addView(mSceneAndFilterLayout); + } else { + params = new ViewGroup.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, size); + mSceneAndFilterLayout.setLayoutParams(params); + ((ViewGroup) mRootView).addView(mSceneAndFilterLayout); + mSceneAndFilterLayout.setY(display.getHeight() - size); + } + gridOuterLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams + .MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + LinearLayout gridLayout = (LinearLayout) gridOuterLayout.findViewById(R.id.layout); + + final View[] views = new View[entries.length]; + int init = mSettingsManager.getValueIndex(SettingsManager.KEY_SCENE_MODE); + for (int i = 0; i < entries.length; i++) { + RotateLayout sceneBox = (RotateLayout) inflater.inflate( + R.layout.scene_mode_view, null, false); + + ImageView imageView = (ImageView) sceneBox.findViewById(R.id.image); + TextView label = (TextView) sceneBox.findViewById(R.id.label); + final int j = i; + + sceneBox.setOnTouchListener(new View.OnTouchListener() { + private long startTime; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + startTime = System.currentTimeMillis(); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (System.currentTimeMillis() - startTime < CLICK_THRESHOLD) { + for (View v1 : views) { + v1.setBackgroundResource(R.drawable.scene_mode_view_border); + } + View border = v.findViewById(R.id.border); + border.setBackgroundResource(R.drawable.scene_mode_view_border_selected); + updateSceneModeIcon(j); + mSettingsManager.setValueIndex(SettingsManager.KEY_SCENE_MODE, j); + removeSceneAndFilterMenu(true); + } + } + return true; + } + }); + + View border = sceneBox.findViewById(R.id.border); + views[j] = border; + if (i == init) + border.setBackgroundResource(R.drawable.scene_mode_view_border_selected); + + imageView.setImageResource(thumbnails[i]); + label.setText(entries[i]); + gridLayout.addView(sceneBox); } + mSceneAndFilterLayout.addView(gridOuterLayout); } - // forward from preview gestures to controller - @Override - public void onSingleTapUp(View view, int x, int y) { - mController.onSingleTapUp(view, x, y); + public void updateSceneModeIcon() { + int[] thumbnails = mSettingsManager.getResource(SettingsManager.KEY_SCENE_MODE, + SettingsManager.RESOURCE_TYPE_THUMBNAIL); + int thumbnail = thumbnails[mSettingsManager.getValueIndex(SettingsManager + .KEY_SCENE_MODE)]; + if (thumbnail == -1) + thumbnail = 0; + ((ImageView) mSceneModeSwitcher).setImageResource(thumbnail); } - public boolean onBackPressed() { - if (mMenu != null && mMenu.handleBackKey()) { - return true; + public void updateSceneModeIcon(int idx) { + int[] thumbnails = mSettingsManager.getResource(SettingsManager.KEY_SCENE_MODE, + SettingsManager.RESOURCE_TYPE_THUMBNAIL); + int thumbnail = thumbnails[idx]; + if (thumbnail == -1) + thumbnail = 0; + ((ImageView) mSceneModeSwitcher).setImageResource(thumbnail); + } + + public void addFilterMode() { + if (mSettingsManager.getValue(SettingsManager.KEY_COLOR_EFFECT) == null) + return; + + int rotation = CameraUtil.getDisplayRotation(mActivity); + boolean mIsDefaultToPortrait = CameraUtil.isDefaultToPortrait(mActivity); + if (!mIsDefaultToPortrait) { + rotation = (rotation + 90) % 360; } + WindowManager wm = (WindowManager) mActivity.getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + CharSequence[] entries = mSettingsManager.getEntries(SettingsManager.KEY_COLOR_EFFECT); + + Resources r = mActivity.getResources(); + int height = (int) (r.getDimension(R.dimen.filter_mode_height) + 2 + * r.getDimension(R.dimen.filter_mode_padding) + 1); + int width = (int) (r.getDimension(R.dimen.filter_mode_width) + 2 + * r.getDimension(R.dimen.filter_mode_padding) + 1); + + int gridRes; + boolean portrait = (rotation == 0) || (rotation == 180); + int size = height; + if (portrait) { + gridRes = R.layout.vertical_grid; + size = width; + } else { + gridRes = R.layout.horiz_grid; + } + mSceneAndFilterMenuSize = size; + hideUI(); - if (mPieRenderer != null && mPieRenderer.showsItems()) { - mPieRenderer.hide(); - return true; + int[] thumbnails = mSettingsManager.getResource(SettingsManager.KEY_COLOR_EFFECT, + SettingsManager.RESOURCE_TYPE_THUMBNAIL); + LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + FrameLayout gridOuterLayout = (FrameLayout) inflater.inflate( + gridRes, null, false); + + removeSceneAndFilterMenu(false); + mSceneAndFilterMenuStatus = SCENE_AND_FILTER_MENU_ON; + mSceneAndFilterMenuMode = MODE_FILTER; + mSceneAndFilterLayout = new LinearLayout(mActivity); + + ViewGroup.LayoutParams params = null; + if (portrait) { + params = new ViewGroup.LayoutParams(size, FrameLayout.LayoutParams.MATCH_PARENT); + mSceneAndFilterLayout.setLayoutParams(params); + ((ViewGroup) mRootView).addView(mSceneAndFilterLayout); + } else { + params = new ViewGroup.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, size); + mSceneAndFilterLayout.setLayoutParams(params); + ((ViewGroup) mRootView).addView(mSceneAndFilterLayout); + mSceneAndFilterLayout.setY(display.getHeight() - size); } - // In image capture mode, back button should: - // 1) if there is any popup, dismiss them, 2) otherwise, get out of - // image capture - if (mController.isImageCaptureIntent()) { - mController.onCaptureCancelled(); - return true; - } else if (!mController.isCameraIdle()) { - // ignore backs while we're taking a picture - return true; + gridOuterLayout.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams + .MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); + LinearLayout gridLayout = (LinearLayout) gridOuterLayout.findViewById(R.id.layout); + final View[] views = new View[entries.length]; + + int init = mSettingsManager.getValueIndex(SettingsManager.KEY_COLOR_EFFECT); + for (int i = 0; i < entries.length; i++) { + RotateLayout filterBox = (RotateLayout) inflater.inflate( + R.layout.filter_mode_view, null, false); + ImageView imageView = (ImageView) filterBox.findViewById(R.id.image); + final int j = i; + + filterBox.setOnTouchListener(new View.OnTouchListener() { + private long startTime; + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_DOWN) { + startTime = System.currentTimeMillis(); + } else if (event.getAction() == MotionEvent.ACTION_UP) { + if (System.currentTimeMillis() - startTime < CLICK_THRESHOLD) { + mSettingsManager.setValueIndex(SettingsManager + .KEY_COLOR_EFFECT, j); + for (View v1 : views) { + v1.setBackground(null); + } + ImageView image = (ImageView) v.findViewById(R.id.image); + image.setBackgroundColor(HIGHLIGHT_COLOR); + } + } + return true; + } + }); + + views[j] = imageView; + if (i == init) + imageView.setBackgroundColor(HIGHLIGHT_COLOR); + TextView label = (TextView) filterBox.findViewById(R.id.label); + + imageView.setImageResource(thumbnails[i]); + label.setText(entries[i]); + gridLayout.addView(filterBox); } - if (mSwitcher != null && mSwitcher.showsPopup()) { - mSwitcher.closePopup(); - return true; + mSceneAndFilterLayout.addView(gridOuterLayout); + } + + private void changeFilterModeControlIcon(String value) { + int index; + if (value.equals("0")) { + index = 0; } else { - return false; + index = 1; } + ImageView iv = (ImageView) mFilterModeSwitcher; + iv.setImageResource(mSettingsManager.getResource(SettingsManager + .KEY_COLOR_EFFECT, SettingsManager.RESOURCE_TYPE_LARGEICON)[index]); } - public void onPreviewFocusChanged(boolean previewFocused) { - if (previewFocused) { - showUI(); + private void updateFilterModeIcon(boolean enable) { + buttonSetEnabled(mFilterModeSwitcher, enable); + } + + private void buttonSetEnabled(View v, boolean enable) { + v.setEnabled(enable); + if (v instanceof ViewGroup) { + View v2 = ((ViewGroup) v).getChildAt(0); + if (v2 != null) + v2.setEnabled(enable); + } + } + + private void animateFadeOut(final View v, final int level) { + if (v == null || mSettingMenuState == SETTING_MENU_IN_ANIMATION) + return; + mSettingMenuState = SETTING_MENU_IN_ANIMATION; + + ViewPropertyAnimator vp = v.animate(); + vp.alpha(0f).setDuration(ANIMATION_DURATION); + vp.setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + finishSettingMenuAnimateOut(level); + } + + @Override + public void onAnimationCancel(Animator animation) { + finishSettingMenuAnimateOut(level); + } + }); + vp.start(); + } + + private void animateSlideOut(final View v, final int level) { + if (v == null || mSettingMenuState == SETTING_MENU_IN_ANIMATION) + return; + mSettingMenuState = SETTING_MENU_IN_ANIMATION; + ViewPropertyAnimator vp = v.animate(); + if (View.LAYOUT_DIRECTION_RTL == TextUtils + .getLayoutDirectionFromLocale(Locale.getDefault())) { + switch (getOrientation()) { + case 0: + vp.translationXBy(v.getWidth()); + break; + case 90: + vp.translationYBy(-2 * v.getHeight()); + break; + case 180: + vp.translationXBy(-2 * v.getWidth()); + break; + case 270: + vp.translationYBy(v.getHeight()); + break; + } } else { - hideUI(); + switch (getOrientation()) { + case 0: + vp.translationXBy(-v.getWidth()); + break; + case 90: + vp.translationYBy(2 * v.getHeight()); + break; + case 180: + vp.translationXBy(2 * v.getWidth()); + break; + case 270: + vp.translationYBy(-v.getHeight()); + break; + } } - if (mGestures != null) { - mGestures.setEnabled(previewFocused); + vp.setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + finishSettingMenuAnimateOut(level); + } + + @Override + public void onAnimationCancel(Animator animation) { + finishSettingMenuAnimateOut(level); + } + }); + vp.setDuration(ANIMATION_DURATION).start(); + } + + private void finishSettingMenuAnimateOut(int level) { + if (level == SETTING_MENU_LEVEL_ONE) { + mSettingMenuState = SETTING_MENU_ON; + removeSettingMenu(level, false); + cleanUpMenus(); + } else if (level == SETTING_MENU_LEVEL_TWO) { + mSettingMenuState = SETTING_MENU_ON; + removeSettingMenu(level, false); } - if (mRenderOverlay != null) { - // this can not happen in capture mode - mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE); + } + + private void finishScenceAndFilterMenuAnimateOut() { + removeSceneAndFilterMenu(false); + cleanUpMenus(); + } + + public void animateFadeIn(View v) { + ViewPropertyAnimator vp = v.animate(); + vp.alpha(0.85f).setDuration(ANIMATION_DURATION); + vp.start(); + } + + private void animateSlideOut(final View v) { + if (v == null || mSceneAndFilterMenuStatus == SCENE_AND_FILTER_MENU_IN_ANIMATION) + return; + mSceneAndFilterMenuStatus = SCENE_AND_FILTER_MENU_IN_ANIMATION; + + ViewPropertyAnimator vp = v.animate(); + if (View.LAYOUT_DIRECTION_RTL == TextUtils + .getLayoutDirectionFromLocale(Locale.getDefault())) { + vp.translationXBy(v.getWidth()).setDuration(ANIMATION_DURATION); + } else { + vp.translationXBy(-v.getWidth()).setDuration(ANIMATION_DURATION); } - if (mPieRenderer != null) { - mPieRenderer.setBlockFocus(!previewFocused); + vp.setListener(new Animator.AnimatorListener() { + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + + } + + @Override + public void onAnimationEnd(Animator animation) { + finishScenceAndFilterMenuAnimateOut(); + } + + @Override + public void onAnimationCancel(Animator animation) { + finishScenceAndFilterMenuAnimateOut(); + } + }); + vp.start(); + } + + public void animateSlideIn(View v, int delta, boolean forcePortrait) { + int orientation = getOrientation(); + if (!forcePortrait) + orientation = 0; + + ViewPropertyAnimator vp = v.animate(); + float dest; + if (View.LAYOUT_DIRECTION_RTL == TextUtils + .getLayoutDirectionFromLocale(Locale.getDefault())) { + switch (orientation) { + case 0: + dest = v.getX(); + v.setX(-(dest - delta)); + vp.translationX(dest); + break; + case 90: + dest = v.getY(); + v.setY(-(dest + delta)); + vp.translationY(dest); + break; + case 180: + dest = v.getX(); + v.setX(-(dest + delta)); + vp.translationX(dest); + break; + case 270: + dest = v.getY(); + v.setY(-(dest - delta)); + vp.translationY(dest); + break; + } + } else { + switch (orientation) { + case 0: + dest = v.getX(); + v.setX(dest - delta); + vp.translationX(dest); + break; + case 90: + dest = v.getY(); + v.setY(dest + delta); + vp.translationY(dest); + break; + case 180: + dest = v.getX(); + v.setX(dest + delta); + vp.translationX(dest); + break; + case 270: + dest = v.getY(); + v.setY(dest - delta); + vp.translationY(dest); + break; + } } - setShowMenu(previewFocused); + vp.setDuration(ANIMATION_DURATION).start(); } - public ViewGroup getMenuLayout() { - return mMenuLayout; + private void initializeSettingMenu() { + LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + mSettingMenu = (ListMenu) inflater.inflate( + R.layout.list_menu, null, false); + + mSettingMenu.setSettingChangedListener(this); + mSettingMenu.setSettingsManager(mSettingsManager); + + String[] keys = mSettingKeys; + if (mActivity.isDeveloperMenuEnabled()) { + keys = mDeveloperKeys; + String[] combined = new String[mSettingKeys.length + mDeveloperKeys.length]; + int idx = 0; + for (String key: mSettingKeys) { + combined[idx++] = key; + } + for (String key: mDeveloperKeys) { + combined[idx++] = key; + } + keys = combined; + } + mSettingMenu.initializeForCamera2(keys); + } + + public boolean isMenuBeingShown() { + return mSettingMenuState != SETTING_MENU_NONE; + } + + public boolean isMenuBeingAnimated() { + return mSettingMenuState == SETTING_MENU_IN_ANIMATION; + } + + + public void showSettingMenu() { + if (isMenuBeingShown() || CameraControls.isAnimating()) { + return; + } + if (mSettingMenu == null) { + initializeSettingMenu(); + } + showSettingMenu(SETTING_MENU_LEVEL_ONE, true); } - public void showPopup(ListView popup, int level, boolean animate) { + private void showSettingMenu(int level, boolean animate) { FrameLayout.LayoutParams params; hideUI(); - popup.setVisibility(View.VISIBLE); - if (level == 1) { + mSettingMenu.setVisibility(View.VISIBLE); + mSettingMenuState = SETTING_MENU_ON; + if (level == SETTING_MENU_LEVEL_ONE) { + mSettingMenuLevel = SETTING_MENU_LEVEL_ONE; if (mMenuLayout == null) { mMenuLayout = new RotateLayout(mActivity, null); if (mRootView.getLayoutDirection() != View.LAYOUT_DIRECTION_RTL) { - params = new FrameLayout.LayoutParams( - CameraActivity.SETTING_LIST_WIDTH_1, LayoutParams.WRAP_CONTENT, - Gravity.LEFT | Gravity.TOP); + params = new FrameLayout.LayoutParams(CameraActivity.SETTING_LIST_WIDTH_1, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP); } else { - params = new FrameLayout.LayoutParams( - CameraActivity.SETTING_LIST_WIDTH_1, LayoutParams.WRAP_CONTENT, - Gravity.RIGHT | Gravity.TOP); + params = new FrameLayout.LayoutParams(CameraActivity.SETTING_LIST_WIDTH_1, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.TOP); } mMenuLayout.setLayoutParams(params); ((ViewGroup) mRootView).addView(mMenuLayout); } mMenuLayout.setOrientation(mOrientation, true); - mMenuLayout.addView(popup); - } - if (level == 2) { + mMenuLayout.addView(mSettingMenu); + } else if (level == SETTING_MENU_LEVEL_TWO) { + mSettingMenuLevel = SETTING_MENU_LEVEL_TWO; if (mSubMenuLayout == null) { mSubMenuLayout = new RotateLayout(mActivity, null); ((ViewGroup) mRootView).addView(mSubMenuLayout); } if (mRootView.getLayoutDirection() != View.LAYOUT_DIRECTION_RTL) { - params = new FrameLayout.LayoutParams( - CameraActivity.SETTING_LIST_WIDTH_2, LayoutParams.WRAP_CONTENT, - Gravity.LEFT | Gravity.TOP); + params = new FrameLayout.LayoutParams(CameraActivity.SETTING_LIST_WIDTH_2, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP); } else { - params = new FrameLayout.LayoutParams( - CameraActivity.SETTING_LIST_WIDTH_2, LayoutParams.WRAP_CONTENT, - Gravity.RIGHT | Gravity.TOP); + params = new FrameLayout.LayoutParams(CameraActivity.SETTING_LIST_WIDTH_2, + FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.TOP); } int screenHeight = (mOrientation == 0 || mOrientation == 180) ? mRootView.getHeight() : mRootView.getWidth(); - int height = ((ListSubMenu) popup).getPreCalculatedHeight(); - int yBase = ((ListSubMenu) popup).getYBase(); + int height = mSettingSubMenu.getPreCalculatedHeight(); + int yBase = mSettingSubMenu.getYBase(); int y = Math.max(0, yBase); if (yBase + height > screenHeight) y = Math.max(0, screenHeight - height); @@ -518,111 +949,143 @@ public class CaptureUI implements PieListener, mSubMenuLayout.setLayoutParams(params); - mSubMenuLayout.addView(popup); + mSubMenuLayout.addView(mSettingSubMenu); mSubMenuLayout.setOrientation(mOrientation, true); } if (animate) { - if (level == 1) - mMenu.animateSlideIn(mMenuLayout, CameraActivity.SETTING_LIST_WIDTH_1, true); - if (level == 2) - mMenu.animateFadeIn(popup); - } else - popup.setAlpha(0.85f); + if (level == SETTING_MENU_LEVEL_ONE) { + animateSlideIn(mMenuLayout, CameraActivity.SETTING_LIST_WIDTH_1, true); + } + if (level == SETTING_MENU_LEVEL_TWO) { + animateFadeIn(mSettingSubMenu); + } + } else { + if (level == SETTING_MENU_LEVEL_ONE) { + mMenuLayout.setAlpha(0.85f); + } + if (level == SETTING_MENU_LEVEL_TWO) { + mSettingSubMenu.setAlpha(0.85f); + } + } } - public void removeLevel2() { - if (mSubMenuLayout != null) { - View v = mSubMenuLayout.getChildAt(0); - mSubMenuLayout.removeView(v); - } + public void hideUIWhileCountDown() { + hideCameraControls(true); + mGestures.setZoomOnly(true); } - public void cleanupListview() { - showUI(); - mActivity.setSystemBarsVisibility(false); + public void showUIAfterCountDown() { + hideCameraControls(false); + mGestures.setZoomOnly(false); } - public void dismissAllPopup() { - if (mPopup != null && mPopup.isShowing()) { - mPopup.dismiss(); - } + public void hideCameraControls(boolean hide) { + final int status = (hide) ? View.INVISIBLE : View.VISIBLE; + if (mMenuButton != null) mMenuButton.setVisibility(status); + if (mFrontBackSwitcher != null) mFrontBackSwitcher.setVisibility(status); + if (mSceneModeSwitcher != null) mSceneModeSwitcher.setVisibility(status); + if (mFilterModeSwitcher != null) mFilterModeSwitcher.setVisibility(status); + if (mSwitcher != null) mSwitcher.setVisibility(status); } - public void dismissLevel1() { - if (mMenuLayout != null) { - ((ViewGroup) mRootView).removeView(mMenuLayout); - mMenuLayout = null; - } + public void initializeControlByIntent() { + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + mThumbnail.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!CameraControls.isAnimating() && !mModule.isTakingPicture()) + mActivity.gotoGallery(); + } + }); + mMenuButton = mRootView.findViewById(R.id.menu); + mMenuButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showSettingMenu(); + } + }); } - public void dismissLevel2() { - if (mSubMenuLayout != null) { - ((ViewGroup) mRootView).removeView(mSubMenuLayout); - mSubMenuLayout = null; - } + public void doShutterAnimation() { + AnimationDrawable frameAnimation = (AnimationDrawable) mShutterButton.getDrawable(); + frameAnimation.stop(); + frameAnimation.start(); } - public boolean sendTouchToPreviewMenu(MotionEvent ev) { - return mPreviewMenuLayout.dispatchTouchEvent(ev); + public void showUI() { + if (!mUIhidden || isMenuBeingShown()) + return; + mUIhidden = false; + mCameraControls.showUI(); } - public boolean sendTouchToMenu(MotionEvent ev) { - if (mMenuLayout != null) { - View v = mMenuLayout.getChildAt(0); - return v.dispatchTouchEvent(ev); - } - return false; + public void hideUI() { + mSwitcher.closePopup(); + if (mUIhidden) + return; + mUIhidden = true; + mCameraControls.hideUI(); } - public void dismissSceneModeMenu() { - if (mPreviewMenuLayout != null) { - ((ViewGroup) mRootView).removeView(mPreviewMenuLayout); - mPreviewMenuLayout = null; - } + public void cleanUpMenus() { + showUI(); + mActivity.setSystemBarsVisibility(false); } - public void onShowSwitcherPopup() { - if (mPieRenderer != null && mPieRenderer.showsItems()) { - mPieRenderer.hide(); - } + public boolean arePreviewControlsVisible() { + return !mUIhidden; } - private void setShowMenu(boolean show) { - if (mOnScreenIndicators != null) { - mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE); - } + public void onOrientationChanged() { + removeSettingMenu(SETTING_MENU_LEVEL_TWO, false); + if (mSettingMenu != null) + mSettingMenu.resetHighlight(); } - public boolean collapseCameraControls() { - // TODO: Mode switcher should behave like a popup and should hide itself when there - // is a touch outside of it. - mSwitcher.closePopup(); - // Remove all the popups/dialog boxes - boolean ret = false; - if (mMenu != null) { - mMenu.removeAllView(); - } - if (mPopup != null) { - dismissAllPopup(); - ret = true; - } - onShowSwitcherPopup(); - return ret; + public void removeAllSettingMenu(boolean animate) { + removeSettingMenu(SETTING_MENU_LEVEL_TWO, false); + removeSettingMenu(SETTING_MENU_LEVEL_ONE, animate); } - public void setDisplayOrientation(int orientation) { - if ((mPreviewOrientation == -1 || mPreviewOrientation != orientation) - && mMenu != null && mMenu.isPreviewMenuBeingShown()) { - dismissSceneModeMenu(); - } - mPreviewOrientation = orientation; + public void removeAllSettingMenu() { + removeAllSettingMenu(false); } - public boolean isShutterPressed() { - return mShutterButton.isPressed(); + public void removeSettingMenu(int level, boolean animate) { + if (mSettingMenuState == SETTING_MENU_NONE) + return; + if (!animate) { + if (level == SETTING_MENU_LEVEL_TWO) { + if (mSubMenuLayout != null) { + mSubMenuLayout.removeView(mSubMenuLayout.getChildAt(0)); + mSubMenuLayout = null; + } + mSettingSubMenu = null; + mSettingMenuState = SETTING_MENU_ON; + mSettingMenuLevel = SETTING_MENU_LEVEL_ONE; + } else if (level == SETTING_MENU_LEVEL_ONE) { + mSettingMenu.resetHighlight(); + if (mMenuLayout != null) { + mMenuLayout.removeView(mMenuLayout.getChildAt(0)); + mMenuLayout = null; + } + mSettingMenu = null; + mSettingMenuState = SETTING_MENU_NONE; + cleanUpMenus(); + } + } else { + if (level == SETTING_MENU_LEVEL_TWO) { + mSettingMenu.resetHighlight(); + animateFadeOut(mSettingSubMenu, level); + } else if (level == SETTING_MENU_LEVEL_ONE) { + animateSlideOut(mSettingMenu, level); + } + } } - // shutter button handling + public void removeAllMenu() { + removeAllSettingMenu(); + } /** * Enables or disables the shutter button. @@ -633,29 +1096,36 @@ public class CaptureUI implements PieListener, } } - public void pressShutterButton() { - if (mShutterButton.isInTouchMode()) { - mShutterButton.requestFocusFromTouch(); - } else { - mShutterButton.requestFocus(); + private boolean handleBackKeyOnMenu() { + if (mSceneAndFilterMenuStatus == SCENE_AND_FILTER_MENU_ON) { + removeSceneAndFilterMenu(true); + return true; } - mShutterButton.setPressed(true); - } - - @Override - public void onPieOpened(int centerX, int centerY) { - setSwipingEnabled(false); - // Close module selection menu when pie menu is opened. - mSwitcher.closePopup(); + if (mSettingMenuState == SETTING_MENU_NONE) + return false; + if (mSettingMenuState == SETTING_MENU_ON) { + removeSettingMenu(mSettingMenuLevel, true); + } + return true; } - @Override - public void onPieClosed() { - setSwipingEnabled(true); - } + public boolean onBackPressed() { + if (handleBackKeyOnMenu()) return true; + if (mPieRenderer != null && mPieRenderer.showsItems()) { + mPieRenderer.hide(); + return true; + } - public void setSwipingEnabled(boolean enable) { - mActivity.setSwipingEnabled(enable); + if (!mModule.isCameraIdle()) { + // ignore backs while we're taking a picture + return true; + } + if (mSwitcher != null && mSwitcher.showsPopup()) { + mSwitcher.closePopup(); + return true; + } else { + return false; + } } public SurfaceHolder getSurfaceHolder() { @@ -677,20 +1147,45 @@ public class CaptureUI implements PieListener, } } - public void hideSurfaceView() { - mSurfaceView.setVisibility(View.INVISIBLE); + private void initializeCountDown() { + mActivity.getLayoutInflater().inflate(R.layout.count_down_to_capture, + (ViewGroup) mRootView, true); + mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture)); + mCountDownView.setCountDownFinishedListener((CountDownView.OnCountDownFinishedListener) mModule); + mCountDownView.bringToFront(); + mCountDownView.setOrientation(mOrientation); } - public void showSurfaceView() { - mSurfaceView.setVisibility(View.VISIBLE); + public boolean isCountingDown() { + return mCountDownView != null && mCountDownView.isCountingDown(); + } + + public void cancelCountDown() { + if (mCountDownView == null) return; + mCountDownView.cancelCountDown(); + showUIAfterCountDown(); + } + + public void startCountDown(int sec, boolean playSound) { + if (mCountDownView == null) initializeCountDown(); + mCountDownView.startCountDown(sec, playSound); + hideUIWhileCountDown(); } public void onPause() { - // Clear UI. + cancelCountDown(); collapseCameraControls(); } - // focus UI implementation + public boolean collapseCameraControls() { + mSwitcher.closePopup(); + // Remove all the popups/dialog boxes + boolean ret = false; + removeAllMenu(); + mCameraControls.showRefocusToast(false); + return ret; + } + private FocusIndicator getFocusIndicator() { return mPieRenderer; } @@ -703,10 +1198,6 @@ public class CaptureUI implements PieListener, public void clearFaces() { } - public void setPreference(String key, String value) { - mMenu.setPreference(key, value); - } - @Override public void clearFocus() { FocusIndicator indicator = getFocusIndicator(); @@ -731,13 +1222,13 @@ public class CaptureUI implements PieListener, } @Override - public void onFocusFailed(boolean timeout) { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showFail(timeout); + public void onFocusFailed(boolean timeOut) { + } @Override public void pauseFaceDetection() { + } @Override @@ -754,11 +1245,20 @@ public class CaptureUI implements PieListener, public void onFaceDetection(Face[] faces, CameraManager.CameraProxy camera) { } - @Override - public void onDisplayChanged() { - Log.d(TAG, "Device flip detected."); - mCameraControls.checkLayoutFlip(); - mController.updateCameraOrientation(); + public Point getSurfaceViewSize() { + Point point = new Point(); + if (mSurfaceView != null) point.set(mSurfaceView.getWidth(), mSurfaceView.getHeight()); + return point; + } + + public Point getSurfaceView2Size() { + Point point = new Point(); + if (mSurfaceView2 != null) point.set(mSurfaceView2.getWidth(), mSurfaceView2.getHeight()); + return point; + } + + public void adjustOrientation() { + setOrientation(mOrientation, true); } public void setOrientation(int orientation, boolean animation) { @@ -768,8 +1268,8 @@ public class CaptureUI implements PieListener, mMenuLayout.setOrientation(orientation, animation); if (mSubMenuLayout != null) mSubMenuLayout.setOrientation(orientation, animation); - if (mPreviewMenuLayout != null) { - ViewGroup vg = (ViewGroup) mPreviewMenuLayout.getChildAt(0); + if (mSceneAndFilterLayout != null) { + ViewGroup vg = (ViewGroup) mSceneAndFilterLayout.getChildAt(0); if (vg != null) vg = (ViewGroup) vg.getChildAt(0); if (vg != null) { @@ -779,33 +1279,151 @@ public class CaptureUI implements PieListener, } } } - + if (mCountDownView != null) + mCountDownView.setOrientation(orientation); RotateTextToast.setOrientation(orientation); if (mZoomRenderer != null) { mZoomRenderer.setOrientation(orientation); } } - public Point getSurfaceViewSize() { - Point point = new Point(); - if (mSurfaceView != null) point.set(mSurfaceView.getWidth(), mSurfaceView.getHeight()); - return point; + public int getOrientation() { + return mOrientation; } - public Point getSurfaceView2Size() { - Point point = new Point(); - if (mSurfaceView2 != null) point.set(mSurfaceView2.getWidth(), mSurfaceView2.getHeight()); - return point; + @Override + public void showGpsOnScreenIndicator(boolean hasSignal) { + } - public int getOrientation() { - return mOrientation; + @Override + public void hideGpsOnScreenIndicator() { + + } + + @Override + public void onSingleTapUp(View view, int x, int y) { + mModule.onSingleTapUp(view, x, y); + } + + public void onPreviewFocusChanged(boolean previewFocused) { + if (previewFocused) { + showUI(); + } else { + hideUI(); + } + if (mGestures != null) { + mGestures.setEnabled(previewFocused); + } + if (mRenderOverlay != null) { + // this can not happen in capture mode + mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE); + } + if (mPieRenderer != null) { + mPieRenderer.setBlockFocus(!previewFocused); + } + if (!previewFocused && mCountDownView != null) mCountDownView.cancelCountDown(); + } + + public ViewGroup getMenuLayout() { + return mMenuLayout; + } + + public boolean isShutterPressed() { + return mShutterButton.isPressed(); + } + + public void pressShutterButton() { + if (mShutterButton.isInTouchMode()) { + mShutterButton.requestFocusFromTouch(); + } else { + mShutterButton.requestFocus(); + } + mShutterButton.setPressed(true); + } + + @Override + public void onSettingsChanged(List<SettingsManager.SettingState> settings) { + for (SettingsManager.SettingState setting : settings) { + String key = setting.key; + SettingsManager.Values values = setting.values; + String value = (values.overriddenValue == null) ? values.value : values.overriddenValue; + switch (key) { + case SettingsManager.KEY_COLOR_EFFECT: + changeFilterModeControlIcon(value); + updateFilterModeIcon(values.overriddenValue == null); + break; + } + } + } + + public void hideSurfaceView() { + mSurfaceView.setVisibility(View.INVISIBLE); + mSurfaceView2.setVisibility(View.INVISIBLE); + } + + public void showSurfaceView() { + mSurfaceView.setVisibility(View.VISIBLE); + mSurfaceView2.setVisibility(View.VISIBLE); + } + + public void setSurfaceView(boolean show) { + if (show) { + mSurfaceView2.setVisibility(View.VISIBLE); + } else { + mSurfaceView2.setVisibility(View.INVISIBLE); + } + } + + public void setPreviewSize(int width, int height) { + mSurfaceView.getHolder().setFixedSize(width, height); + mCameraControls.setPreviewRatio(0, true); + mSurfaceView.setAspectRatio(height, width); + } + + @Override + public void onSettingChanged(ListPreference pref) { + removeAllSettingMenu(); + } + + @Override + public void onPreferenceClicked(ListPreference pref) { + onPreferenceClicked(pref, 0); + } + + @Override + public void onPreferenceClicked(ListPreference pref, int y) { + LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + removeSettingMenu(SETTING_MENU_LEVEL_TWO, false); + mSettingSubMenu = (ListSubMenu) inflater.inflate(R.layout.list_sub_menu, null, false); + mSettingSubMenu.initialize(pref, y); + mSettingSubMenu.setSettingChangedListener(mSettingMenu); + mSettingSubMenu.setAlpha(0f); + + if (mSettingMenuState == SETTING_MENU_ON) { + if (mSettingMenuLevel == SETTING_MENU_LEVEL_TWO) { + showSettingMenu(SETTING_MENU_LEVEL_TWO, false); + } else if (mSettingMenuLevel == SETTING_MENU_LEVEL_ONE) { + showSettingMenu(SETTING_MENU_LEVEL_TWO, true); + } + } + } + + @Override + public void onListMenuTouched() { + removeSettingMenu(SETTING_MENU_LEVEL_TWO, false); + } + + @Override + public void onListPrefChanged(ListPreference pref) { + removeAllSettingMenu(); } private class ZoomChangeListener implements ZoomRenderer.OnZoomChangedListener { @Override public void onZoomValueChanged(float mZoomValue) { - mController.onZoomChanged(mZoomValue); + mModule.onZoomChanged(mZoomValue); if (mZoomRenderer != null) { mZoomRenderer.setZoom(mZoomValue); } diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java index d0039ab9e..7d4d92087 100644 --- a/src/com/android/camera/ComboPreferences.java +++ b/src/com/android/camera/ComboPreferences.java @@ -152,7 +152,13 @@ public class ComboPreferences implements || key.equals(CameraSettings.KEY_TIMER) || key.equals(CameraSettings.KEY_TIMER_SOUND_EFFECTS) || key.equals(CameraSettings.KEY_PHOTOSPHERE_PICTURESIZE) - || key.equals(CameraSettings.KEY_CAMERA_SAVEPATH); + || key.equals(CameraSettings.KEY_CAMERA_SAVEPATH) + || key.equals(SettingsManager.KEY_CAMERA2) + || key.equals(SettingsManager.KEY_INITIAL_CAMERA) + || key.equals(SettingsManager.KEY_CAMERA_ID) + || key.equals(SettingsManager.KEY_MONO_ONLY) + || key.equals(SettingsManager.KEY_MONO_PREVIEW) + || key.equals(SettingsManager.KEY_CLEARSIGHT); } @Override diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index c79d18c1a..000baf125 100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java @@ -38,10 +38,11 @@ public class ListPreference extends CameraPreference { private static final String TAG = "ListPreference"; private final String mKey; private String mValue; - private final CharSequence[] mDefaultValues; + public final CharSequence[] mDefaultValues; private CharSequence[] mEntries; private CharSequence[] mEntryValues; + private CharSequence[] mDependencyList; private CharSequence[] mLabels; private boolean mLoaded = false; @@ -73,6 +74,8 @@ public class ListPreference extends CameraPreference { R.styleable.ListPreference_entryValues)); setLabels(a.getTextArray( R.styleable.ListPreference_labelList)); + setDependencyList(a.getTextArray( + R.styleable.ListPreference_dependencyList)); a.recycle(); } @@ -92,6 +95,10 @@ public class ListPreference extends CameraPreference { return mLabels; } + public CharSequence[] getDependencyList() { + return mDependencyList; + } + public void setEntries(CharSequence entries[]) { mEntries = entries == null ? new CharSequence[0] : entries; } @@ -104,6 +111,10 @@ public class ListPreference extends CameraPreference { mLabels = labels == null ? new CharSequence[0] : labels; } + public void setDependencyList(CharSequence dependencyList[]) { + mDependencyList = dependencyList == null ? new CharSequence[0] : dependencyList; + } + public String getValue() { if (!mLoaded) { mValue = getSharedPreferences().getString(mKey, @@ -113,6 +124,10 @@ public class ListPreference extends CameraPreference { return mValue; } + public String getOffValue() { + return mEntryValues[0].toString(); + } + // Find the first value in mDefaultValues which is supported. private String findSupportedDefaultValue() { for (int i = 0; i < mDefaultValues.length; i++) { diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java index b9ecdcc8d..083b019c4 100644 --- a/src/com/android/camera/PhotoMenu.java +++ b/src/com/android/camera/PhotoMenu.java @@ -234,7 +234,7 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_MANUAL_EXPOSURE, CameraSettings.KEY_MANUAL_WB, CameraSettings.KEY_MANUAL_FOCUS, - CameraSettings.KEY_CAMERA2 + SettingsManager.KEY_CAMERA2 }; initSwitchItem(CameraSettings.KEY_CAMERA_ID, mFrontBackSwitcher); @@ -731,6 +731,8 @@ public class PhotoMenu extends MenuController mActivity.getString(R.string.pref_camera_advanced_feature_default)); popup1.setPreferenceEnabled(CameraSettings.KEY_ADVANCED_FEATURES, false); + popup1.setPreferenceEnabled(CameraSettings.KEY_INSTANT_CAPTURE, false); + if(!TsMakeupManager.HAS_TS_MAKEUP) { if (mHdrSwitcher.getVisibility() == View.VISIBLE) { buttonSetEnabled(mHdrSwitcher, true); @@ -752,7 +754,7 @@ public class PhotoMenu extends MenuController popup1.setPreferenceEnabled(CameraSettings.KEY_COLOR_EFFECT, false); popup1.setPreferenceEnabled(CameraSettings.KEY_TOUCH_AF_AEC, false); popup1.setPreferenceEnabled(CameraSettings.KEY_SCENE_MODE, false); - + popup1.setPreferenceEnabled(CameraSettings.KEY_INSTANT_CAPTURE, false); setPreference(CameraSettings.KEY_CAMERA_HDR, mSettingOff); if(!TsMakeupManager.HAS_TS_MAKEUP) { if (mHdrSwitcher.getVisibility() == View.VISIBLE) { @@ -1467,17 +1469,9 @@ public class PhotoMenu extends MenuController updateFilterModeIcon(pref, pref); super.onSettingChanged(pref); - if (same(pref, CameraSettings.KEY_CAMERA2, "enable")) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(mActivity); - prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, true).apply(); - CameraActivity.CAMERA_2_ON = true; + if (same(pref, SettingsManager.KEY_CAMERA2, "enable")) { mActivity.onModuleSelected(ModuleSwitcher.CAPTURE_MODULE_INDEX); - } else if (notSame(pref, CameraSettings.KEY_CAMERA2, "enable")) { - SharedPreferences prefs = PreferenceManager - .getDefaultSharedPreferences(mActivity); - prefs.edit().putBoolean(CameraSettings.KEY_CAMERA2, false).apply(); - CameraActivity.CAMERA_2_ON = false; + } else if (notSame(pref, SettingsManager.KEY_CAMERA2, "enable")) { mActivity.onModuleSelected(ModuleSwitcher.PHOTO_MODULE_INDEX); } } diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 7324484ae..d6caf7eb8 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -881,8 +881,7 @@ public class PhotoModule } // Initialize location service. - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); + boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); mUI.initializeFirstTime(); @@ -913,8 +912,7 @@ public class PhotoModule // onResume. private void initializeSecondTime() { // Start location update if needed. - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); + boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); MediaSaveService s = mActivity.getMediaSaveService(); if (s != null) { @@ -2939,10 +2937,10 @@ public class PhotoModule CameraSettings.KEY_INSTANT_CAPTURE, mActivity.getString(R.string.pref_camera_instant_capture_default)); if (instantCapture.equals(mActivity.getString( - R.string.pref_camera_instant_capture_value_enable))) { - return true; + R.string.pref_camera_instant_capture_value_disable))) { + return false; } - return false; + return true; } private void qcomUpdateAdvancedFeatures(String ubiFocus, @@ -3225,23 +3223,6 @@ public class PhotoModule mParameters.set(CameraSettings.KEY_SNAPCAM_HDR_NEED_1X, hdrNeed1x); } - // Set Instant Capture - String instantCapture = mPreferences.getString( - CameraSettings.KEY_INSTANT_CAPTURE, - mActivity.getString(R.string.pref_camera_instant_capture_default)); - - if (instantCapture.equals(mActivity.getString( - R.string.pref_camera_instant_capture_value_enable))) { - if (!mInstantCaptureSnapShot) { - // Disable instant capture after first snapshot is taken - instantCapture = mActivity.getString( - R.string.pref_camera_instant_capture_value_disable); - } - } - Log.v(TAG, "Instant capture = " + instantCapture + ", mInstantCaptureSnapShot = " - + mInstantCaptureSnapShot); - mParameters.set(CameraSettings.KEY_QC_INSTANT_CAPTURE, instantCapture); - // Set Advanced features. String advancedFeature = mPreferences.getString( CameraSettings.KEY_ADVANCED_FEATURES, @@ -3491,6 +3472,39 @@ public class PhotoModule } } + // Set Instant Capture + String instantCapture = mPreferences.getString( + CameraSettings.KEY_INSTANT_CAPTURE, + mActivity.getString(R.string.pref_camera_instant_capture_default)); + + if (!instantCapture.equals(mActivity.getString( + R.string.pref_camera_instant_capture_value_disable))) { + if (zsl.equals("on") && + advancedFeature.equals(mActivity.getString(R.string.pref_camera_advanced_feature_value_none))) { + if (!mInstantCaptureSnapShot) { + // Disable instant capture after first snapshot is taken + instantCapture = mActivity.getString( + R.string.pref_camera_instant_capture_value_disable); + } + } else { + mParameters.set(CameraSettings.KEY_QC_INSTANT_CAPTURE, + mActivity.getString(R.string.pref_camera_instant_capture_value_disable)); + instantCapture = mActivity.getString( + R.string.pref_camera_instant_capture_value_disable); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.overrideSettings(CameraSettings.KEY_INSTANT_CAPTURE, + mActivity.getString(R.string.pref_camera_instant_capture_value_disable)); + } + }); + } + } + Log.v(TAG, "Instant capture = " + instantCapture + ", mInstantCaptureSnapShot = " + + mInstantCaptureSnapShot); + mParameters.set(CameraSettings.KEY_QC_INSTANT_CAPTURE, instantCapture); + + //Set Histogram String histogram = mPreferences.getString( CameraSettings.KEY_HISTOGRAM, @@ -4546,8 +4560,7 @@ public class PhotoModule // ignore the events after "onPause()" if (mPaused) return; - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); + boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); if(needRestart()){ Log.v(TAG, "Restarting Preview... Camera Mode Changed"); diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 250f265b2..09e24a710 100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -767,8 +767,7 @@ public class PhotoUI implements PieListener, // make sure the correct value was found // otherwise use auto index mOnScreenIndicators.updateWBIndicator(wbIndex < 0 ? 2 : wbIndex); - boolean location = RecordLocationPreference.get( - prefs, mActivity.getContentResolver()); + boolean location = RecordLocationPreference.get(prefs); mOnScreenIndicators.updateLocationIndicator(location); } @@ -1068,7 +1067,9 @@ public class PhotoUI implements PieListener, mReviewImage.setVisibility(View.GONE); mOnScreenIndicators.setVisibility(View.VISIBLE); mMenuButton.setVisibility(View.VISIBLE); - mMenu.hideTopMenu(false); + if (mMenu != null) { + mMenu.hideTopMenu(false); + } CameraUtil.fadeOut(mReviewDoneButton); mShutterButton.setVisibility(View.VISIBLE); CameraUtil.fadeOut(mReviewRetakeButton); diff --git a/src/com/android/camera/PreviewGestures.java b/src/com/android/camera/PreviewGestures.java index 4f26240ea..48bfb7e58 100644 --- a/src/com/android/camera/PreviewGestures.java +++ b/src/com/android/camera/PreviewGestures.java @@ -55,7 +55,7 @@ public class PreviewGestures private boolean mEnabled; private boolean mZoomOnly; private GestureDetector mGestureDetector; - private CaptureMenu mCaptureMenu; + private CaptureUI mCaptureUI; private PhotoMenu mPhotoMenu; private VideoMenu mVideoMenu; private boolean waitUntilNextDown; @@ -95,8 +95,8 @@ public class PreviewGestures orientation = mPhotoMenu.getOrientation(); else if (mVideoMenu != null) orientation = mVideoMenu.getOrientation(); - else if (mCaptureMenu != null) - orientation = mCaptureMenu.getOrientation(); + else if (mCaptureUI != null) + orientation = mCaptureUI.getOrientation(); if (isLeftSwipe(orientation, deltaX, deltaY)) { waitUntilNextDown = true; @@ -104,8 +104,8 @@ public class PreviewGestures mPhotoMenu.openFirstLevel(); else if (mVideoMenu != null && !mVideoMenu.isMenuBeingShown()) mVideoMenu.openFirstLevel(); - else if (mCaptureMenu != null && !mCaptureMenu.isMenuBeingShown()) - mCaptureMenu.openFirstLevel(); + else if (mCaptureUI != null && !mCaptureUI.isMenuBeingShown()) + mCaptureUI.showSettingMenu(); return true; } else { return onSingleTapUp(e2); @@ -161,8 +161,8 @@ public class PreviewGestures return mEnabled; } - public void setCaptureMenu(CaptureMenu menu) { - mCaptureMenu = menu; + public void setCaptureUI(CaptureUI ui) { + mCaptureUI = ui; } public void setPhotoMenu(PhotoMenu menu) { @@ -173,10 +173,6 @@ public class PreviewGestures mVideoMenu = menu; } - public CaptureMenu getCaptureMenu() { - return mCaptureMenu; - } - public PhotoMenu getPhotoMenu() { return mPhotoMenu; } @@ -213,16 +209,17 @@ public class PreviewGestures return sendToPie(m); } - if (mCaptureMenu != null) { - if (mCaptureMenu.isMenuBeingShown()) { - if (!mCaptureMenu.isMenuBeingAnimated()) { + if (mCaptureUI != null) { + if (mCaptureUI.isMenuBeingShown()) { + if (!mCaptureUI.isMenuBeingAnimated()) { waitUntilNextDown = true; - mCaptureMenu.closeView(); + mCaptureUI.removeAllSettingMenu(true); } return true; } - if (mCaptureMenu.isPreviewMenuBeingShown()) { + if (mCaptureUI.isPreviewMenuBeingShown()) { waitUntilNextDown = true; + mCaptureUI.removeSceneAndFilterMenu(true); return true; } } diff --git a/src/com/android/camera/RecordLocationPreference.java b/src/com/android/camera/RecordLocationPreference.java index 9992afabb..b88f5e4ed 100644 --- a/src/com/android/camera/RecordLocationPreference.java +++ b/src/com/android/camera/RecordLocationPreference.java @@ -40,11 +40,10 @@ public class RecordLocationPreference extends IconListPreference { @Override public String getValue() { - return get(getSharedPreferences(), mResolver) ? VALUE_ON : VALUE_OFF; + return get(getSharedPreferences()) ? VALUE_ON : VALUE_OFF; } - public static boolean get( - SharedPreferences pref, ContentResolver resolver) { + public static boolean get(SharedPreferences pref) { String value = pref.getString( CameraSettings.KEY_RECORD_LOCATION, VALUE_NONE); return VALUE_ON.equals(value); diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java new file mode 100644 index 000000000..215ab8436 --- /dev/null +++ b/src/com/android/camera/SettingsManager.java @@ -0,0 +1,813 @@ +/* + * 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.content.Context; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.util.Log; +import android.util.Range; +import android.util.Rational; +import android.util.Size; + +import com.android.camera.imageprocessor.filter.OptizoomFilter; +import com.android.camera.ui.ListMenu; + +import org.codeaurora.snapcam.R; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +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; + // 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 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"; + public static final String KEY_JPEG_QUALITY = "pref_camera2_jpegquality_key"; + public static final String KEY_FOCUS_MODE = "pref_camera2_focusmode_key"; + public static final String KEY_FLASH_MODE = "pref_camera2_flashmode_key"; + public static final String KEY_WHITE_BALANCE = "pref_camera2_whitebalance_key"; + public static final String KEY_MAKEUP = "pref_camera2_makeup_key"; + public static final String KEY_CAMERA2 = "pref_camera2_camera2_key"; + public static final String KEY_MONO_ONLY = "pref_camera2_mono_only_key"; + public static final String KEY_MONO_PREVIEW = "pref_camera2_mono_preview_key"; + public static final String KEY_CLEARSIGHT = "pref_camera2_clearsight_key"; + public static final String KEY_FILTER_MODE = "pref_camera2_filter_mode_key"; + public static final String KEY_COLOR_EFFECT = "pref_camera2_coloreffect_key"; + public static final String KEY_SCENE_MODE = "pref_camera2_scenemode_key"; + public static final String KEY_REDEYE_REDUCTION = "pref_camera2_redeyereduction_key"; + public static final String KEY_CAMERA_ID = "pref_camera2_id_key"; + public static final String KEY_PICTURE_SIZE = "pref_camera2_picturesize_key"; + public static final String KEY_ISO = "pref_camera2_iso_key"; + public static final String KEY_EXPOSURE = "pref_camera2_exposure_key"; + public static final String KEY_TIMER = "pref_camera2_timer_key"; + public static final String KEY_LONGSHOT = "pref_camera2_longshot_key"; + public static final String KEY_INITIAL_CAMERA = "pref_camera2_initial_camera_key"; + private static final String TAG = "SnapCam_SettingsManager"; + private static final List<CameraCharacteristics> mCharacteristics = new ArrayList<>(); + + private static SettingsManager sInstance; + + private ArrayList<Listener> mListeners; + private Map<String, Values> mValuesMap; + private Context mContext; + private PreferenceGroup mPreferenceGroup; + private ComboPreferences mPreferences; + private Map<String, Set<String>> mDependendsOnMap; + private boolean mIsMonoCameraPresent = false; + private boolean mIsFrontCameraPresent = false; + + private SettingsManager(Context context) { + mListeners = new ArrayList<>(); + mContext = context; + mPreferences = new ComboPreferences(mContext); + CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal(), mContext); + + CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE); + try { + String[] cameraIdList = manager.getCameraIdList(); + for (int i = 0; i < cameraIdList.length; i++) { + String cameraId = cameraIdList[i]; + CameraCharacteristics characteristics + = manager.getCameraCharacteristics(cameraId); + Byte monoOnly = 0; + try { + monoOnly = characteristics.get(CaptureModule.MetaDataMonoOnlyKey); + }catch(Exception e) { + } + if (monoOnly == 1) { + CaptureModule.MONO_ID = i; + mIsMonoCameraPresent = true; + } + int facing = characteristics.get(CameraCharacteristics.LENS_FACING); + if (facing == CameraCharacteristics.LENS_FACING_FRONT) { + CaptureModule.FRONT_ID = i; + mIsFrontCameraPresent = true; + } + mCharacteristics.add(i, characteristics); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public static SettingsManager createInstance(Context context) { + if (sInstance == null) { + sInstance = new SettingsManager(context); + } + return sInstance; + } + + public static SettingsManager getInstance() { + return sInstance; + } + + public List<String> getDisabledList() { + List<String> list = new ArrayList<>(); + Set<String> keySet = mValuesMap.keySet(); + for (String key : keySet) { + Values value = mValuesMap.get(key); + if (value.overriddenValue != null) { + list.add(key); + } + } + return list; + } + + @Override + public void onSettingChanged(ListPreference pref) { + String key = pref.getKey(); + List changed = checkDependencyAndUpdate(key); + if (changed == null) return; + notifyListeners(changed); + } + + public void init() { + Log.d(TAG, "SettingsManager init"); + int cameraId = CameraSettings.getInitialCameraId(mPreferences); + setLocalIdAndInitialize(cameraId); + } + + public void reinit(int cameraId) { + Log.d(TAG, "SettingsManager reinit " + cameraId); + setLocalIdAndInitialize(cameraId); + } + + private void setLocalIdAndInitialize(int cameraId) { + mPreferences.setLocalId(mContext, cameraId); + CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); + + PreferenceInflater inflater = new PreferenceInflater(mContext); + mPreferenceGroup = + (PreferenceGroup) inflater.inflate(R.xml.capture_preferences); + mValuesMap = new HashMap<>(); + mDependendsOnMap = new HashMap<>(); + filterPreferences(cameraId); + initDepedencyTable(); + initializeValueMap(); + } + + private void initDepedencyTable() { + for (int i = 0; i < mPreferenceGroup.size(); i++) { + ListPreference pref = (ListPreference) mPreferenceGroup.get(i); + String baseKey = pref.getKey(); + CharSequence[] dependencyList = null; + if (!specialDepedency(baseKey)) dependencyList = pref.getDependencyList(); + else { + List<KeyValue> keyValue = getSpecialDependencyList(pref); + if (keyValue.size() > 0) { + dependencyList = new CharSequence[keyValue.size()]; + int k = 0; + for (KeyValue kv: keyValue) { + dependencyList[k++] = kv.key; + } + } + pref.setDependencyList(dependencyList); + } + if (dependencyList != null) { + for (int j = 0; j < dependencyList.length; j++) { + String key = dependencyList[j].toString(); + pref = mPreferenceGroup.findPreference(key); + if (pref == null) continue; //filtered? + Set set = mDependendsOnMap.get(key); + if (set == null) { + set = new HashSet<>(); + } + set.add(baseKey); + mDependendsOnMap.put(key, set); + } + } + } + } + + private void initializeValueMap() { + List<String> processLater = new ArrayList<String>(); + for (int i = 0; i < mPreferenceGroup.size(); i++) { + ListPreference pref = (ListPreference) mPreferenceGroup.get(i); + String key = pref.getKey(); + if (mDependendsOnMap.get(key) != null && mDependendsOnMap.get(key).size() != 0) { + processLater.add(key); + continue; + } + Values values = new Values(pref.getValue(), null); + mValuesMap.put(pref.getKey(), values); + } + for (String keyToProcess : processLater) { + Set<String> dependsOnSet = mDependendsOnMap.get(keyToProcess); + boolean active = true; + List<KeyValue> keyValue = null; + for (String s : dependsOnSet) { + if (specialDepedency(s) || isOptionOn(s)) { + active = false; + if (specialDepedency(s)) { + keyValue = getSpecialDependencyList(s); + } + } + break; + } + ListPreference pref = mPreferenceGroup.findPreference(keyToProcess); + Values values = new Values(pref.getValue(), null); + if (!active) { + String offValue = pref.getOffValue(); + if (keyValue != null) { + String matchValue = getMatchingValue(keyToProcess, keyValue); + if (matchValue != null) + offValue = matchValue; + } + values.overriddenValue = offValue; + } + mValuesMap.put(keyToProcess, values); + } + } + + private List<SettingState> checkDependencyAndUpdate(String changedPrefKey) { + ListPreference changedPref = mPreferenceGroup.findPreference(changedPrefKey); + if (changedPref == null) return null; + + String key = changedPref.getKey(); + String value = changedPref.getValue(); + boolean special = specialDepedency(changedPrefKey); + String prevValue = getValue(changedPrefKey); + if (value.equals(prevValue)) return null; + + boolean turnedOff = value.equals(changedPref.getOffValue()); + boolean updateBackDependency = false; + List<SettingState> changed = new ArrayList(); + Values values = new Values(value, null); + mValuesMap.put(key, values); + changed.add(new SettingState(key, values)); + + Set<CharSequence> turnOn = new HashSet<>(); + Set<CharSequence> turnOff = new HashSet<>(); + + CharSequence[] originalDependencyList = changedPref.getDependencyList(); + CharSequence[] dependencyList = null; + List<KeyValue> keyValue = null; + if (special) { + keyValue = getSpecialDependencyList(changedPref); + if (keyValue.size() > 0) { + dependencyList = new CharSequence[keyValue.size()]; + int k = 0; + for (KeyValue kv : keyValue) { + dependencyList[k++] = kv.key; + } + } + } + + if (special) { + boolean same = Arrays.equals(originalDependencyList, dependencyList); + if (!same) { + changedPref.setDependencyList(dependencyList); + if (originalDependencyList != null) + for (CharSequence c : originalDependencyList) { + turnOn.add(c); + } + if (dependencyList != null) + for (CharSequence c : dependencyList) { + turnOff.add(c); + } + + if (originalDependencyList != null) + for (CharSequence c : originalDependencyList) { + turnOff.remove(c); + } + if (dependencyList != null) + for (CharSequence c : dependencyList) { + turnOn.remove(c); + } + updateBackDependency = true; + } + } else { + if (originalDependencyList != null) { + for (CharSequence c : originalDependencyList) { + if (turnedOff) turnOn.add(c); + else turnOff.add(c); + } + } + } + + for (CharSequence c : turnOn) {// turn back on + key = c.toString(); + Set<String> dependsOnSet = mDependendsOnMap.get(key); + if (dependsOnSet == null) continue; + boolean active = true; + for (String s : dependsOnSet) { + if (s.equals(changedPrefKey)) continue; + if (isOptionOn(s)) active = false; + break; + } + if (active) { + values = mValuesMap.get(key); + if (values == null) continue; + values.overriddenValue = null; + mValuesMap.put(key, values); + changed.add(new SettingState(key, values)); + } + } + + for (CharSequence c : turnOff) {// turn off logic + key = c.toString(); + ListPreference pref = mPreferenceGroup.findPreference(key); + if (pref == null) continue; + values = mValuesMap.get(key); + if (values == null) continue; + if (values != null && values.overriddenValue != null) continue; + String offValue = pref.getOffValue(); + if (keyValue != null) { + String matchValue = getMatchingValue(key, keyValue); + if (matchValue != null) + offValue = matchValue; + } + Values newValue = new Values(pref.getValue(), offValue); + mValuesMap.put(key, newValue); + changed.add(new SettingState(key, newValue)); + } + + if (updateBackDependency) { + updateBackDependency(changedPrefKey, turnOn, turnOff); + } + + return changed; + } + + private void updateBackDependency(String key, Set<CharSequence> remove, Set<CharSequence> + add) { + for (CharSequence c : remove) { + String currentKey = c.toString(); + Set<String> dependsOnSet = mDependendsOnMap.get(currentKey); + if (dependsOnSet != null) dependsOnSet.remove(key); + } + for (CharSequence c : add) { + String currentKey = c.toString(); + Set<String> dependsOnSet = mDependendsOnMap.get(currentKey); + if (dependsOnSet == null) { + dependsOnSet = new HashSet<>(); + mDependendsOnMap.put(currentKey, dependsOnSet); + } + dependsOnSet.add(key); + } + } + + public void registerListener(Listener listener) { + mListeners.add(listener); + } + + private void notifyListeners(List<SettingState> changes) { + for (Listener listener : mListeners) { + listener.onSettingsChanged(changes); + } + } + + public boolean isCamera2On() { + return mPreferences.getString(KEY_CAMERA2, "disable").equals("enable"); + } + + public String getValue(String key) { + Values values = mValuesMap.get(key); + if (values == null) return null; + if (values.overriddenValue == null) return values.value; + else return values.overriddenValue; + } + + public int getValueIndex(String key) { + ListPreference pref = mPreferenceGroup.findPreference(key); + String value = getValue(key); + if (value == null) return -1; + return pref.findIndexOfValue(value); + } + + public boolean isOverriden(String key) { + Values values = mValuesMap.get(key); + return values.overriddenValue != null; + } + + public void setValue(String key, String value) { + ListPreference pref = mPreferenceGroup.findPreference(key); + pref.setValue(value); + updateMapAndNotify(pref); + } + + public void setValueIndex(String key, int index) { + ListPreference pref = mPreferenceGroup.findPreference(key); + pref.setValueIndex(index); + updateMapAndNotify(pref); + } + + private void updateMapAndNotify(ListPreference pref) { + String key = pref.getKey(); + List changed = checkDependencyAndUpdate(key); + if (changed == null) return; + notifyListeners(changed); + } + + private boolean isOptionOn(String key) { + ListPreference pref = mPreferenceGroup.findPreference(key); + Values values = mValuesMap.get(key); + return (values.overriddenValue == null && !pref.getValue().equals(pref.getOffValue())); + } + + public PreferenceGroup getPreferenceGroup() { + return mPreferenceGroup; + } + + public CharSequence[] getEntries(String key) { + ListPreference pref = mPreferenceGroup.findPreference(key); + return pref.getEntries(); + } + + public int[] getResource(String key, int type) { + IconListPreference pref = (IconListPreference) mPreferenceGroup.findPreference(key); + switch (type) { + case RESOURCE_TYPE_THUMBNAIL: + return pref.getThumbnailIds(); + case RESOURCE_TYPE_LARGEICON: + return pref.getLargeIconIds(); + } + return null; + } + + private void filterPreferences(int cameraId) { + // filter unsupported preferences + ListPreference whiteBalance = mPreferenceGroup.findPreference(KEY_WHITE_BALANCE); + ListPreference flashMode = mPreferenceGroup.findPreference(KEY_FLASH_MODE); + ListPreference colorEffect = mPreferenceGroup.findPreference(KEY_COLOR_EFFECT); + ListPreference sceneMode = mPreferenceGroup.findPreference(KEY_SCENE_MODE); + ListPreference cameraIdPref = mPreferenceGroup.findPreference(KEY_CAMERA_ID); + ListPreference pictureSize = mPreferenceGroup.findPreference(KEY_PICTURE_SIZE); + ListPreference exposure = mPreferenceGroup.findPreference(KEY_EXPOSURE); + ListPreference iso = mPreferenceGroup.findPreference(KEY_ISO); + ListPreference clearsight = mPreferenceGroup.findPreference(KEY_CLEARSIGHT); + ListPreference monoPreview = mPreferenceGroup.findPreference(KEY_MONO_PREVIEW); + ListPreference monoOnly = mPreferenceGroup.findPreference(KEY_MONO_ONLY); + + if (whiteBalance != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + whiteBalance, getSupportedWhiteBalanceModes(cameraId)); + } + if (flashMode != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + flashMode, getSupportedFlashModes(cameraId)); + } + + if (colorEffect != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + colorEffect, getSupportedColorEffects(cameraId)); + } + + if (sceneMode != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + sceneMode, getSupportedSceneModes(cameraId)); + } + + if (cameraIdPref != null) buildCameraId(); + + if (pictureSize != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + pictureSize, getSupportedPictureSize(cameraId)); + } + + if (exposure != null) buildExposureCompensation(cameraId); + + if (iso != null) { + CameraSettings.filterUnsupportedOptions(mPreferenceGroup, + iso, getSupportedIso(cameraId)); + } + + if (!mIsMonoCameraPresent) { + if (clearsight != null) removePreference(mPreferenceGroup, KEY_CLEARSIGHT); + if (monoPreview != null) removePreference(mPreferenceGroup, KEY_MONO_PREVIEW); + if (monoOnly != null) removePreference(mPreferenceGroup, KEY_MONO_ONLY); + + } + } + + private void buildExposureCompensation(int cameraId) { + Range<Integer> range = mCharacteristics.get(cameraId).get(CameraCharacteristics + .CONTROL_AE_COMPENSATION_RANGE); + int max = range.getUpper(); + int min = range.getLower(); + if (min == 0 && max == 0) { + removePreference(mPreferenceGroup, KEY_EXPOSURE); + return; + } + ListPreference pref = mPreferenceGroup.findPreference(KEY_EXPOSURE); + Rational rational = mCharacteristics.get(cameraId).get(CameraCharacteristics + .CONTROL_AE_COMPENSATION_STEP); + double step = rational.doubleValue(); + int increment = 1; + while ((max - min) / increment > 10) { + increment++; + } + int start = min; + if (start < 0) { + while (Math.abs(start) % increment != 0) { + start++; + } + } + int size = 0; + for (int i = start; i <= max; i += increment) size++; + CharSequence entries[] = new CharSequence[size]; + CharSequence entryValues[] = new CharSequence[size]; + int count = 0; + for (int i = start; i <= max; i += increment, count++) { + entryValues[count] = Integer.toString(i); + StringBuilder builder = new StringBuilder(); + if (i > 0) builder.append('+'); + DecimalFormat format = new DecimalFormat("#.##"); + entries[count] = builder.append(format.format(i * step)).toString(); + } + pref.setEntries(entries); + pref.setEntryValues(entryValues); + } + + private void buildCameraId() { + int numOfCameras = mCharacteristics.size(); + if (!mIsFrontCameraPresent) { + removePreference(mPreferenceGroup, KEY_CAMERA_ID); + return; + } + + CharSequence[] entryValues = new CharSequence[numOfCameras]; + CharSequence[] entries = new CharSequence[numOfCameras]; + //TODO: Modify this after bayer/mono/front/back determination is done + entryValues[0] = "" + CaptureModule.BAYER_ID; + entries[0] = "BACK"; + if (mIsFrontCameraPresent) { + entryValues[1] = "" + CaptureModule.FRONT_ID; + entries[1] = "FRONT"; + } + ListPreference cameraIdPref = mPreferenceGroup.findPreference(KEY_CAMERA_ID); + cameraIdPref.setEntryValues(entryValues); + cameraIdPref.setEntries(entries); + } + + private boolean removePreference(PreferenceGroup group, String key) { + for (int i = 0, n = group.size(); i < n; i++) { + CameraPreference child = group.get(i); + if (child instanceof PreferenceGroup) { + if (removePreference((PreferenceGroup) child, key)) { + return true; + } + } + if (child instanceof ListPreference && + ((ListPreference) child).getKey().equals(key)) { + group.removePreference(i); + return true; + } + } + return false; + } + + public float getMaxZoom(int id) { + return mCharacteristics.get(id).get(CameraCharacteristics + .SCALER_AVAILABLE_MAX_DIGITAL_ZOOM); + } + + public Rect getSensorActiveArraySize(int id) { + return mCharacteristics.get(id).get(CameraCharacteristics + .SENSOR_INFO_ACTIVE_ARRAY_SIZE); + } + + public float getMaxZoom(List<Integer> ids) { + float zoomMax = Float.MAX_VALUE; + for (int id : ids) { + zoomMax = Math.min(getMaxZoom(id), zoomMax); + } + return zoomMax; + } + + public boolean isZoomSupported(int id) { + return mCharacteristics.get(id).get(CameraCharacteristics + .SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) > 1f; + } + + public boolean isAutoFocusSupported(List<Integer> ids) { + for (int id : ids) { + if (!isAutoFocusSupported(id)) + return false; + } + return true; + } + + + public boolean isZoomSupported(List<Integer> ids) { + for (int id : ids) { + if (!isZoomSupported(id)) + return false; + } + return true; + } + + public boolean isAutoFocusSupported(int id) { + Integer maxAfRegions = mCharacteristics.get(id).get( + CameraCharacteristics.CONTROL_MAX_REGIONS_AF); + return maxAfRegions != null && maxAfRegions > 0; + } + + public boolean isFixedFocus(int id) { + Float focusDistance = mCharacteristics.get(id).get(CameraCharacteristics + .LENS_INFO_MINIMUM_FOCUS_DISTANCE); + if (focusDistance == null || focusDistance == 0) { + return true; + } else { + return false; + } + } + + public boolean isFlashSupported(int id) { + return mCharacteristics.get(id).get(CameraCharacteristics.FLASH_INFO_AVAILABLE) && + mValuesMap.get(KEY_FLASH_MODE) != null; + } + + private List<String> getSupportedPictureSize(int cameraId) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] sizes = map.getOutputSizes(ImageFormat.JPEG); + List<String> res = new ArrayList<>(); + for (int i = 0; i < sizes.length; i++) { + res.add(sizes[i].toString()); + } + return res; + } + + private List<String> getSupportedWhiteBalanceModes(int cameraId) { + int[] whiteBalanceModes = mCharacteristics.get(cameraId).get(CameraCharacteristics + .CONTROL_AWB_AVAILABLE_MODES); + List<String> modes = new ArrayList<>(); + for (int mode : whiteBalanceModes) { + modes.add("" + mode); + } + return modes; + } + + private List<String> getSupportedSceneModes(int cameraId) { + int[] sceneModes = mCharacteristics.get(cameraId).get(CameraCharacteristics + .CONTROL_AVAILABLE_SCENE_MODES); + List<String> modes = new ArrayList<>(); + modes.add("0"); // need special case handle for auto scene mode + if (mIsMonoCameraPresent) modes.add(SCENE_MODE_DUAL_STRING); // need special case handle for dual mode + if (OptizoomFilter.isSupportedStatic()) modes.add(SCENE_MODE_OPTIZOOM_INT + ""); // need special case handle for dual mode + for (int mode : sceneModes) { + modes.add("" + mode); + } + return modes; + } + + private List<String> getSupportedFlashModes(int cameraId) { + int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics + .CONTROL_AE_AVAILABLE_MODES); + List<String> modes = new ArrayList<>(); + for (int mode : flashModes) { + modes.add("" + mode); + } + return modes; + } + + public List<String> getSupportedColorEffects(int cameraId) { + int[] flashModes = mCharacteristics.get(cameraId).get(CameraCharacteristics + .CONTROL_AVAILABLE_EFFECTS); + List<String> modes = new ArrayList<>(); + for (int mode : flashModes) { + modes.add("" + mode); + } + return modes; + } + + private List<String> getSupportedIso(int cameraId) { + Range<Integer> range = mCharacteristics.get(cameraId).get(CameraCharacteristics + .SENSOR_INFO_SENSITIVITY_RANGE); + int max = range.getUpper(); + int value = 50; + List<String> supportedIso = new ArrayList<>(); + supportedIso.add("auto"); + while (value <= max) { + if (range.contains(value)) { + supportedIso.add("" + value); + } + value += 50; + } + return supportedIso; + } + + private boolean specialDepedency(String key) { + return key.equals(KEY_SCENE_MODE); + } + + private List<KeyValue> getSpecialDependencyList(String key) { + ListPreference pref = mPreferenceGroup.findPreference(key); + return getSpecialDependencyList(pref); + } + + private List<KeyValue> getSpecialDependencyList(ListPreference pref) { + String key = pref.getKey(); + List<KeyValue> dependency = new ArrayList<>(); + switch (key) { + case KEY_SCENE_MODE: + String value = pref.getValue(); + switch (value) { + case "0": + dependency.add(new KeyValue(KEY_CLEARSIGHT, "off")); + dependency.add(new KeyValue(KEY_MONO_PREVIEW, "off")); + break; + case SCENE_MODE_DUAL_STRING: + dependency.add(new KeyValue(KEY_LONGSHOT, "off")); + dependency.add(new KeyValue(KEY_MONO_ONLY, "off")); + break; + default: + dependency.add(new KeyValue(KEY_COLOR_EFFECT, "0")); + dependency.add(new KeyValue(KEY_FLASH_MODE, "2")); + dependency.add(new KeyValue(KEY_WHITE_BALANCE, "1")); + dependency.add(new KeyValue(KEY_EXPOSURE, "0")); + dependency.add(new KeyValue(KEY_CLEARSIGHT, "off")); + dependency.add(new KeyValue(KEY_MONO_PREVIEW, "off")); + break; + } + break; + } + return dependency; + } + + public interface Listener { + void onSettingsChanged(List<SettingState> settings); + } + + private String getMatchingValue(String key, List<KeyValue> keyValue) { + for (KeyValue kv: keyValue) { + if (key.equals(kv.key)) { + return kv.value; + } + } + return null; + } + + static class KeyValue { + String key; + String value; + + KeyValue(String key, String value) { + this.key = key; + this.value = value; + } + } + static class Values { + String value; + String overriddenValue; + + Values(String value, String overriddenValue) { + this.value = value; + this.overriddenValue = overriddenValue; + } + } + + static class SettingState { + String key; + Values values; + + SettingState(String key, Values values) { + this.key = key; + this.values = values; + } + } + +} diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index cbfe7b94a..96423044f 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -315,7 +315,13 @@ public class VideoModule implements CameraModule, VIDEO_ENCODER_TABLE.put("h263", MediaRecorder.VideoEncoder.H263); VIDEO_ENCODER_TABLE.put("h264", MediaRecorder.VideoEncoder.H264); - VIDEO_ENCODER_TABLE.put("h265", MediaRecorder.VideoEncoder.HEVC); + int h265 = ApiHelper.getIntFieldIfExists(MediaRecorder.VideoEncoder.class, + "HEVC", null, MediaRecorder.VideoEncoder.DEFAULT); + if (h265 == MediaRecorder.VideoEncoder.DEFAULT) { + h265 = ApiHelper.getIntFieldIfExists(MediaRecorder.VideoEncoder.class, + "H265", null, MediaRecorder.VideoEncoder.DEFAULT); + } + VIDEO_ENCODER_TABLE.put("h265", h265); VIDEO_ENCODER_TABLE.put("m4v", MediaRecorder.VideoEncoder.MPEG_4_SP); VIDEO_ENCODER_TABLE.putDefault(MediaRecorder.VideoEncoder.DEFAULT); @@ -1106,8 +1112,7 @@ public class VideoModule implements CameraModule, mOrientationManager.resume(); // Initialize location service. - boolean recordLocation = RecordLocationPreference.get(mPreferences, - mContentResolver); + boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); if (mPreviewing) { @@ -1487,6 +1492,10 @@ public class VideoModule implements CameraModule, mProfile.audioCodec = mAudioEncoder; mProfile.duration = mMaxVideoDurationInMs; + if ((mProfile.audioCodec == MediaRecorder.AudioEncoder.AMR_NB) && + !mCaptureTimeLapse && !isHFR) { + mProfile.fileFormat = MediaRecorder.OutputFormat.THREE_GPP; + } // Set params individually for HFR case, as we do not want to encode audio if ((isHFR || isHSR) && captureRate > 0) { if (isHSR) { @@ -2701,8 +2710,7 @@ public class VideoModule implements CameraModule, // startPreview(). if (mCameraDevice == null) return; - boolean recordLocation = RecordLocationPreference.get( - mPreferences, mContentResolver); + boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); readVideoPreferences(); diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index c99c8c8f5..5203d9e35 100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -705,8 +705,7 @@ public class VideoUI implements PieRenderer.PieListener, public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) { mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode()); - boolean location = RecordLocationPreference.get( - prefs, mActivity.getContentResolver()); + boolean location = RecordLocationPreference.get(prefs); mOnScreenIndicators.updateLocationIndicator(location); } diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java index 7b4bc1901..9a0e9ce7e 100644 --- a/src/com/android/camera/WideAnglePanoramaModule.java +++ b/src/com/android/camera/WideAnglePanoramaModule.java @@ -832,6 +832,7 @@ public class WideAnglePanoramaModule ExifInterface exif = new ExifInterface(); try { exif.readExif(jpegData); + exif.addMakeAndModelTag(); exif.addGpsDateTimeStampTag(mTimeTaken); exif.addDateTimeStampTag(ExifInterface.TAG_DATE_TIME, mTimeTaken, TimeZone.getDefault()); @@ -1010,8 +1011,7 @@ public class WideAnglePanoramaModule mOrientationManager.resume(); // Initialize location service. - boolean recordLocation = RecordLocationPreference.get(mPreferences, - mContentResolver); + boolean recordLocation = RecordLocationPreference.get(mPreferences); mLocationManager.recordLocation(recordLocation); mUI.initDisplayChangeListener(); UsageStatistics.onContentViewChanged( diff --git a/src/com/android/camera/exif/ExifInterface.java b/src/com/android/camera/exif/ExifInterface.java index f353f3586..2fec1bf4f 100644 --- a/src/com/android/camera/exif/ExifInterface.java +++ b/src/com/android/camera/exif/ExifInterface.java @@ -19,6 +19,7 @@ package com.android.camera.exif; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.SparseIntArray; +import android.os.Build; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; @@ -2028,6 +2029,21 @@ public class ExifInterface { return true; } + public boolean addMakeAndModelTag() { + ExifTag t = buildTag(TAG_MAKE, Build.MANUFACTURER); + if (t == null) { + return false; + } + setTag(t); + t = buildTag(TAG_MODEL, Build.MODEL); + if (t == null) { + return false; + } + setTag(t); + return true; + } + + private static Rational[] toExifLatLong(double value) { // convert to the format dd/1 mm/1 ssss/100 value = Math.abs(value); diff --git a/src/com/android/camera/imageprocessor/FrameProcessor.java b/src/com/android/camera/imageprocessor/FrameProcessor.java new file mode 100644 index 000000000..951479de9 --- /dev/null +++ b/src/com/android/camera/imageprocessor/FrameProcessor.java @@ -0,0 +1,335 @@ +/* +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; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.ImageFormat; +import android.graphics.Matrix; +import android.graphics.Rect; +import android.graphics.YuvImage; +import android.media.Image; +import android.media.ImageReader; +import android.os.Handler; +import android.os.HandlerThread; +import android.renderscript.Allocation; +import android.renderscript.Element; +import android.renderscript.RenderScript; +import android.renderscript.ScriptIntrinsicYuvToRGB; +import android.renderscript.Type; +import android.util.Log; +import android.util.Size; +import android.view.Surface; + +import com.android.camera.CaptureModule; +import com.android.camera.PhotoModule; +import com.android.camera.imageprocessor.filter.BeautificationFilter; +import com.android.camera.imageprocessor.filter.ImageFilter; +import com.android.camera.util.CameraUtil; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; + +public class FrameProcessor { + + private ImageReader mInputImageReader; + private Allocation mInputAllocation; + private Allocation mProcessAllocation; + private Allocation mOutputAllocation; + + private HandlerThread mProcessingThread; + private Handler mProcessingHandler; + private HandlerThread mOutingThread; + private Handler mOutingHandler; + + public ProcessingTask mTask; + private RenderScript mRs; + private Activity mActivity; + ScriptC_YuvToRgb mRsYuvToRGB; + ScriptC_rotator mRsRotator; + private Size mSize; + private Object mAllocationLock = new Object(); + private boolean mIsAllocationEverUsed; + private ArrayList<ImageFilter> mPreviewFilters; + private ArrayList<ImageFilter> mFinalFilters; + private Surface mSurfaceAsItIs; + private boolean mIsActive = false; + public static final int FILTER_NONE = 0; + public static final int FILTER_MAKEUP = 1; + private CaptureModule mModule; + + public FrameProcessor(Activity activity, CaptureModule module) { + mActivity = activity; + mModule = module; + mPreviewFilters = new ArrayList<ImageFilter>(); + mFinalFilters = new ArrayList<ImageFilter>(); + } + + public void init(Size previewDim) { + mSize = previewDim; + synchronized (mAllocationLock) { + mRs = RenderScript.create(mActivity); + mRsYuvToRGB = new ScriptC_YuvToRgb(mRs); + mRsRotator = new ScriptC_rotator(mRs); + mInputImageReader = ImageReader.newInstance(mSize.getWidth(), mSize.getHeight(), ImageFormat.YUV_420_888, 8); + + Type.Builder rgbTypeBuilder = new Type.Builder(mRs, Element.RGBA_8888(mRs)); + rgbTypeBuilder.setX(mSize.getHeight()); + rgbTypeBuilder.setY(mSize.getWidth()); + mOutputAllocation = Allocation.createTyped(mRs, rgbTypeBuilder.create(), + Allocation.USAGE_SCRIPT | Allocation.USAGE_IO_OUTPUT); + + if (mProcessingThread == null) { + mProcessingThread = new HandlerThread("FrameProcessor"); + mProcessingThread.start(); + mProcessingHandler = new Handler(mProcessingThread.getLooper()); + } + + if (mOutingThread == null) { + mOutingThread = new HandlerThread("FrameOutingThread"); + mOutingThread.start(); + mOutingHandler = new Handler(mOutingThread.getLooper()); + } + + mTask = new ProcessingTask(); + mInputImageReader.setOnImageAvailableListener(mTask, mProcessingHandler); + mIsAllocationEverUsed = false; + } + } + + private void createAllocation(int width, int height) { + Type.Builder yuvTypeBuilder = new Type.Builder(mRs, Element.YUV(mRs)); + yuvTypeBuilder.setX(width); + yuvTypeBuilder.setY(height); + yuvTypeBuilder.setYuvFormat(ImageFormat.NV21); + mInputAllocation = Allocation.createTyped(mRs, yuvTypeBuilder.create(), Allocation.USAGE_SCRIPT); + Type.Builder nv21TypeBuilder = new Type.Builder(mRs, Element.U8(mRs)); + nv21TypeBuilder.setX(width * height * 3 / 2); + mProcessAllocation = Allocation.createTyped(mRs, nv21TypeBuilder.create(), Allocation.USAGE_SCRIPT); + mRsRotator.set_gIn(mInputAllocation); + mRsRotator.set_gOut(mProcessAllocation); + mRsRotator.set_width(width); + mRsRotator.set_height(height); + mRsYuvToRGB.set_gIn(mProcessAllocation); + mRsYuvToRGB.set_width(height); + mRsYuvToRGB.set_height(width); + } + + public ArrayList<ImageFilter> getFrameFilters() { + return mFinalFilters; + } + + private void cleanFilterSet() { + if(mPreviewFilters != null) { + for (ImageFilter filter : mPreviewFilters) { + filter.deinit(); + } + } + if(mFinalFilters != null) { + for (ImageFilter filter : mFinalFilters) { + filter.deinit(); + } + } + mPreviewFilters = new ArrayList<ImageFilter>(); + mFinalFilters = new ArrayList<ImageFilter>(); + } + + public void onOpen(ArrayList<Integer> filterIds) { + mIsActive = true; + synchronized (mAllocationLock) { + cleanFilterSet(); + if (filterIds != null) { + for (Integer i : filterIds) { + addFilter(i.intValue()); + } + } + } + } + + private void addFilter(int filterId) { + if(filterId == FILTER_MAKEUP) { + ImageFilter filter = new BeautificationFilter(mModule); + if(filter.isSupported()) { + mPreviewFilters.add(filter); + mFinalFilters.add(filter); + } + } + } + + public void onClose() { + mIsActive = false; + synchronized (mAllocationLock) { + if (mIsAllocationEverUsed) { + if (mInputAllocation != null) { + mInputAllocation.destroy(); + } + if (mOutputAllocation != null) { + mOutputAllocation.destroy(); + } + if (mProcessAllocation != null) { + mProcessAllocation.destroy(); + } + } + if (mRs != null) { + mRs.destroy(); + } + mRs = null; + mProcessAllocation = null; + mOutputAllocation = null; + mInputAllocation = null; + } + if (mProcessingThread != null) { + mProcessingThread.quitSafely(); + try { + mProcessingThread.join(); + mProcessingThread = null; + mProcessingHandler = null; + } catch (InterruptedException e) { + } + } + if (mOutingThread != null) { + mOutingThread.quitSafely(); + try { + mOutingThread.join(); + mOutingThread = null; + mOutingHandler = null; + } catch (InterruptedException e) { + } + } + for(ImageFilter filter : mPreviewFilters) { + filter.deinit(); + } + for(ImageFilter filter : mFinalFilters) { + filter.deinit(); + } + } + + public Surface getInputSurface() { + if(mPreviewFilters.size() == 0) { + return mSurfaceAsItIs; + } + synchronized (mAllocationLock) { + if (mInputImageReader == null) + return null; + return mInputImageReader.getSurface(); + } + } + + public boolean isFrameFilterEnabled() { + if(mPreviewFilters.size() == 0) { + return false; + } + return true; + } + + public void setOutputSurface(Surface surface) { + if(mPreviewFilters.size() == 0) { + mSurfaceAsItIs = surface; + } else { + mOutputAllocation.setSurface(surface); + } + } + + class ProcessingTask implements Runnable, ImageReader.OnImageAvailableListener { + byte[] yvuBytes = null; + int ySize; + int stride; + int height; + + public ProcessingTask() { + } + + @Override + public void onImageAvailable(ImageReader reader) { + synchronized (mAllocationLock) { + if(mOutputAllocation == null) + return; + try { + Image image = reader.acquireLatestImage(); + if(image == null) + return; + if(!mIsActive) { + image.close(); + return; + } + mIsAllocationEverUsed = true; + ByteBuffer bY = image.getPlanes()[0].getBuffer(); + ByteBuffer bVU = image.getPlanes()[2].getBuffer(); + if(yvuBytes == null) { + stride = image.getPlanes()[0].getRowStride(); + height = mSize.getHeight(); + ySize = stride * mSize.getHeight(); + yvuBytes = new byte[ySize*3/2]; + } + //Start processing yvu buf + for (ImageFilter filter : mPreviewFilters) { + filter.init(mSize.getWidth(), mSize.getHeight(), stride, stride); + filter.addImage(bY, bVU, 0, new Boolean(true)); + } + //End processing yvu buf + bY.get(yvuBytes, 0, bY.remaining()); + bVU.get(yvuBytes, ySize, bVU.remaining()); + image.close(); + mOutingHandler.post(this); + } catch (IllegalStateException e) { + } + } + } + + @Override + public void run() { + synchronized (mAllocationLock) { + if(!mIsActive) { + return; + } + if(mInputAllocation == null) { + createAllocation(stride, height); + } + mInputAllocation.copyFrom(yvuBytes); + mRsRotator.forEach_rotate90andMerge(mInputAllocation); + mRsYuvToRGB.forEach_nv21ToRgb(mOutputAllocation); + mOutputAllocation.ioSend(); + } + } + } + + 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 new file mode 100644 index 000000000..a126e8817 --- /dev/null +++ b/src/com/android/camera/imageprocessor/PostProcessor.java @@ -0,0 +1,448 @@ +/* +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; + +import android.content.ContentResolver; +import android.graphics.ImageFormat; +import android.graphics.Rect; +import android.graphics.YuvImage; +import android.hardware.camera2.CaptureRequest; +import android.media.Image; +import android.media.ImageReader; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.util.Log; +import android.widget.Toast; + +import com.android.camera.CameraActivity; +import com.android.camera.CaptureModule; +import com.android.camera.MediaSaveService; +import com.android.camera.PhotoModule; +import com.android.camera.SettingsManager; +import com.android.camera.imageprocessor.filter.OptizoomFilter; +import com.android.camera.ui.RotateTextToast; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import com.android.camera.imageprocessor.filter.ImageFilter; + +public class PostProcessor implements ImageReader.OnImageAvailableListener{ + + private CaptureModule mController; + + private static final String TAG = "PostProcessor"; + public static final int FILTER_NONE = 0; + public static final int FILTER_OPTIZOOM = 1; + public static final int FILTER_MAX = 2; + + private int mCurrentNumImage = 0; + private ImageFilter mFilter; + private int mFilterIndex; + private HandlerThread mHandlerThread; + private ProcessorHandler mHandler; + private CameraActivity mActivity; + private int mWidth; + private int mHeight; + private int mStride; + private Object lock = new Object(); + private ImageFilter.ResultImage mDefaultResultImage; //This is used only no filter is chosen. + private Image[] mImages; + private PhotoModule.NamedImages mNamedImages; + private WatchdogThread mWatchdog; + + //This is for the debug feature. + private static boolean DEBUG_FILTER = true; //TODO: This has to be false before releasing. + private ImageFilter.ResultImage mDebugResultImage; + + @Override + public void onImageAvailable(ImageReader reader) { + try { + Image image = reader.acquireNextImage(); + addImage(image); + if (isReadyToProcess()) { + 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; + processImage(title, date, mController.getMediaSavedListener(), mActivity.getContentResolver()); + } + } catch (IllegalStateException e) { + Log.e(TAG, "Max images has been already acquired. "); + } + } + + enum STATUS { + DEINIT, + INIT, + BUSY + } + private STATUS mStatus = STATUS.DEINIT; + + public PostProcessor(CameraActivity activity, CaptureModule module) { + mController = module; + mActivity = activity; + mNamedImages = new PhotoModule.NamedImages(); + + } + + public boolean isItBusy() { + if(mStatus == STATUS.BUSY) + return true; + return false; + } + + public List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder) { + if(mFilter == null) { + List<CaptureRequest> list = new ArrayList<CaptureRequest>(); + list.add(builder.build()); + return list; + } else { + return mFilter.setRequiredImages(builder); + } + } + + public boolean isFilterOn() { + if(mFilter != null) { + return true; + } + if(mController.getFrameFilters().size() != 0) { + return true; + } + return false; + } + + public void onOpen(int postFilterId) { + setFilter(postFilterId); + startBackgroundThread(); + + } + + public int getFilterIndex() { + return mFilterIndex; + } + + public void onClose() { + synchronized (lock) { + if(mHandler != null) { + mHandler.setInActive(); + } + stopBackgroundThread(); + } + setFilter(FILTER_NONE); + } + + private void startBackgroundThread() { + mHandlerThread = new HandlerThread("PostProcessorThread"); + mHandlerThread.start(); + mHandler = new ProcessorHandler(mHandlerThread.getLooper()); + + mWatchdog = new WatchdogThread(); + mWatchdog.start(); + } + + class WatchdogThread extends Thread { + private boolean isAlive = true; + private boolean isMonitor = false; + private int counter = 0; + public void run() { + while(isAlive) { + try { + Thread.sleep(200); + }catch(InterruptedException e) { + } + if(isMonitor) { + counter++; + if(counter >= 40) { //This is 4 seconds. + bark(); + break; + } + } + } + + } + + public void startMonitor() { + isMonitor = true; + } + + public void stopMonitor() { + isMonitor = false; + counter = 0; + } + + public void kill() { + isAlive = false; + } + private void bark() { + Log.e(TAG, "It takes too long to get the images and process the filter!"); + int index = getFilterIndex(); + setFilter(FILTER_NONE); + setFilter(index); + } + } + + class ProcessorHandler extends Handler { + boolean isRunning; + + public ProcessorHandler(Looper looper) { + super(looper); + isRunning = true; + } + + public void setInActive() { + isRunning = false; + } + } + + private void stopBackgroundThread() { + if (mHandlerThread != null) { + mHandlerThread.quitSafely(); + try { + mHandlerThread.join(); + } catch (InterruptedException e) { + } + mHandlerThread = null; + mHandler = null; + } + if(mWatchdog != null) { + mWatchdog.kill(); + mWatchdog = null; + } + clear(); + } + + public boolean setFilter(int index) { + if(index < 0 || index >= FILTER_MAX) { + Log.e(TAG, "Invalid scene filter ID"); + return false; + } + synchronized (lock) { + if (mFilter != null) { + mFilter.deinit(); + } + mStatus = STATUS.DEINIT; + switch (index) { + case FILTER_NONE: + mFilter = null; + break; + case FILTER_OPTIZOOM: + mFilter = new OptizoomFilter(mController); + break; + } + } + + if(mFilter != null && !mFilter.isSupported()) { + final String filterName = mFilter.getStringName(); + mFilter = null; + mActivity.runOnUiThread(new Runnable() { + public void run() { + RotateTextToast.makeText(mActivity, filterName+" is not supported. ", Toast.LENGTH_SHORT).show(); + } + }); + } + + if(mFilter == null) { + mFilterIndex = FILTER_NONE; + return false; + } + mFilterIndex = index; + mImages = new Image[mFilter.getNumRequiredImage()]; + return true; + } + + private boolean isReadyToProcess() { + synchronized (lock) { + if (mFilter == null) { + return true; + } + if (mCurrentNumImage >= mFilter.getNumRequiredImage()) { + return true; + } + } + return false; + } + + private void addImage(final Image image) { + if(mHandler == null || !mHandler.isRunning) { + return; + } + final ProcessorHandler handler = mHandler; + if (mStatus == STATUS.DEINIT) { + mWidth = image.getWidth(); + mHeight = image.getHeight(); + mStride = image.getPlanes()[0].getRowStride(); + mStatus = STATUS.INIT; + mHandler.post(new Runnable() { + public void run() { + synchronized (lock) { + if(!handler.isRunning) { + return; + } + if(mFilter == null) { + //Nothing here we have to do if filter is not chosen. + } else { + mFilter.init(mWidth, mHeight, mStride, mStride); + } + } + } + }); + } + if(mCurrentNumImage == 0) { + mStatus = STATUS.BUSY; + if(mWatchdog != null) { + mWatchdog.startMonitor(); + } + } + if(mFilter != null && mCurrentNumImage >= mFilter.getNumRequiredImage()) { + return; + } + final int numImage = mCurrentNumImage; + mCurrentNumImage++; + if(mHandler == null) { + return; + } + mHandler.post(new Runnable() { + public void run() { + synchronized (lock) { + if(!handler.isRunning) { + return; + } + ByteBuffer yBuf = image.getPlanes()[0].getBuffer(); + ByteBuffer vuBuf = image.getPlanes()[2].getBuffer(); + if(mFilter != null && DEBUG_FILTER && numImage == 0) { + mDebugResultImage = new ImageFilter.ResultImage(ByteBuffer.allocateDirect(mStride * mHeight*3/2), + new Rect(0, 0, mWidth, mHeight), mWidth, mHeight, mStride); + yBuf.get(mDebugResultImage.outBuffer.array(), 0, yBuf.remaining()); + vuBuf.get(mDebugResultImage.outBuffer.array(), mStride * mHeight, vuBuf.remaining()); + yBuf.rewind(); + vuBuf.rewind(); + } + if(mFilter == null) { + mDefaultResultImage = new ImageFilter.ResultImage(ByteBuffer.allocateDirect(mStride * mHeight*3/2), + new Rect(0, 0, mWidth, mHeight), mWidth, mHeight, mStride); + yBuf.get(mDefaultResultImage.outBuffer.array(), 0, yBuf.remaining()); + vuBuf.get(mDefaultResultImage.outBuffer.array(), mStride*mHeight, vuBuf.remaining()); + image.close(); + } else { + mFilter.addImage(image.getPlanes()[0].getBuffer(), + image.getPlanes()[2].getBuffer(), numImage, null); + mImages[numImage] = image; + } + } + } + }); + } + + private void clear() { + mCurrentNumImage = 0; + } + + private void processImage(final String title, final long date, + final MediaSaveService.OnMediaSavedListener mediaSavedListener, + final ContentResolver contentResolver) { + if(mHandler == null || !mHandler.isRunning) { + return; + } + final ProcessorHandler handler = mHandler; + mHandler.post(new Runnable() { + public void run() { + byte[] bytes; + ImageFilter.ResultImage resultImage = null; + synchronized (lock) { + if (!handler.isRunning) { + return; + } + if (mFilter == null) { //In case no post filter is chosen + resultImage = mDefaultResultImage; + } else { + resultImage = mFilter.processImage(); + for (int i = 0; i < mImages.length; i++) { + if(mImages[i] != null) { + mImages[i].close(); + mImages[i] = 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(); + mStatus = STATUS.INIT; + if(mWatchdog != null) { + mWatchdog.stopMonitor(); + } + if((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"); + } else { + if(mFilter != null && DEBUG_FILTER) { + bytes = nv21ToJpeg(mDebugResultImage); + mActivity.getMediaSaveService().addImage( + bytes, title + "_beforeApplyingFilter", date, null, mDebugResultImage.outRoi.width(), mDebugResultImage.outRoi.height(), + 0, null, mediaSavedListener, contentResolver, "jpeg"); + } + bytes = nv21ToJpeg(resultImage); + mController.updateThumbnailJpegData(bytes); + mActivity.getMediaSaveService().addImage( + bytes, title, date, null, resultImage.outRoi.width(), resultImage.outRoi.height(), + 0, null, mediaSavedListener, contentResolver, "jpeg"); + } + } + } + }); + } + + private byte[] nv21ToJpeg(ImageFilter.ResultImage resultImage) { + BitmapOutputStream bos = new BitmapOutputStream(1024); + YuvImage im = new YuvImage(resultImage.outBuffer.array(), ImageFormat.NV21, + resultImage.width, resultImage.height, new int[]{resultImage.stride, resultImage.stride}); + im.compressToJpeg(resultImage.outRoi, 50, bos); + return bos.getArray(); + } + + private class BitmapOutputStream extends ByteArrayOutputStream { + public BitmapOutputStream(int size) { + super(size); + } + + public byte[] getArray() { + return buf; + } + } + + +} diff --git a/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java b/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java new file mode 100644 index 000000000..6ec9376d0 --- /dev/null +++ b/src/com/android/camera/imageprocessor/filter/BeautificationFilter.java @@ -0,0 +1,134 @@ +/* +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.Camera; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.params.Face; +import android.util.Log; +import android.util.Size; + +import com.android.camera.CaptureModule; +import com.android.camera.ui.FilmstripBottomControls; + +import java.nio.ByteBuffer; +import java.util.List; + +public class BeautificationFilter implements ImageFilter { + + int mWidth; + int mHeight; + int mStrideY; + int mStrideVU; + private CaptureModule mModule; + private static boolean DEBUG = false; + private static String TAG = "BeautificationFilter"; + private static boolean mIsSupported = false; + + public BeautificationFilter(CaptureModule module) { + mModule = module; + } + + @Override + public List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder) { + return null; + } + + @Override + public String getStringName() { + return "BeautificationFilter"; + } + + @Override + public int getNumRequiredImage() { + return 0; + } + + @Override + public void init(int width, int height, int strideY, int strideVU) { + mWidth = width; + mHeight = height; + mStrideY = strideY; + mStrideVU = strideVU; + } + + @Override + public void deinit() { + + } + + @Override + public void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object isPreview) { + Rect back = mModule.getCameraRegion(); + Face[] faces; + if(((Boolean)isPreview).booleanValue()) { + faces = mModule.getPreviewFaces(); + } else { + faces = mModule.getStickyFaces(); + } + float widthRatio = (float)mWidth/back.width(); + float heightRatio = (float)mHeight/back.height(); + if(faces == null || faces.length == 0) + return; + Rect rect = faces[0].getBounds(); + int value = nativeBeautificationProcess(bY, bVU, mWidth, mHeight, mStrideY, + (int)(rect.left*widthRatio), (int)(rect.top*heightRatio), + (int)(rect.right*widthRatio), (int)(rect.bottom*heightRatio)); + if(DEBUG && value < 0) { + if(value == -1) { + Log.d(TAG, "library initialization is failed."); + } else if(value == -2) { + Log.d(TAG, "No face is recognized"); + } + } + } + + @Override + public ResultImage processImage() { + return null; + } + + @Override + public boolean isSupported() { + return mIsSupported; + } + + private native int nativeBeautificationProcess(ByteBuffer yB, ByteBuffer vuB, + int width, int height, int stride, int fleft, int ftop, int fright, int fbottom); + + static { + try { + System.loadLibrary("jni_makeup"); + mIsSupported = true; + }catch(UnsatisfiedLinkError e) { + mIsSupported = false; + } + } +} diff --git a/src/com/android/camera/imageprocessor/filter/ImageFilter.java b/src/com/android/camera/imageprocessor/filter/ImageFilter.java new file mode 100644 index 000000000..e62d9b30a --- /dev/null +++ b/src/com/android/camera/imageprocessor/filter/ImageFilter.java @@ -0,0 +1,74 @@ +/* +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.CaptureRequest; + +import java.nio.ByteBuffer; +import java.util.List; + +public interface ImageFilter { + + /* Return the number of required images to process*/ + List<CaptureRequest> setRequiredImages(CaptureRequest.Builder builder); + + String getStringName(); + + int getNumRequiredImage(); + + void init(int width, int height, int strideY, int strideVU); + + /* Free all buffer */ + void deinit(); + + /* Adding the image to process */ + void addImage(ByteBuffer bY, ByteBuffer bVU, int imageNum, Object param); + + /* Processing all the added images and return roi*/ + ResultImage processImage(); + + boolean isSupported(); + + class ResultImage { + public ByteBuffer outBuffer; + public Rect outRoi; + public int width; + public int height; + public int stride; + + public ResultImage(ByteBuffer buf, Rect roi, int width, int height, int stride) { + outBuffer = buf; + outRoi = roi; + this.width = width; + this.height = height; + this.stride = stride; + } + } +} diff --git a/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java b/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java new file mode 100644 index 000000000..4773418de --- /dev/null +++ b/src/com/android/camera/imageprocessor/filter/OptizoomFilter.java @@ -0,0 +1,148 @@ +/* +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.CaptureRequest; +import android.util.Log; + +import com.android.camera.CaptureModule; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +public class OptizoomFilter implements ImageFilter{ + public static final int NUM_REQUIRED_IMAGE = 8; + private int mWidth; + private int mHeight; + private int mStrideY; + private int mStrideVU; + private static String TAG = "OptizoomFilter"; + private static final boolean DEBUG = true; //TODO: Have to be false before releasing. + private int temp; + private static boolean mIsSupported = true; + private ByteBuffer mOutBuf; + private CaptureModule mModule; + + private static void Log(String msg) { + if(DEBUG) { + Log.d(TAG, msg); + } + } + + public OptizoomFilter(CaptureModule module) { + mModule = module; + } + + @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 "OptizoomFilter"; + } + + @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*6); // YUV Buffer to hold (mWidth*2) X (mHeight*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(); + nativeAddImage(bY, bVU, yActualSize, vuActualSize, imageNum); + } + + @Override + public ResultImage processImage() { + Log("processImage " + mModule.getZoomValue()); + int[] roi = new int[4]; + int status = nativeProcessImage(mOutBuf.array(), mModule.getZoomValue(), 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 optizoom. It only processes when zoomValue >= 1.5f"); + return new ResultImage(mOutBuf, new Rect(roi[0], roi[1], roi[0]+roi[2], roi[1] + roi[3]), mWidth, mHeight, mStrideY); + } else { //In success case, it will return twice bigger width and height. + return new ResultImage(mOutBuf, new Rect(roi[0], roi[1], roi[0]+roi[2], roi[1] + roi[3]), mWidth*2, mHeight*2, mStrideY*2); + } + } + + @Override + public boolean isSupported() { + return mIsSupported; + } + + 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, float zoomLvl, int[] roi); + + static { + try { + System.loadLibrary("jni_optizoom"); + mIsSupported = true; + }catch(UnsatisfiedLinkError e) { + mIsSupported = false; + } + } +} diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index 1f9c8d921..e6e00ad56 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -37,7 +37,6 @@ import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.Scroller; -import com.android.camera.CaptureMenu; import com.android.camera.PhotoMenu; import com.android.camera.VideoMenu; import com.android.camera.PreviewGestures; @@ -1832,7 +1831,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return true; PhotoMenu pMenu = mPreviewGestures.getPhotoMenu(); VideoMenu vMenu = mPreviewGestures.getVideoMenu(); - CaptureMenu cMenu = mPreviewGestures.getCaptureMenu(); + if (pMenu != null) { if (pMenu.isMenuBeingShown()) { if (pMenu.isMenuBeingAnimated()) { @@ -1850,23 +1849,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } } } - if (cMenu != null) { - if (cMenu.isMenuBeingShown()) { - if (cMenu.isMenuBeingAnimated()) { - if (cMenu.isOverMenu(ev)) { - mSendToMenu = true; - return true; - } - } - } - if (cMenu.isPreviewMenuBeingShown()) { - if (cMenu.isOverPreviewMenu(ev)) { - mSendToPreviewMenu = true; - return true; - } - } - } if (vMenu != null) { if (vMenu.isMenuBeingShown()) { if (vMenu.isMenuBeingAnimated()) { @@ -1903,7 +1886,6 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { } PhotoMenu pMenu = mPreviewGestures.getPhotoMenu(); VideoMenu vMenu = mPreviewGestures.getVideoMenu(); - CaptureMenu cMenu = mPreviewGestures.getCaptureMenu(); if (pMenu != null) { if (mSendToPreviewMenu) return pMenu.sendTouchToPreviewMenu(ev); @@ -1917,19 +1899,7 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { return pMenu.sendTouchToPreviewMenu(ev); } } - if (cMenu != null) { - if (mSendToPreviewMenu) - return cMenu.sendTouchToPreviewMenu(ev); - if (mSendToMenu) - return cMenu.sendTouchToMenu(ev); - if (cMenu.isMenuBeingShown()) { - return cMenu.sendTouchToMenu(ev); - } - if (cMenu.isPreviewMenuBeingShown()) { - return cMenu.sendTouchToPreviewMenu(ev); - } - } if (vMenu != null) { if (mSendToPreviewMenu) return vMenu.sendTouchToPreviewMenu(ev); diff --git a/src/com/android/camera/ui/ListMenu.java b/src/com/android/camera/ui/ListMenu.java index 1c9c611e3..d83114cce 100644 --- a/src/com/android/camera/ui/ListMenu.java +++ b/src/com/android/camera/ui/ListMenu.java @@ -19,8 +19,6 @@ package com.android.camera.ui; -import java.util.ArrayList; - import android.content.Context; import android.util.AttributeSet; import android.util.Log; @@ -34,21 +32,40 @@ import android.widget.ListView; import com.android.camera.ListPreference; import com.android.camera.PreferenceGroup; +import com.android.camera.SettingsManager; + import org.codeaurora.snapcam.R; +import java.util.ArrayList; +import java.util.List; + /* A popup window that contains several camera settings. */ public class ListMenu extends ListView implements ListMenuItem.Listener, - AdapterView.OnItemClickListener { + AdapterView.OnItemClickListener, + ListSubMenu.Listener { @SuppressWarnings("unused") private static final String TAG = "ListMenu"; private int mHighlighted = -1; private Listener mListener; + private SettingsManager mSettingsManager; private ArrayList<ListPreference> mListItem = new ArrayList<ListPreference>(); // Keep track of which setting items are disabled // e.g. White balance will be disabled when scene mode is set to non-auto private boolean[] mEnabled; + private boolean mForCamera2 = false; + + @Override + public void onListPrefChanged(ListPreference pref) { + // listen from ListSubMenu + if (mListener != null) { + mListener.onSettingChanged(pref); + } + if (mSettingsManager != null) { + mSettingsManager.onSettingChanged(pref); + } + } static public interface Listener { public void onSettingChanged(ListPreference pref); @@ -60,6 +77,11 @@ public class ListMenu extends ListView public void onListMenuTouched(); } + static public interface SettingsListener { + // notify SettingsManager + public void onSettingChanged(ListPreference pref); + } + private class MoreSettingAdapter extends ArrayAdapter<ListPreference> { LayoutInflater mInflater; String mOnString; @@ -90,6 +112,9 @@ public class ListMenu extends ListView view.setSettingChangedListener(ListMenu.this); if (position >= 0 && position < mEnabled.length) { view.setEnabled(mEnabled[position]); + if (mForCamera2 && !mEnabled[position]) { + view.overrideSettings(mSettingsManager.getValue(pref.getKey())); + } } else { Log.w(TAG, "Invalid input: enabled list length, " + mEnabled.length + " position " + position); @@ -109,6 +134,10 @@ public class ListMenu extends ListView } } + public void setSettingsManager(SettingsManager settingsManager) { + mSettingsManager = settingsManager; + } + public void setSettingChangedListener(Listener listener) { mListener = listener; } @@ -117,6 +146,32 @@ public class ListMenu extends ListView super(context, attrs); } + public void initializeForCamera2(String[] keys) { + mForCamera2 = true; + PreferenceGroup group = mSettingsManager.getPreferenceGroup(); + List<String> disabledList = mSettingsManager.getDisabledList(); + // Prepare the setting items. + for (int i = 0; i < keys.length; ++i) { + ListPreference pref = group.findPreference(keys[i]); + if (pref != null) + mListItem.add(pref); + } + + ArrayAdapter<ListPreference> mListItemAdapter = new MoreSettingAdapter(); + setAdapter(mListItemAdapter); + setOnItemClickListener(this); + setSelector(android.R.color.transparent); + // Initialize mEnabled + mEnabled = new boolean[mListItem.size()]; + for (int i = 0; i < mEnabled.length; i++) { + mEnabled[i] = true; + } + + for (String s: disabledList) { + setPreferenceEnabled(s, false); + } + } + public void initialize(PreferenceGroup group, String[] keys) { // Prepare the setting items. for (int i = 0; i < keys.length; ++i) { diff --git a/src/com/android/camera/ui/ListMenuItem.java b/src/com/android/camera/ui/ListMenuItem.java index f3c7f017e..b051fdd27 100644 --- a/src/com/android/camera/ui/ListMenuItem.java +++ b/src/com/android/camera/ui/ListMenuItem.java @@ -159,4 +159,26 @@ public class ListMenuItem extends RelativeLayout { setAlpha(0.3f); } } + + public void setEnabled(boolean enable, String value) { + super.setEnabled(enable); + if (enable) + setAlpha(1f); + else + setAlpha(0.3f); + if (mTitle != null) { + mTitle.setEnabled(enable); + if (enable) + setAlpha(1f); + else + setAlpha(0.3f); + } + if (mEntry != null) { + mEntry.setEnabled(enable); + if (enable) + setAlpha(1f); + else + setAlpha(0.3f); + } + } } diff --git a/src/com/android/camera/ui/ListSubMenu.java b/src/com/android/camera/ui/ListSubMenu.java index 3501af3bc..a425c7680 100644 --- a/src/com/android/camera/ui/ListSubMenu.java +++ b/src/com/android/camera/ui/ListSubMenu.java @@ -136,8 +136,9 @@ public class ListSubMenu extends ListView implements public void onItemClick(AdapterView<?> parent, View view, int index, long id) { mPreference.setValueIndex(index); - if (mListener != null) + if (mListener != null) { mListener.onListPrefChanged(mPreference); + } } @Override diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index b1deca6e0..812220c1a 100644 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java @@ -35,7 +35,6 @@ import android.hardware.Camera; import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.Camera.Size; -import android.hardware.camera2.CameraCharacteristics; import android.location.Location; import android.net.Uri; import android.os.Handler; @@ -1169,32 +1168,4 @@ public class CameraUtil { return retRatio; } - public static boolean isZoomSupported(CameraCharacteristics[] characteristics, List<Integer> - characteristicsIndex) { - for (int i = 0; i < characteristicsIndex.size(); i++) { - if (!isZoomSupported(characteristics[characteristicsIndex.get(i)])) - return false; - } - return true; - } - - public static boolean isZoomSupported(CameraCharacteristics characteristic) { - return characteristic.get(CameraCharacteristics - .SCALER_AVAILABLE_MAX_DIGITAL_ZOOM) > 1f; - } - - public static boolean isAutoFocusSupported(CameraCharacteristics[] characteristics, List<Integer> - characteristicsIndex) { - for (int i = 0; i < characteristicsIndex.size(); i++) { - if (!isAutoFocusSupported(characteristics[characteristicsIndex.get(i)])) - return false; - } - return true; - } - - public static boolean isAutoFocusSupported(CameraCharacteristics characteristic) { - Integer maxAfRegions = characteristic.get( - CameraCharacteristics.CONTROL_MAX_REGIONS_AF); - return maxAfRegions != null && maxAfRegions > 0; - } } diff --git a/src/com/android/camera/util/PersistUtil.java b/src/com/android/camera/util/PersistUtil.java new file mode 100644 index 000000000..c9a5a474a --- /dev/null +++ b/src/com/android/camera/util/PersistUtil.java @@ -0,0 +1,51 @@ +/* + * 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.util; + +import android.os.SystemProperties; + +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"; + + public static int getMemoryLimit() { + return SystemProperties.getInt(PERSIST_MEMORY_LIMIT, 60); + } + + public static boolean getSkipMemoryCheck() { + return SystemProperties.getBoolean(PERSIST_SKIP_MEMORY_CHECK, false); + } + + public static int getLongshotShotLimit() { + return SystemProperties.getInt(PERSIST_LONGSHOT_SHOT_LIMIT, 20); + } + +} |