diff options
Diffstat (limited to 'src')
82 files changed, 4704 insertions, 2256 deletions
diff --git a/src/com/android/camera/AndroidCameraManagerImpl.java b/src/com/android/camera/AndroidCameraManagerImpl.java index cad136cde..414ff8101 100644 --- a/src/com/android/camera/AndroidCameraManagerImpl.java +++ b/src/com/android/camera/AndroidCameraManagerImpl.java @@ -21,10 +21,12 @@ import static com.android.camera.util.CameraUtil.Assert; import java.io.IOException; import android.annotation.TargetApi; +import android.content.Context; import android.graphics.SurfaceTexture; import android.hardware.Camera; import android.hardware.Camera.AutoFocusCallback; import android.hardware.Camera.AutoFocusMoveCallback; +import android.hardware.Camera.CameraInfo; import android.hardware.Camera.ErrorCallback; import android.hardware.Camera.FaceDetectionListener; import android.hardware.Camera.OnZoomChangeListener; @@ -41,11 +43,13 @@ import android.util.Log; import android.view.SurfaceHolder; import android.hardware.Camera.CameraDataCallback; import android.hardware.Camera.CameraMetaDataCallback; +import com.android.camera.app.CameraApp; import com.android.camera.util.ApiHelper; import android.os.ConditionVariable; import java.lang.reflect.Method; import org.codeaurora.snapcam.wrapper.CameraWrapper; +import org.codeaurora.snapcam.R; /** * A class to implement {@link CameraManager} of the Android camera framework. @@ -99,6 +103,7 @@ class AndroidCameraManagerImpl implements CameraManager { private static final int SEND_HISTOGRAM_DATA = 602; //LONGSHOT private static final int SET_LONGSHOT = 701; + private static final int STOP_LONGSHOT = 702; private static final int SET_AUTO_HDR_MODE = 801; //HAL1 version code @@ -235,16 +240,36 @@ class AndroidCameraManagerImpl implements CameraManager { try { switch (msg.what) { case OPEN_CAMERA: - try { - Method openMethod = Class.forName("android.hardware.Camera").getMethod( - "openLegacy", int.class, int.class); - mCamera = (android.hardware.Camera) openMethod.invoke( - null, msg.arg1, CAMERA_HAL_API_VERSION_1_0); - } catch (Exception e) { - /* Retry with open if openLegacy doesn't exist/fails */ - Log.v(TAG, "openLegacy failed due to " + e.getMessage() - + ", using open instead"); - mCamera = android.hardware.Camera.open(msg.arg1); + final int cameraId = msg.arg1; + Context context = CameraApp.getContext(); + + CameraHolder.CameraInfo info = + CameraHolder.instance().getCameraInfo()[cameraId]; + + final boolean isBackCamera = + info.facing == CameraHolder.CameraInfo.CAMERA_FACING_BACK; + final boolean isFrontCamera = + info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT; + final boolean useOpenLegacyForBackCamera = context.getResources() + .getBoolean(R.bool.back_camera_open_legacy); + final boolean useOpenLegacyForFrontCamera = context.getResources() + .getBoolean(R.bool.front_camera_open_legacy); + + if (isBackCamera && useOpenLegacyForBackCamera || + isFrontCamera && useOpenLegacyForFrontCamera) { + try { + Method openMethod = Class.forName("android.hardware.Camera") + .getMethod("openLegacy", int.class, int.class); + mCamera = (android.hardware.Camera) openMethod.invoke(null, + cameraId, CAMERA_HAL_API_VERSION_1_0); + } catch (Exception e) { + /* Retry with open if openLegacy doesn't exist/fails */ + Log.v(TAG, "openLegacy failed due to " + e.getMessage() + + ", using open instead"); + mCamera = android.hardware.Camera.open(cameraId); + } + } else { + mCamera = android.hardware.Camera.open(cameraId); } if (mCamera != null) { @@ -256,7 +281,7 @@ class AndroidCameraManagerImpl implements CameraManager { } } else { if (msg.obj != null) { - ((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(msg.arg1); + ((CameraOpenErrorCallback) msg.obj).onDeviceOpenFailure(cameraId); } } return; @@ -402,6 +427,10 @@ class AndroidCameraManagerImpl implements CameraManager { CameraWrapper.setLongshot(mCamera, (Boolean) msg.obj); break; + case STOP_LONGSHOT: + CameraWrapper.stopLongshot(mCamera); + break; + case SET_AUTO_HDR_MODE: CameraWrapper.setMetadataCb(mCamera, (CameraMetaDataCallback) msg.obj); break; @@ -663,6 +692,11 @@ class AndroidCameraManagerImpl implements CameraManager { } @Override + public void stopLongshot() { + mCameraHandler.sendEmptyMessage(STOP_LONGSHOT); + } + + @Override public void setHistogramMode(CameraDataCallback cb) { mCameraHandler.obtainMessage(SET_HISTOGRAM_MODE, cb).sendToTarget(); } diff --git a/src/com/android/camera/BestpictureActivity.java b/src/com/android/camera/BestpictureActivity.java index 6eaa2a3d0..e4c1ca073 100644 --- a/src/com/android/camera/BestpictureActivity.java +++ b/src/com/android/camera/BestpictureActivity.java @@ -411,7 +411,8 @@ public class BestpictureActivity extends FragmentActivity { @Override public String getContentString() { - return getResources().getString(R.string.save_best_dialog_content, choosenCount); + return getResources().getQuantityString(R.plurals.save_best_dialog_content, + choosenCount, choosenCount); } @Override @@ -496,8 +497,8 @@ public class BestpictureActivity extends FragmentActivity { } } } - String toastString = getResources().getString(R.string.save_best_image_toast, - toSaveCount); + String toastString = getResources().getQuantityString(R.plurals.save_best_image_toast, + toSaveCount, toSaveCount); Toast.makeText(BestpictureActivity.this, toastString, Toast.LENGTH_SHORT).show(); backToViewfinder(); } diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 9e1855ff3..2933ed4e5 100755 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -120,6 +121,8 @@ import com.android.camera.util.PhotoSphereHelper.PanoramaViewHelper; import com.android.camera.util.UsageStatistics; import org.codeaurora.snapcam.R; +import org.lineageos.quickreader.ScannerActivity; + import java.io.File; import java.io.IOException; @@ -169,7 +172,7 @@ public class CameraActivity extends Activity private static final int SWITCH_SAVE_PATH = 2; /** Permission request code */ - private static final int PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION = 1; + private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1; /** Whether onResume should reset the view to the preview. */ private boolean mResetToPreviewOnResume = true; @@ -192,11 +195,15 @@ public class CameraActivity extends Activity private static boolean PIE_MENU_ENABLED = false; private boolean mDeveloperMenuEnabled = false; + private boolean mCamera2supported = false; + private boolean mCamera2enabled = false; + /** This data adapter is used by FilmStripView. */ private LocalDataAdapter mDataAdapter; /** This data adapter represents the real local camera data. */ private LocalDataAdapter mWrappedDataAdapter; + private Context mContext; private PanoramaStitchingManager mPanoramaManager; private PlaceholderManager mPlaceholderManager; private int mCurrentModuleIndex; @@ -221,6 +228,11 @@ public class CameraActivity extends Activity private final Object mStorageSpaceLock = new Object(); private long mStorageSpaceBytes = Storage.LOW_STORAGE_THRESHOLD_BYTES; private boolean mSecureCamera; + private boolean mInCameraApp = true; + // Keep track of powershutter state + public static boolean mPowerShutter = false; + // Keep track of max brightness state + public static boolean mMaxBrightness = false; private int mLastRawOrientation; private MyOrientationEventListener mOrientationListener; private Handler mMainHandler; @@ -390,6 +402,10 @@ public class CameraActivity extends Activity mDeveloperMenuEnabled = true; } + public void disableDeveloperMenu() { + mDeveloperMenuEnabled = false; + } + private String fileNameFromDataID(int dataID) { final LocalData localData = mDataAdapter.getLocalData(dataID); @@ -610,14 +626,36 @@ public class CameraActivity extends Activity intent.putExtra(KEY_TOTAL_NUMBER, (adapter.getTotalNumber() -1)); startActivity(intent); } catch (ActivityNotFoundException ex) { - try { - Log.w(TAG, "Gallery not found"); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.putExtra(KEY_FROM_SNAPCAM, true); - startActivity(intent); - } catch (ActivityNotFoundException e) { - Log.w(TAG, "No Activity could be found to open image or video"); - } + gotoReviewPhoto(uri); + } catch (IllegalArgumentException ex) { + gotoReviewPhoto(uri); + } + } + + private void gotoReviewPhoto(Uri uri) { + try { + Log.w(TAG, "Gallery not found"); + Intent intent = new Intent(CameraUtil.REVIEW_ACTION, uri); + startActivity(intent); + intent.putExtra(KEY_FROM_SNAPCAM, true); + intent.putExtra(KEY_TOTAL_NUMBER, getDataAdapter().getTotalNumber() - 1); + } catch (ActivityNotFoundException e) { + gotoViewPhoto(uri); + } catch (IllegalArgumentException e) { + gotoViewPhoto(uri); + } + } + + private void gotoViewPhoto(Uri uri) { + try { + Log.w(TAG, "Gallery not found"); + Intent intent = new Intent(Intent.ACTION_VIEW, uri); + intent.putExtra(KEY_FROM_SNAPCAM, true); + startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "No Activity could be found to open image or video"); + } catch (IllegalArgumentException e) { + Log.w(TAG, "No Activity could be found to open image or video"); } } @@ -1343,14 +1381,8 @@ public class CameraActivity extends Activity // Handle presses on the action bar items switch (item.getItemId()) { case android.R.id.home: - // ActionBar's Up/Home button was clicked - try { - startActivity(IntentHelper.getGalleryIntent(this)); - return true; - } catch (ActivityNotFoundException e) { - Log.w(TAG, "Failed to launch gallery activity, closing"); - finish(); - } + onBackPressed(); + return true; case R.id.action_delete: UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_DELETE, null, 0, @@ -1465,6 +1497,9 @@ public class CameraActivity extends Activity } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } + + mContext = getApplicationContext(); + // Check if this is in the secure camera mode. Intent intent = getIntent(); String action = intent.getAction(); @@ -1541,9 +1576,16 @@ public class CameraActivity extends Activity } } - boolean cam2on = PersistUtil.getCamera2Mode(); - CameraHolder.setCamera2Mode(this, cam2on); - if (cam2on && (moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX || + // Check if the device supports Camera API 2 + mCamera2supported = CameraUtil.isCamera2Supported(mContext); + Log.d(TAG, "Camera API 2 supported: " + mCamera2supported); + + mCamera2enabled = mCamera2supported && + mContext.getResources().getBoolean(R.bool.support_camera_api_v2); + Log.d(TAG, "Camera API 2 enabled: " + mCamera2enabled); + + CameraHolder.setCamera2Mode(this, mCamera2enabled); + if (mCamera2enabled && (moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX || moduleIndex == ModuleSwitcher.VIDEO_MODULE_INDEX)) moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; @@ -1899,6 +1941,9 @@ public class CameraActivity extends Activity mCursor.close(); mCursor=null; } + if (mDataAdapter != null) { + mDataAdapter.stopLoading(); + } if (mAutoTestEnabled) { unregisterReceiver(mAutoTestReceiver); } @@ -1949,7 +1994,9 @@ public class CameraActivity extends Activity } public void setPreviewGestures(PreviewGestures previewGestures) { - mFilmStripView.setPreviewGestures(previewGestures); + if (mFilmStripView != null) { + mFilmStripView.setPreviewGestures(previewGestures); + } } protected long updateStorageSpace() { @@ -2024,6 +2071,39 @@ public class CameraActivity extends Activity } } + protected void initPowerShutter(ComboPreferences prefs) { + String val = prefs.getString(CameraSettings.KEY_POWER_SHUTTER, + getResources().getString(R.string.pref_camera_power_shutter_default)); + if (!CameraUtil.hasCameraKey()) { + mPowerShutter = val.equals(CameraSettings.VALUE_ON); + } + if (mPowerShutter && mInCameraApp) { + getWindow().addPrivateFlags( + WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY); + } else { + getWindow().clearPrivateFlags( + WindowManager.LayoutParams.PRIVATE_FLAG_PREVENT_POWER_KEY); + } + } + + protected void initMaxBrightness(ComboPreferences prefs) { + String val = prefs.getString(CameraSettings.KEY_MAX_BRIGHTNESS, + getResources().getString(R.string.pref_camera_max_brightness_default)); + + Window win = getWindow(); + WindowManager.LayoutParams params = win.getAttributes(); + + mMaxBrightness = val.equals(CameraSettings.VALUE_ON); + + if (mMaxBrightness && mInCameraApp) { + params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_FULL; + } else { + params.screenBrightness = WindowManager.LayoutParams.BRIGHTNESS_OVERRIDE_NONE; + } + + win.setAttributes(params); + } + protected void setResultEx(int resultCode) { mResultCodeForTesting = resultCode; setResult(resultCode); @@ -2048,13 +2128,13 @@ public class CameraActivity extends Activity } public void requestLocationPermission() { - if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { Log.v(TAG, "Request Location permission"); mCurrentModule.waitingLocationPermissionResult(true); requestPermissions( - new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, - PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION); + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION); } } @@ -2062,7 +2142,7 @@ public class CameraActivity extends Activity public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { - case PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION: { + case PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION: { // If request is cancelled, the result arrays are empty. mCurrentModule.waitingLocationPermissionResult(false); if (grantResults.length > 0 @@ -2082,11 +2162,21 @@ public class CameraActivity extends Activity return mForceReleaseCamera; } + public boolean isInCameraApp() { + return mInCameraApp; + } + @Override public void onModuleSelected(int moduleIndex) { - boolean cam2on = PersistUtil.getCamera2Mode(); + if (moduleIndex == ModuleSwitcher.QR_MODULE_INDEX) { + Intent intent = new Intent(this, ScannerActivity.class); + intent.putExtra(SECURE_CAMERA_EXTRA, mSecureCamera); + startActivity(intent); + return; + } + mForceReleaseCamera = moduleIndex == ModuleSwitcher.CAPTURE_MODULE_INDEX || - (cam2on && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX); + (mCamera2enabled && moduleIndex == ModuleSwitcher.PHOTO_MODULE_INDEX); if (mForceReleaseCamera) { moduleIndex = ModuleSwitcher.CAPTURE_MODULE_INDEX; } @@ -2358,6 +2448,9 @@ public class CameraActivity extends Activity * @return whether controls are visible. */ private boolean arePreviewControlsVisible() { + if (mCurrentModule == null) { + return false; + } return mCurrentModule.arePreviewControlsVisible(); } @@ -2369,6 +2462,10 @@ public class CameraActivity extends Activity */ private void setPreviewControlsVisibility(boolean showControls) { mCurrentModule.onPreviewFocusChanged(showControls); + + // controls are only shown when the camera app is active + // so we can assume to fetch this information from here + mInCameraApp = showControls; } // Accessor methods for getting latency times used in performance testing diff --git a/src/com/android/camera/CameraHolder.java b/src/com/android/camera/CameraHolder.java index 59b305bc6..585e3bb4d 100755 --- a/src/com/android/camera/CameraHolder.java +++ b/src/com/android/camera/CameraHolder.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 The Android Open Source Project + * 2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,8 +31,11 @@ import android.os.Looper; import android.os.Message; import android.util.Log; +import com.android.camera.app.CameraApp; import com.android.camera.CameraManager.CameraProxy; +import org.codeaurora.snapcam.R; + import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -64,11 +68,11 @@ public class CameraHolder { private static CameraProxy mMockCamera[]; private static CameraInfo mMockCameraInfo[]; private static Context mContext; - private static boolean mCam2On = true; + private static boolean mCam2On = false; private ArrayList<CameraCharacteristics> mCharacteristics = new ArrayList<>(); /* Debug double-open issue */ - private static final boolean DEBUG_OPEN_RELEASE = true; + private static final boolean DEBUG_OPEN_RELEASE = false; private static class OpenReleaseState { long time; int id; @@ -169,52 +173,46 @@ public class CameraHolder { HandlerThread ht = new HandlerThread("CameraHolder"); ht.start(); mHandler = new MyHandler(ht.getLooper()); - if (mCam2On) { - android.hardware.camera2.CameraManager manager = - (android.hardware.camera2.CameraManager) mContext.getSystemService( - Context.CAMERA_SERVICE); - String[] cameraIdList = null; - try { - cameraIdList = manager.getCameraIdList(); - for (int i = 0; i < cameraIdList.length; i++) { - String cameraId = cameraIdList[i]; - CameraCharacteristics characteristics - = manager.getCameraCharacteristics(cameraId); - Log.d(TAG,"cameraIdList size ="+cameraIdList.length); - int facing = characteristics.get(CameraCharacteristics.LENS_FACING); - if (facing == CameraCharacteristics.LENS_FACING_FRONT) { - CaptureModule.FRONT_ID = i; - } - mCharacteristics.add(i, characteristics); + android.hardware.camera2.CameraManager manager = + (android.hardware.camera2.CameraManager) mContext.getSystemService( + Context.CAMERA_SERVICE); + String[] cameraIdList = null; + try { + cameraIdList = manager.getCameraIdList(); + mInfo = new CameraInfo[cameraIdList.length]; + for (int i = 0; i < cameraIdList.length; i++) { + String cameraId = cameraIdList[i]; + CameraCharacteristics characteristics + = manager.getCameraCharacteristics(cameraId); + Log.d(TAG,"cameraIdList size ="+cameraIdList.length); + int facing = characteristics.get(CameraCharacteristics.LENS_FACING); + if (facing == CameraCharacteristics.LENS_FACING_FRONT) { + CaptureModule.FRONT_ID = i; } - } catch (CameraAccessException e) { - e.printStackTrace(); + addCameraInfo(i, characteristics); + mCharacteristics.add(i, characteristics); } - mNumberOfCameras = cameraIdList == null ? 0 : cameraIdList.length; - } else { - if (mMockCameraInfo != null) { - mNumberOfCameras = mMockCameraInfo.length; - mInfo = mMockCameraInfo; - } else { - mNumberOfCameras = android.hardware.Camera.getNumberOfCameras(); - mInfo = new CameraInfo[mNumberOfCameras]; - for (int i = 0; i < mNumberOfCameras; i++) { - mInfo[i] = new CameraInfo(); - android.hardware.Camera.getCameraInfo(i, mInfo[i]); - } - } - // get the first (smallest) back and first front camera id - for (int i = 0; i < mNumberOfCameras; i++) { - if (mBackCameraId == -1 && mInfo[i].facing == CameraInfo.CAMERA_FACING_BACK) { - mBackCameraId = i; - } else if (mFrontCameraId == -1 && - mInfo[i].facing == CameraInfo.CAMERA_FACING_FRONT) { - mFrontCameraId = i; - } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mNumberOfCameras = cameraIdList == null ? 0 : cameraIdList.length; + // get the first (smallest) back and first front camera id + for (int i = 0; i < mNumberOfCameras; i++) { + if (mBackCameraId == -1 && mInfo[i].facing == CameraCharacteristics.LENS_FACING_BACK) { + mBackCameraId = i; + } else if (mFrontCameraId == -1 && + mInfo[i].facing == CameraCharacteristics.LENS_FACING_FRONT) { + mFrontCameraId = i; } } } + private void addCameraInfo(int index, CameraCharacteristics characteristics) { + mInfo[index] = new CameraInfo(); + mInfo[index].facing = characteristics.get(CameraCharacteristics.LENS_FACING); + mInfo[index].orientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + } + public CameraCharacteristics getCameraCharacteristics(int id) { return mCharacteristics.get(id); } @@ -230,6 +228,9 @@ public class CameraHolder { public synchronized CameraProxy open( Handler handler, int cameraId, CameraManager.CameraOpenErrorCallback cb) { + + Context context = CameraApp.getContext(); + if (DEBUG_OPEN_RELEASE) { collectState(cameraId, mCameraDevice); if (mCameraOpened) { @@ -262,6 +263,23 @@ public class CameraHolder { } mCameraId = cameraId; mParameters = mCameraDevice.getCamera().getParameters(); + + // Manufacturer specific key values + String manufacturerKeyValues = + context.getResources().getString(R.string.manufacturer_key_values); + if (manufacturerKeyValues != null && !manufacturerKeyValues.isEmpty()) { + String[] keyValuesArray = manufacturerKeyValues.split(";"); + for (String kvPair : keyValuesArray) { + String[] manufacturerParamPair = kvPair.split("="); + if (!manufacturerParamPair[0].isEmpty() && + !manufacturerParamPair[1].isEmpty()) { + Log.d(TAG, "Set manufacturer specific parameter " + + manufacturerParamPair[0] + "=" + manufacturerParamPair[1]); + mParameters.set(manufacturerParamPair[0], manufacturerParamPair[1]); + } + } + mCameraDevice.setParameters(mParameters); + } } else { if (!mCameraDevice.reconnect(handler, cb)) { Log.e(TAG, "fail to reconnect Camera:" + mCameraId + ", aborting."); @@ -334,4 +352,11 @@ public class CameraHolder { public int getFrontCameraId() { return mFrontCameraId; } + + public class CameraInfo { + public static final int CAMERA_FACING_FRONT = CameraCharacteristics.LENS_FACING_FRONT; + public static final int CAMERA_FACING_BACK = CameraCharacteristics.LENS_FACING_BACK; + public int facing; + public int orientation; + } } diff --git a/src/com/android/camera/CameraManager.java b/src/com/android/camera/CameraManager.java index b93e7182b..2aeba7c5d 100644 --- a/src/com/android/camera/CameraManager.java +++ b/src/com/android/camera/CameraManager.java @@ -384,5 +384,10 @@ public interface CameraManager { * {@code false} to disable it. */ public void setLongshot(boolean enable); + /** + * Stop longshot. + * + */ + public void stopLongshot(); } } diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index 9a0a8219b..ab4b1a6a4 100755 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2009 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,11 +40,14 @@ import com.android.camera.util.ApiHelper; import com.android.camera.util.CameraUtil; import com.android.camera.util.GcamHelper; import com.android.camera.util.PersistUtil; +import com.android.camera.util.MultiMap; + import org.codeaurora.snapcam.R; import org.codeaurora.snapcam.wrapper.CamcorderProfileWrapper; import org.codeaurora.snapcam.wrapper.ParametersWrapper; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import android.os.Build; @@ -64,6 +68,9 @@ public class CameraSettings { public static final String KEY_PICTURE_SIZE = "pref_camera_picturesize_key"; public static final String KEY_JPEG_QUALITY = "pref_camera_jpegquality_key"; public static final String KEY_FOCUS_MODE = "pref_camera_focusmode_key"; + public static final String KEY_VIDEOCAMERA_FOCUS_MODE = "pref_camera_video_focusmode_key"; + public static final String KEY_FOCUS_TIME = "pref_camera_focustime_key"; + public static final String KEY_VIDEOCAMERA_FOCUS_TIME = "pref_camera_video_focustime_key"; public static final String KEY_FLASH_MODE = "pref_camera_flashmode_key"; public static final String KEY_VIDEOCAMERA_FLASH_MODE = "pref_camera_video_flashmode_key"; public static final String KEY_WHITE_BALANCE = "pref_camera_whitebalance_key"; @@ -81,17 +88,18 @@ public class CameraSettings { public static final String KEY_PHOTOSPHERE_PICTURESIZE = "pref_photosphere_picturesize_key"; public static final String KEY_STARTUP_MODULE_INDEX = "camera.startup_module"; + public static final String KEY_POWER_SHUTTER = "pref_power_shutter"; + public static final String KEY_MAX_BRIGHTNESS = "pref_max_brightness"; public static final String KEY_VIDEO_ENCODER = "pref_camera_videoencoder_key"; public static final String KEY_AUDIO_ENCODER = "pref_camera_audioencoder_key"; - public static final String KEY_VIDEO_DURATION = "pref_camera_video_duration_key"; public static final String KEY_POWER_MODE = "pref_camera_powermode_key"; public static final String KEY_PICTURE_FORMAT = "pref_camera_pictureformat_key"; public static final String KEY_ZSL = "pref_camera_zsl_key"; public static final String KEY_CAMERA_SAVEPATH = "pref_camera_savepath_key"; public static final String KEY_FILTER_MODE = "pref_camera_filter_mode_key"; public static final String KEY_COLOR_EFFECT = "pref_camera_coloreffect_key"; + public static final String KEY_VIDEOCAMERA_COLOR_EFFECT = "pref_camera_video_coloreffect_key"; public static final String KEY_FACE_DETECTION = "pref_camera_facedetection_key"; - public static final String KEY_TOUCH_AF_AEC = "pref_camera_touchafaec_key"; public static final String KEY_SELECTABLE_ZONE_AF = "pref_camera_selectablezoneaf_key"; public static final String KEY_SATURATION = "pref_camera_saturation_key"; public static final String KEY_CONTRAST = "pref_camera_contrast_key"; @@ -99,6 +107,7 @@ public class CameraSettings { public static final String KEY_AUTOEXPOSURE = "pref_camera_autoexposure_key"; public static final String KEY_ANTIBANDING = "pref_camera_antibanding_key"; public static final String KEY_ISO = "pref_camera_iso_key"; + public static final String KEY_SHUTTER_SPEED = "pref_camera_shutter_speed_key"; public static final String KEY_LENSSHADING = "pref_camera_lensshading_key"; public static final String KEY_HISTOGRAM = "pref_camera_histogram_key"; public static final String KEY_DENOISE = "pref_camera_denoise_key"; @@ -113,7 +122,6 @@ public class CameraSettings { public static final String KEY_AE_BRACKET_HDR = "pref_camera_ae_bracket_hdr_key"; public static final String KEY_ADVANCED_FEATURES = "pref_camera_advanced_features_key"; public static final String KEY_HDR_MODE = "pref_camera_hdr_mode_key"; - public static final String KEY_HDR_NEED_1X = "pref_camera_hdr_need_1x_key"; public static final String KEY_DEVELOPER_MENU = "pref_developer_menu_key"; public static final String KEY_VIDEO_SNAPSHOT_SIZE = "pref_camera_videosnapsize_key"; @@ -153,8 +161,10 @@ public class CameraSettings { private static final String KEY_QC_SUPPORTED_VIDEO_CDS_MODES = "video-cds-mode-values"; private static final String KEY_QC_SUPPORTED_TNR_MODES = "tnr-mode-values"; private static final String KEY_QC_SUPPORTED_VIDEO_TNR_MODES = "video-tnr-mode-values"; + private static final String KEY_QC_SUPPORTED_FACE_DETECTION = "face-detection-values"; private static final String KEY_SNAPCAM_SUPPORTED_HDR_MODES = "hdr-mode-values"; - private static final String KEY_SNAPCAM_SUPPORTED_HDR_NEED_1X = "hdr-need-1x-values"; + public static final String KEY_SNAPCAM_SHUTTER_SPEED = "shutter-speed"; + public static final String KEY_SNAPCAM_SHUTTER_SPEED_MODES = "shutter-speed-values"; public static final String KEY_QC_AE_BRACKETING = "ae-bracket-hdr"; public static final String KEY_QC_AF_BRACKETING = "af-bracket"; public static final String KEY_QC_RE_FOCUS = "re-focus"; @@ -173,13 +183,19 @@ public class CameraSettings { public static final String KEY_QC_TNR_MODE = "tnr-mode"; public static final String KEY_QC_VIDEO_TNR_MODE = "video-tnr-mode"; public static final String KEY_SNAPCAM_HDR_MODE = "hdr-mode"; - public static final String KEY_SNAPCAM_HDR_NEED_1X = "hdr-need-1x"; public static final String KEY_VIDEO_HSR = "video-hsr"; 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_LUMINANCE_CONDITION = "luminance-condition"; + public static final String LUMINANCE_CONDITION_LOW = "low"; + public static final String LUMINANCE_CONDITION_HIGH = "high"; + + public static final String LGE_HDR_MODE_OFF = "0"; + public static final String LGE_HDR_MODE_ON = "1"; + public static final String KEY_INTERNAL_PREVIEW_RESTART = "internal-restart"; public static final String KEY_QC_ZSL_HDR_SUPPORTED = "zsl-hdr-supported"; public static final String KEY_QC_LONGSHOT_SUPPORTED = "longshot-supported"; @@ -201,10 +217,6 @@ public class CameraSettings { private static final String KEY_QC_PICTURE_FORMAT = "picture-format-values"; public static final String KEY_VIDEO_ROTATION = "pref_camera_video_rotation_key"; - private static final String VIDEO_QUALITY_HIGH = "high"; - private static final String VIDEO_QUALITY_MMS = "mms"; - private static final String VIDEO_QUALITY_YOUTUBE = "youtube"; - //manual 3A keys and parameter strings public static final String KEY_MANUAL_EXPOSURE = "pref_camera_manual_exp_key"; @@ -268,28 +280,29 @@ public class CameraSettings { public static final String KEY_REFOCUS_PROMPT = "refocus-prompt"; - public static final String KEY_SHOW_MENU_HELP = "help_menu"; - public static final String KEY_REQUEST_PERMISSION = "request_permission"; public static final String KEY_SELFIE_FLASH = "pref_selfie_flash_key"; public static final String EXPOSURE_DEFAULT_VALUE = "0"; + public static final String VALUE_ON = "on"; + public static final String VALUE_OFF = "off"; public static final int CURRENT_VERSION = 5; public static final int CURRENT_LOCAL_VERSION = 2; - public static final int DEFAULT_VIDEO_DURATION = 0; // no limit - private static final int MMS_VIDEO_DURATION = (CamcorderProfile.get(CamcorderProfile.QUALITY_LOW) != null) ? - CamcorderProfile.get(CamcorderProfile.QUALITY_LOW).duration :30; - private static final int YOUTUBE_VIDEO_DURATION = 15 * 60; // 15 mins - private static final String TAG = "CameraSettings"; private final Context mContext; private final Parameters mParameters; - private final CameraInfo[] mCameraInfo; + private final CameraHolder.CameraInfo[] mCameraInfo; private final int mCameraId; + + public static String mKeyIso = null; + public static String mKeyIsoValues = null; + + private static boolean mSupportBokehMode = false; + private static final HashMap<Integer, String> VIDEO_ENCODER_TABLE = new HashMap<Integer, String>(); public static final HashMap<String, Integer> @@ -438,11 +451,30 @@ public class CameraSettings { } public CameraSettings(Activity activity, Parameters parameters, - int cameraId, CameraInfo[] cameraInfo) { + int cameraId, CameraHolder.CameraInfo[] cameraInfo) { mContext = activity; mParameters = parameters; mCameraId = cameraId; mCameraInfo = cameraInfo; + + // ISO + mKeyIso = mContext.getResources().getString(R.string.key_iso); + mKeyIsoValues = mContext.getResources().getString(R.string.key_iso_values); + + if (mKeyIso == null || mKeyIso.isEmpty()) { + mKeyIso = "iso"; + } else { + Log.d(TAG, "Using key for iso: " + mKeyIso); + } + + if (mKeyIsoValues == null || mKeyIsoValues.isEmpty()) { + mKeyIsoValues = "iso-values"; + } else { + Log.d(TAG, "Using key for iso-values: " + mKeyIsoValues); + } + + // Bokeh mode + mSupportBokehMode = mContext.getResources().getBoolean(R.bool.support_bokeh_mode); } public PreferenceGroup getPreferenceGroup(int preferenceRes) { @@ -453,35 +485,88 @@ public class CameraSettings { return group; } + public static List<String> getSupportedIsoValues(Parameters params) { + String isoValues = params.get(mKeyIsoValues); + if (isoValues == null) { + return null; + } + Log.d(TAG, "Supported iso values: " + isoValues); + return split(isoValues); + } + + public static String getISOValue(Parameters params) { + String iso = params.get(mKeyIso); + + if (iso == null) { + return null; + } + return iso; + } + + public static void setISOValue(Parameters params, String iso) { + params.set(mKeyIso, iso); + } + + public static List<String> getSupportedShutterSpeedValues(Parameters params) { + String shutterSpeedValues = params.get(KEY_SNAPCAM_SHUTTER_SPEED_MODES); + if (shutterSpeedValues == null) { + return null; + } + Log.d(TAG, "Supported shutter speed values: " + shutterSpeedValues); + return split(shutterSpeedValues); + } + public static String getSupportedHighestVideoQuality( - int cameraId, Parameters parameters) { + Context context, int cameraId, Parameters parameters) { // When launching the camera app first time, we will set the video quality // to the first one (i.e. highest quality) in the supported list List<String> supported = getSupportedVideoQualities(cameraId,parameters); assert (supported != null) : "No supported video quality is found"; + for (String candidate : context.getResources().getStringArray( + R.array.pref_video_quality_entryvalues)) { + if (supported.indexOf(candidate) >= 0) { + return candidate; + } + } + Log.w(TAG, "No supported video size matches, using the first reported size"); return supported.get(0); } - public static void initialCameraPictureSize( - Context context, Parameters parameters) { - // When launching the camera app first time, we will set the picture - // size to the first one in the list defined in "arrays.xml" and is also - // supported by the driver. + public static void initialCameraPictureSize(Context context, Parameters parameters) { + // set the picture size to the largest supported size List<Size> supported = parameters.getSupportedPictureSizes(); - if (supported == null) return; - for (String candidate : context.getResources().getStringArray( - R.array.pref_camera_picturesize_entryvalues)) { - if (setCameraPictureSize(candidate, supported, parameters)) { - SharedPreferences.Editor editor = ComboPreferences - .get(context).edit(); - editor.putString(KEY_PICTURE_SIZE, candidate); - editor.apply(); - return; - } + if (supported == null || supported.isEmpty()) { return; } + Size largest = getLargestSize(supported); + String candidate = largest.width + "x" + largest.height; + if (setCameraPictureSize(candidate, supported, parameters)) { + SharedPreferences.Editor editor = + ComboPreferences.get(context).edit(); + editor.putString(KEY_PICTURE_SIZE, candidate); + editor.apply(); + return; } Log.e(TAG, "No supported picture size found"); } + private static Size getLargestSize(List<Size> sizes) { + if (sizes == null || sizes.isEmpty()) { + return null; + } + + Size max = sizes.get(0); + int maxSize = max.width * max.height; + + for (Size candidate : sizes) { + int candidateSize = candidate.width * candidate.height; + if (candidateSize > maxSize) { + maxSize = candidateSize; + max = candidate; + } + } + + return max; + } + public static void removePreferenceFromScreen( PreferenceGroup group, String key) { removePreference(group, key); @@ -519,6 +604,14 @@ public class CameraSettings { return split(str); } + public static List<String> getSupportedFaceDetection(Parameters params) { + String str = params.get(KEY_QC_SUPPORTED_FACE_DETECTION); + if (str == null) { + return null; + } + return split(str); + } + public static List<String> getSupportedDISModes(Parameters params) { String str = params.get(KEY_QC_SUPPORTED_DIS_MODES); if (str == null) { @@ -591,14 +684,6 @@ public class CameraSettings { return split(str); } - public static List<String> getSupportedHDRNeed1x(Parameters params) { - String str = params.get(KEY_SNAPCAM_SUPPORTED_HDR_NEED_1X); - if (str == null) { - return null; - } - return split(str); - } - public List<String> getSupportedAdvancedFeatures(Parameters params) { String str = params.get(KEY_QC_SUPPORTED_AF_BRACKETING_MODES); str += ',' + params.get(KEY_QC_SUPPORTED_CF_MODES); @@ -700,7 +785,7 @@ public class CameraSettings { private List<String> getSupportedPictureFormatLists() { String str = mParameters.get(KEY_QC_PICTURE_FORMAT); if (str == null) { - str = "jpeg,raw"; // if not set, fall back to default behavior + return null; } return split(str); } @@ -731,21 +816,38 @@ public class CameraSettings { ArrayList<String> supported = new ArrayList<String>(); int zoomMaxIdx = params.getMaxZoom(); List <Integer> zoomRatios = params.getZoomRatios(); - int zoomMax = zoomRatios.get(zoomMaxIdx)/100; - - for (int zoomLevel = 0; zoomLevel <= zoomMax; zoomLevel++) { - supported.add(String.valueOf(zoomLevel)); + if (zoomRatios != null ) { + int zoomMax = zoomRatios.get(zoomMaxIdx)/100; + for (int zoomLevel = 0; zoomLevel <= zoomMax; zoomLevel++) { + supported.add(String.valueOf(zoomLevel)); + } } return supported; } + private static ListPreference removeLeadingISO(ListPreference pref) { + CharSequence entryValues[] = pref.getEntryValues(); + if (entryValues.length > 0) { + CharSequence modEntryValues[] = new CharSequence[entryValues.length]; + for (int i = 0, len = entryValues.length; i < len; i++) { + String isoValue = entryValues[i].toString(); + if (isoValue.startsWith("ISO") && !isoValue.startsWith("ISO_")) { + isoValue = isoValue.replaceAll("ISO", ""); + } + modEntryValues[i] = isoValue; + } + pref.setEntryValues(modEntryValues); + } + return pref; + } + private void qcomInitPreferences(PreferenceGroup group){ //Qcom Preference add here ListPreference powerMode = group.findPreference(KEY_POWER_MODE); ListPreference zsl = group.findPreference(KEY_ZSL); ListPreference colorEffect = group.findPreference(KEY_COLOR_EFFECT); + ListPreference camcorderColorEffect = group.findPreference(KEY_VIDEOCAMERA_COLOR_EFFECT); ListPreference faceDetection = group.findPreference(KEY_FACE_DETECTION); - ListPreference touchAfAec = group.findPreference(KEY_TOUCH_AF_AEC); ListPreference selectableZoneAf = group.findPreference(KEY_SELECTABLE_ZONE_AF); ListPreference saturation = group.findPreference(KEY_SATURATION); ListPreference contrast = group.findPreference(KEY_CONTRAST); @@ -753,6 +855,7 @@ public class CameraSettings { ListPreference autoExposure = group.findPreference(KEY_AUTOEXPOSURE); ListPreference antiBanding = group.findPreference(KEY_ANTIBANDING); ListPreference mIso = group.findPreference(KEY_ISO); + ListPreference mShutterSpeed = group.findPreference(KEY_SHUTTER_SPEED); ListPreference lensShade = group.findPreference(KEY_LENSSHADING); ListPreference histogram = group.findPreference(KEY_HISTOGRAM); ListPreference denoise = group.findPreference(KEY_DENOISE); @@ -767,7 +870,6 @@ public class CameraSettings { ListPreference longShot = group.findPreference(KEY_LONGSHOT); ListPreference auto_hdr = group.findPreference(KEY_AUTO_HDR); ListPreference hdr_mode = group.findPreference(KEY_HDR_MODE); - ListPreference hdr_need_1x = group.findPreference(KEY_HDR_NEED_1X); ListPreference cds_mode = group.findPreference(KEY_CDS_MODE); ListPreference video_cds_mode = group.findPreference(KEY_VIDEO_CDS_MODE); ListPreference tnr_mode = group.findPreference(KEY_TNR_MODE); @@ -788,6 +890,12 @@ public class CameraSettings { } } + // Remove leading ISO from iso-values + boolean isoValuesUseNumbers = mContext.getResources().getBoolean(R.bool.iso_values_use_numbers); + if (isoValuesUseNumbers && mIso != null) { + mIso = removeLeadingISO(mIso); + } + if (bokehMode != null) { if (!isBokehModeSupported(mParameters)) { removePreference(group, bokehMode.getKey()); @@ -801,11 +909,6 @@ public class CameraSettings { } } - if (hdr_need_1x != null) { - filterUnsupportedOptions(group, - hdr_need_1x, getSupportedHDRNeed1x(mParameters)); - } - if (hdr_mode != null) { filterUnsupportedOptions(group, hdr_mode, getSupportedHDRModes(mParameters)); @@ -833,11 +936,6 @@ public class CameraSettings { ListPreference videoRotation = group.findPreference(KEY_VIDEO_ROTATION); - if (touchAfAec != null) { - filterUnsupportedOptions(group, - touchAfAec, ParametersWrapper.getSupportedTouchAfAec(mParameters)); - } - if (!ParametersWrapper.isPowerModeSupported(mParameters) && powerMode != null) { removePreference(group, powerMode.getKey()); } @@ -847,9 +945,29 @@ public class CameraSettings { selectableZoneAf, ParametersWrapper.getSupportedSelectableZoneAf(mParameters)); } + if (saturation != null && !CameraUtil.isSupported(mParameters, "saturation") && + !CameraUtil.isSupported(mParameters, "max-saturation")) { + removePreference(group, saturation.getKey()); + } + + if (contrast != null && !CameraUtil.isSupported(mParameters, "contrast") && + !CameraUtil.isSupported(mParameters, "max-contrast")) { + removePreference(group, contrast.getKey()); + } + + if (sharpness != null && !CameraUtil.isSupported(mParameters, "sharpness") && + !CameraUtil.isSupported(mParameters, "max-sharpness")) { + removePreference(group, sharpness.getKey()); + } + if (mIso != null) { filterUnsupportedOptions(group, - mIso, ParametersWrapper.getSupportedIsoValues(mParameters)); + mIso, getSupportedIsoValues(mParameters)); + } + + if (mShutterSpeed != null) { + filterUnsupportedOptions(group, + mShutterSpeed, getSupportedShutterSpeedValues(mParameters)); } if (redeyeReduction != null) { @@ -872,6 +990,11 @@ public class CameraSettings { colorEffect, mParameters.getSupportedColorEffects()); } + if (camcorderColorEffect != null) { + filterUnsupportedOptions(group, + camcorderColorEffect, mParameters.getSupportedColorEffects()); + } + if (aeBracketing != null) { filterUnsupportedOptions(group, aeBracketing, getSupportedAEBracketingModes(mParameters)); @@ -887,14 +1010,24 @@ public class CameraSettings { faceRC, getSupportedFaceRecognitionModes(mParameters)); } + if (faceDetection != null) { + filterUnsupportedOptions(group, + faceDetection, getSupportedFaceDetection(mParameters)); + } + if (autoExposure != null) { filterUnsupportedOptions(group, autoExposure, ParametersWrapper.getSupportedAutoexposure(mParameters)); } - if(videoSnapSize != null) { - filterUnsupportedOptions(group, videoSnapSize, getSupportedVideoSnapSizes(mParameters)); - filterSimilarPictureSize(group, videoSnapSize); + if (videoSnapSize != null) { + if (CameraUtil.isVideoSnapshotSupported(mParameters)) { + filterUnsupportedOptions(group, videoSnapSize, + getSupportedVideoSnapSizes(mParameters)); + filterSimilarPictureSize(group, videoSnapSize); + } else { + removePreference(group, videoSnapSize.getKey()); + } } if (histogram!= null) { @@ -915,6 +1048,10 @@ public class CameraSettings { removePreference(group, longShot.getKey()); } + if (auto_hdr != null && !CameraUtil.isAutoHDRSupported(mParameters)) { + removePreference(group, auto_hdr.getKey()); + } + if (videoRotation != null) { filterUnsupportedOptions(group, videoRotation, ParametersWrapper.getSupportedVideoRotationValues(mParameters)); @@ -960,6 +1097,7 @@ public class CameraSettings { ListPreference sceneMode = group.findPreference(KEY_SCENE_MODE); ListPreference flashMode = group.findPreference(KEY_FLASH_MODE); ListPreference focusMode = group.findPreference(KEY_FOCUS_MODE); + ListPreference videoFocusMode = group.findPreference(KEY_VIDEOCAMERA_FOCUS_MODE); IconListPreference exposure = (IconListPreference) group.findPreference(KEY_EXPOSURE); IconListPreference cameraIdPref = @@ -970,11 +1108,13 @@ public class CameraSettings { ListPreference cameraHdr = group.findPreference(KEY_CAMERA_HDR); ListPreference disMode = group.findPreference(KEY_DIS); ListPreference cameraHdrPlus = group.findPreference(KEY_CAMERA_HDR_PLUS); + ListPreference powerShutter = group.findPreference(KEY_POWER_SHUTTER); ListPreference videoHfrMode = group.findPreference(KEY_VIDEO_HIGH_FRAME_RATE); ListPreference seeMoreMode = group.findPreference(KEY_SEE_MORE); ListPreference videoEncoder = group.findPreference(KEY_VIDEO_ENCODER); ListPreference noiseReductionMode = group.findPreference(KEY_NOISE_REDUCTION); + ListPreference savePath = group.findPreference(KEY_CAMERA_SAVEPATH); // Since the screen could be loaded from different resources, we need // to check if the preference is available here @@ -989,9 +1129,9 @@ public class CameraSettings { getSupportedSeeMoreModes(mParameters)); } - if ((videoHfrMode != null) && - (ParametersWrapper.getSupportedHfrSizes(mParameters) == null)) { - filterUnsupportedOptions(group, videoHfrMode, null); + if (videoHfrMode != null) { + filterUnsupportedOptions(group, videoHfrMode, getSupportedHighFrameRateModes( + mParameters)); } if (videoQuality != null) { @@ -1004,10 +1144,12 @@ public class CameraSettings { } if (pictureSize != null) { - filterUnsupportedOptions(group, pictureSize, sizeListToStringList( - mParameters.getSupportedPictureSizes())); - filterSimilarPictureSize(group, pictureSize); + formatPictureSizes(pictureSize, + fromLegacySizes(mParameters.getSupportedPictureSizes()), mContext); + resetIfInvalid(pictureSize); + } + if (whiteBalance != null) { filterUnsupportedOptions(group, whiteBalance, mParameters.getSupportedWhiteBalance()); @@ -1016,7 +1158,7 @@ public class CameraSettings { if (chromaFlash != null) { List<String> supportedAdvancedFeatures = getSupportedAdvancedFeatures(mParameters); - if (!CameraUtil.isSupported( + if (hasChromaFlashScene(mContext) || !CameraUtil.isSupported( mContext.getString(R.string .pref_camera_advanced_feature_value_chromaflash_on), supportedAdvancedFeatures)) { @@ -1044,6 +1186,13 @@ public class CameraSettings { supportedSceneModes.add(mContext.getString(R.string .pref_camera_advanced_feature_value_optizoom_on)); } + if (hasChromaFlashScene(mContext) && CameraUtil.isSupported( + mContext.getString(R.string + .pref_camera_advanced_feature_value_chromaflash_on), + supportedAdvancedFeatures)) { + supportedSceneModes.add(mContext.getString(R.string + .pref_camera_advanced_feature_value_chromaflash_on)); + } filterUnsupportedOptions(group, sceneMode, supportedSceneModes); } if (flashMode != null) { @@ -1055,10 +1204,12 @@ public class CameraSettings { disMode, getSupportedDISModes(mParameters)); } if (focusMode != null) { - if (!CameraUtil.isFocusAreaSupported(mParameters)) { - filterUnsupportedOptions(group, - focusMode, mParameters.getSupportedFocusModes()); - } + filterUnsupportedOptions(group, + focusMode, mParameters.getSupportedFocusModes()); + } + if (videoFocusMode != null) { + filterUnsupportedOptions(group, + videoFocusMode, mParameters.getSupportedFocusModes()); } if (videoFlashMode != null) { filterUnsupportedOptions(group, @@ -1083,12 +1234,15 @@ public class CameraSettings { !GcamHelper.hasGcamCapture() || isFrontCamera)) { removePreference(group, cameraHdrPlus.getKey()); } + if (powerShutter != null && CameraUtil.hasCameraKey()) { + removePreference(group, powerShutter.getKey()); + } if (PersistUtil.isSaveInSdEnabled()) { final String CAMERA_SAVEPATH_SDCARD = "1"; final int CAMERA_SAVEPATH_SDCARD_IDX = 1; final int CAMERA_SAVEPATH_PHONE_IDX = 0; - ListPreference savePath = group.findPreference(KEY_CAMERA_SAVEPATH); + SharedPreferences pref = group.getSharedPreferences(); String savePathValue = null; if (pref != null) { @@ -1104,7 +1258,13 @@ public class CameraSettings { Log.d(TAG, "set Phone as save path when sdCard is unavailable."); savePath.setValueIndex(CAMERA_SAVEPATH_PHONE_IDX); } - } + } + } + if (savePath != null) { + Log.d(TAG, "check storage menu " + SDCard.instance().isWriteable()); + if (!SDCard.instance().isWriteable()) { + removePreference(group, savePath.getKey()); + } } qcomInitPreferences(group); @@ -1152,9 +1312,11 @@ public class CameraSettings { return; } -// if (numOfCameras > 2 ) { -// numOfCameras = 2; -// } + if (!mSupportBokehMode) { + if (numOfCameras > 2) { + numOfCameras = 2; + } + } CharSequence[] entryValues = new CharSequence[numOfCameras]; for (int i = 0; i < numOfCameras; ++i) { @@ -1211,7 +1373,7 @@ public class CameraSettings { return false; } - private static void resetIfInvalid(ListPreference pref) { + 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) { @@ -1280,17 +1442,20 @@ public class CameraSettings { version = 2; } if (version == 2) { - editor.putString(KEY_RECORD_LOCATION, - pref.getBoolean(KEY_RECORD_LOCATION, false) - ? RecordLocationPreference.VALUE_ON - : RecordLocationPreference.VALUE_NONE); + try { + boolean value = pref.getBoolean(KEY_RECORD_LOCATION, false); + editor.putString(KEY_RECORD_LOCATION, + value ? RecordLocationPreference.VALUE_ON + : RecordLocationPreference.VALUE_NONE); + } catch (ClassCastException e) { + Log.e(TAG, "error convert record location", e); + } version = 3; } if (version == 3) { // Just use video quality to replace it and // ignore the current settings. editor.remove("pref_camera_videoquality_key"); - editor.remove("pref_camera_video_duration_key"); } editor.putInt(KEY_VERSION, CURRENT_VERSION); @@ -1370,124 +1535,61 @@ public class CameraSettings { initialCameraPictureSize(context, parameters); writePreferredCameraId(preferences, currentCameraId); } - private static boolean checkSupportedVideoQuality(Parameters parameters,int width, int height){ - List <Size> supported = parameters.getSupportedVideoSizes(); - int flag = 0; - for (Size size : supported){ - //since we are having two profiles with same height, we are checking with height - if (size.height == 480) { - if (size.height == height && size.width == width) { - flag = 1; - break; - } - } else { - if (size.width == width) { - flag = 1; - break; - } - } - } - if (flag == 1) - return true; - return false; - } - private static ArrayList<String> getSupportedVideoQuality(int cameraId,Parameters parameters) { + public static List<String> getSupportedHighFrameRateModes(Parameters params) { ArrayList<String> supported = new ArrayList<String>(); - // Check for supported quality - if (ApiHelper.HAS_FINE_RESOLUTION_QUALITY_LEVELS) { - getFineResolutionQuality(supported,cameraId,parameters); - } else { - supported.add(Integer.toString(CamcorderProfile.QUALITY_HIGH)); - CamcorderProfile high = CamcorderProfile.get( - cameraId, CamcorderProfile.QUALITY_HIGH); - CamcorderProfile low = CamcorderProfile.get( - cameraId, CamcorderProfile.QUALITY_LOW); - if (high.videoFrameHeight * high.videoFrameWidth > - low.videoFrameHeight * low.videoFrameWidth) { - supported.add(Integer.toString(CamcorderProfile.QUALITY_LOW)); - } - } - - return supported; - } - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) - private static void getFineResolutionQuality(ArrayList<String> supported, - int cameraId,Parameters parameters) { - - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfileWrapper.QUALITY_4KDCI)) { - if (checkSupportedVideoQuality(parameters,4096,2160)) { - supported.add(Integer.toString(CamcorderProfileWrapper.QUALITY_4KDCI)); - } - } + List<String> supportedModes = params.getSupportedVideoHighFrameRateModes(); + String hsr = params.get(KEY_VIDEO_HSR); - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_2160P)) { - if (checkSupportedVideoQuality(parameters,3840,2160)) { - supported.add(Integer.toString(CamcorderProfile.QUALITY_2160P)); - } - } - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P)) { - if (checkSupportedVideoQuality(parameters,1920,1080)){ - supported.add(Integer.toString(CamcorderProfile.QUALITY_1080P)); - } - } - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) { - if (checkSupportedVideoQuality(parameters,1280,720)){ - supported.add(Integer.toString(CamcorderProfile.QUALITY_720P)); - } - } - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) { - if (checkSupportedVideoQuality(parameters,720,480)){ - supported.add(Integer.toString(CamcorderProfile.QUALITY_480P)); - } - } - - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfileWrapper.QUALITY_VGA)) { - if (checkSupportedVideoQuality(parameters,640,480)){ - supported.add(Integer.toString(CamcorderProfileWrapper.QUALITY_VGA)); - } + if (supportedModes == null) { + return null; } - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_CIF)) { - if (checkSupportedVideoQuality(parameters,352,288)){ - supported.add(Integer.toString(CamcorderProfile.QUALITY_CIF)); - } - } - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QVGA)) { - if (checkSupportedVideoQuality(parameters,320,240)){ - supported.add(Integer.toString(CamcorderProfile.QUALITY_QVGA)); - } - } - if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_QCIF)) { - if (checkSupportedVideoQuality(parameters,176,144)){ - supported.add(Integer.toString(CamcorderProfile.QUALITY_QCIF)); - } + for (String highFrameRateMode : supportedModes) { + if (highFrameRateMode.equals("off")) { + supported.add(highFrameRateMode); + } else { + supported.add("hfr" + highFrameRateMode); + if (hsr != null) { + supported.add("hsr" + highFrameRateMode); + } + } } - + return supported; } - public static ArrayList<String> getSupportedVideoQualities(int cameraId,Parameters parameters) { + public static ArrayList<String> getSupportedVideoQualities(int cameraId, + Parameters parameters) { ArrayList<String> supported = new ArrayList<String>(); - List<String> temp = sizeListToStringList(parameters.getSupportedVideoSizes()); + List<Size> supportedVideoSizes = parameters.getSupportedVideoSizes(); + List<String> temp; + if (supportedVideoSizes == null) { + // video-size not specified in parameter list, + // assume all profiles in media_profiles are supported. + temp = new ArrayList<String>(); + temp.add("4096x2160"); + temp.add("3840x2160"); + temp.add("1920x1080"); + temp.add("1280x720"); + temp.add("720x480"); + temp.add("640x480"); + temp.add("352x288"); + temp.add("320x240"); + temp.add("176x144"); + } else { + temp = sizeListToStringList(supportedVideoSizes); + } + for (String videoSize : temp) { if (VIDEO_QUALITY_TABLE.containsKey(videoSize)) { int profile = VIDEO_QUALITY_TABLE.get(videoSize); if (CamcorderProfile.hasProfile(cameraId, profile)) { - supported.add(videoSize); + supported.add(videoSize); } } } return supported; } - public static int getVideoDurationInMillis(String quality) { - if (VIDEO_QUALITY_MMS.equals(quality)) { - return MMS_VIDEO_DURATION * 1000; - } else if (VIDEO_QUALITY_YOUTUBE.equals(quality)) { - return YOUTUBE_VIDEO_DURATION * 1000; - } - return DEFAULT_VIDEO_DURATION * 1000; - } public static boolean isInternalPreviewSupported(Parameters params) { boolean ret = false; @@ -1559,6 +1661,11 @@ public class CameraSettings { public static boolean isBokehModeSupported(Parameters params) { boolean ret = false; + + if (!mSupportBokehMode) { + return ret; + } + if (null != params) { String val = params.get(KEY_QC_IS_BOKEH_MODE_SUPPORTED); if ("1".equals(val)) { @@ -1570,6 +1677,11 @@ public class CameraSettings { public static boolean isBokehMPOSupported(Parameters params) { boolean ret = false; + + if (!mSupportBokehMode) { + return ret; + } + if (null != params) { String val = params.get(KEY_QC_IS_BOKEH_MPO_SUPPORTED); if ("1".equals(val)) { @@ -1587,4 +1699,185 @@ public class CameraSettings { Log.d(TAG,"getSupportedDegreesOfBlur str =" +str); return split(str); } + + // common aspect ratios used for bucketing camera picture sizes + private enum AspectRatio { + FourThree(1.27, 1.42, R.string.pref_camera_aspectratio_43), + ThreeTwo(1.42, 1.55, R.string.pref_camera_aspectratio_32), + SixteenTen(1.55, 1.63, R.string.pref_camera_aspectratio_1610), + FiveThree(1.63, 1.73, R.string.pref_camera_aspectratio_53), + SixteenNine(1.73, 1.81, R.string.pref_camera_aspectratio_169), + OneOne(0.95, 1.05, R.string.pref_camera_aspectratio_11), + Wide(1.81, Float.MAX_VALUE, R.string.pref_camera_aspectratio_wide), + Other(Float.MIN_VALUE, 1.27, 0); + + public double min; + public double max; + public int resourceId; + + AspectRatio(double min, double max, int rid) { + this.min = min; + this.max = max; + this.resourceId = rid; + } + + boolean contains(double ratio) { + return (ratio >= min) && (ratio < max); + } + + static AspectRatio of(int width, int height) { + double ratio = ((double) width) / height; + for (AspectRatio aspect : values()) { + if (aspect.contains(ratio)) { return aspect; } + } + return null; + } + } + + // track a camera picture size along with some derived info + private static class SizeEntry implements Comparable<SizeEntry> { + AspectRatio ratio; + android.util.Size size; + int pixels; + + SizeEntry(android.util.Size size) { + this.ratio = AspectRatio.of(size.getWidth(), size.getHeight()); + this.size = size; + this.pixels = size.getWidth() * size.getHeight(); + } + + @Override + public int compareTo(SizeEntry another) { + return another.pixels - pixels; + } + + String resolution() { return size.getWidth()+"x"+size.getHeight(); } + + String formatted(Context ctx) { + double pixelCount = pixels / 1000000d; // compute megapixels + + if (pixelCount > 1.9 && pixelCount < 2.0) { //conventional rounding + pixelCount = 2.0; + } + + pixelCount = adjustForLocale(pixelCount); // some locales group differently + + if (pixelCount > 0.1) { + String ratioString = ratio.resourceId == 0 + ? resolution() : ctx.getString(ratio.resourceId); + return ctx.getString(R.string.pref_camera_megapixel_format, + ratioString, pixelCount); + } else { + // for super tiny stuff, just give the raw resolution + return resolution(); + } + } + + private static final String KOREAN = Locale.KOREAN.getLanguage(); + private static final String CHINESE = Locale.CHINESE.getLanguage(); + + private double adjustForLocale(double megapixels) { + Locale locale = Locale.getDefault(); + if (locale == null) { return megapixels; } + String language = locale.getLanguage(); + // chinese and korean locales prefer to count by ten thousands + // instead of by millions - so we multiply the megapixels by 100 + // (with a little rounding on the way) + if (KOREAN.equals(language) || CHINESE.equals(language)) { + megapixels = Math.round(megapixels * 10); + return megapixels * 10; // wà n + } + return megapixels; + } + } + + static List<android.util.Size> fromLegacySizes(List<Size> oldSizes) { + final List<android.util.Size> sizes = new ArrayList<>(); + if (oldSizes == null || oldSizes.size() == 0) { + return sizes; + } + for (Size oldSize : oldSizes) { + sizes.add(new android.util.Size(oldSize.width, oldSize.height)); + } + return sizes; + } + + static void formatPictureSizes( + ListPreference pictureSize, List<android.util.Size> supported, Context ctx) { + if (supported == null || supported.isEmpty()) { return; } + + // convert list of sizes to list of "size entries" + List<SizeEntry> sizes = new ArrayList<SizeEntry>(supported.size()); + for (android.util.Size candidate : supported) { sizes.add(new SizeEntry(candidate)); } + + // sort descending by pixel size + Collections.sort(sizes); + + // trim really small sizes but never leave less than six choices + int minimum = ctx.getResources().getInteger(R.integer.minimum_picture_size); + while (sizes.size() > 6) { + int lastIndex = sizes.size() - 1; + SizeEntry last = sizes.get(lastIndex); + if (last.pixels < minimum) { sizes.remove(lastIndex); } + else { break; } + } + // re-sort into aspect ratio buckets + MultiMap<AspectRatio,SizeEntry> buckets = new MultiMap<AspectRatio,SizeEntry>(); + for (SizeEntry size : sizes) { buckets.put(size.ratio, size); } + + // build the final lists - group by aspect ratio, with those + // buckets having the largest image sizes positioned first + List<String> entries = new ArrayList<String>(buckets.size()); + List<String> entryValues = new ArrayList<String>(buckets.size()); + while (!buckets.isEmpty()) { + // find the bucket with the largest first element + int maxSize = 0; + AspectRatio chosenKey = null; + for (AspectRatio ratio : buckets.keySet()) { + int size = buckets.get(ratio).get(0).pixels; + if (size > maxSize) { + maxSize = size; + chosenKey = ratio; + } + } + + List<SizeEntry> bucket = buckets.remove(chosenKey); + + // trim chosen bucket of similarly sized entries, but + // never leave less that three + int index = 0; + while (bucket.size() > 3 && bucket.size() > index + 1) { + SizeEntry current = bucket.get(index); + SizeEntry next = bucket.get(index + 1); + // if the two buckets differ in size by less than 30% + // remove the smaller one, otherwise advance through the list + if (((double) current.pixels) / next.pixels < 1.3) { + bucket.remove(index + 1); + } else { + index++; + } + } + + // transfer chosen, trimmed bucket to final list + for (SizeEntry size : bucket) { + entryValues.add(size.resolution()); + entries.add(size.formatted(ctx)); + } + } + + pictureSize.setEntries(entries.toArray(new String[entries.size()])); + pictureSize.setEntryValues(entryValues.toArray(new String[entryValues.size()])); + } + + public static boolean hasChromaFlashScene(Context context) { + String[] sceneModes = context.getResources().getStringArray( + R.array.pref_camera_scenemode_entryvalues); + for (String mode : sceneModes) { + if (mode.equals(context.getResources().getString(R.string + .pref_camera_advanced_feature_value_chromaflash_on))) { + return true; + } + } + return false; + } } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index a55c9e451..6f6a32a80 100755 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -114,6 +114,7 @@ import com.android.camera.imageprocessor.filter.SharpshooterFilter; import com.android.camera.imageprocessor.filter.StillmoreFilter; import com.android.camera.imageprocessor.filter.UbifocusFilter; import com.android.camera.ui.CountDownView; +import com.android.camera.ui.focus.FocusRing; import com.android.camera.ui.ModuleSwitcher; import com.android.camera.ui.ProMode; import com.android.camera.ui.RotateTextToast; @@ -412,7 +413,7 @@ public class CaptureModule implements CameraModule, PhotoController, private float mZoomValue = 1f; private FocusStateListener mFocusStateListener; private LocationManager mLocationManager; - private SettingsManager mSettingsManager; + public SettingsManager mSettingsManager; private long SECONDARY_SERVER_MEM; private boolean mLongshotActive = false; private CameraCharacteristics mMainCameraCharacteristics; @@ -684,7 +685,12 @@ public class CaptureModule implements CameraModule, PhotoController, private void detectHDRMode(CaptureResult result, int id) { String value = mSettingsManager.getValue(SettingsManager.KEY_SCENE_MODE); String autoHdr = mSettingsManager.getValue(SettingsManager.KEY_AUTO_HDR); - Byte hdrScene = result.get(CaptureModule.isHdr); + Byte hdrScene = null; + try { + hdrScene = result.get(CaptureModule.isHdr); + } catch (IllegalArgumentException e) { + // Ignore exception. + } if (value == null || hdrScene == null) return; mAutoHdrEnable = false; if (autoHdr != null && "enable".equals(autoHdr) && "0".equals(value) && hdrScene == 1) { @@ -767,7 +773,12 @@ public class CaptureModule implements CameraModule, PhotoController, } } if (SettingsManager.getInstance().isStatsVisualizerSupport() == 3) { - int[] histogramStats = result.get(CaptureModule.histogramStats); + int[] histogramStats = null; + try { + histogramStats = result.get(CaptureModule.histogramStats); + } catch (IllegalArgumentException e) { + // Ignore exception. + } if (histogramStats != null && mHiston) { /*The first element in the array stores max hist value . Stats data begin from second value*/ @@ -2168,12 +2179,6 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG, "captureStillPictureForLongshot onCaptureCompleted: " + id); if (mLongshotActive) { checkAndPlayShutterSound(id); - mActivity.runOnUiThread(new Runnable() { - @Override - public void run() { - mUI.doShutterAnimation(); - } - }); } } @@ -2182,14 +2187,6 @@ public class CaptureModule implements CameraModule, PhotoController, CaptureRequest request, CaptureFailure result) { Log.d(TAG, "captureStillPictureForLongshot onCaptureFailed: " + id); - if (mLongshotActive) { - mActivity.runOnUiThread(new Runnable() { - @Override - public void run() { - mUI.doShutterAnimation(); - } - }); - } } @Override @@ -2345,12 +2342,24 @@ public class CaptureModule implements CameraModule, PhotoController, * @param width The width of available size for camera preview * @param height The height of available size for camera preview */ - private void setUpCameraOutputs(int imageFormat) { + private void setUpCameraOutputs(boolean isFilterOrZslEnabled) { + int imageFormat = ImageFormat.JPEG; + 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++) { + int cameraIdListLength = cameraIdList.length; + + if (cameraIdListLength > MAX_NUM_CAM) + Log.w(TAG, "Number of available cameras (" + cameraIdListLength + ") exceeds " + + "max supported cameras (" + MAX_NUM_CAM + ")"); + + for (int i = 0; i < cameraIdListLength; i++) { + if (i >= MAX_NUM_CAM) { + Log.w(TAG, "Skipping set up for camera with id " + i); + break; + } String cameraId = cameraIdList[i]; CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); @@ -2382,6 +2391,23 @@ public class CaptureModule implements CameraModule, PhotoController, } mCameraId[i] = cameraId; + // Set ImageFormat for ZSL + if (isFilterOrZslEnabled) { + for (int capability : capabilities) { + // YUV has higher priority + if (capability == CameraCharacteristics + .REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING) { + Log.d(TAG, "ImageFormat: YUV_420_888"); + imageFormat = ImageFormat.YUV_420_888; + break; + } else if (capability == CameraCharacteristics + .REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING) { + Log.d(TAG, "ImageFormat: PRIVATE"); + imageFormat = ImageFormat.PRIVATE; + } + } + } + if (isClearSightOn()) { if(i == getMainCameraId()) { // ClearSightImageProcessor.getInstance().init(map, mPictureSize.getWidth(), @@ -2543,7 +2569,7 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void run() { if (mUI.getCurrentProMode() != ProMode.MANUAL_MODE) - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); } }); } @@ -3120,15 +3146,12 @@ public class CaptureModule implements CameraModule, PhotoController, mFrameProcessor.onOpen(getFrameProcFilterId(), mPreviewSize); } - if(mPostProcessor.isZSLEnabled()) { - mChosenImageFormat = ImageFormat.PRIVATE; - } else if(mPostProcessor.isFilterOn() || getFrameFilters().size() != 0 || mPostProcessor.isSelfieMirrorOn()) { - mChosenImageFormat = ImageFormat.YUV_420_888; + if (mPostProcessor.isFilterOn() || getFrameFilters().size() != 0 || + mPostProcessor.isSelfieMirrorOn()) { + setUpCameraOutputs(true); } else { - mChosenImageFormat = ImageFormat.JPEG; + setUpCameraOutputs(false); } - setUpCameraOutputs(mChosenImageFormat); - } @Override @@ -3150,27 +3173,10 @@ public class CaptureModule implements CameraModule, PhotoController, openProcessors(); Message msg = Message.obtain(); msg.what = OPEN_CAMERA; - if (isBackCamera()) { - switch (getCameraMode()) { - case DUAL_MODE: - case BAYER_MODE: - msg.arg1 = BAYER_ID; - mCameraHandler.sendMessage(msg); - break; - case MONO_MODE: - msg.arg1 = MONO_ID; - mCameraHandler.sendMessage(msg); - break; - case SWITCH_MODE: - msg.arg1 = SWITCH_ID; - mCameraHandler.sendMessage(msg); - break; - } - } else { - int cameraId = SWITCH_ID == -1? FRONT_ID : SWITCH_ID; - msg.arg1 = cameraId; - mCameraHandler.sendMessage(msg); - } + int cameraId = getMainCameraId(); + msg.arg1 = cameraId; + mCameraHandler.sendMessage(msg); + if (mDeepPortraitMode) { mUI.startDeepPortraitMode(mPreviewSize); if (mUI.getGLCameraPreview() != null) { @@ -3253,9 +3259,6 @@ public class CaptureModule implements CameraModule, PhotoController, switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: - if (CameraUtil.volumeKeyShutterDisable(mActivity)) { - return false; - } case KeyEvent.KEYCODE_FOCUS: if (mFirstTimeInitialized) { if (event.getRepeatCount() == 0) { @@ -3288,8 +3291,7 @@ public class CaptureModule implements CameraModule, PhotoController, switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: - if (mFirstTimeInitialized - && !CameraUtil.volumeKeyShutterDisable(mActivity)) { + if (mFirstTimeInitialized) { onShutterButtonClick(); return true; } @@ -3478,10 +3480,10 @@ public class CaptureModule implements CameraModule, PhotoController, int[] newXY = {x, y}; if (mUI.isOverControlRegion(newXY)) return; if (!mUI.isOverSurfaceView(newXY)) return; - mUI.setFocusPosition(x, y); + mUI.getFocusRing().startActiveFocus(); + mUI.getFocusRing().setFocusLocation(x, y); x = newXY[0]; y = newXY[1]; - mUI.onFocusStarted(); if (isBackCamera()) { switch (getCameraMode()) { case DUAL_MODE: @@ -3795,7 +3797,9 @@ public class CaptureModule implements CameraModule, PhotoController, mSupportedMaxPictureSize = prevSizes[0]; Size[] rawSize = mSettingsManager.getSupportedOutputSize(getMainCameraId(), ImageFormat.RAW10); - mSupportedRawPictureSize = rawSize[0]; + if (rawSize != null && rawSize.length > 0) { + mSupportedRawPictureSize = rawSize[0]; + } mPreviewSize = getOptimalPreviewSize(mPictureSize, prevSizes); Size[] thumbSizes = mSettingsManager.getSupportedThumbnailSizes(getMainCameraId()); mPictureThumbSize = getOptimalPreviewSize(mPictureSize, thumbSizes); // get largest thumb size @@ -3880,6 +3884,10 @@ public class CaptureModule implements CameraModule, PhotoController, private void updateMaxVideoDuration() { String minutesStr = mSettingsManager.getValue(SettingsManager.KEY_VIDEO_DURATION); + if (minutesStr == null) { + mMaxVideoDurationInMs = 0; + return; + } int minutes = Integer.parseInt(minutesStr); if (minutes == -1) { // User wants lowest, set 30s */ @@ -3957,7 +3965,7 @@ public class CaptureModule implements CameraModule, PhotoController, return; } - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); mUI.resetPauseButton(); mRecordingTotalTime = 0L; mRecordingStartTime = SystemClock.uptimeMillis(); @@ -4009,7 +4017,7 @@ public class CaptureModule implements CameraModule, PhotoController, mHandler.post(new Runnable() { @Override public void run() { - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); mUI.resetPauseButton(); mRecordingTotalTime = 0L; mRecordingStartTime = SystemClock.uptimeMillis(); @@ -4143,7 +4151,7 @@ public class CaptureModule implements CameraModule, PhotoController, try { setUpMediaRecorder(cameraId); - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); mUI.hideUIwhileRecording(); mCameraHandler.removeMessages(CANCEL_TOUCH_FOCUS, mCameraId[cameraId]); mState[cameraId] = STATE_PREVIEW; @@ -4238,7 +4246,7 @@ public class CaptureModule implements CameraModule, PhotoController, restartSession(true); return; } - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); mUI.resetPauseButton(); mRecordingTotalTime = 0L; mRecordingStartTime = SystemClock.uptimeMillis(); @@ -5256,6 +5264,8 @@ public class CaptureModule implements CameraModule, PhotoController, } private void applyInstantAEC(CaptureRequest.Builder request) { + if (!VendorTagUtil.isSupported(request, CaptureModule.INSTANT_AEC_MODE)) + return; String value = mSettingsManager.getValue(SettingsManager.KEY_INSTANT_AEC); if (value == null || value.equals("0")) return; @@ -5264,6 +5274,8 @@ public class CaptureModule implements CameraModule, PhotoController, } private void applySaturationLevel(CaptureRequest.Builder request) { + if (!VendorTagUtil.isSupported(request, CaptureModule.SATURATION)) + return; String value = mSettingsManager.getValue(SettingsManager.KEY_SATURATION_LEVEL); if (value != null) { int intValue = Integer.parseInt(value); @@ -5340,6 +5352,8 @@ public class CaptureModule implements CameraModule, PhotoController, } private void applyExposureMeteringModes(CaptureRequest.Builder request) { + if (!VendorTagUtil.isSupported(request, CaptureModule.exposure_metering)) + return; String value = mSettingsManager.getValue(SettingsManager.KEY_EXPOSURE_METERING_MODE); if (value != null) { int intValue = Integer.parseInt(value); @@ -5348,6 +5362,8 @@ public class CaptureModule implements CameraModule, PhotoController, } private void applyHistogram(CaptureRequest.Builder request) { + if (!VendorTagUtil.isSupported(request, CaptureModule.histMode)) + return; String value = mSettingsManager.getValue(SettingsManager.KEY_STATS_VISUALIZER_VALUE); if (value != null ) { if (value.equals("3")) { diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index ccb45e12d..12b7480d5 100755 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -71,11 +71,10 @@ import com.android.camera.imageprocessor.filter.DeepPortraitFilter; import com.android.camera.ui.AutoFitSurfaceView; import com.android.camera.ui.Camera2FaceView; import com.android.camera.ui.CameraControls; -import com.android.camera.ui.MenuHelp; import com.android.camera.ui.OneUICameraControls; import com.android.camera.ui.CountDownView; import com.android.camera.ui.FlashToggleButton; -import com.android.camera.ui.FocusIndicator; +import com.android.camera.ui.focus.FocusRing; import com.android.camera.ui.PieRenderer; import com.android.camera.ui.ProMode; import com.android.camera.ui.RenderOverlay; @@ -94,8 +93,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; -public class CaptureUI implements FocusOverlayManager.FocusUI, - PreviewGestures.SingleTapListener, +public class CaptureUI implements PreviewGestures.SingleTapListener, CameraManager.CameraFaceDetectionCallback, SettingsManager.Listener, PauseButton.OnPauseButtonListener { @@ -107,6 +105,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private static final int ANIMATION_DURATION = 300; private static final int CLICK_THRESHOLD = 200; private static final int AUTOMATIC_MODE = 0; + private final FocusRing mFocusRing; private CameraActivity mActivity; private View mRootView; private View mPreviewCover; @@ -180,7 +179,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private FlashToggleButton mFlashButton; private CountDownView mCountDownView; private OneUICameraControls mCameraControls; - private MenuHelp mMenuHelp; private PieRenderer mPieRenderer; private ZoomRenderer mZoomRenderer; private Allocation mMonoDummyAllocation; @@ -302,6 +300,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mSurfaceHolderMono = mSurfaceViewMono.getHolder(); mSurfaceHolderMono.addCallback(callbackMono); + mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); + mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mVideoButton = (ImageView) mRootView.findViewById(R.id.video_button); @@ -553,7 +553,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mActivity.setPreviewGestures(mGestures); mRecordingTimeRect.setVisibility(View.GONE); - showFirstTimeHelp(); } protected void showCapturedImageForReview(byte[] jpegData, int orientation) { @@ -716,13 +715,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mVideoButton.setVisibility(View.VISIBLE); } mShutterButton.setOnShutterButtonListener(mModule); - mShutterButton.setImageResource(R.drawable.one_ui_shutter_anim); - mShutterButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - doShutterAnimation(); - } - }); + mShutterButton.setImageResource(R.drawable.btn_new_shutter); + mVideoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { @@ -769,7 +763,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, if (mPreviewLayout != null && mPreviewLayout.getVisibility() == View.VISIBLE) { return; } - clearFocus(); removeFilterMenu(false); Intent intent = new Intent(mActivity, SettingsActivity.class); mActivity.startActivity(intent); @@ -815,7 +808,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mSceneModeSwitcher.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - clearFocus(); removeFilterMenu(false); Intent intent = new Intent(mActivity, SceneModeActivity.class); intent.putExtra(CameraUtil.KEY_IS_SECURE_CAMERA, mActivity.isSecureCamera()); @@ -872,7 +864,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } else { mFlashButton.setVisibility(View.VISIBLE); mFlashButton.init(false); - mVideoButton.setImageResource(R.drawable.video_capture); + mVideoButton.setImageResource(R.drawable.btn_new_shutter_video); mRecordingTimeRect.setVisibility(View.GONE); mMuteButton.setVisibility(View.INVISIBLE); } @@ -1010,7 +1002,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } - public void resetTrackingFocus() { if(mModule.isTrackingFocusSettingOn()) { mTrackingFocusRenderer.setVisible(false); @@ -1122,10 +1113,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mSettingsManager.setValueIndex(SettingsManager .KEY_COLOR_EFFECT, j); for (View v1 : views) { - v1.setBackground(null); + v1.setActivated(v1 == v); } - ImageView image = (ImageView) v.findViewById(R.id.image); - image.setBackgroundColor(HIGHLIGHT_COLOR); } } return true; @@ -1133,10 +1122,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, }); views[j] = imageView; - if (i == init) - imageView.setBackgroundColor(HIGHLIGHT_COLOR); + imageView.setActivated(i == init); TextView label = (TextView) filterBox.findViewById(R.id.label); - imageView.setImageResource(thumbnails[i]); label.setText(entries[i]); gridLayout.addView(filterBox); @@ -1151,7 +1138,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, public void animateFadeIn(View v) { ViewPropertyAnimator vp = v.animate(); - vp.alpha(0.85f).setDuration(ANIMATION_DURATION); + vp.alpha(1f).setDuration(ANIMATION_DURATION); vp.start(); } @@ -1284,17 +1271,10 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } - public void doShutterAnimation() { - AnimationDrawable frameAnimation = (AnimationDrawable) mShutterButton.getDrawable(); - frameAnimation.stop(); - frameAnimation.start(); - } - public void showUI() { if (!mUIhidden) return; mUIhidden = false; - mPieRenderer.setBlockFocus(false); mCameraControls.showUI(); } @@ -1302,7 +1282,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, if (mUIhidden) return; mUIhidden = true; - mPieRenderer.setBlockFocus(true); mCameraControls.hideUI(); } @@ -1540,74 +1519,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mCameraControls.showRefocusToast(show); } - private FocusIndicator getFocusIndicator() { - if (mModule.isTrackingFocusSettingOn()) { - if (mPieRenderer != null) { - mPieRenderer.clear(); - } - return mTrackingFocusRenderer; - } - FocusIndicator focusIndicator; - if (mFaceView != null && mFaceView.faceExists() && !mIsTouchAF) { - if (mPieRenderer != null) { - mPieRenderer.clear(); - } - focusIndicator = mFaceView; - } else { - focusIndicator = mPieRenderer; - } - - return focusIndicator; - } - - @Override - public boolean hasFaces() { - return (mFaceView != null && mFaceView.faceExists()); - } - - public void clearFaces() { - if (mFaceView != null) mFaceView.clear(); - } - - @Override - public void clearFocus() { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.clear(); - mIsTouchAF = false; - } - - @Override - public void setFocusPosition(int x, int y) { - mPieRenderer.setFocus(x, y); - mIsTouchAF = true; - } - - @Override - public void onFocusStarted() { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showStart(); - } - - @Override - public void onFocusSucceeded(boolean timeout) { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showSuccess(timeout); - } - - @Override - public void onFocusFailed(boolean timeOut) { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showFail(timeOut); - - } - - @Override - public void pauseFaceDetection() { - - } - - @Override - public void resumeFaceDetection() { + public FocusRing getFocusRing() { + return mFocusRing; } public void onStartFaceDetection(int orientation, boolean mirror, Rect cameraBound, @@ -1657,9 +1570,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, public void setOrientation(int orientation, boolean animation) { mOrientation = orientation; mCameraControls.setOrientation(orientation, animation); - if (mMenuHelp != null) { - mMenuHelp.setOrientation(orientation, animation); - } if (mFilterLayout != null) { ViewGroup vg = (ViewGroup) mFilterLayout.getChildAt(0); if (vg != null) @@ -1716,33 +1626,6 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, return mOrientation; } - public void showFirstTimeHelp() { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity); - boolean isMenuShown = prefs.getBoolean(CameraSettings.KEY_SHOW_MENU_HELP, false); - if(!isMenuShown) { - showFirstTimeHelp(mTopMargin, mBottomMargin); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(CameraSettings.KEY_SHOW_MENU_HELP, true); - editor.apply(); - } - } - - private void showFirstTimeHelp(int topMargin, int bottomMargin) { - mMenuHelp = (MenuHelp) mRootView.findViewById(R.id.menu_help); - mMenuHelp.setForCamera2(true); - mMenuHelp.setVisibility(View.VISIBLE); - mMenuHelp.setMargins(topMargin, bottomMargin); - mMenuHelp.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mMenuHelp != null) { - mMenuHelp.setVisibility(View.GONE); - mMenuHelp = null; - } - } - }); - } - @Override public void onSingleTapUp(View view, int x, int y) { mModule.onSingleTapUp(view, x, y); diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java index de6db8e9d..3ed11ba3a 100755 --- a/src/com/android/camera/ComboPreferences.java +++ b/src/com/android/camera/ComboPreferences.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -157,7 +158,9 @@ public class ComboPreferences implements || key.equals(SettingsManager.KEY_MONO_ONLY) || key.equals(SettingsManager.KEY_MONO_PREVIEW) || key.equals(SettingsManager.KEY_SWITCH_CAMERA) - || key.equals(SettingsManager.KEY_CLEARSIGHT); + || key.equals(SettingsManager.KEY_CLEARSIGHT) + || key.equals(CameraSettings.KEY_POWER_SHUTTER) + || key.equals(CameraSettings.KEY_MAX_BRIGHTNESS); } @Override diff --git a/src/com/android/camera/CountDownTimerPreference.java b/src/com/android/camera/CountDownTimerPreference.java index 57820ac76..3d5f3a4c7 100644 --- a/src/com/android/camera/CountDownTimerPreference.java +++ b/src/com/android/camera/CountDownTimerPreference.java @@ -23,7 +23,7 @@ import org.codeaurora.snapcam.R; public class CountDownTimerPreference extends IconListPreference { private static final int[] DURATIONS = { - 0, 2, 5, 10 + 0, 1, 2, 3, 4, 5, 10, 15, 20, 30, 60 }; public CountDownTimerPreference(Context context, AttributeSet attrs) { super(context, attrs); @@ -39,7 +39,7 @@ public class CountDownTimerPreference extends IconListPreference { entries[0] = context.getString(R.string.setting_off); // Off } else { entries[i] = context.getResources() - .getQuantityString(R.plurals.pref_camera_timer_entry, DURATIONS[i], + .getQuantityString(R.plurals.pref_camera_timer_entry, i, DURATIONS[i]); } } diff --git a/src/com/android/camera/DisableCameraReceiver.java b/src/com/android/camera/DisableCameraReceiver.java index 4cef85f46..624874184 100644 --- a/src/com/android/camera/DisableCameraReceiver.java +++ b/src/com/android/camera/DisableCameraReceiver.java @@ -23,6 +23,8 @@ import android.content.Intent; import android.content.pm.PackageManager; import android.hardware.Camera.CameraInfo; import android.util.Log; +import com.android.camera.util.CameraUtil; +import org.codeaurora.snapcam.R; // We want to disable camera-related activities if there is no camera. This // receiver runs when BOOT_COMPLETED intent is received. After running once @@ -33,9 +35,21 @@ public class DisableCameraReceiver extends BroadcastReceiver { private static final String ACTIVITIES[] = { "com.android.camera.CameraLauncher", }; + private boolean mCamera2supported = false; + private boolean mCamera2enabled = false; @Override public void onReceive(Context context, Intent intent) { + // Check if the device supports Camera API 2 + mCamera2supported = CameraUtil.isCamera2Supported(context); + Log.d(TAG, "Camera API 2 supported: " + mCamera2supported); + + mCamera2enabled = mCamera2supported && + context.getResources().getBoolean(R.bool.support_camera_api_v2); + Log.d(TAG, "Camera API 2 enabled: " + mCamera2enabled); + + CameraHolder.setCamera2Mode(context, mCamera2enabled); + // Disable camera-related activities if there is no camera. boolean needCameraActivity = CHECK_BACK_CAMERA_ONLY ? hasBackCamera() @@ -53,23 +67,15 @@ public class DisableCameraReceiver extends BroadcastReceiver { } private boolean hasCamera() { - int n = android.hardware.Camera.getNumberOfCameras(); + int n = CameraHolder.instance().getNumberOfCameras(); Log.i(TAG, "number of camera: " + n); return (n > 0); } private boolean hasBackCamera() { - int n = android.hardware.Camera.getNumberOfCameras(); - CameraInfo info = new CameraInfo(); - for (int i = 0; i < n; i++) { - android.hardware.Camera.getCameraInfo(i, info); - if (info.facing == CameraInfo.CAMERA_FACING_BACK) { - Log.i(TAG, "back camera found: " + i); - return true; - } - } - Log.i(TAG, "no back camera"); - return false; + int backCameraId = CameraHolder.instance().getBackCameraId(); + Log.i(TAG, backCameraId == -1 ? "no back camera" : ("back camera found: " + backCameraId)); + return backCameraId != -1; } private void disableComponent(Context context, String klass) { diff --git a/src/com/android/camera/FocusOverlayManager.java b/src/com/android/camera/FocusOverlayManager.java index 253c08fb9..287890cbd 100644 --- a/src/com/android/camera/FocusOverlayManager.java +++ b/src/com/android/camera/FocusOverlayManager.java @@ -16,18 +16,22 @@ package com.android.camera; -import android.annotation.TargetApi; -import android.graphics.Matrix; +import android.content.Context; +import android.graphics.Point; import android.graphics.Rect; import android.graphics.RectF; import android.hardware.Camera.Area; import android.hardware.Camera.Parameters; -import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; +import org.codeaurora.snapcam.R; +import com.android.camera.app.CameraApp; +import com.android.camera.ui.focus.CameraCoordinateTransformer; +import com.android.camera.ui.focus.FocusRing; +import com.android.camera.ui.motion.LinearScale; import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; @@ -60,7 +64,9 @@ public class FocusOverlayManager { private static final String TAG = "CAM_FocusManager"; private static final int RESET_TOUCH_FOCUS = 0; - private static final int RESET_TOUCH_FOCUS_DELAY = 3000; + + public static final float AF_REGION_BOX = 0.2f; + public static final float AE_REGION_BOX = 0.3f; private int mState = STATE_IDLE; public static final int STATE_IDLE = 0; // Focus is not active. @@ -75,7 +81,7 @@ public class FocusOverlayManager { private boolean mMeteringAreaSupported; private boolean mLockAeAwbNeeded; private boolean mAeAwbLock; - private Matrix mMatrix; + private CameraCoordinateTransformer mCoordinateTransformer; private boolean mMirror; // true if the camera is front-facing. private int mDisplayOrientation; @@ -93,19 +99,13 @@ public class FocusOverlayManager { private boolean mTouchAFRunning = false; private boolean mIsAFRunning = false; - private FocusUI mUI; + private FocusRing mFocusRing; private final Rect mPreviewRect = new Rect(0, 0, 0, 0); - public interface FocusUI { - public boolean hasFaces(); - public void clearFocus(); - public void setFocusPosition(int x, int y); - public void onFocusStarted(); - public void onFocusSucceeded(boolean timeOut); - public void onFocusFailed(boolean timeOut); - public void pauseFaceDetection(); - public void resumeFaceDetection(); - } + private int mFocusTime; // time after touch-to-focus + private Point mDispSize; + private int mBottomMargin; + private int mTopMargin; public interface Listener { public void autoFocus(); @@ -114,6 +114,7 @@ public class FocusOverlayManager { public void startFaceDetection(); public void stopFaceDetection(); public void setFocusParameters(); + public void setFocusRatio(float ratio); } private class MainHandler extends Handler { @@ -135,19 +136,25 @@ public class FocusOverlayManager { public FocusOverlayManager(ComboPreferences preferences, String[] defaultFocusModes, Parameters parameters, Listener listener, - boolean mirror, Looper looper, FocusUI ui) { + boolean mirror, Looper looper, FocusRing focusRing, CameraActivity activity) { mHandler = new MainHandler(looper); - mMatrix = new Matrix(); mPreferences = preferences; mDefaultFocusModes = defaultFocusModes; setParameters(parameters); mListener = listener; setMirror(mirror); - mUI = ui; + mDispSize = new Point(); + activity.getWindowManager().getDefaultDisplay().getRealSize(mDispSize); + Context context = CameraApp.getContext(); + mBottomMargin = + context.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); + mTopMargin = + context.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); + mFocusRing = focusRing; } - public void setPhotoUI(FocusUI ui) { - mUI = ui; + public void setFocusRing(FocusRing focusRing) { + mFocusRing = focusRing; } public void setParameters(Parameters parameters) { @@ -173,35 +180,29 @@ public class FocusOverlayManager { public void setPreviewRect(Rect previewRect) { if (!mPreviewRect.equals(previewRect)) { mPreviewRect.set(previewRect); - setMatrix(); + mFocusRing.configurePreviewDimensions(CameraUtil.rectToRectF(mPreviewRect)); + resetCoordinateTransformer(); + mInitialized = true; } } - /** Returns a copy of mPreviewRect so that outside class cannot modify preview - * rect except deliberately doing so through the setter. */ - public Rect getPreviewRect() { - return new Rect(mPreviewRect); - } - public void setMirror(boolean mirror) { mMirror = mirror; - setMatrix(); + resetCoordinateTransformer(); } public void setDisplayOrientation(int displayOrientation) { mDisplayOrientation = displayOrientation; - setMatrix(); + resetCoordinateTransformer(); } - private void setMatrix() { - if (mPreviewRect.width() != 0 && mPreviewRect.height() != 0) { - Matrix matrix = new Matrix(); - CameraUtil.prepareMatrix(matrix, mMirror, mDisplayOrientation, getPreviewRect()); - // In face detection, the matrix converts the driver coordinates to UI - // coordinates. In tap focus, the inverted matrix converts the UI - // coordinates to driver coordinates. - matrix.invert(mMatrix); - mInitialized = true; + private void resetCoordinateTransformer() { + if (mPreviewRect.width() > 0 && mPreviewRect.height() > 0) { + mCoordinateTransformer = new CameraCoordinateTransformer(mMirror, mDisplayOrientation, + CameraUtil.rectToRectF(mPreviewRect)); + } else { + Log.w(TAG, "The coordinate transformer could not be built because the preview rect" + + "did not have a width and height"); } } @@ -272,17 +273,27 @@ public class FocusOverlayManager { } } + // set touch-to-focus duration + public void setFocusTime(int time) { + mFocusTime = time; + } + public void onAutoFocus(boolean focused, boolean shutterButtonPressed) { + updateFocusDistance(); if (mState == STATE_FOCUSING_SNAP_ON_FINISH) { // Take the picture no matter focus succeeds or fails. No need // to play the AF sound if we're about to play the shutter // sound. if (focused) { mState = STATE_SUCCESS; + // Lock exposure and white balance + if (mFocusTime != 200) { + setAeAwbLock(true); + mListener.setFocusParameters(); + } } else { mState = STATE_FAIL; } - updateFocusUI(); capture(); } else if (mState == STATE_FOCUSING) { // This happens when (1) user is half-pressing the focus key or @@ -290,14 +301,18 @@ public class FocusOverlayManager { // take the picture now. if (focused) { mState = STATE_SUCCESS; + // Lock exposure and white balance + if (mFocusTime != 200) { + setAeAwbLock(true); + mListener.setFocusParameters(); + } } else { mState = STATE_FAIL; } - updateFocusUI(); // If this is triggered by touch focus, cancel focus after a // while. if (mFocusArea != null) { - mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); + mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, mFocusTime); } if (shutterButtonPressed) { // Lock AE & AWB so users can half-press shutter and recompose. @@ -313,32 +328,33 @@ public class FocusOverlayManager { if (!mInitialized) return; - // Ignore if the camera has detected some faces. - if (mUI.hasFaces()) { - mUI.clearFocus(); - if (mIsAFRunning) { - mUI.onFocusSucceeded(true); - mIsAFRunning = false; - } - return; - } - // Ignore if we have requested autofocus. This method only handles // continuous autofocus. if (mState != STATE_IDLE) return; // animate on false->true trasition only b/8219520 if (moving && !mPreviousMoving) { - mUI.onFocusStarted(); + mFocusRing.startPassiveFocus(); mIsAFRunning = true; } else if (!moving) { - mUI.onFocusSucceeded(true); + mFocusRing.stopFocusAnimations(); mIsAFRunning = false; } + + updateFocusDistance(); mPreviousMoving = moving; } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) + /** Returns width of auto focus region in pixels. */ + private int getAFRegionSizePx() { + return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AF_REGION_BOX); + } + + /** Returns width of metering region in pixels. */ + private int getAERegionSizePx() { + return (int) (Math.min(mPreviewRect.width(), mPreviewRect.height()) * AE_REGION_BOX); + } + private void initializeFocusAreas(int x, int y) { if (mFocusArea == null) { mFocusArea = new ArrayList<Object>(); @@ -346,10 +362,9 @@ public class FocusOverlayManager { } // Convert the coordinates to driver format. - calculateTapArea(x, y, 1f, ((Area) mFocusArea.get(0)).rect); + ((Area) mFocusArea.get(0)).rect = computeCameraRectFromPreviewCoordinates(x, y, getAFRegionSizePx()); } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) private void initializeMeteringAreas(int x, int y) { if (mMeteringArea == null) { mMeteringArea = new ArrayList<Object>(); @@ -359,7 +374,7 @@ public class FocusOverlayManager { // Convert the coordinates to driver format. // AE area is bigger because exposure is sensitive and // easy to over- or underexposure if area is too small. - calculateTapArea(x, y, 1.5f, ((Area) mMeteringArea.get(0)).rect); + ((Area) mMeteringArea.get(0)).rect = computeCameraRectFromPreviewCoordinates(x, y, getAERegionSizePx()); } private void resetMeteringAreas() { @@ -377,7 +392,10 @@ public class FocusOverlayManager { mState == STATE_SUCCESS || mState == STATE_FAIL)) { cancelAutoFocus(); } - if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0) return; + if (mPreviewRect.width() == 0 || mPreviewRect.height() == 0 || + (y > (mDispSize.y - mBottomMargin) || y < mTopMargin)) { + return; + } // Initialize variables. // Initialize mFocusArea. if (mFocusAreaSupported) { @@ -388,8 +406,8 @@ public class FocusOverlayManager { initializeMeteringAreas(x, y); } - // Use margin to set the focus indicator to the touched area. - mUI.setFocusPosition(x, y); + mFocusRing.startActiveFocus(); + mFocusRing.setFocusLocation(x, y); if (mZslEnabled) { mTouchAFRunning = true; @@ -398,15 +416,18 @@ public class FocusOverlayManager { // Stop face detection because we want to specify focus and metering area. mListener.stopFaceDetection(); + if (mFocusTime == 200) { + setAeAwbLock(true); + mListener.setFocusParameters(); + } + // Set the focus area and metering area. mListener.setFocusParameters(); if (mFocusAreaSupported) { autoFocus(); } else { // Just show the indicator in all other cases. - updateFocusUI(); - // Reset the metering area in 3 seconds. mHandler.removeMessages(RESET_TOUCH_FOCUS); - mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, RESET_TOUCH_FOCUS_DELAY); + mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, mFocusTime); } } @@ -418,7 +439,6 @@ public class FocusOverlayManager { // If auto focus was in progress, it would have been stopped. mState = STATE_IDLE; resetTouchFocus(); - updateFocusUI(); } public void onCameraReleased() { @@ -430,7 +450,8 @@ public class FocusOverlayManager { Log.v(TAG, "Start autofocus."); mListener.autoFocus(); mState = STATE_FOCUSING; - updateFocusUI(); + // Pause the face view because the driver will keep sending face + // callbacks after the focus completes. mHandler.removeMessages(RESET_TOUCH_FOCUS); } @@ -441,10 +462,9 @@ public class FocusOverlayManager { // Otherwise, focus mode stays at auto and the tap area passed to the // driver is not reset. resetTouchFocus(); + setAeAwbLock(false); mListener.cancelAutoFocus(); - mUI.resumeFaceDetection(); mState = STATE_IDLE; - updateFocusUI(); mHandler.removeMessages(RESET_TOUCH_FOCUS); } @@ -455,7 +475,7 @@ public class FocusOverlayManager { } } - public String getFocusMode() { + public String getFocusMode(boolean fromVideo) { if (mOverrideFocusMode != null) return mOverrideFocusMode; if (mParameters == null) return Parameters.FOCUS_MODE_AUTO; List<String> supportedFocusModes = mParameters.getSupportedFocusModes(); @@ -465,8 +485,13 @@ public class FocusOverlayManager { mFocusMode = Parameters.FOCUS_MODE_AUTO; } else { // The default is continuous autofocus. - mFocusMode = mPreferences.getString( - CameraSettings.KEY_FOCUS_MODE, null); + if (fromVideo) { + mFocusMode = mPreferences.getString( + CameraSettings.KEY_VIDEOCAMERA_FOCUS_MODE, null); + } else { + mFocusMode = mPreferences.getString( + CameraSettings.KEY_FOCUS_MODE, null); + } // Try to find a supported focus mode from the default list. if (mFocusMode == null) { @@ -500,41 +525,18 @@ public class FocusOverlayManager { return mMeteringArea; } - public void updateFocusUI() { - if (!mInitialized) return; - // Show only focus indicator or face indicator. - - if (mState == STATE_IDLE) { - if (mFocusArea == null) { - mUI.clearFocus(); - } else { - // Users touch on the preview and the indicator represents the - // metering area. Either focus area is not supported or - // autoFocus call is not required. - mUI.onFocusStarted(); - } - } else if (mState == STATE_FOCUSING || mState == STATE_FOCUSING_SNAP_ON_FINISH) { - mUI.onFocusStarted(); + public void restartTouchFocusTimer() { + if (mZslEnabled && (mFocusArea != null) && (mFocusTime != 0x7FFFFFFF)) { + mHandler.removeMessages(RESET_TOUCH_FOCUS); + mHandler.sendEmptyMessageDelayed(RESET_TOUCH_FOCUS, mFocusTime); } else { - if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusMode)) { - // TODO: check HAL behavior and decide if this can be removed. - mUI.onFocusSucceeded(false); - } else if (mState == STATE_SUCCESS) { - mUI.onFocusSucceeded(false); - } else if (mState == STATE_FAIL) { - mUI.onFocusFailed(false); - } + resetTouchFocus(); } } public void resetTouchFocus() { if (!mInitialized) return; - // Put focus indicator to the center. clear reset position - if (mUI != null) { - mUI.clearFocus(); - } - // Initialize mFocusArea. mFocusArea = null; // Initialize mMeteringArea. mMeteringArea = null; @@ -549,16 +551,14 @@ public class FocusOverlayManager { } } - private void calculateTapArea(int x, int y, float areaMultiple, Rect rect) { - int areaSize = (int) (getAreaSize() * areaMultiple); - int left = CameraUtil.clamp(x - areaSize / 2, mPreviewRect.left, - mPreviewRect.right - areaSize); - int top = CameraUtil.clamp(y - areaSize / 2, mPreviewRect.top, - mPreviewRect.bottom - areaSize); + private Rect computeCameraRectFromPreviewCoordinates(int x, int y, int size) { + int left = CameraUtil.clamp(x - size / 2, mPreviewRect.left, + mPreviewRect.right - size); + int top = CameraUtil.clamp(y - size / 2, mPreviewRect.top, + mPreviewRect.bottom - size); - RectF rectF = new RectF(left, top, left + areaSize, top + areaSize); - mMatrix.mapRect(rectF); - CameraUtil.rectFToRect(rectF, rect); + RectF rectF = new RectF(left, top, left + size, top + size); + return CameraUtil.rectFToRect(mCoordinateTransformer.toCameraSpace(rectF)); } private int getAreaSize() { @@ -600,10 +600,8 @@ public class FocusOverlayManager { } private boolean needAutoFocusCall() { - String focusMode = getFocusMode(); - return !(focusMode.equals(Parameters.FOCUS_MODE_INFINITY) - || focusMode.equals(Parameters.FOCUS_MODE_FIXED) - || focusMode.equals(Parameters.FOCUS_MODE_EDOF)); + return getFocusMode(false).equals(Parameters.FOCUS_MODE_AUTO) && + !(mZslEnabled && (mHandler.hasMessages(RESET_TOUCH_FOCUS))); } public void setZslEnable(boolean value) { @@ -618,4 +616,92 @@ public class FocusOverlayManager { return mTouchAFRunning; } + private static class FocusInfo { + public final float near; + public final float far; + public final float current; + + public FocusInfo(float _near, float _far, float _current) { + near = _near; + far = _far; + current = _current; + } + } + + private FocusInfo getFocusInfoFromParameters( + String currentParam, String minParam, String maxParam) { + try { + String current = mParameters.get(currentParam); + if (current != null) { + float min = Float.parseFloat(mParameters.get(minParam)); + float max = Float.parseFloat(mParameters.get(maxParam)); + if (!(min == 0.0f && max == 0.0f)) { + return new FocusInfo(min, max, Float.parseFloat(current)); + } + } + } catch (Exception e) { + // skip it + } + return null; + } + + private FocusInfo getFocusInfo() { + // focus positon is horrifically buggy on some HALs. try to + // make the best of it and attempt a few different techniques + // to get an accurate measurement + + // older QCOM (Bacon) + FocusInfo info = getFocusInfoFromParameters("current-focus-position", + "min-focus-pos-index", "max-focus-pos-index"); + if (info != null) { + return info; + } + + // newer QCOM (Crackling) + info = getFocusInfoFromParameters("cur-focus-scale", + "min-focus-pos-ratio", "max-focus-pos-ratio"); + if (info != null) { + return info; + } + + return null; + } + + /** + * Compute the focus range from the camera characteristics and build + * a linear scale model that maps a focus distance to a ratio between + * the min and max range. + */ + private LinearScale getDiopterToRatioCalculator(FocusInfo focusInfo) { + // From the android documentation: + // + // 0.0f represents farthest focus, and LENS_INFO_MINIMUM_FOCUS_DISTANCE + // represents the nearest focus the device can achieve. + // + // Example: + // + // Infinity Hyperfocal Minimum Camera + // <----------|-----------------------------| | + // [0.0] [0.31] [14.29] + if (focusInfo.near == 0.0f && focusInfo.far == 0.0f) { + return new LinearScale(0, 0, 0, 0); + } + + if (focusInfo.near > focusInfo.far) { + return new LinearScale(focusInfo.far, focusInfo.near, 0, 1); + } + + return new LinearScale(focusInfo.near, focusInfo.far, 0, 1); + } + + private void updateFocusDistance() { + final FocusInfo focusInfo = getFocusInfo(); + if (focusInfo != null) { + LinearScale range = getDiopterToRatioCalculator(focusInfo); + if (range.isInDomain(focusInfo.current) && (mFocusRing.isPassiveFocusRunning() || + mFocusRing.isActiveFocusRunning())) { + mListener.setFocusRatio(range.scale(focusInfo.current)); + } + } + } } diff --git a/src/com/android/camera/FocusStateListener.java b/src/com/android/camera/FocusStateListener.java index 6c536c53b..459e4ec23 100644 --- a/src/com/android/camera/FocusStateListener.java +++ b/src/com/android/camera/FocusStateListener.java @@ -44,31 +44,31 @@ public class FocusStateListener { switch (focusState) { case CaptureResult.CONTROL_AF_STATE_ACTIVE_SCAN: Log.d(TAG, "CONTROL_AF_STATE_ACTIVE_SCAN onFocusStarted"); - mUI.onFocusStarted(); + mUI.getFocusRing().startActiveFocus(); break; case CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED: Log.d(TAG, "CONTROL_AF_STATE_FOCUSED_LOCKED onFocusSucceeded"); - mUI.onFocusSucceeded(false); + mUI.getFocusRing().stopFocusAnimations(); break; case CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED: Log.d(TAG, "CONTROL_AF_STATE_NOT_FOCUSED_LOCKED onFocusFailed"); - mUI.onFocusFailed(false); + mUI.getFocusRing().stopFocusAnimations(); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_FOCUSED: Log.d(TAG, "CONTROL_AF_STATE_PASSIVE_FOCUSED onFocusSucceeded"); - mUI.onFocusSucceeded(true); + mUI.getFocusRing().stopFocusAnimations(); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN: Log.d(TAG, "CONTROL_AF_STATE_PASSIVE_SCAN onFocusStarted"); - mUI.onFocusStarted(); + mUI.getFocusRing().startPassiveFocus(); break; case CaptureResult.CONTROL_AF_STATE_PASSIVE_UNFOCUSED: Log.d(TAG, "CONTROL_AF_STATE_PASSIVE_UNFOCUSED onFocusFailed"); - mUI.onFocusFailed(true); + mUI.getFocusRing().stopFocusAnimations(); break; case CaptureResult.CONTROL_AF_STATE_INACTIVE: Log.d(TAG, "CONTROL_AF_STATE_INACTIVE clearFocus"); - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); break; } } diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index ae37356e1..50afc0849 100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java @@ -184,7 +184,11 @@ public class ListPreference extends CameraPreference { } public String getLabel() { - return mLabels[findIndexOfValue(getValue())].toString(); + int index = findIndexOfValue(getValue()); + if (index < 0) { + return findSupportedDefaultValue(); + } + return mLabels[index].toString(); } protected void persistStringValue(String value) { @@ -203,6 +207,7 @@ public class ListPreference extends CameraPreference { ArrayList<CharSequence> entries = new ArrayList<CharSequence>(); ArrayList<CharSequence> entryValues = new ArrayList<CharSequence>(); for (int i = 0, len = mEntryValues.length; i < len; i++) { + if (i >= mEntries.length) break; if (supported.indexOf(mEntryValues[i].toString()) >= 0) { entries.add(mEntries[i]); entryValues.add(mEntryValues[i]); diff --git a/src/com/android/camera/LocationManager.java b/src/com/android/camera/LocationManager.java index 5aedf060e..2ae901aed 100755 --- a/src/com/android/camera/LocationManager.java +++ b/src/com/android/camera/LocationManager.java @@ -79,7 +79,7 @@ public class LocationManager { } private boolean hasLoationPermission() { - return mContext.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + return mContext.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; } diff --git a/src/com/android/camera/MenuController.java b/src/com/android/camera/MenuController.java index 97240cb18..63815f9b7 100644 --- a/src/com/android/camera/MenuController.java +++ b/src/com/android/camera/MenuController.java @@ -104,6 +104,7 @@ public class MenuController { return; } } + index = index % ((IconListPreference) pref).getLargeIconIds().length; ((ImageView) switcher).setImageResource(pref.getLargeIconIds()[index]); } diff --git a/src/com/android/camera/Mosaic.java b/src/com/android/camera/Mosaic.java index a9bc32e12..829efa5b2 100644 --- a/src/com/android/camera/Mosaic.java +++ b/src/com/android/camera/Mosaic.java @@ -93,7 +93,7 @@ public class Mosaic { static { - System.loadLibrary("jni_snapcammosaic"); + System.loadLibrary("jni_snapmosaic"); } /** diff --git a/src/com/android/camera/MosaicFrameProcessor.java b/src/com/android/camera/MosaicFrameProcessor.java index cb305344d..2e027b3e3 100644 --- a/src/com/android/camera/MosaicFrameProcessor.java +++ b/src/com/android/camera/MosaicFrameProcessor.java @@ -24,7 +24,7 @@ import android.util.Log; public class MosaicFrameProcessor { private static final String TAG = "MosaicFrameProcessor"; private static final int NUM_FRAMES_IN_BUFFER = 2; - private static final int MAX_NUMBER_OF_FRAMES = 100; + private static final int MAX_NUMBER_OF_FRAMES = 100 * 2; // We need 200 frames for 360 degrees private static final int MOSAIC_RET_CODE_INDEX = 10; private static final int FRAME_COUNT_INDEX = 9; private static final int X_COORD_INDEX = 2; diff --git a/src/com/android/camera/MosaicRenderer.java b/src/com/android/camera/MosaicRenderer.java index 5006b364d..bcbaf7227 100644 --- a/src/com/android/camera/MosaicRenderer.java +++ b/src/com/android/camera/MosaicRenderer.java @@ -24,7 +24,7 @@ public class MosaicRenderer { static { - System.loadLibrary("jni_snapcammosaic"); + System.loadLibrary("jni_snapmosaic"); } /** diff --git a/src/com/android/camera/OnScreenIndicators.java b/src/com/android/camera/OnScreenIndicators.java deleted file mode 100644 index 67cbad1c9..000000000 --- a/src/com/android/camera/OnScreenIndicators.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera; - -import android.content.Context; -import android.content.res.TypedArray; -import android.hardware.Camera; -import android.hardware.Camera.Parameters; -import android.util.Log; -import android.view.View; -import android.widget.ImageView; - -import org.codeaurora.snapcam.R; - -/** - * The on-screen indicators of the pie menu button. They show the camera - * settings in the viewfinder. - */ -public class OnScreenIndicators { - public static final String SCENE_MODE_HDR_PLUS = "hdr_plus"; - - private final int[] mWBArray; - private final View mOnScreenIndicators; - private final ImageView mExposureIndicator; - private final ImageView mFlashIndicator; - private final ImageView mSceneIndicator; - private final ImageView mLocationIndicator; - private final ImageView mTimerIndicator; - private final ImageView mWBIndicator; - - public OnScreenIndicators(Context ctx, View onScreenIndicatorsView) { - TypedArray iconIds = ctx.getResources().obtainTypedArray( - R.array.camera_wb_indicators); - final int n = iconIds.length(); - mWBArray = new int[n]; - for (int i = 0; i < n; i++) { - mWBArray[i] = iconIds.getResourceId(i, R.drawable.ic_indicator_wb_off); - } - mOnScreenIndicators = onScreenIndicatorsView; - mExposureIndicator = (ImageView) onScreenIndicatorsView.findViewById( - R.id.menu_exposure_indicator); - mFlashIndicator = (ImageView) onScreenIndicatorsView.findViewById( - R.id.menu_flash_indicator); - mSceneIndicator = (ImageView) onScreenIndicatorsView.findViewById( - R.id.menu_scenemode_indicator); - mLocationIndicator = (ImageView) onScreenIndicatorsView.findViewById( - R.id.menu_location_indicator); - mTimerIndicator = (ImageView) onScreenIndicatorsView.findViewById( - R.id.menu_timer_indicator); - mWBIndicator = (ImageView) onScreenIndicatorsView.findViewById( - R.id.menu_wb_indicator); - mExposureIndicator.setVisibility(View.GONE); - mFlashIndicator.setVisibility(View.GONE); - mSceneIndicator.setVisibility(View.GONE); - mLocationIndicator.setVisibility(View.GONE); - mTimerIndicator.setVisibility(View.GONE); - mWBIndicator.setVisibility(View.GONE); - } - - /** - * Resets all indicators to show the default values. - */ - public void resetToDefault() { - updateExposureOnScreenIndicator(0); - updateFlashOnScreenIndicator(Parameters.FLASH_MODE_OFF); - updateSceneOnScreenIndicator(Parameters.SCENE_MODE_AUTO); - updateWBIndicator(2); - updateTimerIndicator(false); - updateLocationIndicator(false); - } - - /** - * Sets the exposure indicator using exposure compensations step rounding. - */ - public void updateExposureOnScreenIndicator(Camera.Parameters params, int value) { - if (mExposureIndicator == null) { - return; - } - float step = params.getExposureCompensationStep(); - value = Math.round(value * step); - updateExposureOnScreenIndicator(value); - } - - /** - * Set the exposure indicator to the given value. - * - * @param value Value between -3 and 3. If outside this range, 0 is used by - * default. - */ - public void updateExposureOnScreenIndicator(int value) { - int id = 0; - switch(value) { - case -3: - id = R.drawable.ic_indicator_ev_n3; - break; - case -2: - id = R.drawable.ic_indicator_ev_n2; - break; - case -1: - id = R.drawable.ic_indicator_ev_n1; - break; - case 0: - id = R.drawable.ic_indicator_ev_0; - break; - case 1: - id = R.drawable.ic_indicator_ev_p1; - break; - case 2: - id = R.drawable.ic_indicator_ev_p2; - break; - case 3: - id = R.drawable.ic_indicator_ev_p3; - break; - } - mExposureIndicator.setImageResource(R.drawable.ic_settings); - } - - public void updateWBIndicator(int wbIndex) { - if (mWBIndicator == null) return; - mWBIndicator.setImageResource(mWBArray[wbIndex]); - } - - public void updateTimerIndicator(boolean on) { - if (mTimerIndicator == null) return; - mTimerIndicator.setImageResource(on ? R.drawable.ic_indicator_timer_on - : R.drawable.ic_indicator_timer_off); - } - - public void updateLocationIndicator(boolean on) { - if (mLocationIndicator == null) return; - mLocationIndicator.setImageResource(on ? R.drawable.ic_indicator_loc_on - : R.drawable.ic_indicator_loc_off); - } - - /** - * Set the flash indicator to the given value. - * - * @param value One of Parameters.FLASH_MODE_OFF, - * Parameters.FLASH_MODE_AUTO, Parameters.FLASH_MODE_ON. - */ - public void updateFlashOnScreenIndicator(String value) { - if (mFlashIndicator == null) { - return; - } - if (value == null || Parameters.FLASH_MODE_OFF.equals(value)) { - mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off); - } else { - if (Parameters.FLASH_MODE_AUTO.equals(value)) { - mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_auto); - } else if (Parameters.FLASH_MODE_ON.equals(value) - || Parameters.FLASH_MODE_TORCH.equals(value)) { - mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_on); - } else { - mFlashIndicator.setImageResource(R.drawable.ic_indicator_flash_off); - } - } - } - - /** - * Set the scene indicator depending on the given scene mode. - * - * @param value the current Parameters.SCENE_MODE_* value or - * {@link #SCENE_MODE_HDR_PLUS}. - */ - public void updateSceneOnScreenIndicator(String value) { - if (mSceneIndicator == null) { - return; - } - - if (SCENE_MODE_HDR_PLUS.equals(value)) { - mSceneIndicator.setImageResource(R.drawable.ic_indicator_hdr_plus_on); - } else if ((value == null) || Parameters.SCENE_MODE_AUTO.equals(value)) { - mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_off); - } else if (Parameters.SCENE_MODE_HDR.equals(value)) { - mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_hdr); - } else { - mSceneIndicator.setImageResource(R.drawable.ic_indicator_sce_on); - } - } - - /** - * Sets the visibility of all indicators. - * - * @param visibility View.VISIBLE, View.GONE etc. - */ - public void setVisibility(int visibility) { - mOnScreenIndicators.setVisibility(visibility); - } -} diff --git a/src/com/android/camera/PanoCaptureUI.java b/src/com/android/camera/PanoCaptureUI.java index a25ff9f94..2894e683c 100644 --- a/src/com/android/camera/PanoCaptureUI.java +++ b/src/com/android/camera/PanoCaptureUI.java @@ -80,9 +80,6 @@ public class PanoCaptureUI implements private ImageView mSceneModeLabelCloseIcon; private AlertDialog mSceneModeInstructionalDialog = null; - // Small indicators which show the camera settings in the viewfinder. - private OnScreenIndicators mOnScreenIndicators; - private AutoFitSurfaceView mSurfaceView = null; private Matrix mMatrix = null; private boolean mUIhidden = false; @@ -225,7 +222,6 @@ public class PanoCaptureUI implements mSceneModeLabelRect.setVisibility(View.GONE); } }); - initIndicators(); Point size = new Point(); mActivity.getWindowManager().getDefaultDisplay().getSize(size); @@ -278,11 +274,6 @@ public class PanoCaptureUI implements return mRootView; } - private void initIndicators() { - mOnScreenIndicators = new OnScreenIndicators(mActivity, - mRootView.findViewById(R.id.on_screen_indicators)); - } - public void onCameraOpened() { } @@ -354,13 +345,6 @@ public class PanoCaptureUI implements } else { hideUI(); } - setShowMenu(previewFocused); - } - - private void setShowMenu(boolean show) { - if (mOnScreenIndicators != null) { - mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE); - } } public boolean collapseCameraControls() { diff --git a/src/com/android/camera/PanoProgressBar.java b/src/com/android/camera/PanoProgressBar.java index d20878a84..45f08a53e 100644 --- a/src/com/android/camera/PanoProgressBar.java +++ b/src/com/android/camera/PanoProgressBar.java @@ -142,7 +142,9 @@ class PanoProgressBar extends ImageView { // When user move to the opposite direction more than 10 degrees, // change the direction and stop the capture progress in PanoramaModule. if (Math.abs(mOldProgress) - Math.abs(progress) > 10) { - mListener.onDirectionChange(mDirection/2 + 1); + if (mListener != null) { + mListener.onDirectionChange(mDirection / 2 + 1); + } return; } // mDirection might be modified by setRightIncreasing() above. Need to check again. diff --git a/src/com/android/camera/PermissionsActivity.java b/src/com/android/camera/PermissionsActivity.java index 91699c34f..fb0c080db 100755 --- a/src/com/android/camera/PermissionsActivity.java +++ b/src/com/android/camera/PermissionsActivity.java @@ -79,7 +79,7 @@ public class PermissionsActivity extends Activity { mFlagHasStoragePermission = true; } - if (checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { mNumPermissionsToRequest++; mShouldRequestLocationPermission = true; @@ -119,7 +119,7 @@ public class PermissionsActivity extends Activity { } if (mShouldRequestLocationPermission) { permissionsToRequest[permissionsRequestIndex] = - Manifest.permission.ACCESS_COARSE_LOCATION; + Manifest.permission.ACCESS_FINE_LOCATION; mIndexPermissionRequestLocation = permissionsRequestIndex; } requestPermissions(permissionsToRequest, PERMISSION_REQUEST_CODE); diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java index 7debfc7ef..4f6c88688 100644 --- a/src/com/android/camera/PhotoMenu.java +++ b/src/com/android/camera/PhotoMenu.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -94,7 +95,7 @@ public class PhotoMenu extends MenuController private static final int MODE_SCENE = 0; private static final int MODE_FILTER = 1; private static final int MODE_MAKEUP = 2; - private static final int DEVELOPER_MENU_TOUCH_COUNT = 10; + private static final int DEVELOPER_MENU_TOUCH_COUNT = 7; private int mSceneStatus; private View mHdrSwitcher; private View mTsMakeupSwitcher; @@ -112,7 +113,7 @@ public class PhotoMenu extends MenuController private String mPrevSavedCDS; private boolean mIsTNREnabled = false; private boolean mIsCDSUpdated = false; - private int privateCounter = 0; + private int mPrivateCounter = 0; private static final int ANIMATION_DURATION = 300; private static final int CLICK_THRESHOLD = 200; private int previewMenuSize; @@ -193,9 +194,20 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_EXPOSURE, CameraSettings.KEY_WHITE_BALANCE, CameraSettings.KEY_QC_CHROMA_FLASH, + CameraSettings.KEY_FOCUS_TIME, + CameraSettings.KEY_SHUTTER_SPEED, CameraSettings.KEY_REDEYE_REDUCTION, CameraSettings.KEY_SELFIE_MIRROR, - CameraSettings.KEY_SHUTTER_SOUND + CameraSettings.KEY_SHUTTER_SOUND, + CameraSettings.KEY_POWER_SHUTTER, + CameraSettings.KEY_MAX_BRIGHTNESS, + CameraSettings.KEY_SATURATION, + CameraSettings.KEY_CONTRAST, + CameraSettings.KEY_SHARPNESS, + CameraSettings.KEY_AUTOEXPOSURE, + CameraSettings.KEY_ANTIBANDING, + CameraSettings.KEY_DENOISE, + CameraSettings.KEY_AUTO_HDR }; mOtherKeys2 = new String[] { @@ -213,25 +225,27 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_WHITE_BALANCE, CameraSettings.KEY_QC_CHROMA_FLASH, CameraSettings.KEY_FOCUS_MODE, + CameraSettings.KEY_FOCUS_TIME, + CameraSettings.KEY_SHUTTER_SPEED, CameraSettings.KEY_REDEYE_REDUCTION, + CameraSettings.KEY_POWER_SHUTTER, + CameraSettings.KEY_MAX_BRIGHTNESS, + CameraSettings.KEY_SATURATION, + CameraSettings.KEY_CONTRAST, + CameraSettings.KEY_SHARPNESS, + CameraSettings.KEY_AUTOEXPOSURE, + CameraSettings.KEY_ANTIBANDING, + CameraSettings.KEY_DENOISE, CameraSettings.KEY_AUTO_HDR, CameraSettings.KEY_HDR_MODE, - CameraSettings.KEY_HDR_NEED_1X, CameraSettings.KEY_CDS_MODE, CameraSettings.KEY_TNR_MODE, CameraSettings.KEY_HISTOGRAM, CameraSettings.KEY_ZSL, CameraSettings.KEY_TIMER_SOUND_EFFECTS, CameraSettings.KEY_FACE_RECOGNITION, - CameraSettings.KEY_TOUCH_AF_AEC, CameraSettings.KEY_SELECTABLE_ZONE_AF, CameraSettings.KEY_PICTURE_FORMAT, - CameraSettings.KEY_SATURATION, - CameraSettings.KEY_CONTRAST, - CameraSettings.KEY_SHARPNESS, - CameraSettings.KEY_AUTOEXPOSURE, - CameraSettings.KEY_ANTIBANDING, - CameraSettings.KEY_DENOISE, CameraSettings.KEY_ADVANCED_FEATURES, CameraSettings.KEY_AE_BRACKET_HDR, CameraSettings.KEY_INSTANT_CAPTURE, @@ -438,7 +452,7 @@ public class PhotoMenu extends MenuController public void animateFadeIn(final ListView v) { ViewPropertyAnimator vp = v.animate(); - vp.alpha(0.85f).setDuration(ANIMATION_DURATION); + vp.alpha(1f).setDuration(ANIMATION_DURATION); vp.start(); } @@ -693,7 +707,6 @@ public class PhotoMenu extends MenuController || ((autohdr != null) && autohdr.equals("enable"))) { popup1.setPreferenceEnabled(CameraSettings.KEY_FOCUS_MODE, false); popup1.setPreferenceEnabled(CameraSettings.KEY_AUTOEXPOSURE, false); - popup1.setPreferenceEnabled(CameraSettings.KEY_TOUCH_AF_AEC, false); popup1.setPreferenceEnabled(CameraSettings.KEY_SATURATION, false); popup1.setPreferenceEnabled(CameraSettings.KEY_CONTRAST, false); popup1.setPreferenceEnabled(CameraSettings.KEY_SHARPNESS, false); @@ -756,7 +769,6 @@ public class PhotoMenu extends MenuController popup1.setPreferenceEnabled(CameraSettings.KEY_REDEYE_REDUCTION, false); popup1.setPreferenceEnabled(CameraSettings.KEY_EXPOSURE, false); 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); @@ -812,6 +824,7 @@ public class PhotoMenu extends MenuController int index = pref.findIndexOfValue(pref.getValue()); if (!pref.getUseSingleIcon() && iconIds != null) { // Each entry has a corresponding icon. + index = index % iconIds.length; resid = iconIds[index]; } else { // The preference only has a single icon to represent it. @@ -838,8 +851,9 @@ public class PhotoMenu extends MenuController CharSequence[] values = pref.getEntryValues(); index = (index + 1) % values.length; pref.setValueIndex(index); + int iconListLength = ((IconListPreference) pref).getLargeIconIds().length; ((ImageView) v).setImageResource( - ((IconListPreference) pref).getLargeIconIds()[index]); + ((IconListPreference) pref).getLargeIconIds()[index % iconListLength]); if (prefKey.equals(CameraSettings.KEY_CAMERA_ID)) mListener.onCameraPickerClicked(index); reloadPreference(pref); @@ -1059,11 +1073,8 @@ public class PhotoMenu extends MenuController onSettingChanged(pref); updateSceneModeIcon(pref); for (View v1 : views) { - v1.setBackgroundResource(R.drawable.scene_mode_view_border); + v1.setActivated(v1 == v); } - View border = v.findViewById(R.id.border); - border.setBackgroundResource(R.drawable.scene_mode_view_border_selected); - animateSlideOutPreviewMenu(); } } @@ -1071,10 +1082,8 @@ public class PhotoMenu extends MenuController } }); - View border = layout2.findViewById(R.id.border); - views[j] = border; - if (i == init) - border.setBackgroundResource(R.drawable.scene_mode_view_border_selected); + views[j] = layout2; + layout2.setActivated(i == init); imageView.setImageResource(thumbnails[i]); label.setText(entries[i]); layout.addView(layout2); @@ -1210,19 +1219,16 @@ public class PhotoMenu extends MenuController changeFilterModeControlIcon(pref.getValue()); onSettingChanged(pref); for (View v1 : views) { - v1.setBackground(null); + v1.setActivated(v1 == v); } - ImageView image = (ImageView) v.findViewById(R.id.image); - image.setBackgroundColor(0xff33b5e5); } } return true; } }); - views[j] = imageView; - if (i == init) - imageView.setBackgroundColor(0xff33b5e5); + views[j] = layout2; + layout2.setActivated(i == init); TextView label = (TextView) layout2.findViewById(R.id.label); imageView.setImageResource(thumbnails[i]); label.setText(entries[i]); @@ -1291,21 +1297,6 @@ public class PhotoMenu extends MenuController } 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( @@ -1321,6 +1312,31 @@ public class PhotoMenu extends MenuController mUI.showPopup(mListSubMenu, 2, true); } mPopupStatus = POPUP_SECOND_LEVEL; + + // Developer menu + if (pref.getKey().equals(CameraSettings.KEY_MAX_BRIGHTNESS)) { + mPrivateCounter++; + if (mPrivateCounter >= DEVELOPER_MENU_TOUCH_COUNT) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(mActivity); + if (!mActivity.isDeveloperMenuEnabled()) { + mActivity.enableDeveloperMenu(); + prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, true).apply(); + closeAllView(); + RotateTextToast.makeText(mActivity, + R.string.developer_menu_enabled, Toast.LENGTH_SHORT).show(); + } else { + mActivity.disableDeveloperMenu(); + prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, false).apply(); + closeAllView(); + RotateTextToast.makeText(mActivity, + R.string.developer_menu_disabled, Toast.LENGTH_SHORT).show(); + } + mPrivateCounter = 0; + } + } else { + mPrivateCounter = 0; + } } public void onListMenuTouched() { @@ -1414,7 +1430,7 @@ public class PhotoMenu extends MenuController } else if (notSame(pref,CameraSettings.KEY_FLASH_MODE,"Off")) { ListPreference aePref = mPreferenceGroup.findPreference(CameraSettings.KEY_AE_BRACKET_HDR); - if (notSame(aePref,CameraSettings.KEY_AE_BRACKET_HDR,"Off")) { + if (aePref != null && notSame(aePref,CameraSettings.KEY_AE_BRACKET_HDR,"Off")) { RotateTextToast.makeText(mActivity, R.string.flash_aebracket_message,Toast.LENGTH_SHORT).show(); } @@ -1465,13 +1481,12 @@ public class PhotoMenu extends MenuController } } - String chromaFlashOn = mActivity.getString(R.string. - pref_camera_advanced_feature_value_chromaflash_on); - if (notSame(pref, CameraSettings.KEY_SCENE_MODE, Parameters.SCENE_MODE_AUTO)) { + String chromaFlashOn = mActivity.getString(R.string + .pref_camera_advanced_feature_value_chromaflash_on); + if (notSame(pref, CameraSettings.KEY_SCENE_MODE, chromaFlashOn)) { ListPreference lp = mPreferenceGroup .findPreference(CameraSettings.KEY_ADVANCED_FEATURES); if (lp != null && chromaFlashOn.equals(lp.getValue())) { - setPreference(CameraSettings.KEY_QC_CHROMA_FLASH, mSettingOff); setPreference(CameraSettings.KEY_ADVANCED_FEATURES, mActivity.getString(R.string.pref_camera_advanced_feature_default)); } @@ -1495,7 +1510,10 @@ public class PhotoMenu extends MenuController } else { mHdrSwitcher.setVisibility(View.VISIBLE); } - updateFilterModeIcon(pref, pref); + + ListPreference hdrPref = mPreferenceGroup.findPreference(CameraSettings.KEY_CAMERA_HDR); + ListPreference scenePref = mPreferenceGroup.findPreference(CameraSettings.KEY_SCENE_MODE); + updateFilterModeIcon(scenePref, hdrPref); if (same(pref, CameraSettings.KEY_RECORD_LOCATION, "on")) { mActivity.requestLocationPermission(); diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index f3c57cb71..06c7cf619 100644 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -62,12 +63,12 @@ import android.view.SurfaceHolder; import android.view.View; import android.view.WindowManager; import android.widget.Toast; -import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.SeekBar.OnSeekBarChangeListener; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.camera.app.CameraApp; import com.android.camera.CameraManager.CameraAFCallback; import com.android.camera.CameraManager.CameraAFMoveCallback; import com.android.camera.CameraManager.CameraPictureCallback; @@ -104,6 +105,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.util.Arrays; import java.lang.NumberFormatException; import java.util.List; import java.util.Vector; @@ -113,8 +115,6 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.os.SystemProperties; -import java.util.Collections; -import java.util.Formatter; public class PhotoModule implements CameraModule, @@ -130,13 +130,14 @@ public class PhotoModule private static final String TAG = "CAM_PhotoModule"; //QCom data members - public static boolean mBrightnessVisible = false; private static final int MAX_SHARPNESS_LEVEL = 6; private boolean mRestartPreview = false; private int mSnapshotMode; private int mBurstSnapNum = 1; private int mReceivedSnapNum = 0; + private int mLongshotSnapNum = 0; public boolean mFaceDetectionEnabled = false; + private boolean mLgeHdrMode = false; private DrawAutoHDR mDrawAutoHDR; /*Histogram variables*/ private GraphView mGraphView; @@ -159,6 +160,8 @@ public class PhotoModule private static final int SET_PHOTO_UI_PARAMS = 11; private static final int SWITCH_TO_GCAM_MODULE = 12; private static final int ON_PREVIEW_STARTED = 13; + private static final int UNLOCK_CAM_SHUTTER = 14; + private static final int SET_FOCUS_RATIO = 15; // The subset of parameters we need to update in setCameraParameters(). private static final int UPDATE_PARAM_INITIALIZE = 1; @@ -204,7 +207,6 @@ public class PhotoModule private boolean mAeLockSupported; private boolean mAwbLockSupported; private boolean mContinuousFocusSupported; - private boolean mTouchAfAecFlag; private boolean mLongshotSave = false; private boolean mRefocus = false; private boolean mLastPhotoTakenWithRefocus = false; @@ -224,12 +226,9 @@ public class PhotoModule private static final boolean PERSIST_SKIP_MEM_CHECK = PersistUtil.isSkipMemoryCheckEnabled(); - private static final int MINIMUM_BRIGHTNESS = 0; - private static final int MAXIMUM_BRIGHTNESS = 6; - private static final int DEFAULT_BRIGHTNESS = 3; - private int mbrightness = 3; - private int mbrightness_step = 1; - private ProgressBar brightnessProgressBar; + private static final String PERSIST_LONGSHOT_MAX_SNAP = "persist.camera.longshot.max"; + private static int mLongShotMaxSnap = -1; + // Constant from android.hardware.Camera.Parameters private static final String KEY_PICTURE_FORMAT = "picture-format"; private SeekBar mBlurDegreeProgressBar; @@ -265,6 +264,8 @@ public class PhotoModule private byte[] mLastJpegData; private int mLastJpegOrientation = 0; + private static Context mApplicationContext = null; + private Runnable mDoSnapRunnable = new Runnable() { @Override public void run() { @@ -303,6 +304,7 @@ public class PhotoModule private boolean mMirror; private boolean mFirstTimeInitialized; private boolean mIsImageCaptureIntent; + private int mOrientationOffset; private int mCameraState = INIT; private boolean mSnapshotOnIdle = false; @@ -346,7 +348,6 @@ public class PhotoModule private FocusOverlayManager mFocusManager; private String mSceneMode; - private String mCurrTouchAfAec = ParametersWrapper.TOUCH_AF_AEC_ON; private String mSavedFlashMode = null; private final Handler mHandler = new MainHandler(); @@ -537,8 +538,6 @@ public class PhotoModule case SET_PHOTO_UI_PARAMS: { setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE); - mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, - mPreferences); break; } @@ -551,6 +550,16 @@ public class PhotoModule onPreviewStarted(); break; } + + case UNLOCK_CAM_SHUTTER: { + mUI.enableShutter(true); + break; + } + + case SET_FOCUS_RATIO: { + mUI.getFocusRing().setRadiusRatio((Float)msg.obj); + break; + } } } } @@ -570,6 +579,7 @@ public class PhotoModule public void init(CameraActivity activity, View parent) { mActivity = activity; mRootView = parent; + mOrientationOffset = CameraUtil.isDefaultToPortrait(mActivity) ? 0 : 90; mPreferences = ComboPreferences.get(mActivity); if (mPreferences == null) { mPreferences = new ComboPreferences(mActivity); @@ -579,6 +589,7 @@ public class PhotoModule mCameraId = getPreferredCameraId(mPreferences); mContentResolver = mActivity.getContentResolver(); + mApplicationContext = CameraApp.getContext(); // Surface texture is from camera screen nail and startPreview needs it. // This must be done before startPreview. @@ -588,6 +599,13 @@ public class PhotoModule CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); mUI = new PhotoUI(activity, this, parent); + + // Power shutter + mActivity.initPowerShutter(mPreferences); + + // Max brightness + mActivity.initMaxBrightness(mPreferences); + if (mOpenCameraThread == null) { mOpenCameraThread = new OpenCameraThread(); mOpenCameraThread.start(); @@ -597,22 +615,16 @@ public class PhotoModule mLocationManager = new LocationManager(mActivity, this); mSensorManager = (SensorManager)(mActivity.getSystemService(Context.SENSOR_SERVICE)); - brightnessProgressBar = (ProgressBar)mRootView.findViewById(R.id.progress); mBlurDegreeProgressBar = (SeekBar)mRootView.findViewById(R.id.blur_degree_bar); mBlurDegreeProgressBar.setOnSeekBarChangeListener(mBlurDegreeListener); mBlurDegreeProgressBar.setMax(100); - if (brightnessProgressBar instanceof SeekBar) { - SeekBar seeker = (SeekBar) brightnessProgressBar; - seeker.setOnSeekBarChangeListener(mSeekListener); - } - brightnessProgressBar.setMax(MAXIMUM_BRIGHTNESS); - mbrightness = mPreferences.getInt( - CameraSettings.KEY_BRIGHTNESS, - DEFAULT_BRIGHTNESS); - brightnessProgressBar.setProgress(mbrightness); - brightnessProgressBar.setVisibility(View.INVISIBLE); Storage.setSaveSDCard( mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); + + // LGE HDR mode + if (mApplicationContext != null) { + mLgeHdrMode = mApplicationContext.getResources().getBoolean(R.bool.lge_hdr_mode); + } } private void initializeControlByIntent() { @@ -637,7 +649,7 @@ public class PhotoModule // camera only private void locationFirstRun() { boolean enableRecordingLocation = false; - if (mActivity.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) + if (mActivity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { enableRecordingLocation = true; } @@ -735,7 +747,7 @@ public class PhotoModule Log.v(TAG, "onCameraOpened"); openCameraCommon(); resizeForPreviewAspectRatio(); - updateFocusManager(mUI); + mFocusManager.setFocusRing(mUI.getFocusRing()); } private void switchCamera() { @@ -759,7 +771,6 @@ public class PhotoModule } closeCamera(); mUI.collapseCameraControls(); - mUI.clearFaces(); if (mFocusManager != null) mFocusManager.removeMessages(); // Restart the camera and initialize the UI. From onCreate. @@ -776,8 +787,8 @@ public class PhotoModule mParameters = mCameraDevice.getParameters(); mInitialParams = mCameraDevice.getParameters(); initializeCapabilities(); - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; - mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + mMirror = (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT); mFocusManager.setMirror(mMirror); mFocusManager.setParameters(mInitialParams); setupPreview(); @@ -871,8 +882,6 @@ public class PhotoModule mUI.setPreference(CameraSettings.KEY_ZSL, ParametersWrapper.ZSL_OFF); mUI.setPreference(CameraSettings.KEY_FACE_DETECTION, ParametersWrapper.FACE_DETECTION_OFF); - mUI.setPreference(CameraSettings.KEY_TOUCH_AF_AEC, - ParametersWrapper.TOUCH_AF_AEC_OFF); mUI.setPreference(CameraSettings.KEY_FOCUS_MODE, Parameters.FOCUS_MODE_AUTO); mUI.setPreference(CameraSettings.KEY_FLASH_MODE, @@ -884,7 +893,7 @@ public class PhotoModule } void setPreviewFrameLayoutCameraOrientation(){ - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; //if camera mount angle is 0 or 180, we want to resize preview if (info.orientation % 180 == 0){ mUI.cameraOrientationPreviewResize(true); @@ -1013,9 +1022,9 @@ public class PhotoModule || mFaceDetectionStarted || mCameraState != IDLE) return; if (mParameters.getMaxNumDetectedFaces() > 0) { mFaceDetectionStarted = true; - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; mUI.onStartFaceDetection(mDisplayOrientation, - (info.facing == CameraInfo.CAMERA_FACING_FRONT)); + (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT)); mCameraDevice.setFaceDetectionCallback(mHandler, mUI); mCameraDevice.startFaceDetection(); } @@ -1027,12 +1036,19 @@ public class PhotoModule if (mParameters.getMaxNumDetectedFaces() > 0) { mFaceDetectionStarted = false; mCameraDevice.setFaceDetectionCallback(null, null); - mUI.pauseFaceDetection(); mCameraDevice.stopFaceDetection(); mUI.onStopFaceDetection(); } } + @Override + public void setFocusRatio(float ratio) { + mHandler.removeMessages(SET_FOCUS_RATIO); + Message m = mHandler.obtainMessage(SET_FOCUS_RATIO); + m.obj = ratio; + mHandler.sendMessage(m); + } + // TODO: need to check cached background apps memory and longshot ION memory private boolean isLongshotNeedCancel() { @@ -1076,6 +1092,11 @@ public class PhotoModule private final class LongshotShutterCallback implements CameraShutterCallback { + private int mExpectedLongshotSnapNum; + + public LongshotShutterCallback() { + mExpectedLongshotSnapNum = mLongshotSnapNum; + } @Override public void onShutter(CameraProxy camera) { @@ -1083,9 +1104,21 @@ public class PhotoModule mShutterLag = mShutterCallbackTime - mCaptureStartTime; Log.e(TAG, "[KPI Perf] PROFILE_SHUTTER_LAG mShutterLag = " + mShutterLag + "ms"); synchronized(mCameraDevice) { + if (mExpectedLongshotSnapNum != mLongshotSnapNum) { + return; + } + + if (++mLongshotSnapNum >= mLongShotMaxSnap && + (mLongShotMaxSnap != -1)) { + mLongshotActive = false; + mUI.enableShutter(false); + mCameraDevice.stopLongshot(); + return; + } if (mCameraState != LONGSHOT || !mLongshotActive) { + mCameraDevice.stopLongshot(); return; } @@ -1098,8 +1131,6 @@ public class PhotoModule return; } - mUI.doShutterAnimation(); - Location loc = getLocationAccordPictureFormat(mParameters.get(KEY_PICTURE_FORMAT)); mLongShotCaptureCount++; @@ -1212,6 +1243,7 @@ public class PhotoModule implements CameraPictureCallback { @Override public void onPictureTaken(byte [] data, CameraProxy camera) { + Log.d(TAG, "PostViewPictureCallback: onPictureTaken()"); mPostViewPictureCallbackTime = System.currentTimeMillis(); Log.v(TAG, "mShutterToPostViewCallbackTime = " + (mPostViewPictureCallbackTime - mShutterCallbackTime) @@ -1223,6 +1255,7 @@ public class PhotoModule implements CameraPictureCallback { @Override public void onPictureTaken(byte [] rawData, CameraProxy camera) { + Log.d(TAG, "RawPictureCallback: onPictureTaken()"); mRawPictureCallbackTime = System.currentTimeMillis(); Log.v(TAG, "mShutterToRawCallbackTime = " + (mRawPictureCallbackTime - mShutterCallbackTime) + "ms"); @@ -1238,12 +1271,11 @@ public class PhotoModule @Override public void onPictureTaken(final byte [] jpegData, CameraProxy camera) { + Log.d(TAG, "LongshotPictureCallback: onPictureTaken()"); if (mPaused) { return; } - mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden. - String jpegFilePath = new String(jpegData); mNamedImages.nameNewImage(mCaptureStartTime); NamedEntity name = mNamedImages.getNextNameEntity(); @@ -1330,8 +1362,12 @@ public class PhotoModule @Override public void onPictureTaken(byte [] jpegData, CameraProxy camera) { + String focusMode; + mUI.stopSelfieFlash(); - mUI.enableShutter(true); + if (mCameraState != LONGSHOT) { + mUI.enableShutter(true); + } if (mUI.isPreviewCoverVisible()) { // When take picture request is sent before starting preview, onPreviewFrame() // callback doesn't happen so removing preview cover here, instead. @@ -1353,14 +1389,29 @@ public class PhotoModule mUI.setSwipingEnabled(true); } + ExifInterface exif = Exif.getExif(jpegData); + boolean overrideMakerAndModelTag = false; + if (mApplicationContext != null) { + overrideMakerAndModelTag = + mApplicationContext.getResources() + .getBoolean(R.bool.override_maker_and_model_tag); + } + + if (overrideMakerAndModelTag) { + ExifTag maker = exif.buildTag(ExifInterface.TAG_MAKE, Build.MANUFACTURER); + exif.setTag(maker); + ExifTag model = exif.buildTag(ExifInterface.TAG_MODEL, Build.MODEL); + exif.setTag(model); + } + mReceivedSnapNum = mReceivedSnapNum + 1; mJpegPictureCallbackTime = System.currentTimeMillis(); if(mSnapshotMode == CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL) { Log.v(TAG, "JpegPictureCallback : in zslmode"); mParameters = mCameraDevice.getParameters(); - mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter"); + mBurstSnapNum = CameraUtil.getNumSnapsPerShutter(mParameters); } - Log.v(TAG, "JpegPictureCallback: Received = " + mReceivedSnapNum + + Log.v(TAG, "JpegPictureCallback: Received = " + mReceivedSnapNum + " " + "Burst count = " + mBurstSnapNum); // If postview callback has arrived, the captured image is displayed // in postview callback. If not, the captured image is displayed in @@ -1379,27 +1430,61 @@ public class PhotoModule Log.v(TAG, "mPictureDisplayedToJpegCallbackTime = " + mPictureDisplayedToJpegCallbackTime + "ms"); - mFocusManager.updateFocusUI(); // Ensure focus indicator is hidden. + if (isLongshotDone()) { + mCameraDevice.setLongshot(false); + } boolean needRestartPreview = !mIsImageCaptureIntent && !mPreviewRestartSupport && (mCameraState != LONGSHOT) && (mSnapshotMode != CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL) - && (mReceivedSnapNum == mBurstSnapNum); + && ((mReceivedSnapNum == mBurstSnapNum) && (mCameraState != LONGSHOT)); + + needRestartPreview |= (isLongshotDone() && !mFocusManager.isZslEnabled()); + needRestartPreview |= mLgeHdrMode && (mCameraState != LONGSHOT); + + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + + final boolean isAuxCamera = info.facing > CameraHolder.CameraInfo.CAMERA_FACING_FRONT; + final boolean isBackCamera = + info.facing == CameraHolder.CameraInfo.CAMERA_FACING_BACK; + final boolean isFrontCamera = + info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT; + boolean auxCameraRestartPreviewOnPictureTaken = false; + boolean backCameraRestartPreviewOnPictureTaken = false; + boolean frontCameraRestartPreviewOnPictureTaken = false; + if (mApplicationContext != null) { + auxCameraRestartPreviewOnPictureTaken = mApplicationContext.getResources() + .getBoolean(R.bool.additional_camera_restart_preview_onPictureTaken); + backCameraRestartPreviewOnPictureTaken = mApplicationContext.getResources() + .getBoolean(R.bool.back_camera_restart_preview_onPictureTaken); + frontCameraRestartPreviewOnPictureTaken = mApplicationContext.getResources() + .getBoolean(R.bool.front_camera_restart_preview_onPictureTaken); + } + + if ((isAuxCamera && auxCameraRestartPreviewOnPictureTaken || + isBackCamera && backCameraRestartPreviewOnPictureTaken || + isFrontCamera && frontCameraRestartPreviewOnPictureTaken) && + mCameraState != LONGSHOT) { + needRestartPreview = true; + } + if (needRestartPreview) { + Log.d(TAG, "JpegPictureCallback: needRestartPreview"); setupPreview(); - if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals( - mFocusManager.getFocusMode())) { + focusMode = mFocusManager.getFocusMode(false); + if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode) || + CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE.equals(focusMode)) { mCameraDevice.cancelAutoFocus(); } - } else if ((mReceivedSnapNum == mBurstSnapNum) - && (mCameraState != LONGSHOT)){ - mFocusManager.resetTouchFocus(); - if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals( - mFocusManager.getFocusMode())) { + } else if (((mCameraState != LONGSHOT) && (mReceivedSnapNum == mBurstSnapNum)) + || isLongshotDone()){ + mFocusManager.restartTouchFocusTimer(); + focusMode = mFocusManager.getFocusMode(false); + if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode) || + CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE.equals(focusMode)) { mCameraDevice.cancelAutoFocus(); } - mUI.resumeFaceDetection(); if (!mIsImageCaptureIntent) { setCameraState(IDLE); } @@ -1419,14 +1504,12 @@ public class PhotoModule } } if (!mRefocus || (mRefocus && mReceivedSnapNum == 7)) { - ExifInterface exif = Exif.getExif(jpegData); int orientation = Exif.getOrientation(exif); if(mCameraId == CameraHolder.instance().getFrontCameraId()) { IconListPreference selfieMirrorPref = (IconListPreference) mPreferenceGroup .findPreference(CameraSettings.KEY_SELFIE_MIRROR); if (selfieMirrorPref != null && selfieMirrorPref.getValue() != null && selfieMirrorPref.getValue().equalsIgnoreCase("enable")) { - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; jpegData = flipJpeg(jpegData, info.orientation, orientation); jpegData = addExifTags(jpegData, orientation); } @@ -1498,7 +1581,8 @@ public class PhotoModule } } // Animate capture with real jpeg data instead of a preview frame. - if (mCameraState != LONGSHOT) { + if ((mCameraState != LONGSHOT) || + isLongshotDone()) { Size pic_size = mParameters.getPictureSize(); if ((pic_size.width <= 352) && (pic_size.height<= 288)) { mUI.setDownFactor(2); //Downsample by 2 for CIF & below @@ -1545,6 +1629,10 @@ public class PhotoModule mJpegPictureCallbackTime = 0; } + if (isLongshotDone()) { + mLongshotSnapNum = 0; + } + if (mHiston && (mSnapshotMode ==CameraInfoWrapper.CAMERA_SUPPORT_MODE_ZSL)) { mActivity.runOnUiThread(new Runnable() { public void run() { @@ -1565,16 +1653,6 @@ public class PhotoModule } } - private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() { - public void onStartTrackingTouch(SeekBar bar) { - // no support - } - public void onProgressChanged(SeekBar bar, int progress, boolean fromtouch) { - } - public void onStopTrackingTouch(SeekBar bar) { - } - }; - private OnSeekBarChangeListener mBlurDegreeListener = new OnSeekBarChangeListener() { public void onStartTrackingTouch(SeekBar bar) { } @@ -1610,6 +1688,8 @@ public class PhotoModule setCameraState(IDLE); break; } + mCameraDevice.refreshParameters(); + mFocusManager.setParameters(mCameraDevice.getParameters()); mFocusManager.onAutoFocus(focused, mUI.isShutterPressed()); } } @@ -1620,6 +1700,8 @@ public class PhotoModule @Override public void onAutoFocusMoving( boolean moving, CameraProxy camera) { + mCameraDevice.refreshParameters(); + mFocusManager.setParameters(mCameraDevice.getParameters()); mFocusManager.onAutoFocusMoving(moving); } } @@ -1721,11 +1803,12 @@ public class PhotoModule } if (mCameraState == LONGSHOT) { + mLongshotSnapNum = 0; mCameraDevice.setLongshot(true); } // Set rotation and gps data. - int orientation = mOrientation; + int orientation = (mOrientation + mOrientationOffset) % 360; mJpegRotation = CameraUtil.getJpegRotation(mCameraId, orientation); String pictureFormat = mParameters.get(KEY_PICTURE_FORMAT); Location loc = getLocationAccordPictureFormat(pictureFormat); @@ -1753,11 +1836,14 @@ public class PhotoModule mParameters = mCameraDevice.getParameters(); } - try { - mBurstSnapNum = mParameters.getInt("num-snaps-per-shutter"); - }catch (NumberFormatException ex){ - mBurstSnapNum = 1; + // LGE G4: Disable HDR if luminance is low and flash gets used + if (CameraUtil.isLowLuminance(mParameters)) { + mParameters.set(CameraSettings.KEY_SNAPCAM_HDR_MODE, CameraSettings.LGE_HDR_MODE_OFF); + mCameraDevice.setParameters(mParameters); + mParameters = mCameraDevice.getParameters(); } + + mBurstSnapNum = CameraUtil.getNumSnapsPerShutter(mParameters); mReceivedSnapNum = 0; mPreviewRestartSupport = PersistUtil.isPreviewRestartEnabled(); mPreviewRestartSupport &= CameraSettings.isInternalPreviewSupported( @@ -1768,10 +1854,12 @@ public class PhotoModule mPreviewRestartSupport &= PIXEL_FORMAT_JPEG.equalsIgnoreCase( pictureFormat); + mUI.enableShutter(false); + // We don't want user to press the button again while taking a // multi-second HDR photo. For longshot, no need to disable. - if (mCameraState != LONGSHOT) { - mUI.enableShutter(false); + if (!CameraUtil.SCENE_MODE_HDR.equals(mSceneMode)) { + mHandler.sendEmptyMessageDelayed(UNLOCK_CAM_SHUTTER, 120); } if (!isShutterSoundOn()) { @@ -1801,6 +1889,17 @@ public class PhotoModule mRawPictureCallback, mPostViewPictureCallback, new JpegPictureCallback(loc)); setCameraState(SNAPSHOT_IN_PROGRESS); + + // LGE G4: Preview needs to be restarted when flash gets used + if (CameraUtil.isSupported(mParameters, CameraSettings.KEY_LUMINANCE_CONDITION)) { + String flashMode = mPreferences.getString(CameraSettings.KEY_FLASH_MODE, + mActivity.getString(R.string.pref_camera_flashmode_default)); + if (flashMode.equals(Parameters.FLASH_MODE_ON) || + (!flashMode.equals(Parameters.FLASH_MODE_OFF) && + CameraUtil.isLowLuminance(mParameters))) { + setupPreview(); + } + } } mNamedImages.nameNewImage(mCaptureStartTime, mRefocus); @@ -1838,8 +1937,26 @@ public class PhotoModule } } + private void updateLongshotScene() { + String[] longshotScenes = mActivity.getResources().getStringArray( + R.array.longshot_scenemodes); + if (longshotScenes.length == 0) { + mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, null); + return; + } + boolean useLongshot = false; + for (String scene : longshotScenes) { + if (scene.equals(mSceneMode)) { + useLongshot = true; + break; + } + } + mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, + useLongshot ? mActivity.getString(R.string.setting_on_value) : + mActivity.getString(R.string.setting_off_value)); + } + private void updateCommonManual3ASettings() { - String touchAfAec = ParametersWrapper.TOUCH_AF_AEC_OFF; mSceneMode = Parameters.SCENE_MODE_AUTO; String flashMode = Parameters.FLASH_MODE_OFF; String redeyeReduction = mActivity.getString(R.string. @@ -1851,27 +1968,25 @@ public class PhotoModule String exposureCompensation = CameraSettings.EXPOSURE_DEFAULT_VALUE; if (mManual3AEnabled > 0) { overrideCameraSettings(flashMode, null, null, - exposureCompensation, touchAfAec, + exposureCompensation, ParametersWrapper.getAutoExposure(mParameters), - Integer.toString(ParametersWrapper.getSaturation(mParameters)), - Integer.toString(ParametersWrapper.getContrast(mParameters)), - Integer.toString(ParametersWrapper.getSharpness(mParameters)), + getSaturationSafe(), + getContrastSafe(), + getSharpnessSafe(), colorEffect, mSceneMode, redeyeReduction, aeBracketing); mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, mActivity.getString(R.string.setting_off_value)); } else { //enable all - touchAfAec = mActivity.getString( - R.string.pref_camera_touchafaec_default); overrideCameraSettings(null, null, null, - null, touchAfAec, null, + null, null, null, null, null, null, null, null, null); mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, null); } - String isoMode = ParametersWrapper.getISOValue(mParameters); + String isoMode = CameraSettings.getISOValue(mParameters); final String isoManual = CameraSettings.KEY_MANUAL_ISO; if (isoMode.equals(isoManual)) { final String isoPref = mPreferences.getString( @@ -1887,7 +2002,7 @@ public class PhotoModule } if ((mManual3AEnabled & MANUAL_FOCUS) != 0) { mUI.overrideSettings(CameraSettings.KEY_FOCUS_MODE, - mFocusManager.getFocusMode()); + mFocusManager.getFocusMode(false)); } } @@ -1899,7 +2014,6 @@ public class PhotoModule String focusMode = null; String colorEffect = null; String exposureCompensation = null; - String touchAfAec = null; boolean disableLongShot = false; String ubiFocusOn = mActivity.getString(R.string. @@ -1965,8 +2079,10 @@ public class PhotoModule (fssr != null && fssr.equals(fssrOn)) || (truePortrait != null && truePortrait.equals(truPortraitOn)) || (stillMore != null && stillMore.equals(stillMoreOn))) { - if ( (optiZoom != null && optiZoom.equals(optiZoomOn)) || - (reFocus != null && reFocus.equals(reFocusOn)) ) { + if ((optiZoom != null && optiZoom.equals(optiZoomOn)) || + (reFocus != null && reFocus.equals(reFocusOn)) || + (CameraSettings.hasChromaFlashScene(mActivity) && + chromaFlash != null && chromaFlash.equals(chromaFlashOn))) { sceneMode = null; } else { mSceneMode = sceneMode = Parameters.SCENE_MODE_AUTO; @@ -1982,7 +2098,7 @@ public class PhotoModule exposureCompensation = CameraSettings.EXPOSURE_DEFAULT_VALUE; overrideCameraSettings(null, null, focusMode, - exposureCompensation, touchAfAec, null, + exposureCompensation, null, null, null, null, colorEffect, sceneMode, redeyeReduction, aeBracketing); disableLongShot = true; @@ -1990,10 +2106,11 @@ public class PhotoModule // If scene mode is set, for white balance and focus mode // read settings from preferences so we retain user preferences. - if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { + if (!Parameters.SCENE_MODE_AUTO.equals(mSceneMode) && + !"asd".equals(mSceneMode) && !"sports".equals(mSceneMode)) { flashMode = Parameters.FLASH_MODE_OFF; String whiteBalance = Parameters.WHITE_BALANCE_AUTO; - focusMode = mFocusManager.getFocusMode(); + focusMode = mFocusManager.getFocusMode(false); colorEffect = mParameters.getColorEffect(); String defaultEffect = mActivity.getString(R.string.pref_camera_coloreffect_default); if (CameraUtil.SCENE_MODE_HDR.equals(mSceneMode)) { @@ -2011,20 +2128,19 @@ public class PhotoModule } exposureCompensation = Integer.toString(mParameters.getExposureCompensation()); - touchAfAec = mCurrTouchAfAec; overrideCameraSettings(null, whiteBalance, focusMode, - exposureCompensation, touchAfAec, + exposureCompensation, ParametersWrapper.getAutoExposure(mParameters), - Integer.toString(ParametersWrapper.getSaturation(mParameters)), - Integer.toString(ParametersWrapper.getContrast(mParameters)), - Integer.toString(ParametersWrapper.getSharpness(mParameters)), + getSaturationSafe(), + getContrastSafe(), + getSharpnessSafe(), colorEffect, sceneMode, redeyeReduction, aeBracketing); } else if (mFocusManager.isZslEnabled()) { focusMode = mParameters.getFocusMode(); overrideCameraSettings(null, null, focusMode, - exposureCompensation, touchAfAec, null, + exposureCompensation, null, null, null, null, colorEffect, sceneMode, redeyeReduction, aeBracketing); } else { @@ -2032,7 +2148,7 @@ public class PhotoModule updateCommonManual3ASettings(); } else { overrideCameraSettings(null, null, focusMode, - exposureCompensation, touchAfAec, null, + exposureCompensation, null, null, null, null, colorEffect, sceneMode, redeyeReduction, aeBracketing); } @@ -2043,11 +2159,17 @@ public class PhotoModule flashMode = Parameters.FLASH_MODE_OFF; mParameters.setFlashMode(flashMode); } + + if (chromaFlash != null && chromaFlash.equals(chromaFlashOn)) { + flashMode = Parameters.FLASH_MODE_ON; + mParameters.setFlashMode(flashMode); + } + if (disableLongShot) { mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, mActivity.getString(R.string.setting_off_value)); } else { - mUI.overrideSettings(CameraSettings.KEY_LONGSHOT, null); + updateLongshotScene(); } if(TsMakeupManager.HAS_TS_MAKEUP) { @@ -2093,17 +2215,16 @@ public class PhotoModule private void overrideCameraSettings(final String flashMode, final String whiteBalance, final String focusMode, - final String exposureMode, final String touchMode, - final String autoExposure, final String saturation, - final String contrast, final String sharpness, - final String coloreffect, final String sceneMode, - final String redeyeReduction, final String aeBracketing) { + final String exposureMode, final String autoExposure, + final String saturation, final String contrast, + final String sharpness, final String coloreffect, + final String sceneMode, final String redeyeReduction, + final String aeBracketing) { mUI.overrideSettings( CameraSettings.KEY_FLASH_MODE, flashMode, CameraSettings.KEY_WHITE_BALANCE, whiteBalance, CameraSettings.KEY_FOCUS_MODE, focusMode, CameraSettings.KEY_EXPOSURE, exposureMode, - CameraSettings.KEY_TOUCH_AF_AEC, touchMode, CameraSettings.KEY_AUTOEXPOSURE, autoExposure, CameraSettings.KEY_SATURATION, saturation, CameraSettings.KEY_CONTRAST, contrast, @@ -2119,7 +2240,7 @@ public class PhotoModule mCameraId, CameraHolder.instance().getCameraInfo()); mPreferenceGroup = settings.getPreferenceGroup(R.xml.camera_preferences); - int numOfCams = Camera.getNumberOfCameras(); + int numOfCams = CameraHolder.instance().getNumberOfCameras(); Log.e(TAG,"loadCameraPreferences() updating camera_id pref"); @@ -2137,8 +2258,8 @@ public class PhotoModule int[] largeIconIds = new int[numOfCams]; for(int i=0;i<numOfCams;i++) { - CameraInfo info = CameraHolder.instance().getCameraInfo()[i]; - if(info.facing == CameraInfo.CAMERA_FACING_BACK) { + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[i]; + if(info.facing == CameraHolder.CameraInfo.CAMERA_FACING_BACK) { iconIds[i] = R.drawable.ic_switch_back; entries[i] = mActivity.getResources().getString(R.string.pref_camera_id_entry_back); labels[i] = mActivity.getResources().getString(R.string.pref_camera_id_label_back); @@ -2164,6 +2285,7 @@ public class PhotoModule // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return; + orientation = (orientation - mOrientationOffset + 360) % 360; int oldOrientation = mOrientation; mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); if (oldOrientation != mOrientation) { @@ -2314,20 +2436,7 @@ public class PhotoModule synchronized(mCameraDevice) { if (mCameraState == LONGSHOT) { mLongshotActive = false; - mCameraDevice.setLongshot(false); - mUI.animateCapture(mLastJpegData); - mLastJpegData = null; - if (!mFocusManager.isZslEnabled()) { - setupPreview(); - } else { - setCameraState(IDLE); - mFocusManager.resetTouchFocus(); - if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals( - mFocusManager.getFocusMode())) { - mCameraDevice.cancelAutoFocus(); - } - mUI.resumeFaceDetection(); - } + mUI.enableShutter(false); } } @@ -2487,6 +2596,7 @@ public class PhotoModule @Override public void onResumeBeforeSuper() { mPaused = false; + if (mFocusManager == null) initializeFocusManager(); } private void openCamera() { @@ -2596,8 +2706,6 @@ public class PhotoModule } mUI.initDisplayChangeListener(); keepScreenOnAwhile(); - mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, - mPreferences); UsageStatistics.onContentViewChanged( UsageStatistics.COMPONENT_CAMERA, "PhotoModule"); @@ -2672,6 +2780,12 @@ public class PhotoModule // (e.g. onResume -> onPause -> onResume). stopPreview(); + // Load the power shutter + mActivity.initPowerShutter(mPreferences); + + // Load max brightness + mActivity.initMaxBrightness(mPreferences); + mNamedImages = null; if (mLocationManager != null) mLocationManager.recordLocation(false); @@ -2708,29 +2822,17 @@ public class PhotoModule if (mFocusManager != null) { mFocusManager.removeMessages(); } else { - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; - mMirror = (info.facing == CameraInfo.CAMERA_FACING_FRONT); + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + mMirror = (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT); String[] defaultFocusModes = mActivity.getResources().getStringArray( R.array.pref_camera_focusmode_default_array); - mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes, - mInitialParams, this, mMirror, - mActivity.getMainLooper(), mUI); - } - } - - private void updateFocusManager(PhotoUI mUI) { - // Idea here is to let focus manager create in camera open thread - // (in initializeFocusManager) even if photoUI is null by that time so - // as to not block start preview process. Once UI creation is done, - // we will update focus manager with proper UI. - if (mFocusManager != null && mUI != null) { - mFocusManager.setPhotoUI(mUI); - - View root = mUI.getRootView(); - // These depend on camera parameters. - int width = root.getWidth(); - int height = root.getHeight(); - mFocusManager.setPreviewSize(width, height); + synchronized (this) { + if (mFocusManager == null) { + mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes, + mInitialParams, this, mMirror, + mActivity.getMainLooper(), mUI.getFocusRing(), mActivity); + } + } } } @@ -2791,6 +2893,7 @@ public class PhotoModule if (null != mCameraDevice ) { mCameraDevice.cancelAutoFocus(); setCameraState(IDLE); + mFocusManager.setAeAwbLock(false); setCameraParameters(UPDATE_PARAM_PREFERENCE); } } @@ -2804,13 +2907,8 @@ public class PhotoModule || mCameraState == PREVIEW_STOPPED) { return; } - //If Touch AF/AEC is disabled in UI, return - if(this.mTouchAfAecFlag == false) { - return; - } // Check if metering area or focus area is supported. if (!mFocusAreaSupported && !mMeteringAreaSupported) return; - if (! mFocusManager.getPreviewRect().contains(x, y)) return; mFocusManager.onSingleTapUp(x, y); } @@ -2821,14 +2919,34 @@ public class PhotoModule @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + // Do not handle any key if the activity is + // not in active camera/video mode + if (!mActivity.isInCameraApp()) { + return false; + } switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + if (mFirstTimeInitialized && (mUI.mMenuInitialized)) { + if (!CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + onShutterButtonFocus(true); + } else { + mUI.onScaleStepResize(true); + } + } + return true; case KeyEvent.KEYCODE_VOLUME_DOWN: - if (CameraUtil.volumeKeyShutterDisable(mActivity)) { - return false; + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + if (mFirstTimeInitialized && (mUI.mMenuInitialized)) { + if (!CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + onShutterButtonFocus(true); + } else { + mUI.onScaleStepResize(false); + } } + return true; case KeyEvent.KEYCODE_FOCUS: - if (/*TODO: mActivity.isInCameraApp() &&*/ mFirstTimeInitialized) { + if (mFirstTimeInitialized) { if (event.getRepeatCount() == 0) { onShutterButtonFocus(true); } @@ -2836,52 +2954,11 @@ public class PhotoModule } return false; case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_HEADSETHOOK: if (mFirstTimeInitialized && event.getRepeatCount() == 0) { onShutterButtonClick(); } return true; - case KeyEvent.KEYCODE_DPAD_LEFT: - if ( (mCameraState != PREVIEW_STOPPED) && (mFocusManager != null) && - (mFocusManager.getCurrentFocusState() != mFocusManager.STATE_FOCUSING) && - (mFocusManager.getCurrentFocusState() != mFocusManager.STATE_FOCUSING_SNAP_ON_FINISH) ) { - if (mbrightness > MINIMUM_BRIGHTNESS) { - mbrightness-=mbrightness_step; - synchronized (mCameraDevice) { - /* Set the "luma-adaptation" parameter */ - mParameters = mCameraDevice.getParameters(); - mParameters.set("luma-adaptation", String.valueOf(mbrightness)); - mCameraDevice.setParameters(mParameters); - } - } - brightnessProgressBar.setProgress(mbrightness); - Editor editor = mPreferences.edit(); - editor.putInt(CameraSettings.KEY_BRIGHTNESS, mbrightness); - editor.apply(); - brightnessProgressBar.setVisibility(View.INVISIBLE); - mBrightnessVisible = true; - } - break; - case KeyEvent.KEYCODE_DPAD_RIGHT: - if ( (mCameraState != PREVIEW_STOPPED) && (mFocusManager != null) && - (mFocusManager.getCurrentFocusState() != mFocusManager.STATE_FOCUSING) && - (mFocusManager.getCurrentFocusState() != mFocusManager.STATE_FOCUSING_SNAP_ON_FINISH) ) { - if (mbrightness < MAXIMUM_BRIGHTNESS) { - mbrightness+=mbrightness_step; - synchronized (mCameraDevice) { - /* Set the "luma-adaptation" parameter */ - mParameters = mCameraDevice.getParameters(); - mParameters.set("luma-adaptation", String.valueOf(mbrightness)); - mCameraDevice.setParameters(mParameters); - } - } - brightnessProgressBar.setProgress(mbrightness); - Editor editor = mPreferences.edit(); - editor.putInt(CameraSettings.KEY_BRIGHTNESS, mbrightness); - editor.apply(); - brightnessProgressBar.setVisibility(View.INVISIBLE); - mBrightnessVisible = true; - } - break; case KeyEvent.KEYCODE_DPAD_CENTER: // If we get a dpad center event without any focused view, move // the focus to the shutter button and press it. @@ -2893,6 +2970,12 @@ public class PhotoModule mUI.pressShutterButton(); } return true; + case KeyEvent.KEYCODE_POWER: + if (mFirstTimeInitialized && event.getRepeatCount() == 0 + && CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + onShutterButtonFocus(true); + } + return true; } return false; } @@ -2902,17 +2985,24 @@ public class PhotoModule switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: - if (/*mActivity.isInCameraApp() && */ mFirstTimeInitialized - && !CameraUtil.volumeKeyShutterDisable(mActivity)) { + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + if (!CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey() + && mFirstTimeInitialized) { onShutterButtonClick(); - return true; } - return false; + return true; case KeyEvent.KEYCODE_FOCUS: if (mFirstTimeInitialized) { onShutterButtonFocus(false); } return true; + case KeyEvent.KEYCODE_POWER: + if (CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey() + && mFirstTimeInitialized) { + onShutterButtonClick(); + } + return true; } return false; } @@ -2971,6 +3061,8 @@ public class PhotoModule return; } + String focusMode; + synchronized (mCameraDevice) { SurfaceHolder sh = null; Log.v(TAG, "startPreview: SurfaceHolder (MDP path)"); @@ -2988,13 +3080,14 @@ public class PhotoModule } mErrorCallback.setActivity(mActivity); mCameraDevice.setErrorCallback(mErrorCallback); - // ICS camera frameworks has a bug. Face detection state is not cleared 1589 - // after taking a picture. Stop the preview to work around it. The bug - // was fixed in JB. + + // Reset camera state after taking a picture if (mCameraState != PREVIEW_STOPPED && mCameraState != INIT) { - stopPreview(); + setCameraState(IDLE); } + if (mFocusManager == null) initializeFocusManager(); + if (!mSnapshotOnIdle) { mFocusManager.setAeAwbLock(false); // Unlock AE and AWB. } @@ -3016,7 +3109,10 @@ public class PhotoModule if (!mSnapshotOnIdle && !mInstantCaptureSnapShot) { // If the focus mode is continuous autofocus, call cancelAutoFocus to // resume it because it may have been paused by autoFocus call. - if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(mFocusManager.getFocusMode()) && mCameraState !=INIT) { + focusMode = mFocusManager.getFocusMode(false); + if (CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE.equals(focusMode) && + mCameraState != INIT || + CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE.equals(focusMode)) { mCameraDevice.cancelAutoFocus(); } } else { @@ -3028,6 +3124,10 @@ public class PhotoModule @Override public void stopPreview() { if (mCameraDevice != null && mCameraState != PREVIEW_STOPPED) { + if (mCameraState == LONGSHOT) { + mCameraDevice.setLongshot(false); + mLongshotActive = false; + } Log.v(TAG, "stopPreview"); mCameraDevice.stopPreview(); } @@ -3139,42 +3239,55 @@ public class PhotoModule } } + private String getSaturationSafe() { + String ret = null; + if (CameraUtil.isSupported(mParameters, "saturation") && + CameraUtil.isSupported(mParameters, "max-saturation")) { + ret = mPreferences.getString( + CameraSettings.KEY_SATURATION, + mActivity.getString(R.string.pref_camera_saturation_default)); + } + return ret; + } + + private String getContrastSafe() { + String ret = null; + if (CameraUtil.isSupported(mParameters, "contrast") && + CameraUtil.isSupported(mParameters, "max-contrast")) { + ret = mPreferences.getString( + CameraSettings.KEY_CONTRAST, + mActivity.getString(R.string.pref_camera_contrast_default)); + } + return ret; + } + + private String getSharpnessSafe() { + String ret = null; + if (CameraUtil.isSupported(mParameters, "sharpness") && + CameraUtil.isSupported(mParameters, "max-sharpness")) { + ret = mPreferences.getString( + CameraSettings.KEY_SHARPNESS, + mActivity.getString(R.string.pref_camera_sharpness_default)); + } + return ret; + } + /** This can run on a background thread, so don't do UI updates here.*/ private void qcomUpdateCameraParametersPreference() { //qcom Related Parameter update - //Set Brightness. - mParameters.set("luma-adaptation", String.valueOf(mbrightness)); - String longshot_enable = mPreferences.getString( CameraSettings.KEY_LONGSHOT, mActivity.getString(R.string.pref_camera_longshot_default)); mParameters.set("long-shot", longshot_enable); String optizoomOn = mActivity.getString(R.string .pref_camera_advanced_feature_value_optizoom_on); + String chromaFlashOn = mActivity.getString(R.string + .pref_camera_advanced_feature_value_chromaflash_on); - if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode) || - CameraUtil.SCENE_MODE_HDR.equals(mSceneMode) || - optizoomOn.equals(mSceneMode)) { - // Set Touch AF/AEC parameter. - String touchAfAec = mPreferences.getString( - CameraSettings.KEY_TOUCH_AF_AEC, - mActivity.getString(R.string.pref_camera_touchafaec_default)); - if (CameraUtil.isSupported(touchAfAec, - ParametersWrapper.getSupportedTouchAfAec(mParameters))) { - mCurrTouchAfAec = touchAfAec; - ParametersWrapper.setTouchAfAec(mParameters, touchAfAec); - } - } else { - ParametersWrapper.setTouchAfAec(mParameters, ParametersWrapper.TOUCH_AF_AEC_OFF); - mFocusManager.resetTouchFocus(); - } - try { - if(ParametersWrapper.getTouchAfAec(mParameters).equals(ParametersWrapper.TOUCH_AF_AEC_ON)) - this.mTouchAfAecFlag = true; - else - this.mTouchAfAecFlag = false; - } catch(Exception e){ - Log.e(TAG, "Handled NULL pointer Exception"); + // Set Touch AF/AEC parameter. + if (CameraUtil.isSupported(mParameters.TOUCH_AF_AEC_ON, + mParameters.getSupportedTouchAfAec())) { + mParameters.setTouchAfAec(mParameters.TOUCH_AF_AEC_ON); } // Set Picture Format @@ -3199,26 +3312,20 @@ public class PhotoModule String jpegQuality = mPreferences.getString( CameraSettings.KEY_JPEG_QUALITY, mActivity.getString(R.string.pref_camera_jpegquality_default)); - //mUnsupportedJpegQuality = false; Size pic_size = mParameters.getPictureSize(); if (pic_size == null) { Log.e(TAG, "error getPictureSize: size is null"); - } - else{ - if("100".equals(jpegQuality) && (pic_size.width >= 3200)){ - //mUnsupportedJpegQuality = true; - }else { - mParameters.setJpegQuality(JpegEncodingQualityMappings.getQualityNumber(jpegQuality)); - int jpegFileSize = estimateJpegFileSize(pic_size, jpegQuality); - if (jpegFileSize != mJpegFileSizeEstimation) { - mJpegFileSizeEstimation = jpegFileSize; - mHandler.post(new Runnable() { - @Override - public void run() { - updateRemainingPhotos(); - } - }); - } + } else { + mParameters.setJpegQuality(JpegEncodingQualityMappings.getQualityNumber(jpegQuality)); + int jpegFileSize = estimateJpegFileSize(pic_size, jpegQuality); + if (jpegFileSize != mJpegFileSizeEstimation) { + mJpegFileSizeEstimation = jpegFileSize; + mHandler.post(new Runnable() { + @Override + public void run() { + updateRemainingPhotos(); + } + }); } } @@ -3252,10 +3359,18 @@ public class PhotoModule CameraSettings.KEY_ISO, mActivity.getString(R.string.pref_camera_iso_default)); if (CameraUtil.isSupported(iso, - ParametersWrapper.getSupportedIsoValues(mParameters))) { - ParametersWrapper.setISOValue(mParameters, iso); + CameraSettings.getSupportedIsoValues(mParameters))) { + CameraSettings.setISOValue(mParameters, iso); } } + // Set shutter speed parameter + String shutterSpeed = mPreferences.getString( + CameraSettings.KEY_SHUTTER_SPEED, + mActivity.getString(R.string.pref_camera_shutter_speed_default)); + if (CameraUtil.isSupported(shutterSpeed, + CameraSettings.getSupportedShutterSpeedValues(mParameters))) { + mParameters.set(CameraSettings.KEY_SNAPCAM_SHUTTER_SPEED, shutterSpeed); + } // Set color effect parameter. String colorEffect = mPreferences.getString( CameraSettings.KEY_COLOR_EFFECT, @@ -3266,32 +3381,35 @@ public class PhotoModule } //Set Saturation - String saturationStr = mPreferences.getString( - CameraSettings.KEY_SATURATION, - mActivity.getString(R.string.pref_camera_saturation_default)); - int saturation = Integer.parseInt(saturationStr); - Log.v(TAG, "Saturation value =" + saturation); - if((0 <= saturation) && (saturation <= ParametersWrapper.getMaxSaturation(mParameters))){ - ParametersWrapper.setSaturation(mParameters, saturation); + String saturationStr = getSaturationSafe(); + if (saturationStr != null) { + int saturation = Integer.parseInt(saturationStr); + Log.v(TAG, "Saturation value =" + saturation); + if ((0 <= saturation) && + (saturation <= ParametersWrapper.getMaxSaturation(mParameters))) { + ParametersWrapper.setSaturation(mParameters, saturation); + } } // Set contrast parameter. - String contrastStr = mPreferences.getString( - CameraSettings.KEY_CONTRAST, - mActivity.getString(R.string.pref_camera_contrast_default)); - int contrast = Integer.parseInt(contrastStr); - Log.v(TAG, "Contrast value =" +contrast); - if((0 <= contrast) && (contrast <= ParametersWrapper.getMaxContrast(mParameters))){ - ParametersWrapper.setContrast(mParameters, contrast); + String contrastStr = getContrastSafe(); + if (contrastStr != null) { + int contrast = Integer.parseInt(contrastStr); + Log.v(TAG, "Contrast value =" + contrast); + if ((0 <= contrast) && + (contrast <= ParametersWrapper.getMaxContrast(mParameters))) { + ParametersWrapper.setContrast(mParameters, contrast); + } } // Set sharpness parameter - String sharpnessStr = mPreferences.getString( - CameraSettings.KEY_SHARPNESS, - mActivity.getString(R.string.pref_camera_sharpness_default)); - int sharpness = Integer.parseInt(sharpnessStr) * - (ParametersWrapper.getMaxSharpness(mParameters)/MAX_SHARPNESS_LEVEL); - Log.v(TAG, "Sharpness value =" + sharpness); - if((0 <= sharpness) && (sharpness <= ParametersWrapper.getMaxSharpness(mParameters))){ - ParametersWrapper.setSharpness(mParameters, sharpness); + String sharpnessStr = getSharpnessSafe(); + if (sharpnessStr != null) { + int sharpness = Integer.parseInt(sharpnessStr) * + (ParametersWrapper.getMaxSharpness(mParameters)/MAX_SHARPNESS_LEVEL); + Log.v(TAG, "Sharpness value =" + sharpness); + if ((0 <= sharpness) && + (sharpness <= ParametersWrapper.getMaxSharpness(mParameters))) { + ParametersWrapper.setSharpness(mParameters, sharpness); + } } // Set Face Recognition String faceRC = mPreferences.getString( @@ -3363,22 +3481,12 @@ public class PhotoModule String hdrMode = mPreferences.getString( CameraSettings.KEY_HDR_MODE, mActivity.getString(R.string.pref_camera_hdr_mode_default)); - Log.v(TAG, "HDR Mode value =" + hdrMode); if (CameraUtil.isSupported(hdrMode, CameraSettings.getSupportedHDRModes(mParameters))) { + Log.v(TAG, "HDR Mode value =" + hdrMode); mParameters.set(CameraSettings.KEY_SNAPCAM_HDR_MODE, hdrMode); } - // Set hdr need 1x - String hdrNeed1x = mPreferences.getString( - CameraSettings.KEY_HDR_NEED_1X, - mActivity.getString(R.string.pref_camera_hdr_need_1x_default)); - Log.v(TAG, "HDR need 1x value =" + hdrNeed1x); - if (CameraUtil.isSupported(hdrNeed1x, - CameraSettings.getSupportedHDRNeed1x(mParameters))) { - mParameters.set(CameraSettings.KEY_SNAPCAM_HDR_NEED_1X, hdrNeed1x); - } - // Set Advanced features. String advancedFeature = mPreferences.getString( CameraSettings.KEY_ADVANCED_FEATURES, @@ -3566,32 +3674,6 @@ public class PhotoModule String zsl = mPreferences.getString(CameraSettings.KEY_ZSL, mActivity.getString(R.string.pref_camera_zsl_default)); - String auto_hdr = mPreferences.getString(CameraSettings.KEY_AUTO_HDR, - mActivity.getString(R.string.pref_camera_hdr_default)); - if (CameraUtil.isAutoHDRSupported(mParameters)) { - mParameters.set("auto-hdr-enable",auto_hdr); - if (auto_hdr.equals("enable")) { - mActivity.runOnUiThread(new Runnable() { - public void run() { - if (mDrawAutoHDR != null) { - mDrawAutoHDR.setVisibility(View.VISIBLE); - } - } - }); - mParameters.setSceneMode("asd"); - mCameraDevice.setMetadataCb(mMetaDataCallback); - } - else { - mAutoHdrEnable = false; - mActivity.runOnUiThread( new Runnable() { - public void run () { - if (mDrawAutoHDR != null) { - mDrawAutoHDR.setVisibility (View.INVISIBLE); - } - } - }); - } - } ParametersWrapper.setZSLMode(mParameters, zsl); if(zsl.equals("on") && ParametersWrapper.getSupportedZSLModes(mParameters) != null) { //Switch on ZSL Camera mode @@ -3603,16 +3685,22 @@ public class PhotoModule mParameters.set(KEY_PICTURE_FORMAT, PIXEL_FORMAT_JPEG); //Try to set CAF for ZSL - if(CameraUtil.isSupported(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE, - mParameters.getSupportedFocusModes()) && !mFocusManager.isTouch()) { - mFocusManager.overrideFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); - mParameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + if (!mFocusManager.isTouch()) { + if (CameraUtil.isSupported(CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE, + mParameters.getSupportedFocusModes())) { + mFocusManager.overrideFocusMode(CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE); + mParameters.setFocusMode(CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE); + } else if (CameraUtil.isSupported(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE, + mParameters.getSupportedFocusModes())) { + mFocusManager.overrideFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + mParameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + } } else if (mFocusManager.isTouch()) { mFocusManager.overrideFocusMode(null); - mParameters.setFocusMode(mFocusManager.getFocusMode()); + mParameters.setFocusMode(mFocusManager.getFocusMode(false)); } else { // If not supported use the current mode - mFocusManager.overrideFocusMode(mFocusManager.getFocusMode()); + mFocusManager.overrideFocusMode(mFocusManager.getFocusMode(false)); } if (!pictureFormat.equals(PIXEL_FORMAT_JPEG)) { @@ -3629,7 +3717,7 @@ public class PhotoModule mFocusManager.setZslEnable(false); if ((mManual3AEnabled & MANUAL_FOCUS) == 0) { mFocusManager.overrideFocusMode(null); - mParameters.setFocusMode(mFocusManager.getFocusMode()); + mParameters.setFocusMode(mFocusManager.getFocusMode(false)); } } @@ -3665,6 +3753,8 @@ public class PhotoModule + mInstantCaptureSnapShot); mParameters.set(CameraSettings.KEY_QC_INSTANT_CAPTURE, instantCapture); + updateAutoHDR(); + //Set Histogram String histogram = mPreferences.getString( CameraSettings.KEY_HISTOGRAM, @@ -3704,9 +3794,9 @@ public class PhotoModule mParameters.setFlashMode(fMode); } - if(!mFocusManager.getFocusMode().equals(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) && + if(!mFocusManager.getFocusMode(false).equals(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) && !mFocusManager.isFocusCompleted()) { - mUI.clearFocus(); + mUI.getFocusRing().stopFocusAnimations(); } String bokehMode = mPreferences.getString( @@ -3772,6 +3862,9 @@ public class PhotoModule mParameters.set(CameraSettings.KEY_QC_BOKEH_BLUR_VALUE, bokehBlurDegree); Log.v(TAG, "Bokeh Mode = " + bokehMode + " bokehMpo = " + bokehMpo + " bokehBlurDegree = " + bokehBlurDegree); + + mLongShotMaxSnap = SystemProperties.getInt(PERSIST_LONGSHOT_MAX_SNAP, -1); + mParameters.set("max-longshot-snap",mLongShotMaxSnap); } private int estimateJpegFileSize(final Size size, final String quality) { @@ -3793,6 +3886,44 @@ public class PhotoModule } } + private void updateAutoHDR() { + String autoHdr = mPreferences.getString(CameraSettings.KEY_AUTO_HDR, + mActivity.getString(R.string.pref_camera_auto_hdr_default)); + String advancedFeature = mPreferences.getString( + CameraSettings.KEY_ADVANCED_FEATURES, + mActivity.getString(R.string.pref_camera_advanced_feature_default)); + + if (CameraUtil.isAutoHDRSupported(mParameters)) { + if (autoHdr.equals("enable") && + ("asd".equals(mSceneMode) || "auto".equals(mSceneMode)) && + CameraUtil.isSupported("asd", mParameters.getSupportedSceneModes()) && + (advancedFeature == null || "none".equals(advancedFeature))) { + mActivity.runOnUiThread(new Runnable() { + public void run() { + if (mDrawAutoHDR != null) { + mDrawAutoHDR.setVisibility(View.VISIBLE); + } + } + }); + mParameters.setSceneMode("asd"); + mCameraDevice.setMetadataCb(mMetaDataCallback); + mParameters.set("auto-hdr-enable", "enable"); + } + else { + mAutoHdrEnable = false; + mActivity.runOnUiThread( new Runnable() { + public void run () { + if (mDrawAutoHDR != null) { + mDrawAutoHDR.setVisibility (View.INVISIBLE); + } + } + }); + mCameraDevice.setMetadataCb(null); + mParameters.set("auto-hdr-enable", "disable"); + } + } + } + private void setFlipValue() { // Read Flip mode from adb command //value: 0(default) - FLIP_MODE_OFF @@ -3927,7 +4058,7 @@ public class PhotoModule // initialize focus mode if ((mManual3AEnabled & MANUAL_FOCUS) == 0) { mFocusManager.overrideFocusMode(null); - mParameters.setFocusMode(mFocusManager.getFocusMode()); + mParameters.setFocusMode(mFocusManager.getFocusMode(false)); } // Set picture size. @@ -4039,8 +4170,8 @@ public class PhotoModule mActivity.getString(R.string.pref_camera_hdr_plus_default)); boolean hdrOn = onValue.equals(hdr); boolean hdrPlusOn = onValue.equals(hdrPlus); - boolean doGcamModeSwitch = false; + if (hdrPlusOn && GcamHelper.hasGcamCapture()) { // Kick off mode switch to gcam. doGcamModeSwitch = true; @@ -4053,7 +4184,20 @@ public class PhotoModule mCameraDevice.setParameters(mParameters); mParameters = mCameraDevice.getParameters(); } + if (mLgeHdrMode) { + mParameters.set(CameraSettings.KEY_SNAPCAM_HDR_MODE, + CameraSettings.LGE_HDR_MODE_ON); + try { + // Force enable ZSL mode for LG HDR + mUI.setPreference(CameraSettings.KEY_ZSL, Parameters.ZSL_ON); + } catch (NullPointerException e) { + } + } } else { + if (mLgeHdrMode) { + mParameters.set(CameraSettings.KEY_SNAPCAM_HDR_MODE, + CameraSettings.LGE_HDR_MODE_OFF); + } mSceneMode = mPreferences.getString( CameraSettings.KEY_SCENE_MODE, mActivity.getString(R.string.pref_camera_scenemode_default)); @@ -4064,7 +4208,10 @@ public class PhotoModule .pref_camera_advanced_feature_value_refocus_on); String optizoomOn = mActivity.getString(R.string .pref_camera_advanced_feature_value_optizoom_on); + String chromaFlashOn = mActivity.getString(R.string + .pref_camera_advanced_feature_value_chromaflash_on); String scenModeStr = mSceneMode; + if (refocusOn.equals(mSceneMode)) { try { mSceneMode = Parameters.SCENE_MODE_AUTO; @@ -4082,6 +4229,12 @@ public class PhotoModule } } catch (NullPointerException e) { } + } else if (chromaFlashOn.equals(mSceneMode)) { + try { + mUI.setPreference(CameraSettings.KEY_ADVANCED_FEATURES, chromaFlashOn); + mParameters.setSceneMode(Parameters.SCENE_MODE_AUTO); + } catch (NullPointerException e) { + } } else if (mSceneMode == null) { mSceneMode = Parameters.SCENE_MODE_AUTO; } @@ -4112,6 +4265,19 @@ public class PhotoModule mParameters.setJpegQuality(jpegQuality); + // When shutter speed gets disabled preview needs to be restarted + if (CameraUtil.isSupported(mParameters, CameraSettings.KEY_SNAPCAM_SHUTTER_SPEED)) { + String shutterSpeed = mPreferences.getString(CameraSettings.KEY_SHUTTER_SPEED, null); + if (shutterSpeed != null) { + String oldShutterSpeed = mParameters.get(CameraSettings.KEY_SNAPCAM_SHUTTER_SPEED); + if (!shutterSpeed.equals(oldShutterSpeed) && shutterSpeed.equals("0") && + mCameraState != PREVIEW_STOPPED) { + Log.v(TAG, "Shutter speed disabled. Restart Preview"); + mRestartPreview = true; + } + } + } + // For the following settings, we need to check if the settings are // still supported by latest driver, if not, ignore the settings. @@ -4125,7 +4291,8 @@ public class PhotoModule Log.w(TAG, "invalid exposure range: " + value); } - if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode)) { + if (Parameters.SCENE_MODE_AUTO.equals(mSceneMode) || + "asd".equals(mSceneMode) || "sports".equals(mSceneMode)) { // Set flash mode. String flashMode; if (mSavedFlashMode == null) { @@ -4170,13 +4337,19 @@ public class PhotoModule mParameters.setFocusMode(Parameters.FOCUS_MODE_INFINITY); } else if ((mManual3AEnabled & MANUAL_FOCUS) == 0) { mFocusManager.overrideFocusMode(null); - mParameters.setFocusMode(mFocusManager.getFocusMode()); + mParameters.setFocusMode(mFocusManager.getFocusMode(false)); } + + // Set focus time. + mFocusManager.setFocusTime(Integer.decode( + mPreferences.getString(CameraSettings.KEY_FOCUS_TIME, + mActivity.getString(R.string.pref_camera_focustime_default)))); } else { mFocusManager.overrideFocusMode(mParameters.getFocusMode()); - if (CameraUtil.isSupported(Parameters.FLASH_MODE_OFF, - mParameters.getSupportedFlashModes())) { - mParameters.setFlashMode(Parameters.FLASH_MODE_OFF); + String flashMode = chromaFlashOn.equals(mSceneMode) ? + Parameters.FLASH_MODE_ON : Parameters.FLASH_MODE_OFF; + if (CameraUtil.isSupported(flashMode, mParameters.getSupportedFlashModes())) { + mParameters.setFlashMode(flashMode); } if (CameraUtil.isSupported(Parameters.WHITE_BALANCE_AUTO, mParameters.getSupportedWhiteBalance())) { @@ -4211,7 +4384,8 @@ public class PhotoModule @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private void updateAutoFocusMoveCallback() { - if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE)) { + if (mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE) || + mParameters.getFocusMode().equals(CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE)) { mCameraDevice.setAutoFocusMoveCallback(mHandler, (CameraAFMoveCallback) mAutoFocusMoveCallback); } else { @@ -4241,7 +4415,9 @@ public class PhotoModule doModeSwitch = updateCameraParametersPreference(); } + CameraUtil.dumpParameters(mParameters); mCameraDevice.setParameters(mParameters); + mFocusManager.setParameters(mParameters); // Switch to gcam module if HDR+ was selected if (doModeSwitch && !mIsImageCaptureIntent) { @@ -4332,7 +4508,14 @@ public class PhotoModule final int maxFocusPos = mParameters.getInt(CameraSettings.KEY_MAX_FOCUS_SCALE); //update mparameters to fetch latest focus position mParameters = mCameraDevice.getParameters(); - final int CurFocusPos = mParameters.getInt(CameraSettings.KEY_MANUAL_FOCUS_SCALE); + int CurFocusPos = minFocusPos; + + try { + CurFocusPos = mParameters.getInt(CameraSettings.KEY_MANUAL_FOCUS_SCALE); + } catch (NumberFormatException e) { + // Do nothing + } + focusbar.setProgress(CurFocusPos); focusPositionText.setText("Current focus position is " + CurFocusPos); @@ -4384,7 +4567,8 @@ public class PhotoModule //update mparameters to fetch latest focus position mParameters = mCameraDevice.getParameters(); final String CurFocusPos = mParameters.get(CameraSettings.KEY_MANUAL_FOCUS_DIOPTER); - focusPositionText.setText("Current focus position is " + CurFocusPos); + focusPositionText.setText("Current focus position is " + + (CurFocusPos != null ? CurFocusPos : minFocusStr)); linear.addView(input); linear.addView(focusPositionText); alert.setView(linear); @@ -4425,7 +4609,7 @@ public class PhotoModule alert.show(); } else { mManual3AEnabled &= ~MANUAL_FOCUS; - mParameters.setFocusMode(mFocusManager.getFocusMode()); + mParameters.setFocusMode(mFocusManager.getFocusMode(false)); mUI.overrideSettings(CameraSettings.KEY_FOCUS_MODE, null); updateCommonManual3ASettings(); onSharedPreferenceChanged(); @@ -4611,7 +4795,7 @@ public class PhotoModule mParameters = mCameraDevice.getParameters(); final int minISO = mParameters.getInt(CameraSettings.KEY_MIN_ISO); final int maxISO = mParameters.getInt(CameraSettings.KEY_MAX_ISO); - String isoMode = ParametersWrapper.getISOValue(mParameters); + String isoMode = CameraSettings.getISOValue(mParameters); final String isoManual = CameraSettings.KEY_MANUAL_ISO; String currentISO = mParameters.get(CameraSettings.KEY_CURRENT_ISO); if (currentISO != null) { @@ -4653,7 +4837,7 @@ public class PhotoModule if (newISO <= maxISO && newISO >= minISO) { Log.v(TAG, "Setting ISO : " + newISO); mManual3AEnabled |= MANUAL_EXPOSURE; - ParametersWrapper.setISOValue(mParameters, isoManual); + CameraSettings.setISOValue(mParameters, isoManual); mParameters.set(CameraSettings.KEY_CONTINUOUS_ISO, newISO); mParameters.set(CameraSettings.KEY_EXPOSURE_TIME, "0"); updateCommonManual3ASettings(); @@ -4689,7 +4873,7 @@ public class PhotoModule Log.v(TAG, "Setting Exposure time : " + newExpTime); mManual3AEnabled |= MANUAL_EXPOSURE; mParameters.set(CameraSettings.KEY_EXPOSURE_TIME, expTime); - ParametersWrapper.setISOValue(mParameters, ParametersWrapper.ISO_AUTO); + CameraSettings.setISOValue(mParameters, Parameters.ISO_AUTO); mUI.setPreference(CameraSettings.KEY_ISO, ParametersWrapper.ISO_AUTO); mUI.overrideSettings(CameraSettings.KEY_ISO, null); updateCommonManual3ASettings(); @@ -4741,7 +4925,7 @@ public class PhotoModule newExpTime >= Double.parseDouble(minExpTime)) { mManual3AEnabled |= MANUAL_EXPOSURE; Log.v(TAG, "Setting ISO : " + newISO); - ParametersWrapper.setISOValue(mParameters, isoManual); + CameraSettings.setISOValue(mParameters, isoManual); mParameters.set(CameraSettings.KEY_CONTINUOUS_ISO, newISO); Log.v(TAG, "Setting Exposure time : " + newExpTime); mParameters.set(CameraSettings.KEY_EXPOSURE_TIME, expTime); @@ -4776,6 +4960,8 @@ public class PhotoModule //filter off unsupported settings final String settingOff = mActivity.getString(R.string.setting_off_value); + final String settingOn = mActivity.getString(R.string.setting_on_value); + final String zsl = mActivity.getString(R.string.pref_camera_zsl_default); if (!CameraSettings.isZSLHDRSupported(mParameters)) { //HDR internally uses AE-bracketing. Disable both if not supported. if (notSame(pref, CameraSettings.KEY_CAMERA_HDR, settingOff) || @@ -4784,6 +4970,9 @@ public class PhotoModule } else if (notSame(pref,CameraSettings.KEY_ZSL,settingOff)) { mUI.setPreference(CameraSettings.KEY_CAMERA_HDR, settingOff); mUI.setPreference(CameraSettings.KEY_AE_BRACKET_HDR, settingOff); + } else if (notSame(pref, CameraSettings.KEY_CAMERA_HDR, settingOn) || + notSame(pref, CameraSettings.KEY_AE_BRACKET_HDR, settingOn)) { + mUI.setPreference(CameraSettings.KEY_ZSL, zsl); } } @@ -4807,7 +4996,8 @@ public class PhotoModule updateRemainingPhotos(); } - if (CameraSettings.KEY_QC_CHROMA_FLASH.equals(pref.getKey())) { + if (!CameraSettings.hasChromaFlashScene(mActivity) && + CameraSettings.KEY_QC_CHROMA_FLASH.equals(pref.getKey())) { mUI.setPreference(CameraSettings.KEY_ADVANCED_FEATURES, pref.getValue()); } @@ -4820,35 +5010,6 @@ public class PhotoModule mUI.setPreference(CameraSettings.KEY_ADVANCED_FEATURES, pref.getValue()); } - String ubiFocusOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_ubifocus_off); - String chromaFlashOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_chromaflash_off); - String optiZoomOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_optizoom_off); - String reFocusOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_refocus_off); - String fssrOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_FSSR_off); - String truePortraitOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_trueportrait_off); - String multiTouchFocusOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_multi_touch_focus_off); - String stillMoreOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_stillmore_off); - String advancedFeatureOff = mActivity.getString(R.string. - pref_camera_advanced_feature_value_none); - if (notSame(pref, CameraSettings.KEY_QC_OPTI_ZOOM, optiZoomOff) || - notSame(pref, CameraSettings.KEY_QC_AF_BRACKETING, ubiFocusOff) || - notSame(pref, CameraSettings.KEY_QC_FSSR, fssrOff) || - notSame(pref, CameraSettings.KEY_QC_TP, truePortraitOff) || - notSame(pref, CameraSettings.KEY_QC_MULTI_TOUCH_FOCUS, multiTouchFocusOff) || - notSame(pref, CameraSettings.KEY_QC_STILL_MORE, stillMoreOff) || - notSame(pref, CameraSettings.KEY_QC_RE_FOCUS, reFocusOff) || - notSame(pref, CameraSettings.KEY_ADVANCED_FEATURES, advancedFeatureOff)) { - RotateTextToast.makeText(mActivity, R.string.advanced_capture_disable_continuous_shot, - Toast.LENGTH_LONG).show(); - } //call generic onSharedPreferenceChanged onSharedPreferenceChanged(); } @@ -4863,6 +5024,7 @@ public class PhotoModule mLocationManager.recordLocation(recordLocation); if(needRestart()){ Log.v(TAG, "Restarting Preview... Camera Mode Changed"); + setCameraParameters(UPDATE_PARAM_PREFERENCE); stopPreview(); startPreview(); setCameraState(IDLE); @@ -4875,8 +5037,8 @@ public class PhotoModule * later by posting a message to the handler */ if (mUI.mMenuInitialized) { setCameraParametersWhenIdle(UPDATE_PARAM_PREFERENCE); - mUI.updateOnScreenIndicators(mParameters, mPreferenceGroup, - mPreferences); + mActivity.initPowerShutter(mPreferences); + mActivity.initMaxBrightness(mPreferences); } else { mHandler.sendEmptyMessage(SET_PHOTO_UI_PARAMS); } @@ -4943,8 +5105,15 @@ public class PhotoModule mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mInitialParams); mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mInitialParams); mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mInitialParams); - mContinuousFocusSupported = mInitialParams.getSupportedFocusModes().contains( - CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE); + + List<String> supportedFocusModes = mInitialParams.getSupportedFocusModes(); + if (supportedFocusModes != null && + (supportedFocusModes.contains(CameraUtil.FOCUS_MODE_CONTINUOUS_PICTURE) || + supportedFocusModes.contains(CameraUtil.FOCUS_MODE_MW_CONTINUOUS_PICTURE))) { + mContinuousFocusSupported = true; + } else { + mContinuousFocusSupported = false; + } } @Override @@ -5159,6 +5328,10 @@ public class PhotoModule enableRecordingLocation(false); } + public boolean isLongshotDone() { + return ((mCameraState == LONGSHOT) && (mLongshotSnapNum == mReceivedSnapNum) && + !mLongshotActive); + } } /* Below is no longer needed, except to get rid of compile error @@ -5209,7 +5382,6 @@ class GraphView extends View { private CameraManager.CameraProxy mGraphCameraDevice; private float scaled; private static final int STATS_SIZE = 256; - private static final String TAG = "GraphView"; public GraphView(Context context, AttributeSet attrs) { @@ -5229,9 +5401,7 @@ class GraphView extends View { } @Override protected void onDraw(Canvas canvas) { - Log.v(TAG, "in Camera.java ondraw"); - if(mPhotoModule == null || !mPhotoModule.mHiston ) { - Log.e(TAG, "returning as histogram is off "); + if (mPhotoModule == null || !mPhotoModule.mHiston) { return; } diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 50b5f64fe..806a68bad 100755 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java @@ -58,7 +58,6 @@ import android.widget.PopupWindow; import android.widget.Toast; import com.android.camera.CameraPreference.OnPreferenceChangedListener; -import com.android.camera.FocusOverlayManager.FocusUI; import com.android.camera.TsMakeupManager.MakeupLevelListener; import com.android.camera.ui.AbstractSettingPopup; import com.android.camera.ui.CameraControls; @@ -66,10 +65,8 @@ import com.android.camera.ui.CameraRootView; import com.android.camera.ui.CountDownView; import com.android.camera.ui.CountDownView.OnCountDownFinishedListener; import com.android.camera.ui.FaceView; -import com.android.camera.ui.FocusIndicator; import com.android.camera.ui.ListSubMenu; import com.android.camera.ui.ModuleSwitcher; -import com.android.camera.ui.MenuHelp; import com.android.camera.ui.PieRenderer; import com.android.camera.ui.PieRenderer.PieListener; import com.android.camera.ui.RenderOverlay; @@ -78,11 +75,11 @@ import com.android.camera.ui.RotateLayout; import com.android.camera.ui.RotateTextToast; import com.android.camera.ui.SelfieFlashView; import com.android.camera.ui.ZoomRenderer; +import com.android.camera.ui.focus.FocusRing; import com.android.camera.util.CameraUtil; public class PhotoUI implements PieListener, PreviewGestures.SingleTapListener, - FocusUI, SurfaceHolder.Callback, CameraRootView.MyDisplayListener, CameraManager.CameraFaceDetectionCallback { @@ -90,6 +87,7 @@ public class PhotoUI implements PieListener, private static final String TAG = "CAM_UI"; private int mDownSampleFactor = 4; private final AnimationManager mAnimationManager; + private final FocusRing mFocusRing; private CameraActivity mActivity; private PhotoController mController; private PreviewGestures mGestures; @@ -114,12 +112,8 @@ public class PhotoUI implements PieListener, private PhotoMenu mMenu; private ModuleSwitcher mSwitcher; private CameraControls mCameraControls; - private MenuHelp mMenuHelp; private AlertDialog mLocationDialog; - // Small indicators which show the camera settings in the viewfinder. - private OnScreenIndicators mOnScreenIndicators; - private PieRenderer mPieRenderer; private ZoomRenderer mZoomRenderer; private RotateTextToast mNotSelectableToast; @@ -296,17 +290,16 @@ public class PhotoUI implements PieListener, mFaceView = (FaceView) mRootView.findViewById(R.id.face_view); setSurfaceTextureSizeChangedListener(mFaceView); } - initIndicators(); + mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); mAnimationManager = new AnimationManager(); mOrientationResize = false; mPrevOrientationResize = false; Point size = new Point(); - mActivity.getWindowManager().getDefaultDisplay().getSize(size); + mActivity.getWindowManager().getDefaultDisplay().getRealSize(size); mScreenRatio = CameraUtil.determineRatio(size.x, size.y); calculateMargins(size); mCameraControls.setMargins(mTopMargin, mBottomMargin); - showFirstTimeHelp(); } private void calculateMargins(Point size) { @@ -326,27 +319,10 @@ public class PhotoUI implements PieListener, mOrientationResize = orientation; } - private void showFirstTimeHelp(int topMargin, int bottomMargin) { - mMenuHelp = (MenuHelp) mRootView.findViewById(R.id.menu_help); - mMenuHelp.setVisibility(View.VISIBLE); - mMenuHelp.setMargins(topMargin, bottomMargin); - mMenuHelp.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - if (mMenuHelp != null) { - mMenuHelp.setVisibility(View.GONE); - mMenuHelp = null; - } - } - }); - } - public void setAspectRatio(float ratio) { if (ratio <= 0.0) throw new IllegalArgumentException(); - if (mOrientationResize && - mActivity.getResources().getConfiguration().orientation - != Configuration.ORIENTATION_PORTRAIT) { + if (mOrientationResize && CameraUtil.isScreenRotated(mActivity)) { ratio = 1 / ratio; } @@ -363,6 +339,9 @@ public class PhotoUI implements PieListener, FrameLayout.LayoutParams lp; float scaledTextureWidth, scaledTextureHeight; int rotation = CameraUtil.getDisplayRotation(mActivity); + if (!CameraUtil.isDefaultToPortrait(mActivity)) { + rotation = (rotation - 90) % 360; + } mScreenRatio = CameraUtil.determineRatio(ratio); if (mScreenRatio == CameraUtil.RATIO_16_9 && CameraUtil.determinCloseRatio(ratio) == CameraUtil.RATIO_4_3) { @@ -397,8 +376,13 @@ public class PhotoUI implements PieListener, } else { float width = mMaxPreviewWidth, height = mMaxPreviewHeight; if (width == 0 || height == 0) return; - if(mScreenRatio == CameraUtil.RATIO_4_3) - height -= (mTopMargin + mBottomMargin); + if (mScreenRatio == CameraUtil.RATIO_4_3) { + if (height > width) { + height -= (mTopMargin + mBottomMargin); + } else { + width -= (mTopMargin + mBottomMargin); + } + } if (mOrientationResize) { scaledTextureWidth = height * mAspectRatio; if (scaledTextureWidth > width) { @@ -497,11 +481,6 @@ public class PhotoUI implements PieListener, return mRootView; } - private void initIndicators() { - mOnScreenIndicators = new OnScreenIndicators(mActivity, - mRootView.findViewById(R.id.on_screen_indicators)); - } - public void onCameraOpened(PreferenceGroup prefGroup, ComboPreferences prefs, Camera.Parameters params, OnPreferenceChangedListener listener, MakeupLevelListener makeupListener) { if (mPieRenderer == null) { @@ -534,7 +513,6 @@ public class PhotoUI implements PieListener, mRenderOverlay.requestLayout(); initializeZoom(params); - updateOnScreenIndicators(params, prefGroup, prefs); mActivity.setPreviewGestures(mGestures); } @@ -565,8 +543,9 @@ public class PhotoUI implements PieListener, @Override public void onClick(View v) { if (!CameraControls.isAnimating() - && mController.getCameraState() != PhotoController.SNAPSHOT_IN_PROGRESS) + && mController.getCameraState() != PhotoController.SNAPSHOT_IN_PROGRESS) { mActivity.gotoGallery(); + } } }); } @@ -652,29 +631,11 @@ public class PhotoUI implements PieListener, // 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() - { - @Override - public void onClick(View v) { - if (!CameraControls.isAnimating()) - doShutterAnimation(); - if (mController.isImageCaptureIntent()) { - mCameraControls.setTitleBarVisibility(View.VISIBLE); - } - } - }); - + mShutterButton.setImageResource(R.drawable.btn_new_shutter); mShutterButton.setOnShutterButtonListener(mController); mShutterButton.setVisibility(View.VISIBLE); } - public void doShutterAnimation() { - AnimationDrawable frameAnimation = (AnimationDrawable) mShutterButton.getDrawable(); - frameAnimation.stop(); - frameAnimation.start(); - } - // called from onResume every other time public void initializeSecondTime(Camera.Parameters params) { initializeZoom(params); @@ -709,29 +670,6 @@ public class PhotoUI implements PieListener, mMenu.overrideSettings(keyvalues); } - public void updateOnScreenIndicators(Camera.Parameters params, - PreferenceGroup group, ComboPreferences prefs) { - if (params == null || group == null) return; - mOnScreenIndicators.updateSceneOnScreenIndicator(params.getSceneMode()); - mOnScreenIndicators.updateExposureOnScreenIndicator(params, - CameraSettings.readExposure(prefs)); - mOnScreenIndicators.updateFlashOnScreenIndicator(params.getFlashMode()); - int wbIndex = -1; - String wb = Camera.Parameters.WHITE_BALANCE_AUTO; - if (Camera.Parameters.SCENE_MODE_AUTO.equals(params.getSceneMode())) { - wb = params.getWhiteBalance(); - } - ListPreference pref = group.findPreference(CameraSettings.KEY_WHITE_BALANCE); - if (pref != null) { - wbIndex = pref.findIndexOfValue(wb); - } - // make sure the correct value was found - // otherwise use auto index - mOnScreenIndicators.updateWBIndicator(wbIndex < 0 ? 2 : wbIndex); - boolean location = RecordLocationPreference.get(prefs, CameraSettings.KEY_RECORD_LOCATION); - mOnScreenIndicators.updateLocationIndicator(location); - } - public void setCameraState(int state) { } @@ -796,7 +734,6 @@ public class PhotoUI implements PieListener, if (mPieRenderer != null) { mPieRenderer.setBlockFocus(!previewFocused); } - setShowMenu(previewFocused); if (!previewFocused && mCountDownView != null) mCountDownView.cancelCountDown(); } @@ -876,8 +813,9 @@ public class PhotoUI implements PieListener, mMenu.animateSlideIn(mMenuLayout, CameraActivity.SETTING_LIST_WIDTH_1, true); if (level == 2) mMenu.animateFadeIn(popup); - } else - popup.setAlpha(0.85f); + } else { + popup.setAlpha(1f); + } } public void removeLevel2() { @@ -983,12 +921,6 @@ public class PhotoUI implements PieListener, } } - private void setShowMenu(boolean show) { - if (mOnScreenIndicators != null) { - mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE); - } - } - public boolean collapseCameraControls() { // TODO: Mode switcher should behave like a popup and should hide itself when there // is a touch outside of it. @@ -1011,14 +943,12 @@ public class PhotoUI implements PieListener, mCameraControls.hideCameraSettings(); mDecodeTaskForReview = new DecodeImageForReview(jpegData, orientation, mirror); mDecodeTaskForReview.execute(); - mOnScreenIndicators.setVisibility(View.GONE); mMenuButton.setVisibility(View.GONE); CameraUtil.fadeIn(mReviewDoneButton); mShutterButton.setVisibility(View.INVISIBLE); CameraUtil.fadeIn(mReviewRetakeButton); setOrientation(mOrientation, true); mMenu.hideTopMenu(true); - pauseFaceDetection(); } protected void hidePostCaptureAlert() { @@ -1027,7 +957,6 @@ public class PhotoUI implements PieListener, mDecodeTaskForReview.cancel(true); } mReviewImage.setVisibility(View.GONE); - mOnScreenIndicators.setVisibility(View.VISIBLE); mMenuButton.setVisibility(View.VISIBLE); if (mMenu != null) { mMenu.hideTopMenu(false); @@ -1035,7 +964,6 @@ public class PhotoUI implements PieListener, CameraUtil.fadeOut(mReviewDoneButton); mShutterButton.setVisibility(View.VISIBLE); CameraUtil.fadeOut(mReviewRetakeButton); - resumeFaceDetection(); } public void setDisplayOrientation(int orientation) { @@ -1242,61 +1170,8 @@ public class PhotoUI implements PieListener, ((CameraRootView) mRootView).removeDisplayChangeListener(); } - // focus UI implementation - - private FocusIndicator getFocusIndicator() { - return (mFaceView != null && mFaceView.faceExists()) ? mFaceView : mPieRenderer; - } - - @Override - public boolean hasFaces() { - return (mFaceView != null && mFaceView.faceExists()); - } - - public void clearFaces() { - if (mFaceView != null) mFaceView.clear(); - } - - @Override - public void clearFocus() { - FocusIndicator indicator = mPieRenderer; - if (hasFaces()) { - mFaceView.showStart(); - } - if (indicator != null) indicator.clear(); - } - - @Override - public void setFocusPosition(int x, int y) { - mPieRenderer.setFocus(x, y); - } - - @Override - public void onFocusStarted() { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showStart(); - } - - @Override - public void onFocusSucceeded(boolean timeout) { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showSuccess(timeout); - } - - @Override - public void onFocusFailed(boolean timeout) { - FocusIndicator indicator = getFocusIndicator(); - if (indicator != null) indicator.showFail(timeout); - } - - @Override - public void pauseFaceDetection() { - if (mFaceView != null) mFaceView.pause(); - } - - @Override - public void resumeFaceDetection() { - if (mFaceView != null) mFaceView.resume(); + public FocusRing getFocusRing() { + return mFocusRing; } public void onStartFaceDetection(int orientation, boolean mirror) { @@ -1320,6 +1195,13 @@ public class PhotoUI implements PieListener, mFaceView.setFaces(faces); } + public boolean onScaleStepResize(boolean direction) { + if (mGestures != null) { + return mGestures.onScaleStepResize(direction); + } + return false; + } + @Override public void onDisplayChanged() { Log.d(TAG, "Device flip detected."); @@ -1338,8 +1220,6 @@ public class PhotoUI implements PieListener, public void setOrientation(int orientation, boolean animation) { mOrientation = orientation; mCameraControls.setOrientation(orientation, animation); - if (mMenuHelp != null) - mMenuHelp.setOrientation(orientation, animation); if (mMenuLayout != null) mMenuLayout.setOrientation(orientation, animation); if (mSubMenuLayout != null) @@ -1389,6 +1269,7 @@ public class PhotoUI implements PieListener, RotateImageView v = (RotateImageView) mReviewImage; v.setOrientation(orientation, animation); } + mOrientation = orientation; } public void tryToCloseSubList() { @@ -1404,17 +1285,6 @@ public class PhotoUI implements PieListener, setOrientation(mOrientation, true); } - public void showFirstTimeHelp() { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity); - boolean isMenuShown = prefs.getBoolean(CameraSettings.KEY_SHOW_MENU_HELP, false); - if(!isMenuShown) { - showFirstTimeHelp(mTopMargin, mBottomMargin); - SharedPreferences.Editor editor = prefs.edit(); - editor.putBoolean(CameraSettings.KEY_SHOW_MENU_HELP, true); - editor.apply(); - } - } - public void showRefocusDialog() { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivity); int prompt = prefs.getInt(CameraSettings.KEY_REFOCUS_PROMPT, 1); diff --git a/src/com/android/camera/PreviewGestures.java b/src/com/android/camera/PreviewGestures.java index 170476e82..4f01fba1b 100644 --- a/src/com/android/camera/PreviewGestures.java +++ b/src/com/android/camera/PreviewGestures.java @@ -88,17 +88,24 @@ public class PreviewGestures return false; } if (mZoomOnly || mMode == MODE_ZOOM) return false; - int deltaX = (int) (e1.getX() - e2.getX()); int deltaY = (int) (e1.getY() - e2.getY()); int orientation = 0; - if (mCaptureUI != null) + if (mPhotoMenu != null) + orientation = mPhotoMenu.getOrientation(); + else if (mVideoMenu != null) + orientation = mVideoMenu.getOrientation(); + else if (mCaptureUI != null) orientation = mCaptureUI.getOrientation(); if (isLeftSwipe(orientation, deltaX, deltaY)) { waitUntilNextDown = true; - if (mCaptureUI != null) + if (mPhotoMenu != null && !mPhotoMenu.isMenuBeingShown()) + mPhotoMenu.openFirstLevel(); + else if (mVideoMenu != null && !mVideoMenu.isMenuBeingShown()) + mVideoMenu.openFirstLevel(); + else if (mCaptureUI != null) mCaptureUI.openSettingsMenu(); return true; } @@ -264,10 +271,6 @@ public class PreviewGestures return true; } - public boolean waitUntilNextDown() { - return waitUntilNextDown; - } - private MotionEvent makeCancelEvent(MotionEvent m) { MotionEvent c = MotionEvent.obtain(m); c.setAction(MotionEvent.ACTION_CANCEL); @@ -309,5 +312,12 @@ public class PreviewGestures public void onScaleEnd(ScaleGestureDetector detector) { mZoom.onScaleEnd(detector); } + + public boolean onScaleStepResize(boolean direction) { + if (mZoom != null) { + return mZoom.onScaleStepResize(direction); + } + return false; + } } diff --git a/src/com/android/camera/SceneModeActivity.java b/src/com/android/camera/SceneModeActivity.java index 9e0e59506..b32a55eb0 100644 --- a/src/com/android/camera/SceneModeActivity.java +++ b/src/com/android/camera/SceneModeActivity.java @@ -222,7 +222,6 @@ class MyPagerAdapter extends PagerAdapter { v.setBackground(null); } } - view.setBackgroundResource(R.drawable.scene_mode_view_border_selected); SettingsManager.getInstance().setValueIndex(SettingsManager.KEY_SCENE_MODE, index); mActivity.finish(); } @@ -293,9 +292,6 @@ class GridAdapter extends BaseAdapter { int idx = position + mPage * mActivity.getElmentPerPage(); viewHolder.imageView.setImageResource(mActivity.getThumbnails()[idx]); viewHolder.textTitle.setText(mActivity.getEntries()[position + mPage * mActivity.getElmentPerPage()]); - if (idx == mActivity.getCurrentScene()) { - view.setBackgroundResource(R.drawable.scene_mode_view_border_selected); - } return view; } diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index b62573b76..dd798bc68 100755 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -328,14 +328,11 @@ public class SettingsManager implements ListMenu.SettingsListener { } public void updateQcfaPictureSize() { - ListPreference picturePref = mPreferenceGroup.findPreference(KEY_PICTURE_SIZE); - if (picturePref != null) { - picturePref.setEntries(mContext.getResources().getStringArray( - R.array.pref_camera2_picturesize_entries)); - picturePref.setEntryValues(mContext.getResources().getStringArray( - R.array.pref_camera2_picturesize_entryvalues)); - filterUnsupportedOptions(picturePref, getSupportedPictureSize( - getCurrentCameraId())); + ListPreference pictureSize = mPreferenceGroup.findPreference(KEY_PICTURE_SIZE); + if (pictureSize != null) { + CameraSettings.formatPictureSizes(pictureSize, + getSupportedPictureSizeList(getCurrentCameraId()), mContext); + CameraSettings.resetIfInvalid(pictureSize); } } @@ -808,13 +805,9 @@ public class SettingsManager implements ListMenu.SettingsListener { if (cameraIdPref != null) buildCameraId(); if (pictureSize != null) { - if (filterUnsupportedOptions(pictureSize, getSupportedPictureSize(cameraId))) { - mFilteredKeys.add(pictureSize.getKey()); - } else { - if (filterSimilarPictureSize(mPreferenceGroup, pictureSize)) { - mFilteredKeys.add(pictureSize.getKey()); - } - } + CameraSettings.formatPictureSizes(pictureSize, + getSupportedPictureSizeList(cameraId), mContext); + CameraSettings.resetIfInvalid(pictureSize); } if (exposure != null) buildExposureCompensation(cameraId); @@ -1308,6 +1301,43 @@ public class SettingsManager implements ListMenu.SettingsListener { mValuesMap.get(KEY_FLASH_MODE) != null; } + public boolean isZslSupported(int id) { + boolean zslSupported = false; + int [] capabilities = mCharacteristics.get(id) + .get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES); + for (int capability : capabilities) { + if (capability == CameraCharacteristics + .REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING || + capability == CameraCharacteristics + .REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING) { + zslSupported = true; + } + } + Log.d(TAG, "isZslSupported=" + zslSupported); + return zslSupported; + } + + private List<Size> getSupportedPictureSizeList(int cameraId) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] sizes = map.getOutputSizes(ImageFormat.JPEG); + List<Size> res = new ArrayList<>(); + if (sizes != null) { + for (int i = 0; i < sizes.length; i++) { + res.add(sizes[i]); + } + } + + Size[] highResSizes = map.getHighResolutionOutputSizes(ImageFormat.JPEG); + if (highResSizes != null) { + for (int i = 0; i < highResSizes.length; i++) { + res.add(highResSizes[i]); + } + } + + return res; + } + private List<String> getSupportedPictureSize(int cameraId) { StreamConfigurationMap map = mCharacteristics.get(cameraId).get( CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); diff --git a/src/com/android/camera/ShutterButton.java b/src/com/android/camera/ShutterButton.java index b35658070..22223510c 100644 --- a/src/com/android/camera/ShutterButton.java +++ b/src/com/android/camera/ShutterButton.java @@ -22,14 +22,12 @@ import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; -import com.android.camera.ui.RotateImageView; - /** * A button designed to be used for the on-screen shutter button. * It's currently an {@code ImageView} that can call a delegate when the * pressed state changes. */ -public class ShutterButton extends RotateImageView { +public class ShutterButton extends ImageView { private class LongClickListener implements View.OnLongClickListener { public boolean onLongClick(View v) { @@ -84,51 +82,6 @@ public class ShutterButton extends RotateImageView { setLongClickable(enable); } - /** - * Hook into the drawable state changing to get changes to isPressed -- the - * onPressed listener doesn't always get called when the pressed state - * changes. - */ - @Override - protected void drawableStateChanged() { - super.drawableStateChanged(); - final boolean pressed = isPressed(); - if (pressed != mOldPressed) { - if (!pressed) { - // When pressing the physical camera button the sequence of - // events is: - // focus pressed, optional camera pressed, focus released. - // We want to emulate this sequence of events with the shutter - // button. When clicking using a trackball button, the view - // system changes the drawable state before posting click - // notification, so the sequence of events is: - // pressed(true), optional click, pressed(false) - // When clicking using touch events, the view system changes the - // drawable state after posting click notification, so the - // sequence of events is: - // pressed(true), pressed(false), optional click - // Since we're emulating the physical camera button, we want to - // have the same order of events. So we want the optional click - // callback to be delivered before the pressed(false) callback. - // - // To do this, we delay the posting of the pressed(false) event - // slightly by pushing it on the event queue. This moves it - // after the optional click notification, so our client always - // sees events in this sequence: - // pressed(true), optional click, pressed(false) - post(new Runnable() { - @Override - public void run() { - callShutterButtonFocus(pressed); - } - }); - } else { - callShutterButtonFocus(pressed); - } - mOldPressed = pressed; - } - } - private void callShutterButtonFocus(boolean pressed) { if (mListener != null) { mListener.onShutterButtonFocus(pressed); diff --git a/src/com/android/camera/SoundPlayer.java b/src/com/android/camera/SoundPlayer.java new file mode 100644 index 000000000..ff3f37f7a --- /dev/null +++ b/src/com/android/camera/SoundPlayer.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2014 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.content.Context; +import android.media.AudioManager; +import android.media.SoundPool; +import android.util.SparseIntArray; + +import com.android.camera.util.ApiHelper; + +/** + * Loads a plays custom sounds. For playing system-standard sounds for various + * camera actions, please refer to {@link SoundClips}. + */ +public class SoundPlayer { + private final Context mAppContext; + private final SoundPool mSoundPool; + /** Keeps a mapping from sound resource ID to sound ID */ + private final SparseIntArray mResourceToSoundId = new SparseIntArray(); + private boolean mIsReleased = false; + + /** + * Construct a new sound player. + */ + public SoundPlayer(Context appContext) { + mAppContext = appContext; + final int audioType = getAudioTypeForSoundPool(); + mSoundPool = new SoundPool(1 /* max streams */, audioType, 0 /* quality */); + } + + /** + * Load the sound from a resource. + */ + public void loadSound(int resourceId) { + int soundId = mSoundPool.load(mAppContext, resourceId, 1/* priority */); + mResourceToSoundId.put(resourceId, soundId); + } + + /** + * Play the sound with the given resource. The resource has to be loaded + * before it can be played, otherwise an exception will be thrown. + */ + public void play(int resourceId, float volume) { + Integer soundId = mResourceToSoundId.get(resourceId); + if (soundId == null) { + throw new IllegalStateException("Sound not loaded. Must call #loadSound first."); + } + mSoundPool.play(soundId, volume, volume, 0 /* priority */, 0 /* loop */, 1 /* rate */); + } + + /** + * Unload the given sound if it's not needed anymore to release memory. + */ + public void unloadSound(int resourceId) { + Integer soundId = mResourceToSoundId.get(resourceId); + if (soundId == null) { + throw new IllegalStateException("Sound not loaded. Must call #loadSound first."); + } + mSoundPool.unload(soundId); + } + + /** + * Call this if you don't need the SoundPlayer anymore. All memory will be + * released and the object cannot be re-used. + */ + public void release() { + mIsReleased = true; + mSoundPool.release(); + } + + public boolean isReleased() { + return mIsReleased; + } + + private static int getAudioTypeForSoundPool() { + // STREAM_SYSTEM_ENFORCED is hidden API. + return ApiHelper.getIntFieldIfExists(AudioManager.class, + "STREAM_SYSTEM_ENFORCED", null, AudioManager.STREAM_RING); + } +} diff --git a/src/com/android/camera/VideoController.java b/src/com/android/camera/VideoController.java index cf694a391..a5b92c1f4 100644 --- a/src/com/android/camera/VideoController.java +++ b/src/com/android/camera/VideoController.java @@ -40,4 +40,6 @@ public interface VideoController extends OnShutterButtonListener, OnPauseButtonL // Callbacks for camera preview UI events. public void onPreviewUIReady(); public void onPreviewUIDestroyed(); + + public void onScreenSizeChanged(int width, int height); } diff --git a/src/com/android/camera/VideoMenu.java b/src/com/android/camera/VideoMenu.java index ca6aa0687..6baa71c24 100644 --- a/src/com/android/camera/VideoMenu.java +++ b/src/com/android/camera/VideoMenu.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -77,6 +78,7 @@ public class VideoMenu extends MenuController private static final int PREVIEW_MENU_IN_ANIMATION = 1; private static final int PREVIEW_MENU_ON = 2; private static final int MODE_FILTER = 1; + private static final int DEVELOPER_MENU_TOUCH_COUNT = 7; private int mSceneStatus; private View mFrontBackSwitcher; private View mFilterModeSwitcher; @@ -86,6 +88,7 @@ public class VideoMenu extends MenuController private String mPrevSavedVideoCDS; private boolean mIsVideoTNREnabled = false; private boolean mIsVideoCDSUpdated = false; + private int mPrivateCounter = 0; private static final int ANIMATION_DURATION = 300; private static final int CLICK_THRESHOLD = 200; private int previewMenuSize; @@ -111,23 +114,32 @@ public class VideoMenu extends MenuController // settings popup mOtherKeys1 = new String[] { CameraSettings.KEY_VIDEOCAMERA_FLASH_MODE, - CameraSettings.KEY_VIDEO_QUALITY, - CameraSettings.KEY_VIDEO_DURATION, CameraSettings.KEY_RECORD_LOCATION, + CameraSettings.KEY_VIDEO_QUALITY, CameraSettings.KEY_CAMERA_SAVEPATH, + CameraSettings.KEY_EXPOSURE, CameraSettings.KEY_WHITE_BALANCE, + CameraSettings.KEY_VIDEOCAMERA_FOCUS_MODE, + CameraSettings.KEY_VIDEOCAMERA_FOCUS_TIME, CameraSettings.KEY_VIDEO_HIGH_FRAME_RATE, - CameraSettings.KEY_DIS + CameraSettings.KEY_DIS, + CameraSettings.KEY_POWER_SHUTTER, + CameraSettings.KEY_MAX_BRIGHTNESS }; mOtherKeys2 = new String[] { CameraSettings.KEY_VIDEOCAMERA_FLASH_MODE, - CameraSettings.KEY_VIDEO_QUALITY, - CameraSettings.KEY_VIDEO_DURATION, CameraSettings.KEY_RECORD_LOCATION, + CameraSettings.KEY_VIDEO_QUALITY, + CameraSettings.KEY_VIDEO_SNAPSHOT_SIZE, CameraSettings.KEY_CAMERA_SAVEPATH, - CameraSettings.KEY_WHITE_BALANCE, CameraSettings.KEY_FACE_DETECTION, + CameraSettings.KEY_EXPOSURE, + CameraSettings.KEY_WHITE_BALANCE, + CameraSettings.KEY_VIDEOCAMERA_FOCUS_MODE, + CameraSettings.KEY_VIDEOCAMERA_FOCUS_TIME, CameraSettings.KEY_VIDEO_HIGH_FRAME_RATE, + CameraSettings.KEY_POWER_SHUTTER, + CameraSettings.KEY_MAX_BRIGHTNESS, CameraSettings.KEY_SEE_MORE, CameraSettings.KEY_NOISE_REDUCTION, CameraSettings.KEY_DIS, @@ -136,6 +148,7 @@ public class VideoMenu extends MenuController CameraSettings.KEY_VIDEO_ENCODER, CameraSettings.KEY_AUDIO_ENCODER, CameraSettings.KEY_VIDEO_HDR, + CameraSettings.KEY_ANTIBANDING, CameraSettings.KEY_POWER_MODE, CameraSettings.KEY_VIDEO_ROTATION, CameraSettings.KEY_VIDEO_CDS_MODE, @@ -308,7 +321,7 @@ public class VideoMenu extends MenuController public void animateFadeIn(final ListView v) { ViewPropertyAnimator vp = v.animate(); - vp.alpha(0.85f).setDuration(ANIMATION_DURATION); + vp.alpha(1f).setDuration(ANIMATION_DURATION); vp.start(); } @@ -476,6 +489,7 @@ public class VideoMenu extends MenuController return; } // Each entry has a corresponding icon. + index = index % iconIds.length; resid = iconIds[index]; } else { // The preference only has a single icon to represent it. @@ -495,8 +509,9 @@ public class VideoMenu extends MenuController CharSequence[] values = pref.getEntryValues(); index = (index + 1) % values.length; pref.setValueIndex(index); + int iconListLength = ((IconListPreference) pref).getLargeIconIds().length; ((ImageView) v).setImageResource( - ((IconListPreference) pref).getLargeIconIds()[index]); + ((IconListPreference) pref).getLargeIconIds()[index % iconListLength]); if (prefKey.equals(CameraSettings.KEY_CAMERA_ID)) mListener.onCameraPickerClicked(index); reloadPreference(pref); @@ -508,7 +523,7 @@ public class VideoMenu extends MenuController public void initFilterModeButton(View button) { button.setVisibility(View.INVISIBLE); final IconListPreference pref = (IconListPreference) mPreferenceGroup - .findPreference(CameraSettings.KEY_COLOR_EFFECT); + .findPreference(CameraSettings.KEY_VIDEOCAMERA_COLOR_EFFECT); if (pref == null || pref.getValue() == null) return; @@ -536,7 +551,7 @@ public class VideoMenu extends MenuController public void addFilterMode() { final IconListPreference pref = (IconListPreference) mPreferenceGroup - .findPreference(CameraSettings.KEY_COLOR_EFFECT); + .findPreference(CameraSettings.KEY_VIDEOCAMERA_COLOR_EFFECT); if (pref == null) return; @@ -615,10 +630,8 @@ public class VideoMenu extends MenuController pref.setValueIndex(j); changeFilterModeControlIcon(pref.getValue()); for (View v1 : views) { - v1.setBackground(null); + v1.setActivated(v1 == v); } - ImageView image = (ImageView) v.findViewById(R.id.image); - image.setBackgroundColor(0xff33b5e5); onSettingChanged(pref); } @@ -627,9 +640,8 @@ public class VideoMenu extends MenuController } }); - views[j] = imageView; - if (i == init) - imageView.setBackgroundColor(0xff33b5e5); + views[j] = layout2; + layout2.setActivated(i == init); TextView label = (TextView) layout2.findViewById(R.id.label); imageView.setImageResource(thumbnails[i]); label.setText(entries[i]); @@ -796,6 +808,8 @@ public class VideoMenu extends MenuController || !videoHDR.equals("off") || timeLapseInterval != 0) { mListMenu.setPreferenceEnabled(CameraSettings.KEY_VIDEO_HIGH_FRAME_RATE, false); + RotateTextToast.makeText(mActivity, R.string.error_app_unsupported_hfr_selection, + Toast.LENGTH_LONG).show(); } } @@ -889,6 +903,31 @@ public class VideoMenu extends MenuController mUI.showPopup(mListSubMenu, 2, true); } mPopupStatus = POPUP_SECOND_LEVEL; + + // Developer menu + if (pref.getKey().equals(CameraSettings.KEY_MAX_BRIGHTNESS)) { + mPrivateCounter++; + if (mPrivateCounter >= DEVELOPER_MENU_TOUCH_COUNT) { + SharedPreferences prefs = PreferenceManager + .getDefaultSharedPreferences(mActivity); + if (!mActivity.isDeveloperMenuEnabled()) { + mActivity.enableDeveloperMenu(); + prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, true).apply(); + closeAllView(); + RotateTextToast.makeText(mActivity, + R.string.developer_menu_enabled, Toast.LENGTH_SHORT).show(); + } else { + mActivity.disableDeveloperMenu(); + prefs.edit().putBoolean(CameraSettings.KEY_DEVELOPER_MENU, false).apply(); + closeAllView(); + RotateTextToast.makeText(mActivity, + R.string.developer_menu_disabled, Toast.LENGTH_SHORT).show(); + } + mPrivateCounter = 0; + } + } else { + mPrivateCounter = 0; + } } public void onListMenuTouched() { diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index fc65eb5bc..190cc248a 100644 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -1,5 +1,6 @@ /* * Copyright (C) 2012 The Android Open Source Project + * Copyright (C) 2013-2015 The CyanogenMod Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -60,6 +61,7 @@ import android.widget.Toast; import android.media.EncoderCapabilities; import android.media.EncoderCapabilities.VideoEncoderCap; +import com.android.camera.CameraManager.CameraAFCallback; import com.android.camera.CameraManager.CameraPictureCallback; import com.android.camera.CameraManager.CameraProxy; import com.android.camera.app.OrientationManager; @@ -88,6 +90,7 @@ import java.util.regex.Pattern; public class VideoModule implements CameraModule, VideoController, + FocusOverlayManager.Listener, CameraPreference.OnPreferenceChangedListener, ShutterButton.OnShutterButtonListener, LocationManager.Listener, @@ -104,6 +107,7 @@ public class VideoModule implements CameraModule, private static final int SWITCH_CAMERA = 8; private static final int SWITCH_CAMERA_START_ANIMATION = 9; private static final int HANDLE_FLASH_TORCH_DELAY = 10; + private static final int SET_FOCUS_RATIO = 11; private static final int SCREEN_DELAY = 2 * 60 * 1000; @@ -124,6 +128,10 @@ public class VideoModule implements CameraModule, private boolean mPaused; private int mCameraId; private Parameters mParameters; + private boolean mFocusAreaSupported; + private boolean mMeteringAreaSupported; + private boolean mAeLockSupported; + private boolean mAwbLockSupported; private boolean mIsInReviewMode; private boolean mSnapshotInProgress = false; @@ -177,11 +185,18 @@ public class VideoModule implements CameraModule, // true. private int mDisplayRotation; private int mCameraDisplayOrientation; + private int mOrientationOffset; private int mDesiredPreviewWidth; private int mDesiredPreviewHeight; private ContentResolver mContentResolver; + private final AutoFocusCallback mAutoFocusCallback = + new AutoFocusCallback(); + + // This handles everything about focus. + private FocusOverlayManager mFocusManager; + private LocationManager mLocationManager; private OrientationManager mOrientationManager; private int mPendingSwitchCameraId; @@ -270,6 +285,7 @@ public class VideoModule implements CameraModule, @Override public void run() { openCamera(); + if (mFocusManager == null) initializeFocusManager(); } } @@ -284,6 +300,7 @@ public class VideoModule implements CameraModule, return; } mParameters = mCameraDevice.getParameters(); + initializeCapabilities(); mPreviewFocused = arePreviewControlsVisible(); } @@ -361,9 +378,12 @@ public class VideoModule implements CameraModule, private boolean mUnsupportedHFRVideoSize = false; private boolean mUnsupportedHSRVideoSize = false; private boolean mUnsupportedHFRVideoCodec = false; - private String mDefaultAntibanding = null; boolean mUnsupportedProfile = false; + public void onScreenSizeChanged(int width, int height) { + if (mFocusManager != null) mFocusManager.setPreviewSize(width, height); + } + // This Handler is used to post message back onto the main thread of the // application private class MainHandler extends Handler { @@ -426,6 +446,11 @@ public class VideoModule implements CameraModule, break; } + case SET_FOCUS_RATIO: { + mUI.getFocusRing().setRadiusRatio((Float)msg.obj); + break; + } + default: Log.v(TAG, "Unhandled message: " + msg.what); break; @@ -490,6 +515,8 @@ public class VideoModule implements CameraModule, public void init(CameraActivity activity, View root) { mActivity = activity; mUI = new VideoUI(activity, this, root); + mOrientationOffset = CameraUtil.isDefaultToPortrait(mActivity) ? 0 : 90; + mPreferences = ComboPreferences.get(mActivity); if (mPreferences == null) { mPreferences = new ComboPreferences(mActivity); @@ -500,9 +527,17 @@ public class VideoModule implements CameraModule, mPreferences.setLocalId(mActivity, mCameraId); CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); + // we need to reset exposure for the preview + resetExposureCompensation(); mOrientationManager = new OrientationManager(mActivity); + // Power shutter + mActivity.initPowerShutter(mPreferences); + + // Max brightness + mActivity.initMaxBrightness(mPreferences); + /* * To reduce startup time, we start the preview in another thread. * We make sure the preview is started at the end of onCreate. @@ -547,6 +582,20 @@ public class VideoModule implements CameraModule, mPendingSwitchCameraId = -1; } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private void setAutoExposureLockIfSupported() { + if (mAeLockSupported) { + mParameters.setAutoExposureLock(mFocusManager.getAeAwbLock()); + } + } + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + private void setAutoWhiteBalanceLockIfSupported() { + if (mAwbLockSupported) { + mParameters.setAutoWhiteBalanceLock(mFocusManager.getAeAwbLock()); + } + } + @Override public void waitingLocationPermissionResult(boolean result) { mLocationManager.waitingLocationPermissionResult(result); @@ -565,6 +614,38 @@ public class VideoModule implements CameraModule, } @Override + public void autoFocus() { + Log.e(TAG, "start autoFocus."); + mCameraDevice.autoFocus(mHandler, mAutoFocusCallback); + } + + @Override + public void cancelAutoFocus() { + if (null != mCameraDevice) { + setFocusParameters(); + } + } + + @Override + public boolean capture() { + return true; + } + + @Override + public void setFocusParameters() { + if (mFocusAreaSupported) + mParameters.setFocusAreas(mFocusManager.getFocusAreas()); + if (mMeteringAreaSupported) + mParameters.setMeteringAreas(mFocusManager.getMeteringAreas()); + setAutoExposureLockIfSupported(); + setAutoWhiteBalanceLockIfSupported(); + if (mFocusAreaSupported || mMeteringAreaSupported) { + mParameters.setFocusMode(mFocusManager.getFocusMode(true)); + mCameraDevice.setParameters(mParameters); + } + } + + @Override public void setPreferenceForTest(String key, String value) { mUI.setPreference(key, value); onSharedPreferenceChanged(); @@ -575,22 +656,34 @@ public class VideoModule implements CameraModule, @Override public void onSingleTapUp(View view, int x, int y) { if (mMediaRecorderPausing) return; - takeASnapshot(); + boolean snapped = takeASnapshot(); + if (!snapped) { + // Do not trigger touch focus if popup window is opened. + if (mUI.removeTopLevelPopup()) { + return; + } + + // Check if metering area or focus area is supported. + if ((mFocusAreaSupported || mMeteringAreaSupported) && !mSnapshotInProgress) { + mFocusManager.onSingleTapUp(x, y); + } + } } - private void takeASnapshot() { + private boolean takeASnapshot() { // Only take snapshots if video snapshot is supported by device if (CameraUtil.isVideoSnapshotSupported(mParameters) && !mIsVideoCaptureIntent) { if (!mMediaRecorderRecording || mPaused || mSnapshotInProgress) { - return; + return false; } MediaSaveService s = mActivity.getMediaSaveService(); if (s == null || s.isQueueFull()) { - return; + return false; } // Set rotation and gps data. - int rotation = CameraUtil.getJpegRotation(mCameraId, mOrientation); + int orientation = (mOrientation + mOrientationOffset) % 360; + int rotation = CameraUtil.getJpegRotation(mCameraId, orientation); mParameters.setRotation(rotation); Location loc = mLocationManager.getCurrentLocation(); CameraUtil.setGpsParameters(mParameters, loc); @@ -603,7 +696,10 @@ public class VideoModule implements CameraModule, mSnapshotInProgress = true; UsageStatistics.onEvent(UsageStatistics.COMPONENT_CAMERA, UsageStatistics.ACTION_CAPTURE_DONE, "VideoSnapshot"); + + return true; } + return false; } @Override @@ -619,7 +715,7 @@ public class VideoModule implements CameraModule, mPreferenceGroup = filterPreferenceScreenByIntent( settings.getPreferenceGroup(R.xml.video_preferences)); - int numOfCams = Camera.getNumberOfCameras(); + int numOfCams = CameraHolder.instance().getNumberOfCameras(); //TODO: If numOfCams > 2 then corresponding entries needs to be added to the media_profiles.xml @@ -639,8 +735,8 @@ public class VideoModule implements CameraModule, int[] largeIconIds = new int[numOfCams]; for(int i=0;i<numOfCams;i++) { - CameraInfo info = CameraHolder.instance().getCameraInfo()[i]; - if(info.facing == CameraInfo.CAMERA_FACING_BACK) { + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[i]; + if(info.facing == CameraHolder.CameraInfo.CAMERA_FACING_BACK) { iconIds[i] = R.drawable.ic_switch_back; entries[i] = mActivity.getResources().getString(R.string.pref_camera_id_entry_back); labels[i] = mActivity.getResources().getString(R.string.pref_camera_id_label_back); @@ -671,6 +767,7 @@ public class VideoModule implements CameraModule, // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return; + orientation = (orientation - mOrientationOffset + 360) % 360; int newOrientation = CameraUtil.roundOrientation(orientation, mOrientation); if (mOrientation != newOrientation) { @@ -695,6 +792,7 @@ public class VideoModule implements CameraModule, private void startPlayVideoActivity() { Intent intent = new Intent(Intent.ACTION_VIEW); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.setDataAndType(mCurrentVideoUri, convertOutputFormatToMimeType(mProfile.fileFormat)); try { mActivity @@ -840,24 +938,6 @@ public class VideoModule implements CameraModule, Log.v(TAG, "Audio Encoder selected = " +mAudioEncoder); - String minutesStr = mPreferences.getString( - CameraSettings.KEY_VIDEO_DURATION, - mActivity.getString(R.string.pref_camera_video_duration_default)); - int minutes = -1; - try { - minutes = Integer.parseInt(minutesStr); - } catch(NumberFormatException npe) { - // use default value continue - minutes = Integer.parseInt(mActivity.getString( - R.string.pref_camera_video_duration_default)); - } - if (minutes == -1) { - // User wants lowest, set 30s */ - mMaxVideoDurationInMs = 30000; - } else { - // 1 minute = 60000ms - mMaxVideoDurationInMs = 60000 * minutes; - } if(ParametersWrapper.isPowerModeSupported(mParameters)) { String powermode = mPreferences.getString( CameraSettings.KEY_POWER_MODE, @@ -878,12 +958,34 @@ public class VideoModule implements CameraModule, } } + private final class AutoFocusCallback + implements CameraAFCallback { + @Override + public void onAutoFocus( + boolean focused, CameraProxy camera) { + Log.v(TAG, "AutoFocusCallback, mPaused=" + mPaused); + if (mPaused) return; + + //setCameraState(IDLE); + mCameraDevice.refreshParameters(); + mFocusManager.setParameters(mCameraDevice.getParameters()); + mFocusManager.onAutoFocus(focused, false); + } + } + + @Override + public void setFocusRatio(float ratio) { + mHandler.removeMessages(SET_FOCUS_RATIO); + Message m = mHandler.obtainMessage(SET_FOCUS_RATIO); + m.obj = ratio; + mHandler.sendMessage(m); + } + private void readVideoPreferences() { // The preference stores values from ListPreference and is thus string type for all values. // We need to convert it to int manually. - String videoQuality = mPreferences.getString(CameraSettings.KEY_VIDEO_QUALITY, - null); - if (videoQuality == null) { + String videoQuality = mPreferences.getString(CameraSettings.KEY_VIDEO_QUALITY, null); + if (videoQuality == null || (videoQuality.length() < 3 && !videoQuality.contains("x"))) { mParameters = mCameraDevice.getParameters(); String defaultQuality = mActivity.getResources().getString( R.string.pref_video_quality_default); @@ -895,7 +997,7 @@ public class VideoModule implements CameraModule, } else { // check for highest quality supported videoQuality = CameraSettings.getSupportedHighestVideoQuality( - mCameraId, mParameters); + mActivity, mCameraId, mParameters); } mPreferences.edit().putString(CameraSettings.KEY_VIDEO_QUALITY, videoQuality).apply(); } @@ -1088,8 +1190,18 @@ public class VideoModule implements CameraModule, ". mDesiredPreviewHeight=" + mDesiredPreviewHeight); } + private void resetExposureCompensation() { + String value = mPreferences.getString(CameraSettings.KEY_EXPOSURE, + CameraSettings.EXPOSURE_DEFAULT_VALUE); + if (!CameraSettings.EXPOSURE_DEFAULT_VALUE.equals(value)) { + Editor editor = mPreferences.edit(); + editor.putString(CameraSettings.KEY_EXPOSURE, "0"); + editor.apply(); + } + } + void setPreviewFrameLayoutCameraOrientation(){ - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; //if camera mount angle is 0 or 180, we want to resize preview if (info.orientation % 180 == 0) @@ -1129,6 +1241,7 @@ public class VideoModule implements CameraModule, @Override public void onResumeBeforeSuper() { mPaused = false; + if (mFocusManager == null) initializeFocusManager(); } @Override @@ -1141,6 +1254,7 @@ public class VideoModule implements CameraModule, if(mWasMute != mIsMute) { setMute(mIsMute, false); } + resetExposureCompensation(); showVideoSnapshotUI(false); installIntentFilter(); @@ -1191,6 +1305,9 @@ public class VideoModule implements CameraModule, private void setDisplayOrientation() { mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); mCameraDisplayOrientation = CameraUtil.getDisplayOrientation(mDisplayRotation, mCameraId); + if (mFocusManager != null) { + mFocusManager.setDisplayOrientation(mCameraDisplayOrientation); + } mUI.setDisplayOrientation(mCameraDisplayOrientation); // Change the camera display orientation if (mCameraDevice != null) { @@ -1259,6 +1376,8 @@ public class VideoModule implements CameraModule, throw new RuntimeException("startPreview failed", ex); } mStartPrevPending = false; + + mFocusManager.onPreviewStarted(); } private void onPreviewStarted() { @@ -1270,6 +1389,8 @@ public class VideoModule implements CameraModule, public void stopPreview() { mStopPrevPending = true; + if (mFocusManager != null) mFocusManager.onPreviewStopped(); + if (!mPreviewing) { mStopPrevPending = false; return; @@ -1298,6 +1419,7 @@ public class VideoModule implements CameraModule, mCameraDevice = null; mPreviewing = false; mSnapshotInProgress = false; + mFocusManager.onCameraReleased(); mPreviewFocused = false; mFaceDetectionStarted = false; } @@ -1355,6 +1477,28 @@ public class VideoModule implements CameraModule, @Override public void onPauseAfterSuper() { + if (mFocusManager != null) mFocusManager.removeMessages(); + } + + /** + * The focus manager is the first UI related element to get initialized, + * and it requires the RenderOverlay, so initialize it here + */ + private void initializeFocusManager() { + // Create FocusManager object. startPreview needs it. + // if mFocusManager not null, reuse it + // otherwise create a new instance + if (mFocusManager != null) { + mFocusManager.removeMessages(); + } else { + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + boolean mirror = (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT); + String[] defaultFocusModes = mActivity.getResources().getStringArray( + R.array.pref_video_focusmode_default_array); + mFocusManager = new FocusOverlayManager(mPreferences, defaultFocusModes, + mParameters, this, mirror, + mActivity.getMainLooper(), mUI.getFocusRing(), mActivity); + } } @Override @@ -1385,20 +1529,45 @@ public class VideoModule implements CameraModule, } switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + if (event.getRepeatCount() == 0 && !CameraActivity.mPowerShutter && + !CameraUtil.hasCameraKey()) { + mUI.clickShutter(); + } else { + mUI.onScaleStepResize(true); + } + return true; + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + if (event.getRepeatCount() == 0 && !CameraActivity.mPowerShutter && + !CameraUtil.hasCameraKey()) { + mUI.clickShutter(); + } else { + mUI.onScaleStepResize(false); + } + return true; case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_HEADSETHOOK: if (event.getRepeatCount() == 0) { mUI.clickShutter(); - return true; } - break; + return true; case KeyEvent.KEYCODE_DPAD_CENTER: if (event.getRepeatCount() == 0) { mUI.clickShutter(); - return true; } - break; + return true; + case KeyEvent.KEYCODE_POWER: + if (event.getRepeatCount() == 0 && CameraActivity.mPowerShutter && + !CameraUtil.hasCameraKey()) { + mUI.clickShutter(); + } + return true; case KeyEvent.KEYCODE_MENU: - if (mMediaRecorderRecording) return true; + if (mMediaRecorderRecording) { + return true; + } break; } return false; @@ -1407,9 +1576,27 @@ public class VideoModule implements CameraModule, @Override public boolean onKeyUp(int keyCode, KeyEvent event) { switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_MEDIA_NEXT: + if (!CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + mUI.pressShutter(false); + } + return true; + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + if (!CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + mUI.pressShutter(false); + } + return true; case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_HEADSETHOOK: mUI.pressShutter(false); return true; + case KeyEvent.KEYCODE_POWER: + if (CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + mUI.pressShutter(false); + } + return true; } return false; } @@ -1445,6 +1632,7 @@ public class VideoModule implements CameraModule, } private void setupMediaRecorderPreviewDisplay() { + mFocusManager.resetTouchFocus(); // Nothing to do here if using SurfaceTexture. if (!ApiHelper.HAS_SURFACE_TEXTURE_RECORDING) { // We stop the preview here before unlocking the device because we @@ -1582,7 +1770,8 @@ public class VideoModule implements CameraModule, } mMediaRecorder.setOutputFormat(mProfile.fileFormat); mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); - mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate); + mMediaRecorder.setVideoEncodingBitRate(mProfile.videoBitRate * + ((isHSR ? captureRate : 30) / 30)); mMediaRecorder.setVideoEncoder(mProfile.videoCodec); if (isHSR) { Log.i(TAG, "Configuring audio for HSR"); @@ -1620,7 +1809,11 @@ public class VideoModule implements CameraModule, // In case framerate is different, scale the bitrate int scaledBitrate = getHighSpeedVideoEncoderBitRate(mProfile, targetFrameRate); Log.i(TAG, "Scaled Video bitrate : " + scaledBitrate); - mMediaRecorder.setVideoEncodingBitRate(scaledBitrate); + if (scaledBitrate > 0) { + mMediaRecorder.setVideoEncodingBitRate(scaledBitrate); + } else { + Log.e(TAG, "Cannot set Video bitrate because its negative"); + } } setRecordLocation(); @@ -1661,11 +1854,11 @@ public class VideoModule implements CameraModule, // which is the orientation the graphics need to rotate in order to render correctly. int rotation = 0; if (mOrientation != OrientationEventListener.ORIENTATION_UNKNOWN) { - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; - if (info.facing == CameraInfo.CAMERA_FACING_FRONT) { - rotation = (info.orientation - mOrientation + 360) % 360; + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + if (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT) { + rotation = (info.orientation - mOrientation - mOrientationOffset + 360) % 360; } else { // back-facing camera - rotation = (info.orientation + mOrientation) % 360; + rotation = (info.orientation + mOrientation + mOrientationOffset) % 360; } } mMediaRecorder.setOrientationHint(rotation); @@ -1851,7 +2044,6 @@ public class VideoModule implements CameraModule, mStartRecPending = true; mUI.cancelAnimations(); mUI.setSwipingEnabled(false); - mUI.hideUIwhileRecording(); // When recording request is sent before starting preview, onPreviewFrame() // callback doesn't happen so removing preview cover here, instead. if (mUI.isPreviewCoverVisible()) { @@ -1924,6 +2116,8 @@ public class VideoModule implements CameraModule, return false; } + mUI.hideUIwhileRecording(); + // Make sure the video recording has started before announcing // this in accessibility. AccessibilityUtils.makeAnnouncement(mUI.getShutterButton(), @@ -1974,8 +2168,8 @@ public class VideoModule implements CameraModule, if (bitmap != null) { // MetadataRetriever already rotates the thumbnail. We should rotate // it to match the UI orientation (and mirror if it is front-facing camera). - CameraInfo[] info = CameraHolder.instance().getCameraInfo(); - boolean mirror = (info[mCameraId].facing == CameraInfo.CAMERA_FACING_FRONT); + CameraHolder.CameraInfo[] info = CameraHolder.instance().getCameraInfo(); + boolean mirror = (info[mCameraId].facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT); bitmap = CameraUtil.rotateAndMirror(bitmap, 0, mirror); } return bitmap; @@ -2085,6 +2279,16 @@ public class VideoModule implements CameraModule, mUI.hideSurfaceView(); // Switch back to use SurfaceTexture for preview. startPreview(); + } else { + if (is4KEnabled()) { + int fps = CameraUtil.getMaxPreviewFps(mParameters); + if (fps > 0) { + mParameters.setPreviewFrameRate(fps); + } else { + mParameters.setPreviewFrameRate(30); + } + mCameraDevice.setParameters(mParameters); + } } } // Update the parameters here because the parameters might have been altered @@ -2338,7 +2542,7 @@ public class VideoModule implements CameraModule, setZoomMenuValue(); String colorEffect = mPreferences.getString( - CameraSettings.KEY_COLOR_EFFECT, + CameraSettings.KEY_VIDEOCAMERA_COLOR_EFFECT, mActivity.getString(R.string.pref_camera_coloreffect_default)); Log.v(TAG, "Color effect value =" + colorEffect); if (isSupported(colorEffect, mParameters.getSupportedColorEffects())) { @@ -2372,22 +2576,14 @@ public class VideoModule implements CameraModule, } } - if (mDefaultAntibanding == null) { - mDefaultAntibanding = mParameters.getAntibanding(); - Log.d(TAG, "default antibanding value = " + mDefaultAntibanding); - } - - if (disMode.equals("enable")) { - Log.d(TAG, "dis is enabled, set antibanding to auto."); - if (isSupported(Parameters.ANTIBANDING_AUTO, mParameters.getSupportedAntibanding())) { - mParameters.setAntibanding(Parameters.ANTIBANDING_AUTO); - } - } else { - if (isSupported(mDefaultAntibanding, mParameters.getSupportedAntibanding())) { - mParameters.setAntibanding(mDefaultAntibanding); - } + // Set anti banding parameter. + String antiBanding = mPreferences.getString( + CameraSettings.KEY_ANTIBANDING, + mActivity.getString(R.string.pref_camera_antibanding_default)); + Log.v(TAG, "antiBanding value =" + antiBanding); + if (CameraUtil.isSupported(antiBanding, mParameters.getSupportedAntibanding())) { + mParameters.setAntibanding(antiBanding); } - Log.d(TAG, "antiBanding value = " + mParameters.getAntibanding()); mUnsupportedHFRVideoSize = false; mUnsupportedHFRVideoCodec = false; @@ -2671,6 +2867,14 @@ public class VideoModule implements CameraModule, //set power mode settings updatePowerMode(); + // Set focus mode + mParameters.setFocusMode(mFocusManager.getFocusMode(true)); + + // Set focus time. + mFocusManager.setFocusTime(Integer.decode( + mPreferences.getString(CameraSettings.KEY_VIDEOCAMERA_FOCUS_TIME, + mActivity.getString(R.string.pref_camera_video_focustime_default)))); + // Set face detetction parameter. String faceDetection = mPreferences.getString( CameraSettings.KEY_FACE_DETECTION, @@ -2721,7 +2925,18 @@ public class VideoModule implements CameraModule, Log.i(TAG,"NOTE: SetCameraParameters " + videoWidth + " x " + videoHeight); String recordSize = videoWidth + "x" + videoHeight; Log.e(TAG,"Video dimension in App->"+recordSize); - mParameters.set("video-size", recordSize); + if (CameraUtil.isSupported(mParameters, "video-size")) { + mParameters.set("video-size", recordSize); + } + // Set exposure compensation + int value = CameraSettings.readExposure(mPreferences); + int max = mParameters.getMaxExposureCompensation(); + int min = mParameters.getMinExposureCompensation(); + if (value >= min && value <= max) { + mParameters.setExposureCompensation(value); + } else { + Log.w(TAG, "invalid exposure range: " + value); + } // Set white balance parameter. String whiteBalance = mPreferences.getString( CameraSettings.KEY_WHITE_BALANCE, @@ -2744,10 +2959,8 @@ public class VideoModule implements CameraModule, } // Set continuous autofocus. - List<String> supportedFocus = mParameters.getSupportedFocusModes(); - if (isSupported(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO, supportedFocus)) { - mParameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO); - } + // Set focus mode + mParameters.setFocusMode(mFocusManager.getFocusMode(true)); mParameters.set(CameraUtil.RECORDING_HINT, CameraUtil.TRUE); @@ -2802,6 +3015,9 @@ public class VideoModule implements CameraModule, int jpegQuality = CameraProfile.getJpegEncodingQualityParameter(mCameraId, CameraProfile.QUALITY_HIGH); mParameters.setJpegQuality(jpegQuality); + + CameraUtil.dumpParameters(mParameters); + //Call Qcom related Camera Parameters qcomSetCameraParameters(); @@ -2817,8 +3033,7 @@ public class VideoModule implements CameraModule, // Keep preview size up to date. mParameters = mCameraDevice.getParameters(); - // Update UI based on the new parameters. - mUI.updateOnScreenIndicators(mParameters, mPreferences); + mFocusManager.setPreviewSize(videoWidth, videoHeight); } @Override @@ -2893,10 +3108,11 @@ public class VideoModule implements CameraModule, setCameraParameters(false); } mRestartPreview = false; - mUI.updateOnScreenIndicators(mParameters, mPreferences); Storage.setSaveSDCard( mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); mActivity.updateStorageSpaceAndHint(); + mActivity.initPowerShutter(mPreferences); + mActivity.initMaxBrightness(mPreferences); } } @@ -2918,10 +3134,18 @@ public class VideoModule implements CameraModule, closeCamera(); mUI.collapseCameraControls(); + if (mFocusManager != null) mFocusManager.removeMessages(); // Restart the camera and initialize the UI. From onCreate. mPreferences.setLocalId(mActivity, mCameraId); CameraSettings.upgradeLocalPreferences(mPreferences.getLocal()); openCamera(); + + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + boolean mirror = (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT); + mParameters = mCameraDevice.getParameters(); + mFocusManager.setMirror(mirror); + mFocusManager.setParameters(mParameters); + readVideoPreferences(); mUI.applySurfaceChange(VideoUI.SURFACE_STATUS.SURFACE_VIEW); startPreview(); @@ -2929,6 +3153,8 @@ public class VideoModule implements CameraModule, resizeForPreviewAspectRatio(); initializeVideoControl(); + initializeCapabilities(); + // From onResume mZoomValue = 0; mUI.initializeZoom(mParameters); @@ -2937,12 +3163,18 @@ public class VideoModule implements CameraModule, // Start switch camera animation. Post a message because // onFrameAvailable from the old camera may already exist. mHandler.sendEmptyMessage(SWITCH_CAMERA_START_ANIMATION); - mUI.updateOnScreenIndicators(mParameters, mPreferences); //Display timelapse msg depending upon selection in front/back camera. mUI.showTimeLapseUI(mCaptureTimeLapse); } + private void initializeCapabilities() { + mFocusAreaSupported = CameraUtil.isFocusAreaSupported(mParameters); + mMeteringAreaSupported = CameraUtil.isMeteringAreaSupported(mParameters); + mAeLockSupported = CameraUtil.isAutoExposureLockSupported(mParameters); + mAwbLockSupported = CameraUtil.isAutoWhiteBalanceLockSupported(mParameters); + } + // Preview texture has been copied. Now camera can be released and the // animation can be started. @Override @@ -3012,8 +3244,7 @@ public class VideoModule implements CameraModule, return; } forceFlashOffIfSupported(forceOff); - mCameraDevice.setParameters(mCameraDevice.getParameters()); - mUI.updateOnScreenIndicators(mParameters, mPreferences); + mCameraDevice.setParameters(mParameters); } @Override @@ -3175,9 +3406,9 @@ public class VideoModule implements CameraModule, || mFaceDetectionStarted) return; if (mParameters.getMaxNumDetectedFaces() > 0) { mFaceDetectionStarted = true; - CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; + CameraHolder.CameraInfo info = CameraHolder.instance().getCameraInfo()[mCameraId]; mUI.onStartFaceDetection(mCameraDisplayOrientation, - (info.facing == CameraInfo.CAMERA_FACING_FRONT)); + (info.facing == CameraHolder.CameraInfo.CAMERA_FACING_FRONT)); mCameraDevice.setFaceDetectionCallback(mHandler, mUI); Log.d(TAG, "start face detection Video "+mParameters.getMaxNumDetectedFaces()); mCameraDevice.startFaceDetection(); @@ -3190,7 +3421,6 @@ public class VideoModule implements CameraModule, if (mParameters.getMaxNumDetectedFaces() > 0) { mFaceDetectionStarted = false; mCameraDevice.setFaceDetectionCallback(null, null); - mUI.pauseFaceDetection(); mCameraDevice.stopFaceDetection(); mUI.onStopFaceDetection(); } diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 3a21a060a..beb623e50 100755 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java @@ -44,6 +44,7 @@ import android.widget.LinearLayout; import android.widget.ListView; import android.widget.PopupWindow; import android.widget.TextView; +import android.view.View.OnLayoutChangeListener; import com.android.camera.CameraManager.CameraProxy; import com.android.camera.CameraPreference.OnPreferenceChangedListener; @@ -60,6 +61,7 @@ import com.android.camera.ui.RotateImageView; import com.android.camera.ui.RotateLayout; import com.android.camera.ui.RotateTextToast; import com.android.camera.ui.ZoomRenderer; +import com.android.camera.ui.focus.FocusRing; import com.android.camera.util.CameraUtil; public class VideoUI implements PieRenderer.PieListener, @@ -70,6 +72,7 @@ public class VideoUI implements PieRenderer.PieListener, CameraManager.CameraFaceDetectionCallback{ private static final String TAG = "CAM_VideoUI"; // module fields + private final FocusRing mFocusRing; private CameraActivity mActivity; private View mRootView; private SurfaceHolder mSurfaceHolder; @@ -93,7 +96,6 @@ public class VideoUI implements PieRenderer.PieListener, private ZoomRenderer mZoomRenderer; private PreviewGestures mGestures; private View mMenuButton; - private OnScreenIndicators mOnScreenIndicators; private RotateLayout mRecordingTimeRect; private boolean mRecordingStarted = false; private VideoController mController; @@ -243,6 +245,7 @@ public class VideoUI implements PieRenderer.PieListener, } }); + mFocusRing = (FocusRing) mRootView.findViewById(R.id.focus_ring); mFlashOverlay = mRootView.findViewById(R.id.flash_overlay); mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mSwitcher = (ModuleSwitcher) mRootView.findViewById(R.id.camera_switcher); @@ -276,8 +279,8 @@ public class VideoUI implements PieRenderer.PieListener, }); initializeMiscControls(); - initializeControlByIntent(); initializeOverlay(); + initializeControlByIntent(); initializePauseButton(); mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls); @@ -293,7 +296,7 @@ public class VideoUI implements PieRenderer.PieListener, mPrevOrientationResize = false; Point size = new Point(); - mActivity.getWindowManager().getDefaultDisplay().getSize(size); + mActivity.getWindowManager().getDefaultDisplay().getRealSize(size); mScreenRatio = CameraUtil.determineRatio(size.x, size.y); calculateMargins(size); mCameraControls.setMargins(mTopMargin, mBottomMargin); @@ -336,9 +339,6 @@ public class VideoUI implements PieRenderer.PieListener, }); mCameraControls = (CameraControls) mRootView.findViewById(R.id.camera_controls); - mOnScreenIndicators = new OnScreenIndicators(mActivity, - mRootView.findViewById(R.id.on_screen_indicators)); - mOnScreenIndicators.resetToDefault(); if (mController.isVideoCaptureIntent()) { hideSwitcher(); mActivity.getLayoutInflater().inflate(R.layout.review_module_control, @@ -382,9 +382,7 @@ public class VideoUI implements PieRenderer.PieListener, } else { ratio = (float) height / width; } - if (mOrientationResize && - mActivity.getResources().getConfiguration().orientation - != Configuration.ORIENTATION_PORTRAIT) { + if (mOrientationResize && CameraUtil.isScreenRotated(mActivity)) { ratio = 1 / ratio; } @@ -400,8 +398,11 @@ public class VideoUI implements PieRenderer.PieListener, private void layoutPreview(float ratio) { FrameLayout.LayoutParams lp = null; - float scaledTextureWidth, scaledTextureHeight; + float scaledTextureWidth = 0.0f, scaledTextureHeight = 0.0f; int rotation = CameraUtil.getDisplayRotation(mActivity); + if (!CameraUtil.isDefaultToPortrait(mActivity)) { + rotation = (rotation - 90) % 360; + } mScreenRatio = CameraUtil.determineRatio(ratio); if (mScreenRatio == CameraUtil.RATIO_16_9 && CameraUtil.determinCloseRatio(ratio) == CameraUtil.RATIO_4_3) { @@ -503,6 +504,10 @@ public class VideoUI implements PieRenderer.PieListener, } + if (scaledTextureWidth > 0 && scaledTextureHeight > 0) { + mController.onScreenSizeChanged((int) scaledTextureWidth, + (int) scaledTextureHeight); + } } /** @@ -719,17 +724,8 @@ public class VideoUI implements PieRenderer.PieListener, mPauseButton.setOnPauseButtonListener(this); } - public void updateOnScreenIndicators(Parameters param, ComboPreferences prefs) { - mOnScreenIndicators.updateFlashOnScreenIndicator(param.getFlashMode()); - boolean location = RecordLocationPreference.get(prefs, CameraSettings.KEY_RECORD_LOCATION); - mOnScreenIndicators.updateLocationIndicator(location); - - } - public void setAspectRatio(double ratio) { - if (mOrientationResize && - mActivity.getResources().getConfiguration().orientation - != Configuration.ORIENTATION_PORTRAIT) { + if (mOrientationResize && CameraUtil.isScreenRotated(mActivity)) { ratio = 1 / ratio; } @@ -896,9 +892,9 @@ public class VideoUI implements PieRenderer.PieListener, mVideoMenu.animateSlideIn(mMenuLayout, CameraActivity.SETTING_LIST_WIDTH_1, true); if (level == 2) mVideoMenu.animateFadeIn(popup); + } else { + popup.setAlpha(1f); } - else - popup.setAlpha(0.85f); } public ViewGroup getMenuLayout() { @@ -982,7 +978,6 @@ public class VideoUI implements PieRenderer.PieListener, public void showRecordingUI(boolean recording) { mRecordingStarted = recording; mMenuButton.setVisibility(recording ? View.GONE : View.VISIBLE); - mOnScreenIndicators.setVisibility(recording ? View.GONE : View.VISIBLE); if (recording) { mShutterButton.setImageResource(R.drawable.shutter_button_video_stop); hideSwitcher(); @@ -1022,7 +1017,6 @@ public class VideoUI implements PieRenderer.PieListener, mMenuButton.setVisibility(View.GONE); mCameraControls.hideUI(); mVideoMenu.hideUI(); - mOnScreenIndicators.setVisibility(View.GONE); } public void hideReviewUI() { @@ -1031,20 +1025,11 @@ public class VideoUI implements PieRenderer.PieListener, mMenuButton.setVisibility(View.VISIBLE); mCameraControls.showUI(); mVideoMenu.showUI(); - mOnScreenIndicators.setVisibility(View.VISIBLE); CameraUtil.fadeOut(mReviewDoneButton); CameraUtil.fadeOut(mReviewPlayButton); CameraUtil.fadeIn(mShutterButton); } - private void setShowMenu(boolean show) { - if (mController.isVideoCaptureIntent()) - return; - if (mOnScreenIndicators != null) { - mOnScreenIndicators.setVisibility(show ? View.VISIBLE : View.GONE); - } - } - public void onPreviewFocusChanged(boolean previewFocused) { if (previewFocused) { showUI(); @@ -1058,7 +1043,6 @@ public class VideoUI implements PieRenderer.PieListener, // this can not happen in capture mode mRenderOverlay.setVisibility(previewFocused ? View.VISIBLE : View.GONE); } - setShowMenu(previewFocused); } public void initializePopup(PreferenceGroup pref) { @@ -1105,6 +1089,13 @@ public class VideoUI implements PieRenderer.PieListener, return mCameraControls.getVisibility() == View.VISIBLE; } + public boolean onScaleStepResize(boolean direction) { + if (mGestures != null) { + return mGestures.onScaleStepResize(direction); + } + return false; + } + @Override public void onDisplayChanged() { mCameraControls.checkLayoutFlip(); @@ -1250,14 +1241,6 @@ public class VideoUI implements PieRenderer.PieListener, mFaceView.setFaces(faces); } - public void pauseFaceDetection() { - if (mFaceView != null) mFaceView.pause(); - } - - public void resumeFaceDetection() { - if (mFaceView != null) mFaceView.resume(); - } - public void onStartFaceDetection(int orientation, boolean mirror) { mFaceView.setBlockDraw(false); mFaceView.clear(); @@ -1273,4 +1256,8 @@ public class VideoUI implements PieRenderer.PieListener, mFaceView.clear(); } } + + public FocusRing getFocusRing() { + return mFocusRing; + } } diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java index 0aab8f804..364e815ff 100644 --- a/src/com/android/camera/WideAnglePanoramaModule.java +++ b/src/com/android/camera/WideAnglePanoramaModule.java @@ -16,7 +16,6 @@ package com.android.camera; -import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -68,9 +67,10 @@ public class WideAnglePanoramaModule implements CameraModule, WideAnglePanoramaController, SurfaceTexture.OnFrameAvailableListener { - public static final int DEFAULT_SWEEP_ANGLE = 160; + public static final int DEFAULT_SWEEP_ANGLE_PORTRAIT = 160; + public static final int DEFAULT_SWEEP_ANGLE_LANDSCAPE = 270; public static final int DEFAULT_BLEND_MODE = Mosaic.BLENDTYPE_HORIZONTAL; - public static final int DEFAULT_CAPTURE_PIXELS = 960 * 720; + public static final int DEFAULT_CAPTURE_PIXELS = 1440 * 1000; private static final int MSG_LOW_RES_FINAL_MOSAIC_READY = 1; private static final int MSG_GENERATE_FINAL_MOSAIC_ERROR = 2; @@ -124,15 +124,12 @@ public class WideAnglePanoramaModule private float mHorizontalViewAngle; private float mVerticalViewAngle; - // Prefer FOCUS_MODE_INFINITY to FOCUS_MODE_CONTINUOUS_VIDEO because of - // getting a better image quality by the former. - private String mTargetFocusMode = Parameters.FOCUS_MODE_INFINITY; - private PanoOrientationEventListener mOrientationEventListener; // The value could be 0, 90, 180, 270 for the 4 different orientations measured in clockwise // respectively. private int mDeviceOrientation; private int mDeviceOrientationAtCapture; + private int mOrientationOffset; private int mCameraOrientation; private int mOrientationCompensation; private boolean mOrientationLocked; @@ -197,6 +194,7 @@ public class WideAnglePanoramaModule // the camera then point the camera to floor or sky, we still have // the correct orientation. if (orientation == ORIENTATION_UNKNOWN) return; + orientation = (orientation - mOrientationOffset + 360) % 360; int oldOrientation = mDeviceOrientation; mDeviceOrientation = CameraUtil.roundOrientation(orientation, mDeviceOrientation); // When the screen is unlocked, display rotation may change. Always @@ -230,6 +228,7 @@ public class WideAnglePanoramaModule public void init(CameraActivity activity, View parent) { mActivity = activity; mRootView = parent; + mOrientationOffset = CameraUtil.isDefaultToPortrait(mActivity) ? 0 : 90; mOrientationManager = new OrientationManager(activity); mCaptureState = CAPTURE_STATE_VIEWFINDER; @@ -310,6 +309,9 @@ public class WideAnglePanoramaModule CameraSettings.upgradeGlobalPreferences(mPreferences.getGlobal(), activity); mLocationManager = new LocationManager(mActivity, null); + // Power shutter + mActivity.initPowerShutter(mPreferences); + mMainHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -427,7 +429,7 @@ public class WideAnglePanoramaModule int w = size.width; // we only want 4:3 format. int d = DEFAULT_CAPTURE_PIXELS - h * w; - if (needSmaller && d < 0) { // no bigger preview than 960x720. + if (needSmaller && d < 0) { continue; } if (need4To3 && (h * 4 != w * 3)) { @@ -456,6 +458,7 @@ public class WideAnglePanoramaModule Log.d(TAG, "camera preview h = " + mCameraPreviewHeight + " , w = " + mCameraPreviewWidth); parameters.setPreviewSize(mCameraPreviewWidth, mCameraPreviewHeight); + mUI.setPreviewSize(mCameraPreviewWidth, mCameraPreviewHeight); List<int[]> frameRates = parameters.getSupportedPreviewFpsRange(); int last = frameRates.size() - 1; @@ -464,12 +467,13 @@ public class WideAnglePanoramaModule parameters.setPreviewFpsRange(minFps, maxFps); Log.d(TAG, "preview fps: " + minFps + ", " + maxFps); + // use CAF for viewfinder until starting the real mosaic, then lock List<String> supportedFocusModes = parameters.getSupportedFocusModes(); - if (supportedFocusModes.indexOf(mTargetFocusMode) >= 0) { - parameters.setFocusMode(mTargetFocusMode); + if (supportedFocusModes.indexOf(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) >= 0) { + parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); } else { // Use the default focus mode and log a message - Log.w(TAG, "Cannot set the focus mode to " + mTargetFocusMode + + Log.w(TAG, "Cannot set the focus mode to " + Parameters.FOCUS_MODE_CONTINUOUS_PICTURE + " becuase the mode is not supported."); } @@ -567,15 +571,28 @@ public class WideAnglePanoramaModule public void startCapture() { // Reset values so we can do this again. + + Parameters parameters = mCameraDevice.getParameters(); + List<String> supportedFocusModes = parameters.getSupportedFocusModes(); + if (supportedFocusModes.indexOf(Parameters.FOCUS_MODE_FIXED) >= 0) { + parameters.setFocusMode(Parameters.FOCUS_MODE_FIXED); + } else { + // Use the default focus mode and log a message + Log.w(TAG, "Cannot set the focus mode to " + Parameters.FOCUS_MODE_FIXED + + " becuase the mode is not supported."); + } + parameters.setAutoExposureLock(true); + parameters.setAutoWhiteBalanceLock(true); + configureCamera(parameters); + mCancelComputation = false; mTimeTaken = System.currentTimeMillis(); mActivity.setSwipingEnabled(false); mCaptureState = CAPTURE_STATE_MOSAIC; mUI.onStartCapture(); - Parameters parameters = mCameraDevice.getParameters(); - parameters.setAutoExposureLock(true); - parameters.setAutoWhiteBalanceLock(true); - configureCamera(parameters); + + final int sweepAngle = (mDeviceOrientation == 90 || mDeviceOrientation == 270) ? + DEFAULT_SWEEP_ANGLE_LANDSCAPE : DEFAULT_SWEEP_ANGLE_PORTRAIT; mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() { @Override @@ -585,8 +602,8 @@ public class WideAnglePanoramaModule float accumulatedVerticalAngle = progressY * mVerticalViewAngle; boolean isRotated = !(mDeviceOrientationAtCapture == mDeviceOrientation); if (isFinished - || (Math.abs(accumulatedHorizontalAngle) >= DEFAULT_SWEEP_ANGLE) - || (Math.abs(accumulatedVerticalAngle) >= DEFAULT_SWEEP_ANGLE) + || (Math.abs(accumulatedHorizontalAngle) >= sweepAngle) + || (Math.abs(accumulatedVerticalAngle) >= sweepAngle) || isRotated) { stopCapture(false); } else { @@ -606,7 +623,7 @@ public class WideAnglePanoramaModule mUI.resetCaptureProgress(); // TODO: calculate the indicator width according to different devices to reflect the actual // angle of view of the camera device. - mUI.setMaxCaptureProgress(DEFAULT_SWEEP_ANGLE); + mUI.setMaxCaptureProgress(sweepAngle); mUI.showCaptureProgress(); mDeviceOrientationAtCapture = mDeviceOrientation; keepScreenOn(); @@ -624,6 +641,14 @@ public class WideAnglePanoramaModule Parameters parameters = mCameraDevice.getParameters(); parameters.setAutoExposureLock(false); parameters.setAutoWhiteBalanceLock(false); + List<String> supportedFocusModes = parameters.getSupportedFocusModes(); + if (supportedFocusModes.indexOf(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) >= 0) { + parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE); + } else { + // Use the default focus mode and log a message + Log.w(TAG, "Cannot set the focus mode to " + Parameters.FOCUS_MODE_CONTINUOUS_PICTURE + + " becuase the mode is not supported."); + } configureCamera(parameters); mMosaicFrameProcessor.setProgressListener(null); @@ -701,13 +726,21 @@ public class WideAnglePanoramaModule } catch (InterruptedException e) { throw new RuntimeException("Panorama reportProgress failed", e); } - // Update the progress bar - mActivity.runOnUiThread(new Runnable() { - @Override - public void run() { - mUI.updateSavingProgress(progress); - } - }); + // Update the progress bar if we haven't paused. In the case where + // we pause the UI, then launch the camera from the lockscreen with + // this thread still running, a new WideAnglePanoramaModule is + // created, but this thread is left running to finish the task (and + // mPaused continues to be true for that instance. + if (!mPaused) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + if (!mPaused) { + mUI.updateSavingProgress(progress); + } + } + }); + } } } }; @@ -725,9 +758,11 @@ public class WideAnglePanoramaModule if (mUsingFrontCamera) { // mCameraOrientation is negative with respect to the front facing camera. // See document of android.hardware.Camera.Parameters.setRotation. - orientation = (mDeviceOrientationAtCapture - mCameraOrientation - 360) % 360; + orientation = (mDeviceOrientationAtCapture - mCameraOrientation - + mOrientationOffset + 360) % 360; } else { - orientation = (mDeviceOrientationAtCapture + mCameraOrientation) % 360; + orientation = (mDeviceOrientationAtCapture + mCameraOrientation + + mOrientationOffset) % 360; } return orientation; } @@ -882,12 +917,7 @@ public class WideAnglePanoramaModule return; } - int perct = 100; - final ActivityManager am = (ActivityManager) - mActivity.getSystemService(Context.ACTIVITY_SERVICE); - if (am.isLowRamDevice()) { - perct = mActivity.getResources().getInteger(R.integer.panorama_frame_size_reduction); - } + int perct = mActivity.getResources().getInteger(R.integer.panorama_frame_size_reduction); int width = (mCameraPreviewWidth * perct) / 100; int height = (mCameraPreviewHeight * perct) / 100; @@ -943,6 +973,10 @@ public class WideAnglePanoramaModule } mUI.showPreviewCover(); releaseCamera(); + + // Load the power shutter + mActivity.initPowerShutter(mPreferences); + synchronized (mRendererLock) { mCameraTexture = null; @@ -1139,6 +1173,13 @@ public class WideAnglePanoramaModule mCameraTexture.setOnFrameAvailableListener(this); mCameraDevice.setPreviewTexture(mCameraTexture); } + mCameraDevice.setOneShotPreviewCallback(mMainHandler, + new CameraManager.CameraPreviewDataCallback() { + @Override + public void onPreviewFrame(byte[] data, CameraProxy camera) { + mUI.hidePreviewCover(); + } + }); mCameraDevice.startPreview(); mCameraState = PREVIEW_ACTIVE; } @@ -1216,7 +1257,7 @@ public class WideAnglePanoramaModule @Override public void cancelHighResStitching() { - if (mPaused || mCameraTexture == null) return; + if (mPaused) return; cancelHighResComputation(); } @@ -1235,14 +1276,51 @@ public class WideAnglePanoramaModule public void onActivityResult(int requestCode, int resultCode, Intent data) { } - @Override public boolean onKeyDown(int keyCode, KeyEvent event) { + // Do not handle any key if the activity is paused + // or not in active camera/video mode + if (mPaused) { + return true; + } else if (!mActivity.isInCameraApp()) { + return false; + } + + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + return true; + case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_HEADSETHOOK: + if (event.getRepeatCount() == 0) { + onShutterButtonClick(); + } + return true; + case KeyEvent.KEYCODE_POWER: + return true; + } return false; } @Override public boolean onKeyUp(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_VOLUME_UP: + case KeyEvent.KEYCODE_VOLUME_DOWN: + case KeyEvent.KEYCODE_MEDIA_NEXT: + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + if (!CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + onShutterButtonClick(); + } + return true; + case KeyEvent.KEYCODE_POWER: + if (CameraActivity.mPowerShutter && !CameraUtil.hasCameraKey()) { + onShutterButtonClick(); + } + return true; + } return false; } diff --git a/src/com/android/camera/WideAnglePanoramaUI.java b/src/com/android/camera/WideAnglePanoramaUI.java index 46cf173bb..523abe411 100644 --- a/src/com/android/camera/WideAnglePanoramaUI.java +++ b/src/com/android/camera/WideAnglePanoramaUI.java @@ -99,7 +99,6 @@ public class WideAnglePanoramaUI implements // Color definitions. private int mIndicatorColor; private int mIndicatorColorFast; - private int mReviewBackground; private SurfaceTexture mSurfaceTexture; private View mPreviewCover; @@ -109,6 +108,10 @@ public class WideAnglePanoramaUI implements private RotateLayout mPanoFailedDialog; private Button mPanoFailedButton; + private int mTopMargin = 0; + private int mBottomMargin = 0; + private float mAspectRatio = 1.0f; + /** Constructor. */ public WideAnglePanoramaUI( CameraActivity activity, @@ -145,6 +148,14 @@ public class WideAnglePanoramaUI implements muteButton.setVisibility(View.GONE); } + private void calculateMargins(Point size) { + int l = size.x > size.y ? size.x : size.y; + int tm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); + int bm = mActivity.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); + mTopMargin = l / 4 * tm / (tm + bm); + mBottomMargin = l / 4 - mTopMargin; + } + public void onStartCapture() { hideSwitcher(); mShutterButton.setImageResource(R.drawable.shutter_button_stop); @@ -364,32 +375,27 @@ public class WideAnglePanoramaUI implements Display display = mActivity.getWindowManager().getDefaultDisplay(); Point size = new Point(); display.getSize(size); + mActivity.getWindowManager().getDefaultDisplay().getRealSize(size); + calculateMargins(size); + mCameraControls.setMargins(mTopMargin, mBottomMargin); int width = size.x; - int height = size.y; + int height = size.y - mTopMargin - mBottomMargin; int xOffset = 0; int yOffset = 0; int w = width; int h = height; h = w * 4 / 3; - yOffset = (height - h) / 2; + yOffset = (height - h) / 2 + mTopMargin; FrameLayout.LayoutParams param = new FrameLayout.LayoutParams(w, h); mTextureView.setLayoutParams(param); mTextureView.setX(xOffset); mTextureView.setY(yOffset); - mPreviewBorder.setLayoutParams(param); - mPreviewBorder.setX(xOffset); - mPreviewBorder.setY(yOffset); mPreviewYOffset = yOffset; - int t = mPreviewYOffset; - int b1 = mTextureView.getBottom() - mPreviewYOffset; - int r = mTextureView.getRight(); - int b2 = mTextureView.getBottom(); - - mCameraControls.setPreviewRatio(1.0f, true); + mTextureView.layout(0, mPreviewYOffset, mTextureView.getRight(), mTextureView.getBottom()); } public void resetSavingProgress() { @@ -442,7 +448,6 @@ public class WideAnglePanoramaUI implements Resources appRes = mActivity.getResources(); mIndicatorColor = appRes.getColor(R.color.pano_progress_indication); - mReviewBackground = appRes.getColor(R.color.review_background); mIndicatorColorFast = appRes.getColor(R.color.pano_progress_indication_fast); mPreviewCover = mRootView.findViewById(R.id.preview_cover); @@ -469,10 +474,8 @@ public class WideAnglePanoramaUI implements mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); mShutterButton.setImageResource(R.drawable.btn_new_shutter); mShutterButton.setOnShutterButtonListener(this); - // Hide menu and indicators. + // Hide menu mRootView.findViewById(R.id.menu).setVisibility(View.GONE); - mRootView.findViewById(R.id.on_screen_indicators).setVisibility(View.GONE); - mReview.setBackgroundColor(mReviewBackground); // TODO: set display change listener properly. ((CameraRootView) mRootView).setDisplayChangeListener(null); @@ -705,7 +708,34 @@ public class WideAnglePanoramaUI implements mPanoFailedDialog.setRotation(-orientation); mReview.setRotation(-orientation); mTooFastPrompt.setRotation(-orientation); + mPreviewBorder.setRotation(-orientation); mCameraControls.setOrientation(orientation, animation); RotateTextToast.setOrientation(orientation); } + + public void hidePreviewCover() { + // Hide the preview cover if need. + if (mPreviewCover.getVisibility() != View.GONE) { + mPreviewCover.setVisibility(View.GONE); + } + } + + public void setPreviewSize(int width, int height) { + if (width == 0 || height == 0) { + Log.w(TAG, "Preview size should not be 0."); + return; + } + float ratio; + if (width > height) { + ratio = (float) width / height; + } else { + ratio = (float) height / width; + } + + if (ratio != mAspectRatio) { + mAspectRatio = ratio; + } + + mCameraControls.setPreviewRatio(mAspectRatio, false); + } } diff --git a/src/com/android/camera/app/CameraApp.java b/src/com/android/camera/app/CameraApp.java index 837e22cd1..dc5825cb1 100644 --- a/src/com/android/camera/app/CameraApp.java +++ b/src/com/android/camera/app/CameraApp.java @@ -18,6 +18,7 @@ package com.android.camera.app; import android.app.ActivityManager; import android.app.Application; +import android.content.Context; import com.android.camera.SDCard; import com.android.camera.util.CameraUtil; @@ -28,9 +29,12 @@ public class CameraApp extends Application { private static long mMaxSystemMemory; public static boolean mIsLowMemoryDevice = false; private static final long LOW_MEMORY_DEVICE_THRESHOLD = 2L*1024*1024*1024; + private static Application mApp = null; + @Override public void onCreate() { super.onCreate(); + mApp = this; ActivityManager actManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE); ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); actManager.getMemoryInfo(memInfo); @@ -43,5 +47,9 @@ public class CameraApp extends Application { CameraUtil.initialize(this); SDCard.initialize(this); } + + public static Context getContext() { + return mApp.getApplicationContext(); + } } diff --git a/src/com/android/camera/async/HandlerExecutor.java b/src/com/android/camera/async/HandlerExecutor.java new file mode 100644 index 000000000..87c8c0ce0 --- /dev/null +++ b/src/com/android/camera/async/HandlerExecutor.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2014 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.async; + +import java.util.concurrent.Executor; + +import android.os.Handler; + +/** + * An {@link Executor} which posts to a {@link Handler}. + */ +public class HandlerExecutor implements Executor { + private final Handler mHandler; + + public HandlerExecutor(Handler handler) { + mHandler = handler; + } + + @Override + public void execute(Runnable runnable) { + mHandler.post(runnable); + } +} diff --git a/src/com/android/camera/async/MainThread.java b/src/com/android/camera/async/MainThread.java new file mode 100644 index 000000000..7fdb3ec9b --- /dev/null +++ b/src/com/android/camera/async/MainThread.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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.async; + +import android.os.Handler; +import android.os.Looper; + +public class MainThread extends HandlerExecutor { + private MainThread(Handler handler) { + super(handler); + } + + public static MainThread create() { + return new MainThread(new Handler(Looper.getMainLooper())); + } + + /** + * Caches whether or not the current thread is the main thread. + */ + private static final ThreadLocal<Boolean> sIsMainThread = new ThreadLocal<Boolean>() { + @Override + protected Boolean initialValue() { + return Looper.getMainLooper().getThread() == Thread.currentThread(); + } + }; + + /** + * Returns true if the method is run on the main android thread. + */ + public static boolean isMainThread() { + return sIsMainThread.get(); + } +} diff --git a/src/com/android/camera/crop/CropActivity.java b/src/com/android/camera/crop/CropActivity.java index 9c09b9a87..45455ccad 100644 --- a/src/com/android/camera/crop/CropActivity.java +++ b/src/com/android/camera/crop/CropActivity.java @@ -31,6 +31,7 @@ import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; +import android.media.MediaScannerConnection; import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; @@ -46,6 +47,7 @@ import org.codeaurora.snapcam.R; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -581,6 +583,16 @@ public class CropActivity extends Activity { } } } + + File dest = SaveImage.getLocalFileFromUri(CropActivity.this, mOutUri); + if (dest != null) { + MediaScannerConnection.scanFile( + CropActivity.this, + new String[] { dest.toString() }, + null /* no mimetype, let scanner guess */, + null /* no callback */); + } + return !failure; // True if any of the operations failed } @@ -590,7 +602,6 @@ public class CropActivity extends Activity { Utils.closeSilently(mInStream); doneBitmapIO(result.booleanValue(), mResultIntent); } - } private void done() { diff --git a/src/com/android/camera/crop/SaveImage.java b/src/com/android/camera/crop/SaveImage.java index c48e861fe..1701fb6eb 100644 --- a/src/com/android/camera/crop/SaveImage.java +++ b/src/com/android/camera/crop/SaveImage.java @@ -142,16 +142,19 @@ public class SaveImage { return saveDirectory; } - public static File getNewFile(Context context, Uri sourceUri) { + public static File getNewFile(Context context, Uri sourceUri, long time) { File saveDirectory = getFinalSaveDirectory(context, sourceUri); - String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date( - System.currentTimeMillis())); + String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(time)); if (hasPanoPrefix(context, sourceUri)) { return new File(saveDirectory, PREFIX_PANO + filename + POSTFIX_JPG); } return new File(saveDirectory, PREFIX_IMG + filename + POSTFIX_JPG); } + public static File getNewFile(Context context, Uri sourceUri) { + return getNewFile(context, sourceUri, System.currentTimeMillis()); + } + /** * Remove the files in the auxiliary directory whose names are the same as * the source image. @@ -339,9 +342,7 @@ public class SaveImage { public static Uri makeAndInsertUri(Context context, Uri sourceUri) { long time = System.currentTimeMillis(); - String filename = new SimpleDateFormat(TIME_STAMP_NAME).format(new Date(time)); - File saveDirectory = getFinalSaveDirectory(context, sourceUri); - File file = new File(saveDirectory, filename + ".JPG"); + File file = getNewFile(context, sourceUri, time); return linkNewFileToUri(context, sourceUri, file, time, false); } @@ -384,7 +385,7 @@ public class SaveImage { * @return The file object. Return null if srcUri is invalid or not a local * file. */ - private static File getLocalFileFromUri(Context context, Uri srcUri) { + static File getLocalFileFromUri(Context context, Uri srcUri) { if (srcUri == null) { Log.e(LOGTAG, "srcUri is null."); return null; diff --git a/src/com/android/camera/data/AbstractLocalDataAdapterWrapper.java b/src/com/android/camera/data/AbstractLocalDataAdapterWrapper.java index ef1e4258c..bc239788b 100644 --- a/src/com/android/camera/data/AbstractLocalDataAdapterWrapper.java +++ b/src/com/android/camera/data/AbstractLocalDataAdapterWrapper.java @@ -98,4 +98,9 @@ public abstract class AbstractLocalDataAdapterWrapper implements LocalDataAdapte public void refresh(ContentResolver resolver, Uri uri) { mAdapter.refresh(resolver, uri); } + + @Override + public void stopLoading() { + mAdapter.stopLoading(); + } } diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java index 4b810a0fa..95c1ea723 100755 --- a/src/com/android/camera/data/CameraDataAdapter.java +++ b/src/com/android/camera/data/CameraDataAdapter.java @@ -52,6 +52,7 @@ public class CameraDataAdapter implements LocalDataAdapter { private int mSuggestedHeight = DEFAULT_DECODE_SIZE; private LocalData mLocalDataToDelete; + private QueryTask mQueryTask; public CameraDataAdapter(Drawable placeHolder) { mImages = new LocalDataList(); @@ -60,8 +61,15 @@ public class CameraDataAdapter implements LocalDataAdapter { @Override public void requestLoad(ContentResolver resolver) { - QueryTask qtask = new QueryTask(); - qtask.execute(resolver); + mQueryTask = new QueryTask(); + mQueryTask.execute(resolver); + } + + @Override + public void stopLoading() { + if (mQueryTask != null) { + mQueryTask.cancel(true); + } } @Override @@ -329,7 +337,7 @@ public class CameraDataAdapter implements LocalDataAdapter { if (c != null && c.moveToFirst()) { // build up the list. c.moveToFirst(); - while (true) { + while (!isCancelled()) { LocalData data = LocalMediaData.VideoData.buildFromCursor(c); if (data != null) { l.add(data); @@ -357,7 +365,12 @@ public class CameraDataAdapter implements LocalDataAdapter { @Override protected void onPostExecute(LocalDataList l) { - replaceData(l); + if (!isCancelled()) { + replaceData(l); + } + if (mQueryTask == this) { + mQueryTask = null; + } } } diff --git a/src/com/android/camera/data/LocalDataAdapter.java b/src/com/android/camera/data/LocalDataAdapter.java index 0a5fde0b5..d8c5971ac 100644 --- a/src/com/android/camera/data/LocalDataAdapter.java +++ b/src/com/android/camera/data/LocalDataAdapter.java @@ -115,4 +115,7 @@ public interface LocalDataAdapter extends DataAdapter { /** Insert a data. */ public void insertData(LocalData data); + + /** Stop data loading */ + public void stopLoading(); } diff --git a/src/com/android/camera/exif/ExifInterface.java b/src/com/android/camera/exif/ExifInterface.java index 4ecdd7703..82dee3a19 100644 --- a/src/com/android/camera/exif/ExifInterface.java +++ b/src/com/android/camera/exif/ExifInterface.java @@ -1097,7 +1097,16 @@ public class ExifInterface { throws FileNotFoundException, IOException { // Attempt in-place write - if (!rewriteExif(filename, tags)) { + boolean rewriteOkay = false; + try { + rewriteOkay = rewriteExif(filename, tags); + } catch (IOException e) { + // If jpeg does not contains exif, rewriteExif + // will throw EOF IOException, let's catch + // it and fall back to do a copy instead + // of in-place replacement. + } + if (!rewriteOkay) { // Fall back to doing a copy ExifData tempData = mData; mData = new ExifData(DEFAULT_BYTE_ORDER); diff --git a/src/com/android/camera/exif/ExifOutputStream.java b/src/com/android/camera/exif/ExifOutputStream.java index 90d432769..47b95d80f 100644 --- a/src/com/android/camera/exif/ExifOutputStream.java +++ b/src/com/android/camera/exif/ExifOutputStream.java @@ -491,7 +491,7 @@ class ExifOutputStream extends FilterOutputStream { switch (tag.getDataType()) { case ExifTag.TYPE_ASCII: byte buf[] = tag.getStringByte(); - if (buf.length == tag.getComponentCount()) { + if (buf.length == tag.getComponentCount() && buf.length > 0) { buf[buf.length - 1] = 0; dataOutputStream.write(buf); } else { diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java index c227f32b2..77040aa09 100755 --- a/src/com/android/camera/imageprocessor/PostProcessor.java +++ b/src/com/android/camera/imageprocessor/PostProcessor.java @@ -139,7 +139,7 @@ public class PostProcessor{ private CameraCaptureSession mCaptureSession; private ImageReader mImageReader; private ImageReader mZSLReprocessImageReader; - private boolean mUseZSL = true; + private boolean mUseZSL = false; private boolean mSaveRaw = false; private Handler mZSLHandler; private HandlerThread mZSLHandlerThread; @@ -155,6 +155,14 @@ public class PostProcessor{ public int mMaxRequiredImageNum; private boolean mIsDeepPortrait = false; + private void checkAndEnableZSL(int cameraId) { + if (mController.mSettingsManager.isZslSupported(cameraId)) { + mUseZSL = true; + } else { + mUseZSL = false; + } + } + public int getMaxRequiredImageNum() { return mMaxRequiredImageNum; } @@ -633,6 +641,7 @@ public class PostProcessor{ public PostProcessor(CameraActivity activity, CaptureModule module) { mController = module; mActivity = activity; + checkAndEnableZSL(mController.getMainCameraId()); mNamedImages = new PhotoModule.NamedImages(); } @@ -694,19 +703,23 @@ public class PostProcessor{ mImageHandlerTask = new ImageHandlerTask(); mSaveRaw = isSaveRaw; mIsDeepPortrait = isDeepPortrait; - if(setFilter(postFilterId) || isFlashModeOn || isTrackingFocusOn || isMakeupOn || isSelfieMirrorOn - || PersistUtil.getCameraZSLDisabled() - || !SettingsManager.getInstance().isZSLInAppEnabled() - || "enable".equals( - SettingsManager.getInstance().getValue(SettingsManager.KEY_AUTO_HDR)) - || SettingsManager.getInstance().isCamera2HDRSupport() - || "18".equals(SettingsManager.getInstance().getValue( - SettingsManager.KEY_SCENE_MODE)) - || mController.getCameraMode() == CaptureModule.DUAL_MODE - || isSupportedQcfa || isDeepPortrait) { - mUseZSL = false; + if (mController.mSettingsManager.isZslSupported(mController.getMainCameraId())) { + if (setFilter(postFilterId) || isFlashModeOn || isTrackingFocusOn || isMakeupOn + || isSelfieMirrorOn || PersistUtil.getCameraZSLDisabled() + || !SettingsManager.getInstance().isZSLInAppEnabled() + || "enable".equals( + SettingsManager.getInstance().getValue(SettingsManager.KEY_AUTO_HDR)) + || SettingsManager.getInstance().isCamera2HDRSupport() + || "18".equals( + SettingsManager.getInstance().getValue(SettingsManager.KEY_SCENE_MODE)) + || mController.getCameraMode() == CaptureModule.DUAL_MODE + || isSupportedQcfa || isDeepPortrait) { + mUseZSL = false; + } else { + mUseZSL = true; + } } else { - mUseZSL = true; + mUseZSL = false; } Log.d(TAG,"ZSL is "+mUseZSL); startBackgroundThread(); @@ -1326,6 +1339,6 @@ public class PostProcessor{ private native int nativeResizeImage(byte[] oldBuf, byte[] newBuf, int oldWidth, int oldHeight, int oldStride, int newWidth, int newHeight); private native int nativeFlipNV21(byte[] buf, int stride, int height, int gap, boolean isVertical); static { - System.loadLibrary("jni_imageutil"); + System.loadLibrary("jni_snapimageutil"); } } diff --git a/src/com/android/camera/tinyplanet/TinyPlanetNative.java b/src/com/android/camera/tinyplanet/TinyPlanetNative.java index 1d456de9e..8f320a960 100644 --- a/src/com/android/camera/tinyplanet/TinyPlanetNative.java +++ b/src/com/android/camera/tinyplanet/TinyPlanetNative.java @@ -23,7 +23,7 @@ import android.graphics.Bitmap; */ public class TinyPlanetNative { static { - System.loadLibrary("jni_snapcamtinyplanet"); + System.loadLibrary("jni_snaptinyplanet"); } /** diff --git a/src/com/android/camera/ui/Arrows.java b/src/com/android/camera/ui/Arrows.java deleted file mode 100644 index 4923eb10e..000000000 --- a/src/com/android/camera/ui/Arrows.java +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright (c) 2016, The Linux Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of The Linux Foundation nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -package com.android.camera.ui; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Path; -import android.util.AttributeSet; -import android.view.View; - -import java.util.ArrayList; - -public class Arrows extends View { - private static final int ARROW_COLOR = Color.WHITE; - private static final double ARROW_END_DEGREE = 15d; - private static final int ARROW_END_LENGTH = 50; - - private Paint mPaint; - private ArrayList<Path> mPaths; - - public Arrows(Context context, AttributeSet attrs) { - super(context, attrs); - mPaths = new ArrayList<Path>(); - mPaint = new Paint(); - mPaint.setStyle(Paint.Style.STROKE); - mPaint.setColor(ARROW_COLOR); - mPaint.setStrokeWidth(2f); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (mPaths != null) { - for(int i=0; i < mPaths.size(); i++) { - canvas.drawPath(mPaths.get(i), mPaint); - } - } - } - - public void addPath(float[] x, float[] y) { - Path path = new Path(); - path.reset(); - path.moveTo(x[0], y[0]); - for(int i=1; i < x.length; i++) { - if(i == x.length-1) { - path.lineTo(x[i], y[i]); - - double setha = Math.toDegrees(Math.atan2(y[i] - y[i - 1], x[i] - x[i - 1])); - setha = (setha + ARROW_END_DEGREE + 360) % 360; - path.lineTo(x[i]-(float)(ARROW_END_LENGTH*Math.cos(Math.toRadians(setha))), - y[i]-(float)(ARROW_END_LENGTH*Math.sin(Math.toRadians(setha)))); - path.lineTo(x[i], y[i]); - setha = (setha - ARROW_END_DEGREE*2 + 360) % 360; - path.lineTo(x[i]-(float)(ARROW_END_LENGTH*Math.cos(Math.toRadians(setha))), - y[i]-(float)(ARROW_END_LENGTH*Math.sin(Math.toRadians(setha)))); - } - else - path.quadTo(x[i],y[i], x[i+1], y[i+1]); - } - mPaths.add(path); - invalidate(); - } -} diff --git a/src/com/android/camera/ui/CameraControls.java b/src/com/android/camera/ui/CameraControls.java index b85d7f1ca..26a405727 100644 --- a/src/com/android/camera/ui/CameraControls.java +++ b/src/com/android/camera/ui/CameraControls.java @@ -59,7 +59,6 @@ public class CameraControls extends RotatableLayout { private View mFrontBackSwitcher; private View mHdrSwitcher; private View mTsMakeupSwitcher; - private View mIndicators; private View mPreview; private View mSceneModeSwitcher; private View mFilterModeSwitcher; @@ -135,7 +134,6 @@ public class CameraControls extends RotatableLayout { mMenu.setVisibility(View.INVISIBLE); mMute.setVisibility(View.INVISIBLE); mExitPanorama.setVisibility(View.INVISIBLE); - mIndicators.setVisibility(View.INVISIBLE); mPreview.setVisibility(View.INVISIBLE); isAnimating = false; enableTouch(true); @@ -160,7 +158,6 @@ public class CameraControls extends RotatableLayout { mMenu.setVisibility(View.INVISIBLE); mMute.setVisibility(View.INVISIBLE); mExitPanorama.setVisibility(View.INVISIBLE); - mIndicators.setVisibility(View.INVISIBLE); mPreview.setVisibility(View.INVISIBLE); isAnimating = false; enableTouch(true); @@ -276,8 +273,6 @@ public class CameraControls extends RotatableLayout { mViewList.add(mMute); if (mExitPanorama.getVisibility() == View.VISIBLE) mViewList.add(mExitPanorama); - if (mIndicators.getVisibility() == View.VISIBLE) - mViewList.add(mIndicators); } public void removeFromViewList(View view) { @@ -303,7 +298,6 @@ public class CameraControls extends RotatableLayout { mMute = findViewById(R.id.mute_button); mExitPanorama = findViewById(R.id.exit_panorama); mExitPanorama.setVisibility(View.GONE); - mIndicators = findViewById(R.id.on_screen_indicators); mPreview = findViewById(R.id.preview_thumb); mSceneModeSwitcher = findViewById(R.id.scene_mode_switcher); mFilterModeSwitcher = findViewById(R.id.filter_mode_switcher); @@ -349,6 +343,7 @@ public class CameraControls extends RotatableLayout { mReviewDoneButton = null; } layoutRemaingPhotos(); + mRemainingPhotos.setVisibility(View.VISIBLE); } @Override @@ -381,19 +376,18 @@ public class CameraControls extends RotatableLayout { int rotation = getUnifiedRotation(); toIndex(mSwitcher, w, h, rotation, 4, 6, SWITCHER_INDEX); toIndex(mVideoShutter, w, h, rotation, 3, 6, VIDEO_SHUTTER_INDEX); - toIndex(mMenu, w, h, rotation, 4, 0, MENU_INDEX); - toIndex(mMute, w, h, rotation, 3, 0, MUTE_INDEX); + toIndex(mSceneModeSwitcher, w, h, rotation, 4, 0, SCENE_MODE_INDEX); + toIndex(mMute, w, h, rotation, 4, 0, MUTE_INDEX); + toIndex(mFilterModeSwitcher, w, h, rotation, 3, 0, FILTER_MODE_INDEX); toIndex(mExitPanorama, w, h, rotation, 0, 0, EXIT_PANORAMA_INDEX); - toIndex(mIndicators, w, h, rotation, 0, 6, INDICATOR_INDEX); - toIndex(mFrontBackSwitcher, w, h, rotation, 2, 0, FRONT_BACK_INDEX); + toIndex(mFrontBackSwitcher, w, h, rotation, 1, 0, FRONT_BACK_INDEX); toIndex(mPreview, w, h, rotation, 0, 6, PREVIEW_INDEX); if(TsMakeupManager.HAS_TS_MAKEUP) { - toIndex(mTsMakeupSwitcher, w, h, rotation, 3, 0, TS_MAKEUP_INDEX); + toIndex(mTsMakeupSwitcher, w, h, rotation, 2, 0, TS_MAKEUP_INDEX); } else { - toIndex(mHdrSwitcher, w, h, rotation, 3, 0, HDR_INDEX); + toIndex(mHdrSwitcher, w, h, rotation, 2, 0, HDR_INDEX); } - toIndex(mFilterModeSwitcher, w, h, rotation, 1, 0, FILTER_MODE_INDEX); - toIndex(mSceneModeSwitcher, w, h, rotation, 0, 0, SCENE_MODE_INDEX); + toIndex(mMenu, w, h, rotation, 0, 0, MENU_INDEX); layoutToast(mRefocusToast, w, h, rotation); } @@ -498,7 +492,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.setX(mLocX[idx1][SWITCHER_INDEX] - x); mShutter.setX(mLocX[idx1][SHUTTER_INDEX] - x); mVideoShutter.setX(mLocX[idx1][VIDEO_SHUTTER_INDEX] - x); - mIndicators.setX(mLocX[idx1][INDICATOR_INDEX] - x); mPreview.setX(mLocX[idx1][PREVIEW_INDEX] - x); mFrontBackSwitcher.setY(mLocY[idx1][FRONT_BACK_INDEX] + y); @@ -515,7 +508,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.setY(mLocY[idx1][SWITCHER_INDEX] - y); mShutter.setY(mLocY[idx1][SHUTTER_INDEX] - y); mVideoShutter.setY(mLocY[idx1][VIDEO_SHUTTER_INDEX] - y); - mIndicators.setY(mLocY[idx1][INDICATOR_INDEX] - y); mPreview.setY(mLocY[idx1][PREVIEW_INDEX] - y); } @@ -550,7 +542,6 @@ public class CameraControls extends RotatableLayout { mMenu.animate().cancel(); mMute.animate().cancel(); mExitPanorama.animate().cancel(); - mIndicators.animate().cancel(); mPreview.animate().cancel(); mFrontBackSwitcher.animate().setListener(outlistener); ((ModuleSwitcher) mSwitcher).removePopup(); @@ -573,7 +564,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationYBy(mSize).setDuration(ANIME_DURATION); mShutter.animate().translationYBy(mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationYBy(mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationYBy(mSize).setDuration(ANIME_DURATION); mPreview.animate().translationYBy(mSize).setDuration(ANIME_DURATION); break; case 90: @@ -592,7 +582,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationXBy(mSize).setDuration(ANIME_DURATION); mShutter.animate().translationXBy(mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationXBy(mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationXBy(mSize).setDuration(ANIME_DURATION); mPreview.animate().translationXBy(mSize).setDuration(ANIME_DURATION); break; case 180: @@ -611,7 +600,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); mShutter.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); mPreview.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); break; case 270: @@ -630,7 +618,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); mShutter.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); mPreview.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); break; } @@ -657,7 +644,6 @@ public class CameraControls extends RotatableLayout { mMenu.animate().cancel(); mMute.animate().cancel(); mExitPanorama.animate().cancel(); - mIndicators.animate().cancel(); mPreview.animate().cancel(); if (mViewList != null) for (View v : mViewList) { @@ -669,7 +655,6 @@ public class CameraControls extends RotatableLayout { shutterAnim.stop(); mMenu.setVisibility(View.VISIBLE); - mIndicators.setVisibility(View.VISIBLE); mPreview.setVisibility(View.VISIBLE); mFrontBackSwitcher.animate().setListener(inlistener); @@ -692,7 +677,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); mShutter.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); mPreview.animate().translationYBy(-mSize).setDuration(ANIME_DURATION); break; case 90: @@ -713,7 +697,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); mShutter.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); mPreview.animate().translationXBy(-mSize).setDuration(ANIME_DURATION); break; case 180: @@ -734,7 +717,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationYBy(mSize).setDuration(ANIME_DURATION); mShutter.animate().translationYBy(mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationYBy(mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationYBy(mSize).setDuration(ANIME_DURATION); mPreview.animate().translationYBy(mSize).setDuration(ANIME_DURATION); break; case 270: @@ -755,7 +737,6 @@ public class CameraControls extends RotatableLayout { mSwitcher.animate().translationXBy(mSize).setDuration(ANIME_DURATION); mShutter.animate().translationXBy(mSize).setDuration(ANIME_DURATION); mVideoShutter.animate().translationXBy(mSize).setDuration(ANIME_DURATION); - mIndicators.animate().translationXBy(mSize).setDuration(ANIME_DURATION); mPreview.animate().translationXBy(mSize).setDuration(ANIME_DURATION); break; } @@ -1010,7 +991,7 @@ public class CameraControls extends RotatableLayout { mPaint.setColor(getResources().getColor(R.color.camera_control_bg_transparent)); } } - invalidate(); + requestLayout(); } public void showRefocusToast(boolean show) { @@ -1025,7 +1006,7 @@ public class CameraControls extends RotatableLayout { View[] views = { mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, TsMakeupManager.HAS_TS_MAKEUP ? mTsMakeupSwitcher : mHdrSwitcher, - mMenu, mShutter, mPreview, mSwitcher, mMute, mReviewRetakeButton, + mMenu, mPreview, mSwitcher, mMute, mReviewRetakeButton, mReviewCancelButton, mReviewDoneButton, mExitPanorama }; for (View v : views) { diff --git a/src/com/android/camera/ui/FilmStripView.java b/src/com/android/camera/ui/FilmStripView.java index e6e00ad56..6a767862f 100644 --- a/src/com/android/camera/ui/FilmStripView.java +++ b/src/com/android/camera/ui/FilmStripView.java @@ -2825,8 +2825,6 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public boolean onScroll(float x, float y, float dx, float dy) { - if (mPreviewGestures != null && mPreviewGestures.waitUntilNextDown()) - return false; ViewItem currItem = mViewItem[mCurrentItem]; if (currItem == null) { return false; @@ -2892,8 +2890,6 @@ public class FilmStripView extends ViewGroup implements BottomControlsListener { @Override public boolean onFling(float velocityX, float velocityY) { - if (mPreviewGestures != null && mPreviewGestures.waitUntilNextDown()) - return false; final ViewItem currItem = mViewItem[mCurrentItem]; if (currItem == null) { return false; diff --git a/src/com/android/camera/ui/ListMenu.java b/src/com/android/camera/ui/ListMenu.java index d83114cce..88ad8fe2f 100644 --- a/src/com/android/camera/ui/ListMenu.java +++ b/src/com/android/camera/ui/ListMenu.java @@ -120,8 +120,7 @@ public class ListMenu extends ListView + " position " + position); } if (position == mHighlighted) - view.setBackgroundColor(getContext().getResources() - .getColor(R.color.setting_color)); + view.setActivated(true); return view; } @@ -160,7 +159,6 @@ public class ListMenu extends ListView 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++) { @@ -183,7 +181,6 @@ public class ListMenu extends ListView 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++) { @@ -229,7 +226,7 @@ public class ListMenu extends ListView mEnabled[j] = enable; int offset = getFirstVisiblePosition(); if (offset >= 0) { - int indexInView = j - offset; + int indexInView = j + getHeaderViewsCount() - offset; if (getChildCount() > indexInView && indexInView >= 0) { getChildAt(indexInView).setEnabled(enable); } @@ -243,8 +240,7 @@ public class ListMenu extends ListView public void resetHighlight() { int count = getChildCount(); for (int i = 0; i < count; i++) { - View v = getChildAt(i); - v.setBackground(null); + getChildAt(i).setActivated(false); } mHighlighted = -1; } @@ -265,7 +261,7 @@ public class ListMenu extends ListView resetHighlight(); ListPreference pref = mListItem.get(position); mHighlighted = position; - view.setBackgroundColor(getContext().getResources().getColor(R.color.setting_color)); + view.setActivated(true); mListener.onPreferenceClicked(pref, (int) view.getY()); } @@ -274,11 +270,9 @@ public class ListMenu extends ListView public void reloadPreference() { int count = getChildCount(); for (int i = 0; i < count; i++) { - ListPreference pref = mListItem.get(i); - if (pref != null) { - ListMenuItem listMenuItem = - (ListMenuItem) getChildAt(i); - listMenuItem.reloadPreference(); + View view = getChildAt(i); + if (view instanceof ListMenuItem) { + ((ListMenuItem) view).reloadPreference(); } } } diff --git a/src/com/android/camera/ui/MenuHelp.java b/src/com/android/camera/ui/MenuHelp.java deleted file mode 100644 index 4adff0fd6..000000000 --- a/src/com/android/camera/ui/MenuHelp.java +++ /dev/null @@ -1,387 +0,0 @@ -/* -Copyright (c) 2016, The Linux Foundation. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of The Linux Foundation nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS -BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ -package com.android.camera.ui; - -import android.animation.Animator; -import android.animation.Animator.AnimatorListener; -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Typeface; -import android.graphics.drawable.AnimationDrawable; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.Paint; -import android.graphics.Path; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewPropertyAnimator; -import android.widget.FrameLayout; -import android.widget.LinearLayout; -import android.widget.TableLayout; -import android.widget.TextView; -import java.util.ArrayList; - -import org.codeaurora.snapcam.R; -import com.android.camera.ui.ModuleSwitcher; -import com.android.camera.ui.RotateImageView; -import com.android.camera.ShutterButton; -import com.android.camera.Storage; -import com.android.camera.util.CameraUtil; -import com.android.camera.TsMakeupManager; - -public class MenuHelp extends RotatableLayout { - - private static final String TAG = "MenuHelp"; - private View mBackgroundView; - private Arrows mArrows; - private static int mTopMargin = 0; - private static int mBottomMargin = 0; - private static final int HELP_0_0_INDEX = 0; - private static final int HELP_1_0_INDEX = 1; - private static final int HELP_3_0_INDEX = 2; - private static final int HELP_4_6_INDEX = 3; - private static final int OK_2_4_INDEX = 4; - private static final int MAX_INDEX = 5; - private float[][] mLocX = new float[4][MAX_INDEX]; - private float[][] mLocY = new float[4][MAX_INDEX]; - private RotateLayout mHelp0_0; - private RotateLayout mHelp1_0; - private RotateLayout mHelp3_0; - private RotateLayout mHelp4_6; - private RotateLayout mOk2_4; - private Context mContext; - private int mOrientation; - private final static int POINT_MARGIN = 50; - private static final int WIDTH_GRID = 5; - private static final int HEIGHT_GRID = 7; - private Typeface mTypeface; - private boolean forCamera2 = false; - - public MenuHelp(Context context, AttributeSet attrs) { - super(context, attrs); - mContext = context; - mTypeface = Typeface.create(Typeface.SERIF, Typeface.NORMAL); - } - - public MenuHelp(Context context) { - this(context, null); - } - - public void setForCamera2(boolean forCamera2) { - this.forCamera2 = forCamera2; - } - - public void setMargins(int top, int bottom) { - mTopMargin = top; - mBottomMargin = bottom; - } - - @Override - public void onLayout(boolean changed, int l, int t, int r, int b) { - r = r - l; - b = b - t; - l = 0; - t = 0; - for (int i = 0; i < getChildCount(); i++) { - View v = getChildAt(i); - v.layout(l, t, r, b); - } - setLocation(r - l, b - t); - } - - private void setLocation(int w, int h) { - int rotation = getUnifiedRotation(); - toIndex(mHelp0_0, w, h, rotation, 1, 3, HELP_0_0_INDEX); - toIndex(mHelp1_0, w, h, rotation, 2, 2, HELP_1_0_INDEX); - if(TsMakeupManager.HAS_TS_MAKEUP) - toIndex(mHelp3_0, w, h, rotation, 3, 1, HELP_3_0_INDEX); - if (!forCamera2) { - toIndex(mHelp4_6, w, h, rotation, 3, 4, HELP_4_6_INDEX); - } else { - mHelp4_6.setVisibility(View.GONE); - } - toIndex(mOk2_4, w, h, rotation, 1, 5, OK_2_4_INDEX); - fillArrows(w, h, rotation); - } - - private void fillArrows(int w, int h, int rotation) { - View v1 = new View(mContext); - View v2 = new View(mContext); - View v3 = new View(mContext); - { - toIndex(v1, w, h, rotation, 1, 3, -1); - toIndex(v2, w, h, rotation, 0, 1, -1); - toIndex(v3, w, h, rotation, 0, 0, -1); - float[] x = {v1.getX()-POINT_MARGIN, v2.getX(), v3.getX()}; - float[] y = {v1.getY()-POINT_MARGIN, v2.getY(), v3.getY()+POINT_MARGIN}; - mArrows.addPath(x, y); - } - - { - toIndex(v1, w, h, rotation, 2, 2, -1); - toIndex(v2, w, h, rotation, 1, 1, -1); - toIndex(v3, w, h, rotation, 1, 0, -1); - float[] x = {v1.getX()-POINT_MARGIN, v2.getX(), v3.getX()}; - float[] y = {v1.getY()-POINT_MARGIN, v2.getY(), v3.getY()+POINT_MARGIN}; - mArrows.addPath(x, y); - } - - if(TsMakeupManager.HAS_TS_MAKEUP) { - toIndex(v1, w, h, rotation, 3, 1, -1); - toIndex(v2, w, h, rotation, 3, 0, -1); - float[] x = {v1.getX(), v2.getX()}; - float[] y = {v1.getY()-POINT_MARGIN*2, v2.getY()+POINT_MARGIN}; - mArrows.addPath(x, y); - } - - if (!forCamera2) { - toIndex(v1, w, h, rotation, 3, 4, -1); - toIndex(v2, w, h, rotation, 3, 5, -1); - toIndex(v3, w, h, rotation, 4, 6, -1); - float[] x = {v1.getX(), v2.getX(), v3.getX()}; - float[] y = {v1.getY()+POINT_MARGIN, v2.getY(), v3.getY()-POINT_MARGIN}; - mArrows.addPath(x, y); - } - } - - private void toIndex(View v, int w, int h, int rotation, int index, int index2, int index3) { - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) v.getLayoutParams(); - int tw = v.getMeasuredWidth(); - int th = v.getMeasuredHeight(); - int l = 0, r = 0, t = 0, b = 0; - - int wnumber = WIDTH_GRID; - int hnumber = HEIGHT_GRID; - int windex = 0; - int hindex = 0; - switch (rotation) { - case 0: - // portrait, to left of anchor at bottom - wnumber = WIDTH_GRID; - hnumber = HEIGHT_GRID; - windex = index; - hindex = index2; - break; - case 90: - // phone landscape: below anchor on right - wnumber = HEIGHT_GRID; - hnumber = WIDTH_GRID; - windex = index2; - hindex = hnumber - index - 1; - break; - case 180: - // phone upside down: right of anchor at top - wnumber = WIDTH_GRID; - hnumber = HEIGHT_GRID; - windex = wnumber - index - 1; - hindex = hnumber - index2 - 1; - break; - case 270: - // reverse landscape: above anchor on left - wnumber = HEIGHT_GRID; - hnumber = WIDTH_GRID; - windex = wnumber - index2 - 1; - hindex = index; - break; - } - int boxh = h / hnumber; - int boxw = w / wnumber; - int cx = (2 * windex + 1) * boxw / 2; - int cy = (2 * hindex + 1) * boxh / 2; - - if (index2 == 0 && mTopMargin != 0) { - switch (rotation) { - case 90: - cx = mTopMargin / 2; - break; - case 180: - cy = h - mTopMargin / 2; - break; - case 270: - cx = w - mTopMargin / 2; - break; - default: - cy = mTopMargin / 2; - break; - } - } - - l = cx - tw / 2; - r = cx + tw / 2; - t = cy - th / 2; - b = cy + th / 2; - - if (index3 != -1) { - int idx1 = rotation / 90; - int idx2 = index3; - mLocX[idx1][idx2] = l; - mLocY[idx1][idx2] = t; - } - v.layout(l, t, r, b); - } - - public void setOrientation(int orientation, boolean animation) { - mOrientation = orientation; - RotateLayout[] layouts = { - mHelp0_0, mHelp1_0, mHelp3_0, mHelp4_6, mOk2_4 - }; - for (RotateLayout l : layouts) { - l.setOrientation(orientation, animation); - } - } - - @Override - public void onFinishInflate() { - super.onFinishInflate(); - mBackgroundView = findViewById(R.id.background); - mBackgroundView.setBackgroundColor(Color.argb(200, 0, 0, 0)); - mHelp0_0 = (RotateLayout)findViewById(R.id.help_text_0_0); - fillHelp0_0(); - mHelp1_0 = (RotateLayout)findViewById(R.id.help_text_1_0); - fillHelp1_0(); - mHelp3_0 = (RotateLayout) findViewById(R.id.help_text_3_0); - fillHelp3_0(); - mHelp4_6 = (RotateLayout)findViewById(R.id.help_text_4_6); - fillHelp4_6(); - mOk2_4 = (RotateLayout)findViewById(R.id.help_ok_2_4); - fillOk2_4(); - mArrows = (Arrows)findViewById(R.id.arrows); - } - - private void fillOk2_4() { - LinearLayout linearLayout = new LinearLayout(mContext); - mOk2_4.addView(linearLayout); - linearLayout.setGravity(Gravity.CENTER); - linearLayout.setPadding(40, 20, 40, 20); - linearLayout.setBackgroundColor(Color.WHITE); - TextView text1 = new TextView(mContext); - text1.setText(getResources().getString(R.string.help_menu_ok)); - text1.setTextColor(Color.BLACK); - text1.setTypeface(mTypeface); - linearLayout.addView(text1); - } - - private void fillHelp0_0() { - TableLayout tableLayout = new TableLayout(mContext); - mHelp0_0.addView(tableLayout); - LinearLayout linearLayout = new LinearLayout(mContext); - TextView text1 = new TextView(mContext); - text1.setTextColor(getResources().getColor(R.color.help_menu_scene_mode_1)); - text1.setText(getResources().getString(R.string.help_menu_scene_mode_1)+" "); - text1.setTypeface(mTypeface); - linearLayout.addView(text1); - TextView text2 = new TextView(mContext); - text2.setText(getResources().getString(R.string.help_menu_scene_mode_2)); - text2.setTypeface(mTypeface); - linearLayout.addView(text2); - text2.setTextColor(getResources().getColor(R.color.help_menu_scene_mode_2)); - tableLayout.addView(linearLayout); - TextView text3 = new TextView(mContext); - text3.setText(getResources().getString(R.string.help_menu_scene_mode_3)); - text3.setTextColor(getResources().getColor(R.color.help_menu_scene_mode_3)); - text3.setTypeface(mTypeface); - tableLayout.addView(text3); - } - - private void fillHelp1_0() { - TableLayout tableLayout = new TableLayout(mContext); - mHelp1_0.addView(tableLayout); - LinearLayout linearLayout = new LinearLayout(mContext); - TextView text1 = new TextView(mContext); - text1.setText(getResources().getString(R.string.help_menu_color_filter_1)+" "); - text1.setTextColor(getResources().getColor(R.color.help_menu_color_filter_1)); - text1.setTypeface(mTypeface); - linearLayout.addView(text1); - TextView text2 = new TextView(mContext); - text2.setText(getResources().getString(R.string.help_menu_color_filter_2)+" "); - text2.setTextColor(getResources().getColor(R.color.help_menu_color_filter_2)); - text2.setTypeface(mTypeface); - linearLayout.addView(text2); - TextView text3 = new TextView(mContext); - text3.setText(getResources().getString(R.string.help_menu_color_filter_3)); - text3.setTextColor(getResources().getColor(R.color.help_menu_color_filter_3)); - text3.setTypeface(mTypeface); - linearLayout.addView(text3); - tableLayout.addView(linearLayout); - TextView text4 = new TextView(mContext); - text4.setText(getResources().getString(R.string.help_menu_color_filter_4)); - text4.setTextColor(getResources().getColor(R.color.help_menu_color_filter_4)); - text4.setTypeface(mTypeface); - tableLayout.addView(text4); - } - - private void fillHelp3_0() { - TableLayout tableLayout = new TableLayout(mContext); - mHelp3_0.addView(tableLayout); - if(TsMakeupManager.HAS_TS_MAKEUP) { - TextView text1 = new TextView(mContext); - text1.setText(getResources().getString(R.string.help_menu_beautify_1)); - text1.setTextColor(getResources().getColor(R.color.help_menu_beautify_1)); - text1.setTypeface(mTypeface); - tableLayout.addView(text1); - TextView text2 = new TextView(mContext); - text2.setText(getResources().getString(R.string.help_menu_beautify_2)); - text2.setTextColor(getResources().getColor(R.color.help_menu_beautify_2)); - text2.setTypeface(mTypeface); - tableLayout.addView(text2); - TextView text3 = new TextView(mContext); - text3.setText(getResources().getString(R.string.help_menu_beautify_3)); - text3.setTextColor(getResources().getColor(R.color.help_menu_beautify_3)); - text3.setTypeface(mTypeface); - tableLayout.addView(text3); - } - } - - private void fillHelp4_6() { - TableLayout tableLayout = new TableLayout(mContext); - mHelp4_6.addView(tableLayout); - LinearLayout linearLayout = new LinearLayout(mContext); - TextView text1 = new TextView(mContext); - text1.setText(getResources().getString(R.string.help_menu_switcher_1)+" "); - text1.setTextColor(Color.GREEN); - text1.setTypeface(mTypeface); - linearLayout.addView(text1); - TextView text2 = new TextView(mContext); - text2.setText(getResources().getString(R.string.help_menu_switcher_2)); - text2.setTextColor(Color.WHITE); - text2.setTypeface(mTypeface); - linearLayout.addView(text2); - tableLayout.addView(linearLayout); - TextView text3 = new TextView(mContext); - text3.setText(getResources().getString(R.string.help_menu_switcher_3)); - text3.setTextColor(Color.WHITE); - text3.setTypeface(mTypeface); - tableLayout.addView(text3); - } -} diff --git a/src/com/android/camera/ui/ModuleSwitcher.java b/src/com/android/camera/ui/ModuleSwitcher.java index d96775d14..058e64d7b 100644 --- a/src/com/android/camera/ui/ModuleSwitcher.java +++ b/src/com/android/camera/ui/ModuleSwitcher.java @@ -51,10 +51,11 @@ public class ModuleSwitcher extends RotateImageView public static final int PHOTO_MODULE_INDEX = 0; public static final int VIDEO_MODULE_INDEX = 1; public static final int WIDE_ANGLE_PANO_MODULE_INDEX = 2; - public static final int LIGHTCYCLE_MODULE_INDEX = 3; - public static final int GCAM_MODULE_INDEX = 4; - public static final int CAPTURE_MODULE_INDEX = 5; - public static final int PANOCAPTURE_MODULE_INDEX = 6; + public static final int QR_MODULE_INDEX = 3; + public static final int LIGHTCYCLE_MODULE_INDEX = 4; + public static final int GCAM_MODULE_INDEX = 5; + public static final int CAPTURE_MODULE_INDEX = 6; + public static final int PANOCAPTURE_MODULE_INDEX = 7; private boolean mTouchEnabled = true; private boolean mIsVisible = true; @@ -63,6 +64,7 @@ public class ModuleSwitcher extends RotateImageView R.drawable.ic_switch_camera, R.drawable.ic_switch_video, R.drawable.ic_switch_pan, + R.drawable.ic_cam_switcher_qr, R.drawable.ic_switch_photosphere, R.drawable.ic_switch_gcam, }; @@ -102,7 +104,7 @@ public class ModuleSwitcher extends RotateImageView private void init(Context context) { mItemSize = context.getResources().getDimensionPixelSize(R.dimen.switcher_size); - mIndicator = context.getResources().getDrawable(R.drawable.ic_switcher_menu_indicator); + mIndicator = context.getResources().getDrawable(R.color.transparent); initializeDrawables(context); } @@ -231,6 +233,10 @@ public class ModuleSwitcher extends RotateImageView item.setContentDescription(getContext().getResources().getString( R.string.accessibility_switch_to_panorama)); break; + case R.drawable.ic_cam_switcher_qr: + item.setContentDescription(getContext().getResources().getString( + R.string.accessibility_switch_to_qr)); + break; case R.drawable.ic_switch_photosphere: item.setContentDescription(getContext().getResources().getString( R.string.accessibility_switch_to_photo_sphere)); diff --git a/src/com/android/camera/ui/OneUICameraControls.java b/src/com/android/camera/ui/OneUICameraControls.java index 646596912..73cb68e16 100755 --- a/src/com/android/camera/ui/OneUICameraControls.java +++ b/src/com/android/camera/ui/OneUICameraControls.java @@ -232,7 +232,7 @@ public class OneUICameraControls extends RotatableLayout { mViews = new View[]{ mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, - mTsMakeupSwitcher,mDeepportraitSwitcher, mFlashButton, mShutter, + mTsMakeupSwitcher, mDeepportraitSwitcher, mFlashButton, mPreview, mVideoShutter, mPauseButton, mCancelButton }; mBottomLargeSize = getResources().getDimensionPixelSize( @@ -501,7 +501,7 @@ public class OneUICameraControls extends RotatableLayout { View[] views = { mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, mTsMakeupSwitcher, mFlashButton, mDeepportraitSwitcher, mPreview, mMute, - mShutter, mVideoShutter, mMakeupSeekBarLowText, mMakeupSeekBarHighText, + mMakeupSeekBarLowText, mMakeupSeekBarHighText, mPauseButton, mExitBestPhotpMode }; diff --git a/src/com/android/camera/ui/Switch.java b/src/com/android/camera/ui/Switch.java index 3e7b9bfb9..68fb36542 100644 --- a/src/com/android/camera/ui/Switch.java +++ b/src/com/android/camera/ui/Switch.java @@ -123,7 +123,7 @@ public class Switch extends CompoundButton { mSwitchMinWidth = res.getDimensionPixelSize(R.dimen.switch_min_width); mSwitchTextMaxWidth = res.getDimensionPixelSize(R.dimen.switch_text_max_width); mSwitchPadding = res.getDimensionPixelSize(R.dimen.switch_padding); - setSwitchTextAppearance(context, android.R.style.TextAppearance_Holo_Small); + setSwitchTextAppearance(context, android.R.style.TextAppearance_Material_Small); ViewConfiguration config = ViewConfiguration.get(context); mTouchSlop = config.getScaledTouchSlop(); diff --git a/src/com/android/camera/ui/ZoomRenderer.java b/src/com/android/camera/ui/ZoomRenderer.java index 48a565a6f..eb88decc2 100755 --- a/src/com/android/camera/ui/ZoomRenderer.java +++ b/src/com/android/camera/ui/ZoomRenderer.java @@ -16,12 +16,15 @@ package com.android.camera.ui; +import android.app.Activity; import android.content.Context; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Point; import android.graphics.Rect; +import android.os.Handler; import android.view.ScaleGestureDetector; import org.codeaurora.snapcam.R; @@ -35,6 +38,8 @@ public class ZoomRenderer extends OverlayRenderer private int mMinZoom; private OnZoomChangedListener mListener; + private final Handler mHandler = new Handler(); + private ScaleGestureDetector mDetector; private Paint mPaint; private Paint mTextPaint; @@ -47,12 +52,17 @@ public class ZoomRenderer extends OverlayRenderer private int mOuterStroke; private int mZoomSig; private int mZoomFraction; + private boolean mInZoom; private Rect mTextBounds; private int mOrientation; private boolean mCamera2 = false; private float mZoomMinValue; private float mZoomMaxValue; + private Point mDispSize; + private int mBottomMargin; + private int mTopMargin; + public interface OnZoomChangedListener { void onZoomStart(); void onZoomEnd(); @@ -77,6 +87,13 @@ public class ZoomRenderer extends OverlayRenderer mMinCircle = res.getDimensionPixelSize(R.dimen.zoom_ring_min); mTextBounds = new Rect(); setVisible(false); + mBottomMargin = + ctx.getResources().getDimensionPixelSize(R.dimen.preview_bottom_margin); + mTopMargin = + ctx.getResources().getDimensionPixelSize(R.dimen.preview_top_margin); + mDispSize = new Point(); + Activity activity = (Activity) ctx; + activity.getWindowManager().getDefaultDisplay().getRealSize(mDispSize); } // set from module @@ -115,9 +132,13 @@ public class ZoomRenderer extends OverlayRenderer @Override public void layout(int l, int t, int r, int b) { + l = 0; + t = mTopMargin; + r = mDispSize.x; + b = mDispSize.y - mBottomMargin; super.layout(l, t, r, b); mCenterX = (r - l) / 2; - mCenterY = (b - t) / 2; + mCenterY = ((b - t) / 2) + t; mMaxCircle = Math.min(getWidth(), getHeight()); mMaxCircle = (mMaxCircle - mMinCircle) / 2; } @@ -165,6 +186,48 @@ public class ZoomRenderer extends OverlayRenderer return true; } + public boolean onScaleStepResize(boolean direction) { + int zoom; + float circle; + float circleStep = (mMaxCircle - mMinCircle) / 18; + if (direction) { + circle = (int) (mCircleSize + circleStep); + } else { + circle = (int) (mCircleSize - circleStep); + } + circle = Math.max(mMinCircle, circle); + circle = Math.min(mMaxCircle, circle); + if (mListener != null && (int) circle != mCircleSize && (mMaxCircle - mMinCircle) > 0) { + mCircleSize = (int) circle; + zoom = mMinZoom + (int) ((mCircleSize - mMinCircle) + * (mMaxZoom - mMinZoom) / (mMaxCircle - mMinCircle)); + if (mListener != null) { + mHandler.removeCallbacks(mOnZoomEnd); + if (!mInZoom) { + mInZoom = true; + setVisible(true); + mListener.onZoomStart(); + update(); + } + mListener.onZoomValueChanged(zoom); + mHandler.postDelayed(mOnZoomEnd, 300); + } + return true; + } else { + return false; + } + } + + Runnable mOnZoomEnd = new Runnable() { + public void run() { + mInZoom = false; + setVisible(false); + if (mListener != null) { + mListener.onZoomEnd(); + } + } + }; + @Override public boolean onScaleBegin(ScaleGestureDetector detector) { setVisible(true); diff --git a/src/com/android/camera/ui/focus/AutoFocusRing.java b/src/com/android/camera/ui/focus/AutoFocusRing.java new file mode 100644 index 000000000..ff09ebc65 --- /dev/null +++ b/src/com/android/camera/ui/focus/AutoFocusRing.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.android.camera.ui.motion.InterpolateUtils; +import com.android.camera.ui.motion.Invalidator; + +/** + * Passive focus ring animation renderer. + */ +class AutoFocusRing extends FocusRingRenderer { + private static final String TAG = "AutoFocusRing"; + + /** + * The auto focus ring encapsulates the animation logic for visualizing + * a focus event when triggered by the camera subsystem. + * + * @param invalidator the object to invalidate while running. + * @param ringPaint the paint to draw the ring with. + * @param enterDurationMillis the fade in time in milliseconds. + * @param exitDurationMillis the fade out time in milliseconds. + */ + public AutoFocusRing(Invalidator invalidator, Paint ringPaint, float enterDurationMillis, + float exitDurationMillis) { + super(invalidator, ringPaint, enterDurationMillis, exitDurationMillis); + } + + @Override + public void draw(long t, long dt, Canvas canvas) { + float ringRadius = mRingRadius.update(dt); + processStates(t); + + if (!isActive()) { + return; + } + + mInvalidator.invalidate(); + int ringAlpha = 255; + + if (mFocusState == FocusState.STATE_ENTER) { + float rFade = InterpolateUtils.unitRatio(t, mEnterStartMillis, mEnterDurationMillis); + ringAlpha = (int) InterpolateUtils + .lerp(0, 255, mEnterOpacityCurve.valueAt(rFade)); + } else if (mFocusState == FocusState.STATE_FADE_OUT) { + float rFade = InterpolateUtils.unitRatio(t, mExitStartMillis, mExitDurationMillis); + ringAlpha = (int) InterpolateUtils + .lerp(255, 0, mExitOpacityCurve.valueAt(rFade)); + } else if (mFocusState == FocusState.STATE_HARD_STOP) { + float rFade = InterpolateUtils + .unitRatio(t, mHardExitStartMillis, mHardExitDurationMillis); + ringAlpha = (int) InterpolateUtils + .lerp(255, 0, mExitOpacityCurve.valueAt(rFade)); + } else if (mFocusState == FocusState.STATE_INACTIVE) { + ringAlpha = 0; + } + + mRingPaint.setAlpha(ringAlpha); + canvas.drawCircle(getCenterX(), getCenterY(), ringRadius, mRingPaint); + } + + private void processStates(long t) { + if (mFocusState == FocusState.STATE_INACTIVE) { + return; + } + + if (mFocusState == FocusState.STATE_ENTER && t > mEnterStartMillis + mEnterDurationMillis) { + mFocusState = FocusState.STATE_ACTIVE; + } + + if (mFocusState == FocusState.STATE_ACTIVE && !mRingRadius.isActive()) { + mFocusState = FocusState.STATE_FADE_OUT; + mExitStartMillis = t; + } + + if (mFocusState == FocusState.STATE_FADE_OUT && t > mExitStartMillis + mExitDurationMillis) { + mFocusState = FocusState.STATE_INACTIVE; + } + + if (mFocusState == FocusState.STATE_HARD_STOP + && t > mHardExitStartMillis + mHardExitDurationMillis) { + mFocusState = FocusState.STATE_INACTIVE; + } + } +} diff --git a/src/com/android/camera/ui/focus/CameraCoordinateTransformer.java b/src/com/android/camera/ui/focus/CameraCoordinateTransformer.java new file mode 100644 index 000000000..809503c9a --- /dev/null +++ b/src/com/android/camera/ui/focus/CameraCoordinateTransformer.java @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.Matrix; +import android.graphics.RectF; + +/** + * Transform coordinates to and from preview coordinate space and camera driver + * coordinate space. + */ +public class CameraCoordinateTransformer { + // http://developer.android.com/guide/topics/media/camera.html#metering-focus-areas + private static final RectF CAMERA_DRIVER_RECT = new RectF(-1000, -1000, 1000, 1000); + + private final Matrix mCameraToPreviewTransform; + private final Matrix mPreviewToCameraTransform; + + /** + * Convert rectangles to / from camera coordinate and preview coordinate space. + * + * @param mirrorX if the preview is mirrored along the X axis. + * @param displayOrientation orientation in degrees. + * @param previewRect the preview rectangle size and position. + */ + public CameraCoordinateTransformer(boolean mirrorX, int displayOrientation, + RectF previewRect) { + if (!hasNonZeroArea(previewRect)) { + throw new IllegalArgumentException("previewRect"); + } + + mCameraToPreviewTransform = cameraToPreviewTransform(mirrorX, displayOrientation, + previewRect); + mPreviewToCameraTransform = inverse(mCameraToPreviewTransform); + } + + /** + * Transform a rectangle in camera space into a new rectangle in preview + * view space. + * + * @param source the rectangle in camera space + * @return the rectangle in preview view space. + */ + public RectF toPreviewSpace(RectF source) { + RectF result = new RectF(); + mCameraToPreviewTransform.mapRect(result, source); + return result; + } + + /** + * Transform a rectangle in preview view space into a new rectangle in + * camera view space. + * + * @param source the rectangle in preview view space + * @return the rectangle in camera view space. + */ + public RectF toCameraSpace(RectF source) { + RectF result = new RectF(); + mPreviewToCameraTransform.mapRect(result, source); + return result; + } + + private Matrix cameraToPreviewTransform(boolean mirrorX, int displayOrientation, + RectF previewRect) { + Matrix transform = new Matrix(); + + // Need mirror for front camera. + transform.setScale(mirrorX ? -1 : 1, 1); + + // Apply a rotate transform. + // This is the value for android.hardware.Camera.setDisplayOrientation. + transform.postRotate(displayOrientation); + + // Map camera driver coordinates to preview rect coordinates + Matrix fill = new Matrix(); + fill.setRectToRect(CAMERA_DRIVER_RECT, + previewRect, + Matrix.ScaleToFit.FILL); + + // Concat the previous transform on top of the fill behavior. + transform.setConcat(fill, transform); + + return transform; + } + + private Matrix inverse(Matrix source) { + Matrix newMatrix = new Matrix(); + source.invert(newMatrix); + return newMatrix; + } + + private boolean hasNonZeroArea(RectF rect) { + return rect.width() != 0 && rect.height() != 0; + } +} diff --git a/src/com/android/camera/ui/focus/FocusController.java b/src/com/android/camera/ui/focus/FocusController.java new file mode 100644 index 000000000..a0c103443 --- /dev/null +++ b/src/com/android/camera/ui/focus/FocusController.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.RectF; +import android.util.Log; + +import com.android.camera.async.MainThread; +import com.android.camera.ui.motion.LinearScale; + +/** + * The focus controller interacts with the focus ring UI element. + */ +public class FocusController { + private static final String TAG = "FocusController"; + + private final FocusRing mFocusRing; + private final FocusSound mFocusSound; + private final MainThread mMainThread; + + public FocusController(FocusRing focusRing, FocusSound focusSound, MainThread mainThread) { + mFocusRing = focusRing; + mFocusSound = focusSound; + mMainThread = mainThread; + } + + /** + * Show a passive focus animation at the center of the active area. + * This will likely be different than the view bounds due to varying image + * ratios and dimensions. + */ + public void showPassiveFocusAtCenter() { + mMainThread.execute(new Runnable() { + @Override + public void run() { + Log.v(TAG, "Running showPassiveFocusAtCenter()"); + mFocusRing.startPassiveFocus(); + mFocusRing.centerFocusLocation(); + } + }); + } + + /** + * Show a passive focus animation at the given viewX and viewY position. + * This is usually indicates the camera subsystem kicked off an auto-focus + * at the given screen position. + * + * @param viewX the view's x coordinate + * @param viewY the view's y coordinate + */ + public void showPassiveFocusAt(final int viewX, final int viewY) { + mMainThread.execute(new Runnable() { + @Override + public void run() { + Log.v(TAG, "Running showPassiveFocusAt(" + viewX + ", " + viewY + ")"); + mFocusRing.startPassiveFocus(); + mFocusRing.setFocusLocation(viewX, viewY); + } + }); + } + + /** + * Show an active focus animation at the given viewX and viewY position. + * This is normally initiated by the user touching the screen at a given + * point. + * + * @param viewX the view's x coordinate + * @param viewY the view's y coordinate + */ + public void showActiveFocusAt(final int viewX, final int viewY) { + mMainThread.execute(new Runnable() { + @Override + public void run() { + Log.v(TAG, "showActiveFocusAt(" + viewX + ", " + viewY + ")"); + mFocusRing.startActiveFocus(); + mFocusRing.setFocusLocation(viewX, viewY); + + // TODO: Enable focus sound when better audio controls exist. + // mFocusSound.play(); + } + }); + } + + /** + * Computing the correct location for the focus ring requires knowing + * the screen position and size of the preview area so the drawing + * operations can be clipped correctly. + */ + public void configurePreviewDimensions(final RectF previewArea) { + mMainThread.execute(new Runnable() { + @Override + public void run() { + Log.v(TAG, "configurePreviewDimensions(" + previewArea + ")"); + mFocusRing.configurePreviewDimensions(previewArea); + } + }); + } + + /** + * Set the radius of the focus ring as a radius between 0 and 1. + * This will map to the min and max values computed for the UI. + */ + public void setFocusRatio(final float ratio) { + mMainThread.execute(new Runnable() { + @Override + public void run() { + if (mFocusRing.isPassiveFocusRunning() || + mFocusRing.isActiveFocusRunning()) { + mFocusRing.setRadiusRatio(ratio); + } + } + }); + } +} diff --git a/src/com/android/camera/ui/focus/FocusRing.java b/src/com/android/camera/ui/focus/FocusRing.java new file mode 100644 index 000000000..89de357ad --- /dev/null +++ b/src/com/android/camera/ui/focus/FocusRing.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.RectF; + +/** + * Primary interface for interacting with the focus ring UI. + */ +public interface FocusRing { + /** + * Check the state of the passive focus ring animation. + * + * @return whether the passive focus animation is running. + */ + public boolean isPassiveFocusRunning(); + /** + * Check the state of the active focus ring animation. + * + * @return whether the active focus animation is running. + */ + public boolean isActiveFocusRunning(); + /** + * Start a passive focus animation. + */ + public void startPassiveFocus(); + /** + * Start an active focus animation. + */ + public void startActiveFocus(); + /** + * Stop any currently running focus animations. + */ + public void stopFocusAnimations(); + /** + * Set the location of the focus ring animation center. + */ + public void setFocusLocation(float viewX, float viewY); + + /** + * Set the location of the focus ring animation center. + */ + public void centerFocusLocation(); + + /** + * Set the target radius as a ratio of min to max visible radius + * which will internally convert and clamp the value to the + * correct pixel radius. + */ + public void setRadiusRatio(float ratio); + + /** + * The physical size of preview can vary and does not map directly + * to the size of the view. This allows for conversions between view + * and preview space for values that are provided in preview space. + */ + void configurePreviewDimensions(RectF previewArea); +}
\ No newline at end of file diff --git a/src/com/android/camera/ui/focus/FocusRingRenderer.java b/src/com/android/camera/ui/focus/FocusRingRenderer.java new file mode 100644 index 000000000..264af2ace --- /dev/null +++ b/src/com/android/camera/ui/focus/FocusRingRenderer.java @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.Paint; +import android.util.Log; + +import com.android.camera.ui.motion.DampedSpring; +import com.android.camera.ui.motion.DynamicAnimation; +import com.android.camera.ui.motion.Invalidator; +import com.android.camera.ui.motion.UnitCurve; +import com.android.camera.ui.motion.UnitCurves; + +/** + * Base class for defining the focus ring states, enter and exit durations, and + * positioning logic. + */ +abstract class FocusRingRenderer implements DynamicAnimation { + private static final String TAG = "FocusRingRenderer"; + + /** + * Primary focus states that a focus ring renderer can go through. + */ + protected static enum FocusState { + STATE_INACTIVE, + STATE_ENTER, + STATE_ACTIVE, + STATE_FADE_OUT, + STATE_HARD_STOP, + } + + protected final Invalidator mInvalidator; + protected final Paint mRingPaint; + protected final DampedSpring mRingRadius; + protected final UnitCurve mEnterOpacityCurve; + protected final UnitCurve mExitOpacityCurve; + protected final UnitCurve mHardExitOpacityCurve; + protected final float mEnterDurationMillis; + protected final float mExitDurationMillis; + protected final float mHardExitDurationMillis = 64; + + private int mCenterX; + private int mCenterY; + protected long mEnterStartMillis = 0; + protected long mExitStartMillis = 0; + protected long mHardExitStartMillis = 0; + + protected FocusState mFocusState = FocusState.STATE_INACTIVE; + + /** + * A dynamic, configurable, self contained ring render that will inform + * via invalidation if it should continue to be receive updates + * and re-draws. + * + * @param invalidator the object to inform if it requires more draw calls. + * @param ringPaint the paint to use to draw the ring. + * @param enterDurationMillis the fade in duration in milliseconds + * @param exitDurationMillis the fade out duration in milliseconds. + */ + FocusRingRenderer(Invalidator invalidator, Paint ringPaint, float enterDurationMillis, + float exitDurationMillis) { + mInvalidator = invalidator; + mRingPaint = ringPaint; + mEnterDurationMillis = enterDurationMillis; + mExitDurationMillis = exitDurationMillis; + + mEnterOpacityCurve = UnitCurves.FAST_OUT_SLOW_IN; + mExitOpacityCurve = UnitCurves.FAST_OUT_LINEAR_IN; + mHardExitOpacityCurve = UnitCurves.FAST_OUT_LINEAR_IN; + + mRingRadius = new DampedSpring(); + } + + /** + * Set the centerX position for this focus ring renderer. + * + * @param value the x position + */ + public void setCenterX(int value) { + mCenterX = value; + } + + protected int getCenterX() { + return mCenterX; + } + + /** + * Set the centerY position for this focus ring renderer. + * + * @param value the y position + */ + public void setCenterY(int value) { + mCenterY = value; + } + + protected int getCenterY() { + return mCenterY; + } + + /** + * Set the physical radius of this ring. + * + * @param value the radius of the ring. + */ + public void setRadius(long tMs, float value) { + if (mFocusState == FocusState.STATE_FADE_OUT + && Math.abs(mRingRadius.getTarget() - value) > 0.1) { + Log.v(TAG, "FOCUS STATE ENTER VIA setRadius(" + tMs + ", " + value + ")"); + mFocusState = FocusState.STATE_ENTER; + mEnterStartMillis = computeEnterStartTimeMillis(tMs, mEnterDurationMillis); + } + + mRingRadius.setTarget(value); + } + + /** + * returns true if the renderer is not in an inactive state. + */ + @Override + public boolean isActive() { + return mFocusState != FocusState.STATE_INACTIVE; + } + + /** + * returns true if the renderer is in an exit state. + */ + public boolean isExiting() { + return mFocusState == FocusState.STATE_FADE_OUT + || mFocusState == FocusState.STATE_HARD_STOP; + } + + /** + * returns true if the renderer is in an enter state. + */ + public boolean isEntering() { + return mFocusState == FocusState.STATE_ENTER; + } + + /** + * Initialize and start the animation with the given start and + * target radius. + */ + public void start(long startMs, float initialRadius, float targetRadius) { + if (mFocusState != FocusState.STATE_INACTIVE) { + Log.w(TAG, "start() called while the ring was still focusing!"); + } + mRingRadius.stop(); + mRingRadius.setValue(initialRadius); + mRingRadius.setTarget(targetRadius); + mEnterStartMillis = startMs; + + mFocusState = FocusState.STATE_ENTER; + mInvalidator.invalidate(); + } + + /** + * Put the animation in the exit state regardless of the current + * dynamic transition. If the animation is currently in an enter state + * this will compute an exit start time such that the exit time lines + * up with the enter time at the current transition value. + * + * @param t the current animation time. + */ + public void exit(long t) { + if (mRingRadius.isActive()) { + mRingRadius.stop(); + } + + mFocusState = FocusState.STATE_FADE_OUT; + mExitStartMillis = computeExitStartTimeMs(t, mExitDurationMillis); + } + + /** + * Put the animation in the hard stop state regardless of the current + * dynamic transition. If the animation is currently in an enter state + * this will compute an exit start time such that the exit time lines + * up with the enter time at the current transition value. + * + * @param tMillis the current animation time in milliseconds. + */ + public void stop(long tMillis) { + if (mRingRadius.isActive()) { + mRingRadius.stop(); + } + + mFocusState = FocusState.STATE_HARD_STOP; + mHardExitStartMillis = computeExitStartTimeMs(tMillis, mHardExitDurationMillis); + } + + private long computeExitStartTimeMs(long tMillis, float exitDuration) { + if (mEnterStartMillis + mEnterDurationMillis <= tMillis) { + return tMillis; + } + + // Compute the current progress on the enter animation. + float enterT = (tMillis - mEnterStartMillis) / mEnterDurationMillis; + + // Find a time on the exit curve such that it will produce the same value. + float exitT = UnitCurves.mapEnterCurveToExitCurveAtT(mEnterOpacityCurve, mExitOpacityCurve, + enterT); + + // Compute the a start time before tMs such that the ratio of time completed + // equals the computed exit curve animation position. + return tMillis - (long) (exitT * exitDuration); + } + + private long computeEnterStartTimeMillis(long tMillis, float enterDuration) { + if (mExitStartMillis + mExitDurationMillis <= tMillis) { + return tMillis; + } + + // Compute the current progress on the enter animation. + float exitT = (tMillis - mExitStartMillis) / mExitDurationMillis; + + // Find a time on the exit curve such that it will produce the same value. + float enterT = UnitCurves.mapEnterCurveToExitCurveAtT(mExitOpacityCurve, mEnterOpacityCurve, + exitT); + + // Compute the a start time before tMs such that the ratio of time completed + // equals the computed exit curve animation position. + return tMillis - (long) (enterT * enterDuration); + } +} diff --git a/src/com/android/camera/ui/focus/FocusRingView.java b/src/com/android/camera/ui/focus/FocusRingView.java new file mode 100644 index 000000000..14a7f6cc9 --- /dev/null +++ b/src/com/android/camera/ui/focus/FocusRingView.java @@ -0,0 +1,211 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Point; +import android.graphics.RectF; +import android.graphics.Region; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; + +import com.android.camera.ui.motion.AnimationClock.SystemTimeClock; +import com.android.camera.ui.motion.DynamicAnimator; +import com.android.camera.ui.motion.Invalidator; +import com.android.camera.ui.motion.LinearScale; + +import org.codeaurora.snapcam.R; + +/** + * Custom view for running the focus ring animations. + */ +public class FocusRingView extends View implements Invalidator, FocusRing { + private static final String TAG = "FocusRingView"; + private static final float FADE_IN_DURATION_MILLIS = 1000f; + private static final float FADE_OUT_DURATION_MILLIS = 250f; + + private final AutoFocusRing mAutoFocusRing; + private final ManualFocusRing mManualFocusRing; + private final DynamicAnimator mAnimator; + private final LinearScale mRatioScale; + private final float mDefaultRadiusPx; + + private FocusRingRenderer currentFocusAnimation; + private boolean isFirstDraw; + private float mLastRadiusPx; + + private RectF mPreviewSize; + + public FocusRingView(Context context, AttributeSet attrs) { + super(context, attrs); + + Resources res = getResources(); + Paint paint = makePaint(res, R.color.focus_color); + + float focusCircleMinSize = res.getDimensionPixelSize(R.dimen.focus_circle_min_size); + float focusCircleMaxSize = res.getDimensionPixelSize(R.dimen.focus_circle_max_size); + mDefaultRadiusPx = res.getDimensionPixelSize(R.dimen.focus_circle_initial_size); + + mRatioScale = new LinearScale(0, 1, focusCircleMinSize, focusCircleMaxSize); + mAnimator = new DynamicAnimator(this, new SystemTimeClock()); + + mAutoFocusRing = new AutoFocusRing(mAnimator, paint, + FADE_IN_DURATION_MILLIS, + FADE_OUT_DURATION_MILLIS); + mManualFocusRing = new ManualFocusRing(mAnimator, paint, + FADE_OUT_DURATION_MILLIS); + + mAnimator.animations.add(mAutoFocusRing); + mAnimator.animations.add(mManualFocusRing); + + isFirstDraw = true; + mLastRadiusPx = mDefaultRadiusPx; + } + + @Override + public boolean isPassiveFocusRunning() { + return mAutoFocusRing.isActive(); + } + + @Override + public boolean isActiveFocusRunning() { + return mManualFocusRing.isActive(); + } + + @Override + public void startPassiveFocus() { + mAnimator.invalidate(); + long tMs = mAnimator.getTimeMillis(); + + if (mManualFocusRing.isActive() && !mManualFocusRing.isExiting()) { + mManualFocusRing.stop(tMs); + } + + mAutoFocusRing.start(tMs, mLastRadiusPx, mLastRadiusPx); + currentFocusAnimation = mAutoFocusRing; + } + + @Override + public void startActiveFocus() { + mAnimator.invalidate(); + long tMs = mAnimator.getTimeMillis(); + + if (mAutoFocusRing.isActive() && !mAutoFocusRing.isExiting()) { + mAutoFocusRing.stop(tMs); + } + + mManualFocusRing.start(tMs, 0.0f, mLastRadiusPx); + currentFocusAnimation = mManualFocusRing; + } + + @Override + public void stopFocusAnimations() { + long tMs = mAnimator.getTimeMillis(); + if (mManualFocusRing.isActive() && !mManualFocusRing.isExiting() + && !mManualFocusRing.isEntering()) { + mManualFocusRing.exit(tMs); + } + + if (mAutoFocusRing.isActive() && !mAutoFocusRing.isExiting()) { + mAutoFocusRing.exit(tMs); + } + } + + @Override + public void setFocusLocation(float viewX, float viewY) { + mAutoFocusRing.setCenterX((int) viewX); + mAutoFocusRing.setCenterY((int) viewY); + mManualFocusRing.setCenterX((int) viewX); + mManualFocusRing.setCenterY((int) viewY); + } + + @Override + public void centerFocusLocation() { + Point center = computeCenter(); + mAutoFocusRing.setCenterX(center.x); + mAutoFocusRing.setCenterY(center.y); + mManualFocusRing.setCenterX(center.x); + mManualFocusRing.setCenterY(center.y); + } + + @Override + public void setRadiusRatio(float ratio) { + setRadius(mRatioScale.scale(mRatioScale.clamp(ratio))); + } + + @Override + public void configurePreviewDimensions(RectF previewArea) { + mPreviewSize = previewArea; + mLastRadiusPx = mDefaultRadiusPx; + + if (!isFirstDraw) { + centerAutofocusRing(); + } + } + + @Override + protected void onDraw(Canvas canvas) { + if (isFirstDraw) { + isFirstDraw = false; + centerAutofocusRing(); + } + + if (mPreviewSize != null) { + canvas.clipRect(mPreviewSize, Region.Op.REPLACE); + } + + mAnimator.draw(canvas); + } + + private void setRadius(float radiusPx) { + long tMs = mAnimator.getTimeMillis(); + // Some devices return zero for invalid or "unknown" diopter values. + if (currentFocusAnimation != null && radiusPx > 0.1f) { + currentFocusAnimation.setRadius(tMs, radiusPx); + mLastRadiusPx = radiusPx; + } + } + + private void centerAutofocusRing() { + Point center = computeCenter(); + mAutoFocusRing.setCenterX(center.x); + mAutoFocusRing.setCenterY(center.y); + } + + private Point computeCenter() { + if (mPreviewSize != null && (mPreviewSize.width() * mPreviewSize.height() > 0.01f)) { + Log.i(TAG, "Computing center via preview size."); + return new Point((int) mPreviewSize.centerX(), (int) mPreviewSize.centerY()); + } + Log.i(TAG, "Computing center via view bounds."); + return new Point(getWidth() / 2, getHeight() / 2); + } + + private Paint makePaint(Resources res, int color) { + Paint paint = new Paint(); + paint.setAntiAlias(true); + paint.setColor(res.getColor(color)); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeWidth(res.getDimension(R.dimen.focus_circle_stroke)); + return paint; + } +} diff --git a/src/com/android/camera/ui/focus/FocusSound.java b/src/com/android/camera/ui/focus/FocusSound.java new file mode 100644 index 000000000..c3ff0107d --- /dev/null +++ b/src/com/android/camera/ui/focus/FocusSound.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import com.android.camera.SoundPlayer; + +/** + * Wraps the focus sound and the player into a single object that can + * be played on demand. + * + * TODO: This needs some way to better manage the sound lifetimes + */ +public class FocusSound { + private static final float DEFAULT_VOLUME = 0.6f; + private final SoundPlayer mPlayer; + private final int mSoundId; + public FocusSound(SoundPlayer player, int soundId) { + mPlayer = player; + mSoundId = soundId; + + mPlayer.loadSound(mSoundId); + } + + /** + * Play the focus sound with the sound player at the default + * volume. + */ + public void play() { + if(!mPlayer.isReleased()) { + mPlayer.play(mSoundId, DEFAULT_VOLUME); + } + } +} diff --git a/src/com/android/camera/ui/focus/LensRangeCalculator.java b/src/com/android/camera/ui/focus/LensRangeCalculator.java new file mode 100644 index 000000000..ef9cbaec7 --- /dev/null +++ b/src/com/android/camera/ui/focus/LensRangeCalculator.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.annotation.TargetApi; +import android.hardware.camera2.CameraCharacteristics; +import android.os.Build.VERSION_CODES; + +import com.android.camera.ui.motion.LinearScale; + +/** + * Compute diopter range scale to convert lens focus distances into + * a ratio value. + */ +@TargetApi(VERSION_CODES.LOLLIPOP) +public class LensRangeCalculator { + + /** + * A NoOp linear scale for computing diopter values will always return 0 + */ + public static LinearScale getNoOp() { + return new LinearScale(0, 0, 0, 0); + } + + /** + * Compute the focus range from the camera characteristics and build + * a linear scale model that maps a focus distance to a ratio between + * the min and max range. + */ + public static LinearScale getDiopterToRatioCalculator(CameraCharacteristics characteristics) { + // From the android documentation: + // + // 0.0f represents farthest focus, and LENS_INFO_MINIMUM_FOCUS_DISTANCE + // represents the nearest focus the device can achieve. + // + // Example: + // + // Infinity Hyperfocal Minimum Camera + // <----------|-----------------------------| | + // [0.0] [0.31] [14.29] + Float nearest = characteristics.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + Float hyperfocal = characteristics.get(CameraCharacteristics.LENS_INFO_HYPERFOCAL_DISTANCE); + + if (nearest == null && hyperfocal == null) { + return getNoOp(); + } + + nearest = (nearest == null) ? 0.0f : nearest; + hyperfocal = (hyperfocal == null) ? 0.0f : hyperfocal; + + if (nearest > hyperfocal) { + return new LinearScale(hyperfocal, nearest, 0, 1); + } + + return new LinearScale(nearest, hyperfocal, 0, 1); + } +} diff --git a/src/com/android/camera/ui/focus/ManualFocusRing.java b/src/com/android/camera/ui/focus/ManualFocusRing.java new file mode 100644 index 000000000..0133d8e09 --- /dev/null +++ b/src/com/android/camera/ui/focus/ManualFocusRing.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.focus; + +import android.graphics.Canvas; +import android.graphics.Paint; + +import com.android.camera.ui.motion.InterpolateUtils; +import com.android.camera.ui.motion.Invalidator; + +/** + * Manual focus ring animation renderer. + */ +class ManualFocusRing extends FocusRingRenderer { + /** + * The manual focus ring encapsulates the animation logic for visualizing + * a focus event when triggered by a physical screen touch. + * + * @param invalidator the object to invalidate while running. + * @param ringPaint the paint to draw the ring with. + * @param exitDurationMillis the fade out time in milliseconds. + */ + public ManualFocusRing(Invalidator invalidator, Paint ringPaint, + float exitDurationMillis) { + super(invalidator, ringPaint, 0.0f, exitDurationMillis); + } + + @Override + public void draw(long t, long dt, Canvas canvas) { + float ringRadius = mRingRadius.update(dt); + processStates(t); + + if (!isActive()) { + return; + } + + mInvalidator.invalidate(); + int ringAlpha = 255; + + if (mFocusState == FocusState.STATE_FADE_OUT) { + float rFade = InterpolateUtils.unitRatio(t, mExitStartMillis, mExitDurationMillis); + ringAlpha = (int) InterpolateUtils.lerp(255, 0, mExitOpacityCurve.valueAt(rFade)); + } else if (mFocusState == FocusState.STATE_HARD_STOP) { + float rFade = InterpolateUtils.unitRatio(t, mHardExitStartMillis, + mHardExitDurationMillis); + ringAlpha = (int) InterpolateUtils.lerp(255, 0, mExitOpacityCurve.valueAt(rFade)); + } else if (mFocusState == FocusState.STATE_INACTIVE) { + ringAlpha = 0; + } + + mRingPaint.setAlpha(ringAlpha); + canvas.drawCircle(getCenterX(), getCenterY(), ringRadius, mRingPaint); + } + + private void processStates(long t) { + if (mFocusState == FocusState.STATE_INACTIVE) { + return; + } + + if (mFocusState == FocusState.STATE_ENTER + && (t > mEnterStartMillis + mEnterDurationMillis)) { + mFocusState = FocusState.STATE_ACTIVE; + } + + if (mFocusState == FocusState.STATE_ACTIVE && !mRingRadius.isActive()) { + mFocusState = FocusState.STATE_FADE_OUT; + mExitStartMillis = t; + } + + if (mFocusState == FocusState.STATE_FADE_OUT && t > mExitStartMillis + mExitDurationMillis) { + mFocusState = FocusState.STATE_INACTIVE; + } + + if (mFocusState == FocusState.STATE_HARD_STOP + && t > mHardExitStartMillis + mHardExitDurationMillis) { + mFocusState = FocusState.STATE_INACTIVE; + } + } +} diff --git a/src/com/android/camera/ui/motion/AnimationClock.java b/src/com/android/camera/ui/motion/AnimationClock.java new file mode 100644 index 000000000..d2504de6b --- /dev/null +++ b/src/com/android/camera/ui/motion/AnimationClock.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.os.SystemClock; + +/** + * Wraps the SystemClock static time methods so they can be exercised in tests. + */ +public abstract class AnimationClock { + + public abstract long getTimeMillis(); + + /** + * Forwards calls to SystemClock.uptimeMillis() since it is the most consistent clock for + * animations. + */ + public static class SystemTimeClock extends AnimationClock { + + @Override + public long getTimeMillis() { + return SystemClock.uptimeMillis(); + } + } +} diff --git a/src/com/android/camera/ui/motion/DampedSpring.java b/src/com/android/camera/ui/motion/DampedSpring.java new file mode 100644 index 000000000..84cbfa6f8 --- /dev/null +++ b/src/com/android/camera/ui/motion/DampedSpring.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * This models a value after the behavior of a spring. The value tracks the current value, a target + * value, and the current velocity and applies both a directional force and a damping force to the + * value on each update call. + */ +public class DampedSpring { + public static final float DEFAULT_TIME_TO_90_PERCENT_MILLIS = 200.0f; + public static final float DEFAULT_SPRING_STIFFNESS = 3.75f; + public static final float EPSILON = 0.01f; + + private final float mSpringStiffness; + private final float mTimeTo90PercentMs; + + private float mTarget = 0f; + private float mVelocity = 0f; + private float mValue = 0f; + + public DampedSpring() { + this(DEFAULT_TIME_TO_90_PERCENT_MILLIS, DEFAULT_SPRING_STIFFNESS); + } + + public DampedSpring(float timeTo90PercentMs) { + this(timeTo90PercentMs, DEFAULT_SPRING_STIFFNESS); + } + + public DampedSpring(float timeTo90PercentMs, float springStiffness) { + // TODO: Assert timeTo90PercentMs >= 1ms, it might behave badly at low values. + // TODO: Assert springStiffness > 2.0f + + mTimeTo90PercentMs = timeTo90PercentMs; + mSpringStiffness = springStiffness; + + if (springStiffness > timeTo90PercentMs) { + throw new IllegalArgumentException("Creating a spring value with " + + "excessive stiffness will oscillate endlessly."); + } + } + + /** + * @return the current value. + */ + public float getValue() { + return mValue; + } + + /** + * @param value the value to set this instance's current state too. + */ + public void setValue(float value) { + mValue = value; + } + + /** + * @return the current target value. + */ + public float getTarget() { + return mTarget; + } + + /** + * Set a target value. The current value will maintain any existing velocity values and will + * move towards the new target value. To forcibly stopAt the value use the stopAt() method. + * + * @param value the new value to move the current value towards. + */ + public void setTarget(float value) { + mTarget = value; + } + + /** + * Update the current value, moving it towards the actual value over the given + * time delta (in milliseconds) since the last update. This works off of the + * principle of a critically damped spring such that any given current value + * will move elastically towards the target value. The current value maintains + * and applies velocity, acceleration, and a damping force to give a continuous, + * smooth transition towards the target value. + * + * @param dtMs the time since the last update, or zero. + * @return the current value after the update occurs. + */ + public float update(float dtMs) { + float dt = dtMs / mTimeTo90PercentMs; + float dts = dt * mSpringStiffness; + + // If the dts > 1, and the velocity is zero, the force will exceed the + // distance to the target value and it will overshoot the value, causing + // weird behavior and unintended oscillation. since a critically damped + // spring should never overshoot the value, simply the current value to the + // target value. + if (dts > 1.0f || dts < 0.0f) { + stop(); + return mValue; + } + + float delta = (mTarget - mValue); + float force = delta - 2.0f * mVelocity; + + mVelocity += force * dts; + mValue += mVelocity * dts; + + // If we get close enough to the actual value, simply set the current value + // to the current target value and stop. + if (!isActive()) { + stop(); + } + + return mValue; + } + + /** + * @return true if this instance has velocity or it is not at the target value. + */ + public boolean isActive() { + boolean hasVelocity = Math.abs(mVelocity) >= EPSILON; + boolean atTarget = Math.abs(mTarget - mValue) < EPSILON; + return hasVelocity || !atTarget; + } + + /** + * Stop the spring motion wherever it is currently at. Sets target to the + * current value and sets the velocity to zero. + */ + public void stop() { + mTarget = mValue; + mVelocity = 0.0f; + } +} diff --git a/src/com/android/camera/ui/motion/DynamicAnimation.java b/src/com/android/camera/ui/motion/DynamicAnimation.java new file mode 100644 index 000000000..57d5a1021 --- /dev/null +++ b/src/com/android/camera/ui/motion/DynamicAnimation.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.graphics.Canvas; + +/** + * Rendering object that can be driven by an animator instance. + */ +public interface DynamicAnimation { + + /** + * Check to determine if this animation is currently in a stable state. + * + * @return true if the animation is stable, false if it should continue to be redrawn. + */ + boolean isActive(); + + /** + * Update and draw the animation onto the given canvas. + * + * @param t current animation frame time. + * @param dt delta since the last update. + * @param canvas the canvas to draw the animation onto. + */ + void draw(long t, long dt, Canvas canvas); +} diff --git a/src/com/android/camera/ui/motion/DynamicAnimator.java b/src/com/android/camera/ui/motion/DynamicAnimator.java new file mode 100644 index 000000000..542ac1e37 --- /dev/null +++ b/src/com/android/camera/ui/motion/DynamicAnimator.java @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.graphics.Canvas; + +import java.util.ArrayList; +import java.util.List; + +/** + * Designed to handle the lifecycle of a view that needs a continuous update / + * redraw cycle that does not have a defined start / end time. + * + * Fixed length animations should NOT use this class. + */ +public class DynamicAnimator implements Invalidator { + + public final List<DynamicAnimation> animations = new ArrayList<>(); + + private final Invalidator mInvalidator; + private final AnimationClock mClock; + + private boolean mUpdateRequested = false; + private boolean mIsDrawing = false; + private long mLastDrawTimeMillis = 0; + private long mDrawTimeMillis = 0; + + public DynamicAnimator(Invalidator invalidator, AnimationClock clock) { + mInvalidator = invalidator; + mClock = clock; + } + + public void draw(Canvas canvas) { + mIsDrawing = true; + mUpdateRequested = false; + + mDrawTimeMillis = mClock.getTimeMillis(); + + if (mLastDrawTimeMillis <= 0) { + mLastDrawTimeMillis = mDrawTimeMillis; // On the initial draw, dt is zero. + } + + long dt = mDrawTimeMillis - mLastDrawTimeMillis; + mLastDrawTimeMillis = mDrawTimeMillis; + + // Run the animation + for (DynamicAnimation renderer : animations) { + if (renderer.isActive()) { + renderer.draw(mDrawTimeMillis, dt, canvas); + } + } + + // If either the update or the draw methods requested new frames, then + // invalidate the view which should give us another frame to work with. + // Otherwise, stopAt the last update time. + if (mUpdateRequested) { + mInvalidator.invalidate(); + } else { + mLastDrawTimeMillis = -1; + } + + mIsDrawing = false; + } + + /** + * If a scheduleNewFrame request comes in outside of the animation loop, + * and we didn't schedule a frame after the previous loop (or it's the + * first time we've used this instance), invalidate the view and set the + * last update time to the current time. Theoretically, a few milliseconds + * have elapsed before the view gets updated. + */ + @Override + public void invalidate() { + if (!mIsDrawing && !mUpdateRequested) { + mInvalidator.invalidate(); + mLastDrawTimeMillis = mClock.getTimeMillis(); + } + + mUpdateRequested = true; + } + + /** + * This will return the "best guess" for the most current animation frame + * time. If the loop is currently drawing, then it will return the time the + * draw began, and if an update is currently requested it will return the + * time that the update was requested at, and if neither of these are true + * it will return the current system clock time. + * + * This method will not trigger a new update. + */ + public long getTimeMillis() { + if (mIsDrawing) { + return mDrawTimeMillis; + } + + if (mUpdateRequested) { + return mLastDrawTimeMillis; + } + + return mClock.getTimeMillis(); + } +} diff --git a/src/com/android/camera/ui/motion/InterpolateUtils.java b/src/com/android/camera/ui/motion/InterpolateUtils.java new file mode 100644 index 000000000..3c3cd532f --- /dev/null +++ b/src/com/android/camera/ui/motion/InterpolateUtils.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Various static helper functions for interpolating between values. + */ +public class InterpolateUtils { + + private InterpolateUtils() { + } + + /** + * Linear interpolation from v0 to v1 as t goes from 0...1 + * + * @param v0 the value at t=0 + * @param v1 the value at t=1 + * @param t value in the range of 0 to 1. + * @return the value between v0 and v1 as a ratio between 0 and 1 defined by t. + */ + public static float lerp(float v0, float v1, float t) { + return v0 + t * (v1 - v0); + } + + /** + * Project a value that is within the in(Min/Max) number space into the to(Min/Max) number + * space. + * + * @param v value to scale into the 'to' number space. + * @param vMin min value of the values number space. + * @param vMax max value of the values number space. + * @param pMin min value of the projection number space. + * @param pMax max value of the projection number space. + * @return the ratio of the value in the source number space as a value in the to(Min/Max) + * number space. + */ + public static float scale(float v, float vMin, float vMax, float pMin, float pMax) { + return (pMax - pMin) * (v - vMin) / (vMax - vMin) + pMin; + } + + /** + * Value between 0 and 1 as a ratio between tBegin over tDuration + * with no upper bound. + */ + public static float unitRatio(long t, long tBegin, float tDuration) { + if (t <= tBegin) { + return 0.0f; + } + + return (t - tBegin) / tDuration; + } +} diff --git a/src/com/android/camera/ui/motion/InterpolatorHelper.java b/src/com/android/camera/ui/motion/InterpolatorHelper.java new file mode 100644 index 000000000..84114cb03 --- /dev/null +++ b/src/com/android/camera/ui/motion/InterpolatorHelper.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +import android.content.Context; +import android.view.animation.AnimationUtils; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.android.camera.util.ApiHelper; + +public class InterpolatorHelper { + private static Interpolator LINEAR_OUT_SLOW_IN = null; + + public static Interpolator getLinearOutSlowInInterpolator(final Context context) { + if (LINEAR_OUT_SLOW_IN != null) { + return LINEAR_OUT_SLOW_IN; + } + + LINEAR_OUT_SLOW_IN = AnimationUtils.loadInterpolator( + context, android.R.interpolator.linear_out_slow_in); + return LINEAR_OUT_SLOW_IN; + } +} diff --git a/src/com/android/camera/ui/motion/Invalidator.java b/src/com/android/camera/ui/motion/Invalidator.java new file mode 100644 index 000000000..fdb548748 --- /dev/null +++ b/src/com/android/camera/ui/motion/Invalidator.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Basic interface for objects that can be invalidated. + */ +public interface Invalidator { + /** + * Request that the object should be redrawn whenever it gets + * the chance. + */ + void invalidate(); +} diff --git a/src/com/android/camera/ui/motion/LinearScale.java b/src/com/android/camera/ui/motion/LinearScale.java new file mode 100644 index 000000000..5886f6882 --- /dev/null +++ b/src/com/android/camera/ui/motion/LinearScale.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Represents a discrete linear scale function. + */ +public final class LinearScale { + private final float mDomainA; + private final float mDomainB; + private final float mRangeA; + private final float mRangeB; + + private final float mScale; + + public LinearScale(float domainA, float domainB, float rangeA, float rangeB) { + mDomainA = domainA; + mDomainB = domainB; + mRangeA = rangeA; + mRangeB = rangeB; + + // Precomputed ratio between input domain and output range. + float scale = (mRangeB - mRangeA) / (mDomainB - mDomainA); + mScale = Float.isNaN(scale) ? 0.0f : scale; + } + + /** + * Clamp a given domain value to the given domain. + */ + public float clamp(float domainValue) { + if (mDomainA > mDomainB) { + return Math.max(mDomainB, Math.min(mDomainA, domainValue)); + } + + return Math.max(mDomainA, Math.min(mDomainB, domainValue)); + } + + /** + * Returns true if the value is within the domain. + */ + public boolean isInDomain(float domainValue) { + if (mDomainA > mDomainB) { + return domainValue <= mDomainA && domainValue >= mDomainB; + } + return domainValue >= mDomainA && domainValue <= mDomainB; + } + + /** + * Linearly scale a given domain value into the output range. + */ + public float scale(float domainValue) { + return mRangeA + (domainValue - mDomainA) * mScale; + } + + /** + * For the current domain and range parameters produce a new scale function + * that is the inverse of the current scale function. + */ + public LinearScale inverse() { + return new LinearScale(mRangeA, mRangeB, mDomainA, mDomainB); + } + + @Override + public String toString() { + return "LinearScale{" + + "mDomainA=" + mDomainA + + ", mDomainB=" + mDomainB + + ", mRangeA=" + mRangeA + + ", mRangeB=" + mRangeB + "}"; + } +}
\ No newline at end of file diff --git a/src/com/android/camera/ui/motion/UnitBezier.java b/src/com/android/camera/ui/motion/UnitBezier.java new file mode 100644 index 000000000..242f54556 --- /dev/null +++ b/src/com/android/camera/ui/motion/UnitBezier.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * This represents is a precomputed cubic bezier curve starting at (0,0) and + * going to (1,1) with two configurable control points. Once the instance is + * created, the control points cannot be modified. + * + * Generally, this will be used for computing timing curves for with control + * points where an x value will be provide from 0.0 - 1.0, and the y value will + * be solved for where y is used as the timing value in some linear + * interpolation of a value. + */ +public class UnitBezier implements UnitCurve { + + private static final float EPSILON = 1e-6f; + + private final DerivableFloatFn mXFn; + private final DerivableFloatFn mYFn; + + /** + * Build and pre-compute a unit bezier. This assumes a starting point of + * (0, 0) and end point of (1.0, 1.0). + * + * @param c0x control point x value for p0 + * @param c0y control point y value for p0 + * @param c1x control point x value for p1 + * @param c1y control point y value for p1 + */ + public UnitBezier(float c0x, float c0y, float c1x, float c1y) { + mXFn = new CubicBezierFn(c0x, c1x); + mYFn = new CubicBezierFn(c0y, c1y); + } + + /** + * Given a unit bezier curve find the height of the curve at t (which is + * internally represented as the xAxis). + * + * @param t the x position between 0 and 1 to solve for y. + * @return the closest approximate height of the curve at x. + */ + @Override + public float valueAt(float t) { + return mYFn.value(solve(t, mXFn)); + } + + /** + * Given a unit bezier curve find a value along the x axis such that + * valueAt(result) produces the input value. + * + * @param value the y position between 0 and 1 to solve for x + * @return the closest approximate input that will produce value when provided + * to the valueAt function. + */ + @Override + public float tAt(float value) { + return mXFn.value(solve(value, mYFn)); + } + + private float solve(float target, DerivableFloatFn fn) { + // For a linear fn, t = value. This makes value a good starting guess. + float input = target; + + // Newton's method (Faster than bisection) + for (int i = 0; i < 8; i++) { + float value = fn.value(input) - target; + if (Math.abs(value) < EPSILON) { + return input; + } + float derivative = fn.derivative(input); + if (Math.abs(derivative) < EPSILON) { + break; + } + input = input - value / derivative; + } + + // Fallback on bi-section + float min = 0.0f; + float max = 1.0f; + input = target; + + if (input < min) { + return min; + } + if (input > max) { + return max; + } + + while (min < max) { + float value = fn.value(input); + if (Math.abs(value - target) < EPSILON) { + return input; + } + + if (target > value) { + min = input; + } else { + max = input; + } + + input = (max - min) * .5f + min; + } + + // Give up, return the closest match we got too. + return input; + } + + private interface DerivableFloatFn { + float value(float x); + float derivative(float x); + } + + /** + * Precomputed constants for a given set of control points along a given + * cubic bezier axis. + */ + private static class CubicBezierFn implements DerivableFloatFn { + private final float c; + private final float a; + private final float b; + + /** + * Build and pre-compute a single axis for a unit bezier. This assumes p0 + * is 0 and p1 is 1. + * + * @param c0 start control point. + * @param c1 end control point. + */ + public CubicBezierFn(float c0, float c1) { + c = 3.0f * c0; + b = 3.0f * (c1 - c0) - c; + a = 1.0f - c - b; + } + + public float value(float x) { + return ((a * x + b) * x + c) * x; + } + public float derivative(float x) { + return (3.0f * a * x + 2.0f * b) * x + c; + } + } +} diff --git a/src/com/android/camera/ui/motion/UnitCurve.java b/src/com/android/camera/ui/motion/UnitCurve.java new file mode 100644 index 000000000..d89f1fa4d --- /dev/null +++ b/src/com/android/camera/ui/motion/UnitCurve.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Simple functions that produce values along a curve for any given input and can compute input + * times for a given output value. + */ +public interface UnitCurve { + + /** + * Produce a unit value of this curve at time t. The function should always return a valid + * return value for any valid t input. + * + * @param t ratio of time passed from (0..1) + * @return the unit value at t. + */ + float valueAt(float t); + + /** + * If possible, find a value for t such that valueAt(t) == value or best guess. + * + * @param value to match to the output of valueAt(t) + * @return t where valueAt(t) == value or throw. + */ + float tAt(float value); +} diff --git a/src/com/android/camera/ui/motion/UnitCurves.java b/src/com/android/camera/ui/motion/UnitCurves.java new file mode 100644 index 000000000..a1117fa96 --- /dev/null +++ b/src/com/android/camera/ui/motion/UnitCurves.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.ui.motion; + +/** + * Predefined material curves and animations. + */ +public class UnitCurves { + public static final UnitCurve FAST_OUT_SLOW_IN = new UnitBezier(0.4f, 0.0f, 0.2f, 1.0f); + public static final UnitCurve LINEAR_OUT_SLOW_IN = new UnitBezier(0.0f, 0.0f, 0.2f, 1.0f); + public static final UnitCurve FAST_OUT_LINEAR_IN = new UnitBezier(0.4f, 0.0f, 1.0f, 1.0f); + public static final UnitCurve LINEAR = new UnitBezier(0.0f, 0.0f, 1.0f, 1.0f); + + /** + * Given two curves (from and to) and a time along the from curve, compute + * the time at t, and find a t along the 'toCurve' that will produce the + * same output. This is useful when interpolating between two different curves + * when the animation is not at the beginning or end. + * + * @param enterCurve the curve to compute the value from + * @param exitCurve the curve to find a time t on that matches output of + * enterCurve at T. + * @param t the time at which to compute the value (0..1) + * @return the time along the exitCurve. + */ + public static float mapEnterCurveToExitCurveAtT(UnitCurve enterCurve, UnitCurve exitCurve, + float t) { + return exitCurve.tAt(1 - enterCurve.valueAt(t)); + } +} diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index 34787e4d8..db86e753f 100755 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java @@ -43,6 +43,7 @@ import android.net.Uri; import android.os.Handler; import android.os.ParcelFileDescriptor; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; @@ -70,6 +71,7 @@ import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Method; import java.text.SimpleDateFormat; +import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.List; @@ -91,6 +93,7 @@ import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.Set; +import java.util.TreeSet; import static android.content.Context.MODE_PRIVATE; @@ -109,7 +112,7 @@ public class CameraUtil { public static final String KEY_SHOW_WHEN_LOCKED = "showWhenLocked"; // Orientation hysteresis amount used in rounding, in degrees - public static final int ORIENTATION_HYSTERESIS = 5; + public static final int ORIENTATION_HYSTERESIS = 10; public static final String REVIEW_ACTION = "com.android.camera.action.REVIEW"; // See android.hardware.Camera.ACTION_NEW_PICTURE. @@ -133,6 +136,7 @@ public class CameraUtil { public static final String SECURE_CAMERA_EXTRA = "secure_camera"; // Fields from android.hardware.Camera.Parameters public static final String FOCUS_MODE_CONTINUOUS_PICTURE = "continuous-picture"; + public static final String FOCUS_MODE_MW_CONTINUOUS_PICTURE = "mw_continuous-picture"; public static final String RECORDING_HINT = "recording-hint"; private static final String AUTO_EXPOSURE_LOCK_SUPPORTED = "auto-exposure-lock-supported"; private static final String AUTO_WHITE_BALANCE_LOCK_SUPPORTED = "auto-whitebalance-lock-supported"; @@ -142,6 +146,9 @@ public class CameraUtil { public static final String TRUE = "true"; public static final String FALSE = "false"; + // Hardware camera key mask + private static final int KEY_MASK_CAMERA = 0x20; + private static final Class<?>[] CTOR_SIGNATURE = new Class[] {CaptureRequest.class, CameraMetadataNative.class, boolean.class, int.class}; @@ -181,6 +188,7 @@ public class CameraUtil { } public static boolean isVideoSnapshotSupported(Parameters params) { + if (params == null) return false; return TRUE.equals(params.get(VIDEO_SNAPSHOT_SUPPORTED)); } @@ -189,6 +197,10 @@ public class CameraUtil { return (supported != null) && supported.contains(SCENE_MODE_HDR); } + public static boolean hasCameraKey() { + return (sDeviceKeysPresent & KEY_MASK_CAMERA) != 0; + } + public static boolean isMeteringAreaSupported(Parameters params) { return params.getMaxNumMeteringAreas() > 0; } @@ -199,6 +211,22 @@ public class CameraUtil { params.getSupportedFocusModes())); } + public static boolean isSupported(Parameters params, String key) { + return (params.get(key) != null && !"null".equals(params.get(key))); + } + + public static int getNumSnapsPerShutter(Parameters params) { + String numJpegs = params.get("num-jpegs-per-shutter"); + if (!TextUtils.isEmpty(numJpegs)) { + return Integer.valueOf(numJpegs); + } + String numSnaps = params.get("num-snaps-per-shutter"); + if (!TextUtils.isEmpty(numSnaps)) { + return Integer.valueOf(numSnaps); + } + return 1; + } + // Private intent extras. Test only. private static final String EXTRAS_CAMERA_FACING = "android.intent.extras.CAMERA_FACING"; @@ -206,6 +234,9 @@ public class CameraUtil { private static float sPixelDensity = 1; private static ImageFileNamer sImageFileNamer; + // Get available hardware keys + private static int sDeviceKeysPresent; + private CameraUtil() { } @@ -217,6 +248,8 @@ public class CameraUtil { sPixelDensity = metrics.density; sImageFileNamer = new ImageFileNamer( context.getString(R.string.image_file_name_format)); + sDeviceKeysPresent = context.getResources().getInteger( + org.lineageos.platform.internal.R.integer.config_deviceHardwareKeys); } public static int dpToPixel(int dp) { @@ -377,6 +410,37 @@ public class CameraUtil { } } + public static boolean isCamera2Supported(Context context) { + android.hardware.camera2.CameraManager manager = (android.hardware.camera2.CameraManager) + context.getSystemService(Context.CAMERA_SERVICE); + + try { + String[] cameraIds = manager.getCameraIdList(); + + if (cameraIds != null && cameraIds.length > 0) { + CameraCharacteristics characteristics = + manager.getCameraCharacteristics(cameraIds[0]); + int deviceLevel = + characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); + + switch (deviceLevel) { + case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED: + return true; + case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL: + return true; + case CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3: + return true; + default: + return false; + } + } + } catch(CameraAccessException | NumberFormatException e) { + Log.e(TAG, "exception trying to get camera characteristics"); + } + + return false; + } + public static CameraManager.CameraProxy openCamera( Activity activity, final int cameraId, Handler handler, final CameraManager.CameraOpenErrorCallback cb) { @@ -464,6 +528,11 @@ public class CameraUtil { return 0; } + public static boolean isScreenRotated(Activity activity) { + int rotation = activity.getWindowManager().getDefaultDisplay().getRotation(); + return rotation != Surface.ROTATION_0 && rotation != Surface.ROTATION_180; + } + /** * Calculate the default orientation of the device based on the width and * height of the display when rotation = 0 (i.e. natural width and height) @@ -514,7 +583,7 @@ public class CameraUtil { } else { int dist = Math.abs(orientation - orientationHistory); dist = Math.min( dist, 360 - dist ); - changeOrientation = ( dist >= 45 + ORIENTATION_HYSTERESIS ); + changeOrientation = ( dist >= 60 + ORIENTATION_HYSTERESIS ); } if (changeOrientation) { return ((orientation + 45) / 90 * 90) % 360; @@ -578,7 +647,7 @@ public class CameraUtil { // TODO(andyhuibers): Don't hardcode this but use device's measurements. final int MAX_ASPECT_HEIGHT = 1080; // Use a very small tolerance because we want an exact match. - final double ASPECT_TOLERANCE = 0.01; + final double ASPECT_TOLERANCE = 0.02; if (sizes == null) return -1; int optimalSizeIndex = -1; @@ -762,13 +831,25 @@ public class CameraUtil { return optimalSize; } - public static void dumpParameters(Parameters parameters) { - String flattened = parameters.flatten(); - StringTokenizer tokenizer = new StringTokenizer(flattened, ";"); - Log.d(TAG, "Dump all camera parameters:"); - while (tokenizer.hasMoreElements()) { - Log.d(TAG, tokenizer.nextToken()); + public static void dumpParameters(Parameters params) { + Set<String> sortedParams = new TreeSet<String>(); + sortedParams.addAll(Arrays.asList(params.flatten().split(";"))); + StringBuilder sb = new StringBuilder(); + sb.append("["); + Iterator<String> i = sortedParams.iterator(); + while (i.hasNext()) { + String nextParam = i.next(); + if ((sb.length() + nextParam.length()) > 2044) { + Log.d(TAG, "Parameters: " + sb.toString()); + sb = new StringBuilder(); + } + sb.append(nextParam); + if (i.hasNext()) { + sb.append(", "); + } } + sb.append("]"); + Log.d(TAG, "Parameters: " + sb.toString()); } /** @@ -871,7 +952,7 @@ public class CameraUtil { + "," + rect.right + "," + rect.bottom + ")"); } - public static void rectFToRect(RectF rectF, Rect rect) { + public static void inlineRectToRectF(RectF rectF, Rect rect) { rect.left = Math.round(rectF.left); rect.top = Math.round(rectF.top); rect.right = Math.round(rectF.right); @@ -880,7 +961,7 @@ public class CameraUtil { public static Rect rectFToRect(RectF rectF) { Rect rect = new Rect(); - rectFToRect(rectF, rect); + inlineRectToRectF(rectF, rect); return rect; } @@ -900,21 +981,6 @@ public class CameraUtil { matrix.postTranslate(viewWidth / 2f, viewHeight / 2f); } - public static void prepareMatrix(Matrix matrix, boolean mirror, int displayOrientation, - Rect previewRect) { - // Need mirror for front camera. - matrix.setScale(mirror ? -1 : 1, 1); - // This is the value for android.hardware.Camera.setDisplayOrientation. - matrix.postRotate(displayOrientation); - - // Camera driver coordinates range from (-1000, -1000) to (1000, 1000). - // We need to map camera driver coordinates to preview rect coordinates - Matrix mapping = new Matrix(); - mapping.setRectToRect(new RectF(-1000, -1000, 1000, 1000), rectToRectF(previewRect), - Matrix.ScaleToFit.FILL); - matrix.setConcat(mapping, matrix); - } - public static String createJpegName(long dateTaken, boolean refocus) { synchronized (sImageFileNamer) { return sImageFileNamer.generateName(dateTaken, refocus); @@ -1057,6 +1123,20 @@ public class CameraUtil { } } } + + public static boolean isLowLuminance(Parameters parameters) { + String lC = parameters.get(CameraSettings.KEY_LUMINANCE_CONDITION); + + if (lC != null) { + if (lC.equals(CameraSettings.LUMINANCE_CONDITION_LOW)) { + Log.d(TAG, "Parameter " + CameraSettings.KEY_LUMINANCE_CONDITION + + "=" + CameraSettings.LUMINANCE_CONDITION_LOW); + return true; + } + } + return false; + } + public static String getFilpModeString(int value){ switch(value){ case 0: @@ -1132,6 +1212,15 @@ public class CameraUtil { return new int[0]; } + public static int getMaxPreviewFps(Parameters params) { + List<Integer> frameRates = params.getSupportedPreviewFrameRates(); + if (frameRates != null && frameRates.size() > 0) { + // The list is sorted. Return the last element. + return frameRates.get(frameRates.size() - 1).intValue(); + } + return -1; + } + private static class ImageFileNamer { private final SimpleDateFormat mFormat; @@ -1259,10 +1348,6 @@ public class CameraUtil { return ret; } - public static boolean volumeKeyShutterDisable(Context context) { - return context.getResources().getBoolean(R.bool.volume_key_shutter_disable); - } - public static int determineRatio(int width, int height) { if (height != 0) { return determineRatio(((float) width) / height); diff --git a/src/com/android/camera/util/MultiMap.java b/src/com/android/camera/util/MultiMap.java new file mode 100644 index 000000000..b2e9003fe --- /dev/null +++ b/src/com/android/camera/util/MultiMap.java @@ -0,0 +1,45 @@ +package com.android.camera.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class MultiMap<K,V> { + Map<K,List<V>> map = new HashMap<K,List<V>>(); + + public void put(K key, V value) { + List<V> l = map.get(key); + if(l == null) { + l = new ArrayList<V>(); + map.put(key, l); + } + l.add(value); + } + + public List<V> get(K key) { + List<V> l = map.get(key); + if(l == null) { return Collections.emptyList(); } + else return l; + } + + public List<V> remove(K key) { + List<V> l = map.remove(key); + if(l == null) { return Collections.emptyList(); } + else return l; + } + + public Set<K> keySet() { return map.keySet(); } + + public int size() { + int total = 0; + for(List<V> l : map.values()) { + total += l.size(); + } + return total; + } + + public boolean isEmpty() { return map.isEmpty(); } +}
\ No newline at end of file diff --git a/src/com/android/camera/util/PersistUtil.java b/src/com/android/camera/util/PersistUtil.java index 32b76d1e9..baba03cfa 100644 --- a/src/com/android/camera/util/PersistUtil.java +++ b/src/com/android/camera/util/PersistUtil.java @@ -52,8 +52,6 @@ public class PersistUtil { SystemProperties.get("persist.sys.camera.preview.size", ""); private static final String PERSIST_CAMERA_VIDEO_SIZE = SystemProperties.get("persist.sys.camera.video.size", ""); - private static final boolean PERSIST_CAMERA_CAMERA2 = - SystemProperties.getBoolean("persist.sys.camera.camera2", true); private static final boolean PERSIST_CAMERA_ZSL = SystemProperties.getBoolean("persist.sys.camera.zsl.disabled", false); private static final int PERSIST_CAMERA_CANCEL_TOUCHFOCUS_DELAY = @@ -163,10 +161,6 @@ public class PersistUtil { return result; } - public static boolean getCamera2Mode() { - return PERSIST_CAMERA_CAMERA2; - } - public static boolean getCameraZSLDisabled() { return PERSIST_CAMERA_ZSL; } diff --git a/src/com/android/camera/util/VendorTagUtil.java b/src/com/android/camera/util/VendorTagUtil.java index b2efbfa54..0cc6359ec 100755 --- a/src/com/android/camera/util/VendorTagUtil.java +++ b/src/com/android/camera/util/VendorTagUtil.java @@ -71,7 +71,7 @@ public class VendorTagUtil { private static final int MANUAL_WB_CCT_MODE = 1; private static final int MANUAL_WB_GAINS_MODE = 2; - private static boolean isSupported(CaptureRequest.Builder builder, + public static boolean isSupported(CaptureRequest.Builder builder, CaptureRequest.Key<?> key) { boolean supported = true; try { |