From 07647ea0189247f806d157d6f4598ccf19dd3197 Mon Sep 17 00:00:00 2001 From: Byunghun Jeon Date: Tue, 10 May 2016 14:06:42 -0700 Subject: SnapdragonCamera: Add continuous shot and count down Add continous shot and count down to CaptureModule Change-Id: I986302201f742d7671a67c26953eecc2a0276294 CRs-Fixed: 1018118 --- src/com/android/camera/CaptureModule.java | 266 ++++++++++++++++++++++++--- src/com/android/camera/CaptureUI.java | 69 ++++++- src/com/android/camera/util/PersistUtil.java | 51 +++++ 3 files changed, 352 insertions(+), 34 deletions(-) create mode 100644 src/com/android/camera/util/PersistUtil.java (limited to 'src') diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index c31658ae8..7c6e5e73b 100644 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -19,6 +19,7 @@ package com.android.camera; +import android.app.ActivityManager; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; @@ -42,6 +43,7 @@ import android.media.CameraProfile; import android.media.Image; import android.media.ImageReader; import android.net.Uri; +import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; @@ -58,8 +60,11 @@ import android.widget.Toast; import com.android.camera.PhotoModule.NamedImages; import com.android.camera.PhotoModule.NamedImages.NamedEntity; +import com.android.camera.ui.CountDownView; import com.android.camera.ui.RotateTextToast; import com.android.camera.util.CameraUtil; +import com.android.camera.util.PersistUtil; +import com.android.internal.util.MemInfoReader; import org.codeaurora.snapcam.R; import org.codeaurora.snapcam.filter.ClearSightImageProcessor; @@ -76,7 +81,7 @@ import java.util.concurrent.TimeUnit; public class CaptureModule implements CameraModule, PhotoController, MediaSaveService.Listener, ClearSightImageProcessor.Callback, - SettingsManager.Listener { + SettingsManager.Listener, CountDownView.OnCountDownFinishedListener { public static final int DUAL_MODE = 0; public static final int BAYER_MODE = 1; public static final int MONO_MODE = 2; @@ -122,6 +127,11 @@ public class CaptureModule implements CameraModule, PhotoController, private static final int STATE_WAITING_TOUCH_FOCUS = 5; private static final String TAG = "SnapCam_CaptureModule"; + // Used for check memory status for longshot mode + // Currently, this cancel threshold selection is based on test experiments, + // we can change it based on memory status or other requirements. + private static final int LONGSHOT_CANCEL_THRESHOLD = 40 * 1024 * 1024; + static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); @@ -164,6 +174,11 @@ public class CaptureModule implements CameraModule, PhotoController, private FocusStateListener mFocusStateListener; private LocationManager mLocationManager; private SettingsManager mSettingsManager; + private int mLongShotCaptureCount; + private int mLongShotCaptureCountLimit; + private long SECONDARY_SERVER_MEM; + private boolean mLongshotActive = false; + /** * A {@link CameraCaptureSession } for camera preview. */ @@ -188,12 +203,51 @@ public class CaptureModule implements CameraModule, PhotoController, private ImageReader[] mImageReader = new ImageReader[MAX_NUM_CAM]; private NamedImages mNamedImages; private ContentResolver mContentResolver; + + private class MediaSaveNotifyThread extends Thread { + private Uri uri; + + public MediaSaveNotifyThread(Uri uri) { + this.uri = uri; + } + + public void setUri(Uri uri) { + this.uri = uri; + } + + public void run() { + while (mLongshotActive) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + mActivity.runOnUiThread(new Runnable() { + public void run() { + if (uri != null) + mActivity.notifyNewMedia(uri); + mActivity.updateStorageSpaceAndHint(); + } + }); + mediaSaveNotifyThread = null; + } + } + + private MediaSaveNotifyThread mediaSaveNotifyThread; private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = new MediaSaveService.OnMediaSavedListener() { @Override public void onMediaSaved(Uri uri) { - if (uri != null) { - mActivity.notifyNewMedia(uri); + if (mLongshotActive) { + if (mediaSaveNotifyThread == null) { + mediaSaveNotifyThread = new MediaSaveNotifyThread(uri); + mediaSaveNotifyThread.start(); + } else + mediaSaveNotifyThread.setUri(uri); + } else { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } } } }; @@ -720,29 +774,79 @@ public class CaptureModule implements CameraModule, PhotoController, captureBuilder.addTarget(mImageReader[id].getSurface()); mCaptureSession[id].stopRepeating(); - mCaptureSession[id].capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() { + if (mLongshotActive) { + mCaptureSession[id].setRepeatingRequest(captureBuilder.build(), new + CameraCaptureSession.CaptureCallback() { - @Override - public void onCaptureCompleted(CameraCaptureSession session, - CaptureRequest request, - TotalCaptureResult result) { - Log.d(TAG, "captureStillPicture onCaptureCompleted: " + id); - } + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, "captureStillPicture Longshot onCaptureCompleted: " + id); + mActivity.updateStorageSpaceAndHint(); + if (checkLongShotMemoAndLimit()) { + cancelAutoFocus(); + return; + } + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.doShutterAnimation(); + } + }); + mLongShotCaptureCount++; + } - @Override - public void onCaptureFailed(CameraCaptureSession session, - CaptureRequest request, - CaptureFailure result) { - Log.d(TAG, "captureStillPicture onCaptureFailed: " + id); - } + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, "captureStillPicture Longshot onCaptureFailed: " + id); + if (checkLongShotMemoAndLimit()) { + cancelAutoFocus(); + return; + } + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mUI.doShutterAnimation(); + } + }); + } - @Override - public void onCaptureSequenceCompleted(CameraCaptureSession session, int - sequenceId, long frameNumber) { - Log.d(TAG, "captureStillPicture onCaptureSequenceCompleted: " + id); - unlockFocus(id); - } - }, mCaptureCallbackHandler); + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, "captureStillPicture Longshot onCaptureSequenceCompleted: " + + id); + unlockFocus(id); + } + }, mCaptureCallbackHandler); + } else { + mCaptureSession[id].capture(captureBuilder.build(), new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, "captureStillPicture onCaptureCompleted: " + id); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, "captureStillPicture onCaptureFailed: " + id); + } + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, "captureStillPicture onCaptureSequenceCompleted: " + id); + unlockFocus(id); + } + }, mCaptureCallbackHandler); + } } } catch (CameraAccessException e) { Log.d(TAG, "Capture still picture has failed"); @@ -1095,6 +1199,7 @@ public class CaptureModule implements CameraModule, PhotoController, initializeSecondTime(); } mUI.hidePreviewCover(); + mActivity.updateStorageSpaceAndHint(); mUI.enableShutter(true); } @@ -1251,7 +1356,8 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onCountDownFinished() { - + mUI.showUIAfterCountDown(); + takePicture(); } @Override @@ -1372,17 +1478,125 @@ public class CaptureModule implements CameraModule, PhotoController, @Override public void onShutterButtonFocus(boolean pressed) { - + if (!pressed && mLongshotActive) { + cancelLongshot(); + } } @Override public void onShutterButtonClick() { - takePicture(); + String timer = mSettingsManager.getValue(SettingsManager.KEY_TIMER); + + int seconds = Integer.parseInt(timer); + // When shutter button is pressed, check whether the previous countdown is + // finished. If not, cancel the previous countdown and start a new one. + if (mUI.isCountingDown()) { + mUI.cancelCountDown(); + } + if (seconds > 0) { + mUI.startCountDown(seconds, true); + } else { + takePicture(); + } } @Override public void onShutterButtonLongClick() { + if (isBackMode() && getMode() == DUAL_MODE) return; + if (mActivity.getStorageSpaceBytes() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { + Log.i(TAG, "Not enough space or storage not ready. remaining=" + + mActivity.getStorageSpaceBytes()); + return; + } + + String longshot = mSettingsManager.getValue(SettingsManager.KEY_LONGSHOT); + if (longshot.equals("on")) { + //Cancel the previous countdown when long press shutter button for longshot. + if (mUI.isCountingDown()) { + mUI.cancelCountDown(); + } + //check whether current memory is enough for longshot. + mActivity.updateStorageSpaceAndHint(); + + if (isLongshotNeedCancel()) { + mLongshotActive = false; + return; + } + mLongShotCaptureCountLimit = PersistUtil.getLongshotShotLimit(); + mLongShotCaptureCount = 1; + mLongshotActive = true; + takePicture(); + } + } + + private void cancelLongshot() { + Log.d(TAG, "cancelLongshot"); + mLongshotActive = false; + try { + if (isBackMode()) { + switch (getMode()) { + case BAYER_MODE: + mCaptureSession[BAYER_ID].stopRepeating(); + break; + case MONO_MODE: + mCaptureSession[MONO_MODE].stopRepeating(); + break; + } + } else { + mCaptureSession[FRONT_ID].stopRepeating(); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private boolean isLongshotNeedCancel() { + if (PersistUtil.getSkipMemoryCheck()) { + return false; + } + if (Storage.getAvailableSpace() <= Storage.LOW_STORAGE_THRESHOLD_BYTES) { + Log.w(TAG, "current storage is full"); + return true; + } + if (SECONDARY_SERVER_MEM == 0) { + ActivityManager am = (ActivityManager) mActivity.getSystemService( + Context.ACTIVITY_SERVICE); + ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo(); + am.getMemoryInfo(memInfo); + SECONDARY_SERVER_MEM = memInfo.secondaryServerThreshold; + } + + long totalMemory = Runtime.getRuntime().totalMemory(); + long maxMemory = Runtime.getRuntime().maxMemory(); + long remainMemory = maxMemory - totalMemory; + + MemInfoReader reader = new MemInfoReader(); + reader.readMemInfo(); + long[] info = reader.getRawInfo(); + long availMem = (info[Debug.MEMINFO_FREE] + info[Debug.MEMINFO_CACHED]) * 1024; + + if (availMem <= SECONDARY_SERVER_MEM || remainMemory <= LONGSHOT_CANCEL_THRESHOLD) { + Log.e(TAG, "cancel longshot: free=" + info[Debug.MEMINFO_FREE] * 1024 + + " cached=" + info[Debug.MEMINFO_CACHED] * 1024 + + " threshold=" + SECONDARY_SERVER_MEM); + RotateTextToast.makeText(mActivity, R.string.msg_cancel_longshot_for_limited_memory, + Toast.LENGTH_SHORT).show(); + return true; + } + + return false; + } + + private boolean checkLongShotMemoAndLimit() { + if (isLongshotNeedCancel()) { + return true; + } + + if (mLongShotCaptureCount == mLongShotCaptureCountLimit) { + return true; + } + return false; } private boolean isFlashOff(int id) { diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index c26d90eea..331d2dfe0 100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -43,6 +43,7 @@ import android.widget.TextView; import com.android.camera.ui.AutoFitSurfaceView; import com.android.camera.ui.CameraControls; +import com.android.camera.ui.CountDownView; import com.android.camera.ui.FocusIndicator; import com.android.camera.ui.ListMenu; import com.android.camera.ui.ListSubMenu; @@ -81,13 +82,14 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private static final int MODE_SCENE = 1; private static final int ANIMATION_DURATION = 300; private static final int CLICK_THRESHOLD = 200; - public boolean mMenuInitialized = false; String[] mSettingKeys = new String[]{ SettingsManager.KEY_FLASH_MODE, SettingsManager.KEY_RECORD_LOCATION, SettingsManager.KEY_PICTURE_SIZE, SettingsManager.KEY_JPEG_QUALITY, + SettingsManager.KEY_TIMER, SettingsManager.KEY_CAMERA_SAVEPATH, + SettingsManager.KEY_LONGSHOT, SettingsManager.KEY_ISO, SettingsManager.KEY_EXPOSURE, SettingsManager.KEY_WHITE_BALANCE, @@ -100,7 +102,9 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, SettingsManager.KEY_RECORD_LOCATION, SettingsManager.KEY_PICTURE_SIZE, SettingsManager.KEY_JPEG_QUALITY, + SettingsManager.KEY_TIMER, SettingsManager.KEY_CAMERA_SAVEPATH, + SettingsManager.KEY_LONGSHOT, SettingsManager.KEY_ISO, SettingsManager.KEY_EXPOSURE, SettingsManager.KEY_WHITE_BALANCE, @@ -160,6 +164,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, private RenderOverlay mRenderOverlay; private View mMenuButton; private ModuleSwitcher mSwitcher; + private CountDownView mCountDownView; private CameraControls mCameraControls; private PieRenderer mPieRenderer; private ZoomRenderer mZoomRenderer; @@ -253,14 +258,11 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mBottomMargin = l / 4 - mTopMargin; } mCameraControls.setMargins(mTopMargin, mBottomMargin); - } - public void onCameraOpened(List cameraIds) { if (mPieRenderer == null) { mPieRenderer = new PieRenderer(mActivity); mRenderOverlay.addRenderer(mPieRenderer); } - mMenuInitialized = true; if (mZoomRenderer == null) { mZoomRenderer = new ZoomRenderer(mActivity); @@ -272,16 +274,19 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, mGestures = new PreviewGestures(mActivity, this, mZoomRenderer, mPieRenderer); mRenderOverlay.setGestures(mGestures); } - mGestures.setCaptureUI(this); // need to handle touch - mGestures.setZoomEnabled(mSettingsManager.isZoomSupported(cameraIds)); mGestures.setRenderOverlay(mRenderOverlay); mRenderOverlay.requestLayout(); - initializeZoom(cameraIds); mActivity.setPreviewGestures(mGestures); } + public void onCameraOpened(List cameraIds) { + mGestures.setCaptureUI(this); + mGestures.setZoomEnabled(mSettingsManager.isZoomSupported(cameraIds)); + initializeZoom(cameraIds); + } + public ViewGroup getSceneAndFilterLayout() { return mSceneAndFilterLayout; } @@ -956,6 +961,25 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } + public void hideUIWhileCountDown() { + hideCameraControls(true); + mGestures.setZoomOnly(true); + } + + public void showUIAfterCountDown() { + hideCameraControls(false); + mGestures.setZoomOnly(false); + } + + public void hideCameraControls(boolean hide) { + final int status = (hide) ? View.INVISIBLE : View.VISIBLE; + if (mMenuButton != null) mMenuButton.setVisibility(status); + if (mFrontBackSwitcher != null) mFrontBackSwitcher.setVisibility(status); + if (mSceneModeSwitcher != null) mSceneModeSwitcher.setVisibility(status); + if (mFilterModeSwitcher != null) mFilterModeSwitcher.setVisibility(status); + if (mSwitcher != null) mSwitcher.setVisibility(status); + } + public void initializeControlByIntent() { mMenuButton = mRootView.findViewById(R.id.menu); mMenuButton.setOnClickListener(new View.OnClickListener() { @@ -1107,8 +1131,34 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } + + private void initializeCountDown() { + mActivity.getLayoutInflater().inflate(R.layout.count_down_to_capture, + (ViewGroup) mRootView, true); + mCountDownView = (CountDownView) (mRootView.findViewById(R.id.count_down_to_capture)); + mCountDownView.setCountDownFinishedListener((CountDownView.OnCountDownFinishedListener) mModule); + mCountDownView.bringToFront(); + mCountDownView.setOrientation(mOrientation); + } + + public boolean isCountingDown() { + return mCountDownView != null && mCountDownView.isCountingDown(); + } + + public void cancelCountDown() { + if (mCountDownView == null) return; + mCountDownView.cancelCountDown(); + showUIAfterCountDown(); + } + + public void startCountDown(int sec, boolean playSound) { + if (mCountDownView == null) initializeCountDown(); + mCountDownView.startCountDown(sec, playSound); + hideUIWhileCountDown(); + } + public void onPause() { - // Clear UI. + cancelCountDown(); collapseCameraControls(); } @@ -1216,6 +1266,8 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, } } } + if (mCountDownView != null) + mCountDownView.setOrientation(orientation); RotateTextToast.setOrientation(orientation); if (mZoomRenderer != null) { mZoomRenderer.setOrientation(orientation); @@ -1257,6 +1309,7 @@ public class CaptureUI implements FocusOverlayManager.FocusUI, if (mPieRenderer != null) { mPieRenderer.setBlockFocus(!previewFocused); } + if (!previewFocused && mCountDownView != null) mCountDownView.cancelCountDown(); } public ViewGroup getMenuLayout() { diff --git a/src/com/android/camera/util/PersistUtil.java b/src/com/android/camera/util/PersistUtil.java new file mode 100644 index 000000000..0f3f43f0b --- /dev/null +++ b/src/com/android/camera/util/PersistUtil.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.android.camera.util; + +import android.os.SystemProperties; + +public class PersistUtil { + + private static final String PERSIST_MEMORY_LIMIT = "persist.camera.perf.memlimit"; + private static final String PERSIST_SKIP_MEMORY_CHECK = "persist.camera.perf.skip_memck"; + private static final String PERSIST_LONGSHOT_SHOT_LIMIT = "persist.camera.longshot.shotnum"; + + public static int getMemoryLimit() { + return SystemProperties.getInt(PERSIST_MEMORY_LIMIT, 60); + } + + public static boolean getSkipMemoryCheck() { + return SystemProperties.getBoolean(PERSIST_SKIP_MEMORY_CHECK, false); + } + + public static int getLongshotShotLimit() { + return SystemProperties.getInt(PERSIST_LONGSHOT_SHOT_LIMIT, 0); + } + +} -- cgit v1.2.3