diff options
| author | Julian Veit <claymore1298@gmail.com> | 2022-03-20 00:06:45 +0100 |
|---|---|---|
| committer | Julian Veit <claymore1298@gmail.com> | 2022-03-20 00:06:45 +0100 |
| commit | d250e4a60f3b6219c6925534629eee12834a92b8 (patch) | |
| tree | 52fb90369927e24dc962b6ec283f6a3f710f7a6c /src/com | |
| parent | ef111dceb9e3fce395bff4f6a4ef1591c701e80c (diff) | |
| parent | 8c444160bd565319468cce5bfdcfc11d20e8b7cb (diff) | |
Merge branch 'lineage-18.1' of https://github.com/LineageOS/android_packages_apps_Snap into HEADHEADs12.1
Change-Id: Ia09760085480a6a028c8969f16f503070ab816e5
Diffstat (limited to 'src/com')
71 files changed, 4337 insertions, 3643 deletions
diff --git a/src/com/android/camera/CameraActivity.java b/src/com/android/camera/CameraActivity.java index 6cf522dd9..6cca8da7f 100644..100755 --- a/src/com/android/camera/CameraActivity.java +++ b/src/com/android/camera/CameraActivity.java @@ -170,6 +170,8 @@ public class CameraActivity extends Activity private static final int HIDE_ACTION_BAR = 1; private static final long SHOW_ACTION_BAR_TIMEOUT_MS = 3000; + private static final int SWITCH_SAVE_PATH = 2; + /** Permission request code */ private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 1; @@ -256,8 +258,6 @@ public class CameraActivity extends Activity private Intent mStandardShareIntent; private ShareActionProvider mPanoramaShareActionProvider; private Intent mPanoramaShareIntent; - private LocalMediaObserver mLocalImagesObserver; - private LocalMediaObserver mLocalVideosObserver; private SettingsManager mSettingsManager; private final int DEFAULT_SYSTEM_UI_VISIBILITY = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; @@ -353,6 +353,24 @@ public class CameraActivity extends Activity } }; + // update the status of storage space when SD card status changed. + private BroadcastReceiver mSDcardMountedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + Log.d(TAG, "SDcard status changed, update storage space"); + updateStorageSpaceAndHint(); + } + }; + + private void registerSDcardMountedReceiver() { + // filter for SDcard status + IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); + filter.addAction(Intent.ACTION_MEDIA_SHARED); + filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); + filter.addDataScheme("file"); + registerReceiver(mSDcardMountedReceiver, filter); + } + private class MainHandler extends Handler { public MainHandler(Looper looper) { super(looper); @@ -363,6 +381,8 @@ public class CameraActivity extends Activity if (msg.what == HIDE_ACTION_BAR) { removeMessages(HIDE_ACTION_BAR); CameraActivity.this.setSystemBarsVisibility(false); + }else if ( msg.what == SWITCH_SAVE_PATH ) { + mCurrentModule.onSwitchSavePath(); } } } @@ -1689,16 +1709,6 @@ public class CameraActivity extends Activity setupNfcBeamPush(); - mLocalImagesObserver = new LocalMediaObserver(); - mLocalVideosObserver = new LocalMediaObserver(); - - getContentResolver().registerContentObserver( - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, true, - mLocalImagesObserver); - getContentResolver().registerContentObserver( - MediaStore.Video.Media.EXTERNAL_CONTENT_URI, true, - mLocalVideosObserver); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); mDeveloperMenuEnabled = prefs.getBoolean(CameraSettings.KEY_DEVELOPER_MENU, false); @@ -1713,6 +1723,7 @@ public class CameraActivity extends Activity int offset = lower * 7 / 100; SETTING_LIST_WIDTH_1 = lower / 2 + offset; SETTING_LIST_WIDTH_2 = lower / 2 - offset; + registerSDcardMountedReceiver(); mAutoTestEnabled = PersistUtil.isAutoTestEnabled(); @@ -1775,8 +1786,6 @@ public class CameraActivity extends Activity mCurrentModule.onPauseAfterSuper(); mPaused = true; - mLocalImagesObserver.setActivityPaused(true); - mLocalVideosObserver.setActivityPaused(true); } @Override @@ -1914,17 +1923,12 @@ public class CameraActivity extends Activity // than the preview. mResetToPreviewOnResume = true; - if (mLocalVideosObserver.isMediaDataChangedDuringPause() - || mLocalImagesObserver.isMediaDataChangedDuringPause()) { - if (!mSecureCamera) { - // If it's secure camera, requestLoad() should not be called - // as it will load all the data. - mDataAdapter.requestLoad(getContentResolver()); - mThumbnailDrawable = null; - } + if (!mSecureCamera) { + // If it's secure camera, requestLoad() should not be called + // as it will load all the data. + mDataAdapter.requestLoad(getContentResolver()); + mThumbnailDrawable = null; } - mLocalImagesObserver.setActivityPaused(false); - mLocalVideosObserver.setActivityPaused(false); if (PersistUtil.isTraceEnable()) Trace.endSection(); } @@ -1980,8 +1984,7 @@ public class CameraActivity extends Activity Log.d(TAG, "wake lock release"); } if (mCursor != null) { - getContentResolver().unregisterContentObserver(mLocalImagesObserver); - getContentResolver().unregisterContentObserver(mLocalVideosObserver); + unregisterReceiver(mSDcardMountedReceiver); mCursor.close(); mCursor=null; @@ -2047,6 +2050,10 @@ public class CameraActivity extends Activity protected long updateStorageSpace() { synchronized (mStorageSpaceLock) { mStorageSpaceBytes = Storage.getAvailableSpace(); + if (Storage.switchSavePath()) { + mStorageSpaceBytes = Storage.getAvailableSpace(); + mMainHandler.sendEmptyMessage(SWITCH_SAVE_PATH); + } return mStorageSpaceBytes; } } @@ -2059,6 +2066,9 @@ public class CameraActivity extends Activity public void updateStorageSpaceAndHint() { if (mIsStartup) { + if (!SDCard.instance().isWriteable()) { + Storage.setSaveSDCard(false); + } mIsStartup = false; } updateStorageSpace(); diff --git a/src/com/android/camera/CameraBackupAgent.java b/src/com/android/camera/CameraBackupAgent.java index 348a08d28..348a08d28 100755..100644 --- a/src/com/android/camera/CameraBackupAgent.java +++ b/src/com/android/camera/CameraBackupAgent.java diff --git a/src/com/android/camera/CameraHolder.java b/src/com/android/camera/CameraHolder.java index 4daf84320..1be5fbd6b 100755..100644 --- a/src/com/android/camera/CameraHolder.java +++ b/src/com/android/camera/CameraHolder.java @@ -24,6 +24,7 @@ import android.hardware.Camera.CameraInfo; import android.hardware.Camera.Parameters; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraMetadata; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; @@ -186,10 +187,10 @@ public class CameraHolder { = manager.getCameraCharacteristics(cameraId); Log.d(TAG,"cameraIdList size ="+cameraIdList.length); int facing = characteristics.get(CameraCharacteristics.LENS_FACING); - if (facing == CameraCharacteristics.LENS_FACING_FRONT) { + if (mFrontCameraId == -1 && facing == CameraMetadata.LENS_FACING_FRONT) { CaptureModule.FRONT_ID = i; mFrontCameraId = i; - } else if (mBackCameraId != -1) { + } else if (mBackCameraId == -1 && facing == CameraMetadata.LENS_FACING_BACK) { mBackCameraId = i; } addCameraInfo(i, characteristics); diff --git a/src/com/android/camera/CameraModule.java b/src/com/android/camera/CameraModule.java index aed1e57db..14fe0ea4c 100644..100755 --- a/src/com/android/camera/CameraModule.java +++ b/src/com/android/camera/CameraModule.java @@ -73,6 +73,8 @@ public interface CameraModule { public void resizeForPreviewAspectRatio(); + public void onSwitchSavePath(); + public void waitingLocationPermissionResult(boolean waiting); public void enableRecordingLocation(boolean enable); diff --git a/src/com/android/camera/CameraSettings.java b/src/com/android/camera/CameraSettings.java index c1f02ff3a..ad0c30ac6 100644..100755 --- a/src/com/android/camera/CameraSettings.java +++ b/src/com/android/camera/CameraSettings.java @@ -92,6 +92,7 @@ public class CameraSettings { 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"; @@ -1134,6 +1135,7 @@ public class CameraSettings { 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 @@ -1257,6 +1259,35 @@ public class CameraSettings { 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; + + SharedPreferences pref = group.getSharedPreferences(); + String savePathValue = null; + if (pref != null) { + savePathValue = pref.getString(KEY_CAMERA_SAVEPATH, CAMERA_SAVEPATH_SDCARD); + } + if (savePath != null && CAMERA_SAVEPATH_SDCARD.equals(savePathValue)) { + // If sdCard is present, set sdCard as default save path. + // Only for the first time when camera start. + if (SDCard.instance().isWriteable()) { + Log.d(TAG, "set Sdcard as save path."); + savePath.setValueIndex(CAMERA_SAVEPATH_SDCARD_IDX); + } else { + 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); } diff --git a/src/com/android/camera/CaptureModule.java b/src/com/android/camera/CaptureModule.java index 7f546fdf7..b7773c873 100644..100755 --- a/src/com/android/camera/CaptureModule.java +++ b/src/com/android/camera/CaptureModule.java @@ -30,6 +30,7 @@ import android.content.SharedPreferences; import android.content.DialogInterface; import android.content.Intent; import android.content.res.Configuration; +import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.ImageFormat; import android.graphics.Matrix; @@ -133,6 +134,8 @@ import com.android.camera.util.VendorTagUtil; import org.codeaurora.snapcam.R; import org.codeaurora.snapcam.filter.ClearSightImageProcessor; +import org.lineageos.quickreader.ScannerActivity; + import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; @@ -557,7 +560,7 @@ public class CaptureModule implements CameraModule, PhotoController, private boolean[] mCameraOpened = new boolean[MAX_NUM_CAM]; private CameraDevice[] mCameraDevice = new CameraDevice[MAX_NUM_CAM]; private String[] mCameraId = new String[MAX_NUM_CAM]; - private String[] mSelectableModes = {"Video", "HFR", "Photo", "Bokeh", "SAT", "ProMode"}; + private String[] mSelectableModes = {"Video", "HFR", "Photo", "Bokeh", "SAT", "ProMode", "QR"}; private ArrayList<SceneModule> mSceneCameraIds = new ArrayList<>(); private SceneModule mCurrentSceneMode; private int mNextModeIndex = 1; @@ -570,7 +573,8 @@ public class CaptureModule implements CameraModule, PhotoController, DEFAULT, RTB, SAT, - PRO_MODE + PRO_MODE, + QR, } public enum MFNRSupportValues { @@ -604,6 +608,7 @@ public class CaptureModule implements CameraModule, PhotoController, private boolean mExistAECWarmTag = true; private boolean mExistAECFrameControlTag = true; + private static final long SDCARD_SIZE_LIMIT = 4000 * 1024 * 1024L; private static final String sTempCropFilename = "crop-temp"; private static final int REQUEST_CROP = 1000; private int mIntentMode = INTENT_MODE_NORMAL; @@ -954,22 +959,28 @@ public class CaptureModule implements CameraModule, PhotoController, mPreviewCaptureResult = result; } updateCaptureStateMachine(id, result); - Integer ssmStatus = result.get(ssmCaptureComplete); - if (ssmStatus != null) { - Log.d(TAG, "ssmStatus: CaptureComplete is " + ssmStatus); - updateProgressBar(true); - } - Integer procComplete = result.get(ssmProcessingComplete); - if (procComplete != null && ++mCaptureCompleteCount == 1) { - Log.d(TAG, "ssmStatus: ProcessingComplete is " + procComplete); - mCaptureCompleteCount = 0; - mSSMCaptureCompleteFlag = true; - mActivity.runOnUiThread(new Runnable() { - @Override - public void run() { - stopRecordingVideo(getMainCameraId()); + if (isSSMEnabled()) { + try { + Integer ssmStatus = result.get(ssmCaptureComplete); + if (ssmStatus != null) { + Log.d(TAG, "ssmStatus: CaptureComplete is " + ssmStatus); + updateProgressBar(true); } - }); + Integer procComplete = result.get(ssmProcessingComplete); + if (procComplete != null && ++mCaptureCompleteCount == 1) { + Log.d(TAG, "ssmStatus: ProcessingComplete is " + procComplete); + mCaptureCompleteCount = 0; + mSSMCaptureCompleteFlag = true; + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + stopRecordingVideo(getMainCameraId()); + } + }); + } + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } } } @@ -1818,6 +1829,10 @@ public class CaptureModule implements CameraModule, PhotoController, if (!HFR_RATE.equals("")) { mSettingsManager.setValue(SettingsManager.KEY_VIDEO_HIGH_FRAME_RATE, HFR_RATE); } + if (!mSettingsManager.isHFRSupportedOnCurrentResolution()) { + Toast.makeText(mActivity, R.string.hfr_unsupported_current_resolution, + Toast.LENGTH_LONG).show(); + } createSessionForVideo(cameraId); break; default: @@ -2334,11 +2349,20 @@ public class CaptureModule implements CameraModule, PhotoController, mSettingsManager = SettingsManager.getInstance(); mSettingsManager.createCaptureModule(this); mSettingsManager.registerListener(this); - if (isBackCameraId()) { - CURRENT_ID = BACK_MODE; - } else { - CURRENT_ID = FRONT_MODE; + + SceneModule module; + for (int i = 0; i < mSelectableModes.length; i++) { + module = new SceneModule(); + module.mode = CameraMode.values()[i]; + mSceneCameraIds.add(module); + if (module.mode == CURRENT_MODE) { + mNextModeIndex = i; + mCurrentModeIndex = i; + } } + mCurrentSceneMode = mSceneCameraIds.get(mCurrentModeIndex); + CURRENT_ID = mCurrentSceneMode.getNextCameraId(CURRENT_MODE); + mSettingsManager.init(); mFirstPreviewLoaded = false; Log.d(TAG, "init"); @@ -2349,12 +2373,6 @@ public class CaptureModule implements CameraModule, PhotoController, for (int i = 0; i < MAX_NUM_CAM; i++) { mState[i] = STATE_PREVIEW; } - SceneModule module; - for (int i = 0; i < mSelectableModes.length; i++) { - module = new SceneModule(); - module.mode = CameraMode.values()[i]; - mSceneCameraIds.add(module); - } mPostProcessor = new PostProcessor(mActivity, this); mFrameProcessor = new FrameProcessor(mActivity, this); @@ -2430,6 +2448,7 @@ public class CaptureModule implements CameraModule, PhotoController, removeList[CameraMode.DEFAULT.ordinal()] = false; removeList[CameraMode.VIDEO.ordinal()] = false; removeList[CameraMode.PRO_MODE.ordinal()] = false; + removeList[CameraMode.QR.ordinal()] = false; if (facing == CameraCharacteristics.LENS_FACING_FRONT) { CaptureModule.FRONT_ID = cameraId; mSceneCameraIds.get(CameraMode.DEFAULT.ordinal()).frontCameraId = cameraId; @@ -2445,6 +2464,7 @@ public class CaptureModule implements CameraModule, PhotoController, mSceneCameraIds.get(CameraMode.DEFAULT.ordinal()).rearCameraId = cameraId; mSceneCameraIds.get(CameraMode.VIDEO.ordinal()).rearCameraId = cameraId; mSceneCameraIds.get(CameraMode.PRO_MODE.ordinal()).rearCameraId = cameraId; + mSceneCameraIds.get(CameraMode.QR.ordinal()).rearCameraId = cameraId; if (mSettingsManager.isHFRSupported()) { // filter HFR mode removeList[CameraMode.HFR.ordinal()] = false; mSceneCameraIds.get(CameraMode.HFR.ordinal()).rearCameraId = cameraId; @@ -4566,6 +4586,7 @@ public class CaptureModule implements CameraModule, PhotoController, mSoundPlayer = SoundClips.getPlayer(mActivity); } + updateSaveStorageState(); setDisplayOrientation(); if (!resumeFromRestartAll) { startBackgroundThread(); @@ -4933,6 +4954,9 @@ public class CaptureModule implements CameraModule, PhotoController, return; } Log.d(TAG, "onSingleTapUp " + x + " " + y); + + mUI.closeModeSwitcher(true); + int currentId = mCurrentSceneMode.getCurrentId(); if(mLockAFAE) { mLockAFAE = false; @@ -5288,6 +5312,13 @@ public class CaptureModule implements CameraModule, PhotoController, } @Override + public void onSwitchSavePath() { + mSettingsManager.setValue(SettingsManager.KEY_CAMERA_SAVEPATH, "1"); + RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard, + Toast.LENGTH_SHORT).show(); + } + + @Override public void onShutterButtonFocus(boolean pressed) { if (!pressed && mLongshotActive) { Log.d(TAG, "Longshot button up"); @@ -6486,7 +6517,12 @@ public class CaptureModule implements CameraModule, PhotoController, String title = createName(dateTaken); String filename = title + CameraUtil.convertOutputFormatToFileExt(outputFileFormat); String mime = CameraUtil.convertOutputFormatToMimeType(outputFileFormat); - String path = Storage.DIRECTORY + '/' + filename; + String path; + if (Storage.isSaveSDCard() && SDCard.instance().isWriteable()) { + path = SDCard.instance().getDirectory() + '/' + filename; + } else { + path = Storage.DIRECTORY + '/' + filename; + } mCurrentVideoValues = new ContentValues(9); mCurrentVideoValues.put(MediaStore.Video.Media.TITLE, title); mCurrentVideoValues.put(MediaStore.Video.Media.DISPLAY_NAME, filename); @@ -6691,6 +6727,9 @@ public class CaptureModule implements CameraModule, PhotoController, maxFileSize = requestedSizeLimit; } + if (Storage.isSaveSDCard() && maxFileSize > SDCARD_SIZE_LIMIT) { + maxFileSize = SDCARD_SIZE_LIMIT; + } Log.i(TAG, "MediaRecorder setMaxFileSize: " + maxFileSize); try { mMediaRecorder.setMaxFileSize(maxFileSize); @@ -6788,6 +6827,8 @@ public class CaptureModule implements CameraModule, PhotoController, Log.d(TAG,"onShutterButtonClick"); + mUI.closeModeSwitcher(true); + if (mCurrentSceneMode.mode == CameraMode.HFR || mCurrentSceneMode.mode == CameraMode.VIDEO) { if (mSettingsManager.isLiveshotSupported(mVideoSize,mSettingsManager.getVideoFPS())){ @@ -8178,6 +8219,10 @@ public class CaptureModule implements CameraModule, PhotoController, } Log.d(TAG, "CaptureModule onSettingsChanged, key = " + key); switch (key) { + case SettingsManager.KEY_CAMERA_SAVEPATH: + Storage.setSaveSDCard(value.equals("1")); + mActivity.updateStorageSpaceAndHint(); + continue; case SettingsManager.KEY_JPEG_QUALITY: estimateJpegFileSize(); continue; @@ -8644,7 +8689,7 @@ public class CaptureModule implements CameraModule, PhotoController, } mUI.showUIafterRecording(); if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { - // We may have run out of storage space. + // We may have run out of space on the sdcard. mActivity.updateStorageSpaceAndHint(); } else { warningToast("MediaRecorder error. what=" + what + ". extra=" + extra); @@ -8675,6 +8720,11 @@ public class CaptureModule implements CameraModule, PhotoController, return bytes; } + private void updateSaveStorageState() { + Storage.setSaveSDCard(mSettingsManager.getValue(SettingsManager + .KEY_CAMERA_SAVEPATH).equals("1")); + } + public void startPlayVideoActivity() { Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(mCurrentVideoUri, @@ -8943,6 +8993,13 @@ public class CaptureModule implements CameraModule, PhotoController, if (mCurrentSceneMode.mode == mSceneCameraIds.get(mode).mode) { return -1; } + if (mSceneCameraIds.get(mode).mode == CameraMode.QR) { + mUI.closeModeSwitcher(true); + Intent intent = new Intent(mActivity, ScannerActivity.class); + mActivity.startActivity(intent); + // Don't change the selected item in modeswitcher + return -1; + } setCameraModeSwitcherAllowed(false); setNextSceneMode(mode); SceneModule nextSceneMode = mSceneCameraIds.get(mode); @@ -8952,12 +9009,16 @@ public class CaptureModule implements CameraModule, PhotoController, nextSceneMode.mode == CameraMode.SAT || nextSceneMode.mode == CameraMode.PRO_MODE)) { mSettingsManager.setValue(SettingsManager.KEY_FRONT_REAR_SWITCHER_VALUE, "rear"); + mUI.setFrontBackSwitcherDrawable(); } else { restartAll(); } updateZoomSeekBarVisible(); mUI.updateZoomSeekBar(1.0f); updateZoom(); + mUI.closeModeSwitcher(true); + mUI.setCurrentModeIcon(mode); + mUI.setSwitcherAnimationNeeded(true); return 1; } @@ -8995,6 +9056,16 @@ public class CaptureModule implements CameraModule, PhotoController, return mSelectableModes; } + public List<Integer> getCameraModeIconList() { + ArrayList<Integer> cameraModeIcons = new ArrayList<>(); + TypedArray ic = mActivity.getResources() + .obtainTypedArray(R.array.camera_modes_back); + for (SceneModule sceneModule : mSceneCameraIds) { + cameraModeIcons.add(ic.getResourceId(sceneModule.mode.ordinal(), 0)); + } + return cameraModeIcons; + } + private class SceneModule { CameraMode mode = CameraMode.DEFAULT; public int rearCameraId; @@ -9005,7 +9076,8 @@ public class CaptureModule implements CameraModule, PhotoController, int cameraId = isBackCamera() ? rearCameraId : frontCameraId; cameraId = isForceAUXOn(this.mode) ? auxCameraId : cameraId; if ((this.mode == CameraMode.DEFAULT || this.mode == CameraMode.VIDEO || - this.mode == CameraMode.HFR || this.mode == CameraMode.PRO_MODE) + this.mode == CameraMode.HFR || this.mode == CameraMode.PRO_MODE || + this.mode == CameraMode.QR) && (mSettingsManager.isDeveloperEnabled() || swithCameraId != -1)) { String value = mSettingsManager.getValue(SettingsManager.KEY_SWITCH_CAMERA); if (value != null && !value.equals("-1")) { @@ -9022,7 +9094,8 @@ public class CaptureModule implements CameraModule, PhotoController, int cameraId = isBackCamera() ? rearCameraId : frontCameraId; cameraId = isForceAUXOn(this.mode) ? auxCameraId : cameraId; if ((this.mode == CameraMode.DEFAULT || this.mode == CameraMode.VIDEO || - this.mode == CameraMode.HFR || this.mode == CameraMode.PRO_MODE) + this.mode == CameraMode.HFR || this.mode == CameraMode.PRO_MODE || + this.mode == CameraMode.QR) && (mSettingsManager.isDeveloperEnabled() || swithCameraId != -1)) { final SharedPreferences pref = mActivity.getSharedPreferences( ComboPreferences.getLocalSharedPreferencesName(mActivity, diff --git a/src/com/android/camera/CaptureUI.java b/src/com/android/camera/CaptureUI.java index 7874f963f..398b0ab0b 100755..100644 --- a/src/com/android/camera/CaptureUI.java +++ b/src/com/android/camera/CaptureUI.java @@ -20,6 +20,8 @@ package com.android.camera; import android.animation.Animator; +import android.animation.Animator.AnimatorListener; +import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.app.AlertDialog; import android.content.Context; @@ -46,6 +48,7 @@ import android.text.TextUtils; import android.util.DisplayMetrics; import android.util.Log; import android.util.Size; +import android.util.TypedValue; import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; @@ -209,6 +212,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, private View mFilterModeSwitcher; private View mSceneModeSwitcher; private View mFrontBackSwitcher; + private View mModeSwitcher; private ImageView mMakeupButton; private SeekBar mMakeupSeekBar; private SeekBar mDeepportraitSeekBar; @@ -260,6 +264,12 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, private TextView mStatsAecSensitivityText; private TextView mStatsAecExposureTimeText; + private TextView mExposureText; + private TextView mManualText; + private TextView mWhiteBalanceText; + private TextView mIsoText; + private TextView mZoomSeekbarText; + private LinearLayout mZoomLinearLayout; private TextView mZoomSwitch; @@ -270,6 +280,9 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, private boolean mIsVideoUI = false; private boolean mIsSceneModeLabelClose = false; + private boolean mNeedsAnimationSetup = true; + private boolean mIsAnimating = false; + private void previewUIReady() { if((mSurfaceHolder != null && mSurfaceHolder.getSurface().isValid())) { mModule.onPreviewUIReady(); @@ -384,6 +397,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, } @Override public void onStartTrackingTouch(SeekBar seekBar) { + closeModeSwitcher(true); } @Override public void onStopTrackingTouch(SeekBar seekBar) { @@ -425,17 +439,25 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, initZoomSeekBar(); mFlashButton = (FlashToggleButton) mRootView.findViewById(R.id.flash_button); + mFlashButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + closeModeSwitcher(true); + mFlashButton.handleClick(); + } + }); mModeSelectLayout = (RecyclerView) mRootView.findViewById(R.id.mode_select_layout); mModeSelectLayout.setLayoutManager(new LinearLayoutManager(mActivity, - LinearLayoutManager.HORIZONTAL, false)); - mCameraModeAdapter = new Camera2ModeAdapter(mModule.getCameraModeList()); + LinearLayoutManager.VERTICAL, false)); + mModeSelectLayout.setVisibility(View.INVISIBLE); + mCameraModeAdapter = new Camera2ModeAdapter(mModule.getCameraModeList(), mModule.getCameraModeIconList()); mCameraModeAdapter.setOnItemClickListener(mModule.getModeItemClickListener()); mModeSelectLayout.setAdapter(mCameraModeAdapter); mSettingsIcon = (ImageView) mRootView.findViewById(R.id.settings); - mSettingsIcon.setImageResource(R.drawable.settings); mSettingsIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(false); openSettingsMenu(); } }); @@ -509,6 +531,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, }); mCameraControls = (OneUICameraControls) mRootView.findViewById(R.id.camera_controls); + mCameraControls.initialize(this); mFaceView = (Camera2FaceView) mRootView.findViewById(R.id.face_view); mFaceView.initMode(); @@ -526,6 +549,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mZoomSwitch.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(true); String[] entries = mActivity.getResources().getStringArray( R.array.pref_camera2_zomm_switch_entries); String[] values = mActivity.getResources().getStringArray( @@ -544,7 +568,6 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mCancelButton = (ImageView) mRootView.findViewById(R.id.cancel_button); final int intentMode = mModule.getCurrentIntentMode(); if (intentMode != CaptureModule.INTENT_MODE_NORMAL) { - mModeSelectLayout.setVisibility(View.GONE); mCameraControls.setIntentMode(intentMode); mCameraControls.setVideoMode(false); mCancelButton.setVisibility(View.VISIBLE); @@ -603,6 +626,55 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, }); } + mModeSwitcher = mRootView.findViewById(R.id.mode_switcher); + mModeSwitcher.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + showModeSwitcher(true); + } + }); + + mExposureText = mRootView.findViewById(R.id.exposure_value); + mExposureText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + closeModeSwitcher(true); + mCameraControls.onExposureTextClick(); + } + }); + mManualText = mRootView.findViewById(R.id.manual_value); + mManualText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + closeModeSwitcher(true); + mCameraControls.onManualTextClick(); + } + }); + mWhiteBalanceText = mRootView.findViewById(R.id.white_balance_value); + mWhiteBalanceText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + closeModeSwitcher(true); + mCameraControls.onWhiteBalanceTextClick(); + } + }); + mIsoText = mRootView.findViewById(R.id.iso_value); + mIsoText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + closeModeSwitcher(true); + mCameraControls.onIsoTextClick(); + } + }); + mZoomSeekbarText = mRootView.findViewById(R.id.zoom_seekbar_value); + mZoomSeekbarText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + closeModeSwitcher(true); + mCameraControls.onZoomSeekbarTextClick(); + } + }); + mActivity.getWindowManager().getDefaultDisplay().getSize(mDisplaySize); mScreenRatio = CameraUtil.determineRatio(mDisplaySize.x, mDisplaySize.y); if (mScreenRatio == CameraUtil.RATIO_16_9) { @@ -674,6 +746,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, @Override public void onStartTrackingTouch(SeekBar seekBar) { + closeModeSwitcher(false); } @Override @@ -962,6 +1035,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mVideoButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(true); cancelCountDown(); mModule.onVideoButtonClick(); } @@ -991,6 +1065,79 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, return mFilterMenuStatus == FILTER_MENU_ON; } + public void setCurrentModeIcon(int mode) { + ((ImageView) mModeSwitcher).setImageResource( + mModule.getCameraModeIconList().get(mode)); + } + + private void modeSwitcherAnimSetup() { + mModeSelectLayout.setAlpha(0f); + mModeSelectLayout.setScaleX(0.3f); + mModeSelectLayout.setScaleY(0.3f); + mModeSelectLayout.setTranslationY(mModeSwitcher.getY() + - mModeSelectLayout.getMeasuredHeight() / 2); + mNeedsAnimationSetup = false; + } + + public void setSwitcherAnimationNeeded(boolean needed) { + mNeedsAnimationSetup = needed; + } + + public void showModeSwitcher(boolean animation) { + if (mModeSelectLayout == null || + mModeSelectLayout.getVisibility() == View.VISIBLE) { + return; + } + + if (isPreviewMenuBeingShown()) { + removeFilterMenu(true); + } + + if (animation) { + if (mNeedsAnimationSetup) { + modeSwitcherAnimSetup(); + } + + mModeSelectLayout.setVisibility(View.VISIBLE); + mModeSelectLayout.animate() + .alpha(1f) + .scaleX(1f).scaleY(1f) + .translationY(mModeSwitcher.getY() + - mModeSelectLayout.getMeasuredHeight() + + mModeSwitcher.getMeasuredHeight()) + .setDuration(ANIMATION_DURATION) + .setListener(null); + } else { + mModeSelectLayout.setVisibility(View.VISIBLE); + } + } + + public void closeModeSwitcher(boolean animation) { + if (mModeSelectLayout == null || + mModeSelectLayout.getVisibility() != View.VISIBLE) { + return; + } + + if (animation && !mIsAnimating) { + mIsAnimating = true; + mModeSelectLayout.animate() + .alpha(0f) + .scaleX(0.3f).scaleY(0.3f) + .translationY(mModeSwitcher.getY() + - mModeSelectLayout.getMeasuredHeight() / 2) + .setDuration(ANIMATION_DURATION) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mModeSelectLayout.setVisibility(View.INVISIBLE); + mIsAnimating = false; + } + }); + } else { + mModeSelectLayout.setVisibility(View.INVISIBLE); + } + } + public void removeFilterMenu(boolean animate) { if (animate) { animateSlideOut(mFilterLayout); @@ -1000,9 +1147,6 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, ((ViewGroup) mRootView).removeView(mFilterLayout); mFilterLayout = null; } - if (mModule.getCurrentIntentMode() == CaptureModule.INTENT_MODE_NORMAL) { - mModeSelectLayout.setVisibility(View.VISIBLE); - } mModule.updateZoomSeekBarVisible(); } updateMenus(); @@ -1012,6 +1156,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, if (mPreviewLayout != null && mPreviewLayout.getVisibility() == View.VISIBLE) { return; } + closeModeSwitcher(true); removeFilterMenu(false); Intent intent = new Intent(mActivity, SettingsActivity.class); intent.putExtra(SettingsActivity.CAMERA_MODULE, mModule.getCurrenCameraMode()); @@ -1025,10 +1170,12 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, if (value == null) return; + setFrontBackSwitcherDrawable(); mFrontBackSwitcher.setVisibility(View.VISIBLE); mFrontBackSwitcher.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(false); mModule.writeXMLForWarmAwb(); switchFrontBackCamera(); } @@ -1055,6 +1202,16 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, switchToPhotoModeDueToError(false); } mSettingsManager.setValueIndex(SettingsManager.KEY_FRONT_REAR_SWITCHER_VALUE, index); + setFrontBackSwitcherDrawable(); + } + + public void setFrontBackSwitcherDrawable() { + if (mSettingsManager.getValue(SettingsManager.KEY_FRONT_REAR_SWITCHER_VALUE) + .equals("front")) { + ((ImageView) mFrontBackSwitcher).setImageResource(R.drawable.ic_switch_back); + } else { + ((ImageView) mFrontBackSwitcher).setImageResource(R.drawable.ic_switch_front); + } } private boolean isSupportFrontCamera(CaptureModule.CameraMode mode) { @@ -1076,6 +1233,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mSceneModeSwitcher.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(false); removeFilterMenu(false); Intent intent = new Intent(mActivity, SceneModeActivity.class); intent.putExtra(CameraUtil.KEY_IS_SECURE_CAMERA, mActivity.isSecureCamera()); @@ -1096,10 +1254,10 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mFilterModeSwitcher.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(false); addFilterMode(); adjustOrientation(); updateMenus(); - mModeSelectLayout.setVisibility(View.GONE); hideZoomSeekBar(); } }); @@ -1131,7 +1289,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, } else { mFlashButton.init(true); } - mVideoButton.setImageResource(R.drawable.video_stop); + mVideoButton.setImageResource(R.drawable.shutter_button_video_stop); mRecordingTimeView.setText("00:00"); mRecordingTimeRect.setVisibility(View.VISIBLE); mMuteButton.setVisibility((mModule.isHSRMode() || mModule.getCurrenCameraMode() == CaptureModule.CameraMode.VIDEO) ? View.VISIBLE : View.INVISIBLE); @@ -1294,13 +1452,13 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, public void hideUIwhileRecording() { mCameraControls.setVideoMode(true); - mModeSelectLayout.setVisibility(View.INVISIBLE); mSceneModeLabelRect.setVisibility(View.INVISIBLE); mDeepZoomModeRect.setVisibility(View.INVISIBLE); mFrontBackSwitcher.setVisibility(View.INVISIBLE); mFilterModeSwitcher.setVisibility(View.INVISIBLE); mSceneModeSwitcher.setVisibility(View.INVISIBLE); mSettingsIcon.setVisibility(View.INVISIBLE); + closeModeSwitcher(true); String value = mSettingsManager.getValue(SettingsManager.KEY_MAKEUP); if(value != null && value.equals("0")) { mMakeupButton.setVisibility(View.GONE); @@ -1322,7 +1480,6 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mPauseButton.setVisibility(View.INVISIBLE); if (mModule.getCurrentIntentMode() == CaptureModule.INTENT_MODE_NORMAL) { mShutterButton.setVisibility(View.INVISIBLE); - mModeSelectLayout.setVisibility(View.VISIBLE); } mFilterModeSwitcher.setVisibility(View.VISIBLE); if (mFilterMenuStatus == FILTER_MENU_ON) { @@ -1605,6 +1762,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, if (mSceneModeSwitcher != null) mSceneModeSwitcher.setEnabled(status); if (mFilterModeSwitcher != null) mFilterModeSwitcher.setEnabled(status); if (mMakeupButton != null) mMakeupButton.setVisibility(View.GONE); + closeModeSwitcher(true); } public void initializeControlByIntent() { @@ -1612,6 +1770,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, mThumbnail.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { + closeModeSwitcher(true); if (!CameraControls.isAnimating() && !mModule.isTakingPicture() && !mModule.isRecordingVideo()) mActivity.gotoGallery(); @@ -1700,6 +1859,8 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, } mFilterModeSwitcher.setEnabled(enableFilterMenu); mSceneModeSwitcher.setEnabled(enableSceneMenu); + mNeedsAnimationSetup = true; + mModeSelectLayout.bringToFront(); } public void toggleProgressBar(boolean show) { @@ -1740,6 +1901,10 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, removeFilterMenu(true); return true; } + if (mModeSelectLayout.getVisibility() == View.VISIBLE) { + closeModeSwitcher(true); + return true; + } return false; } @@ -2008,6 +2173,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, @Override public void onSingleTapUp(View view, int x, int y) { + closeModeSwitcher(true); mModule.onSingleTapUp(view, x, y); } @@ -2062,6 +2228,7 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, } public void pressShutterButton() { + closeModeSwitcher(true); if (mShutterButton.isInTouchMode()) { mShutterButton.requestFocusFromTouch(); } else { @@ -2266,7 +2433,6 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, int mode = index % modeListSize; mModule.setCameraModeSwitcherAllowed(false); mCameraModeAdapter.setSelectedPosition(mode); - mModeSelectLayout.smoothScrollToPosition(mode); mModule.selectCameraMode(mode); } @@ -2280,12 +2446,12 @@ public class CaptureUI implements PreviewGestures.SingleTapListener, } } mCameraModeAdapter.setSelectedPosition(photoModeIndex); - mModeSelectLayout.smoothScrollToPosition(photoModeIndex); if (switchCamera) { mModule.selectCameraMode(photoModeIndex); } else { mModule.setNextSceneMode(photoModeIndex); } + setFrontBackSwitcherDrawable(); } } diff --git a/src/com/android/camera/ComboPreferences.java b/src/com/android/camera/ComboPreferences.java index dab69e85b..c20d40561 100644..100755 --- a/src/com/android/camera/ComboPreferences.java +++ b/src/com/android/camera/ComboPreferences.java @@ -107,6 +107,7 @@ public class ComboPreferences implements movePrefFrom(prefMap, CameraSettings.KEY_CAMERA_FIRST_USE_HINT_SHOWN, src); movePrefFrom(prefMap, CameraSettings.KEY_VIDEO_FIRST_USE_HINT_SHOWN, src); movePrefFrom(prefMap, CameraSettings.KEY_VIDEO_EFFECT, src); + movePrefFrom(prefMap, CameraSettings.KEY_CAMERA_SAVEPATH, src); } public static String[] getSharedPreferencesNames(Context context, ArrayList<String> keys) { @@ -156,10 +157,12 @@ public class ComboPreferences implements || key.equals(CameraSettings.KEY_TIMER) || key.equals(CameraSettings.KEY_TIMER_SOUND_EFFECTS) || key.equals(CameraSettings.KEY_PHOTOSPHERE_PICTURESIZE) + || key.equals(CameraSettings.KEY_CAMERA_SAVEPATH) || key.equals(SettingsManager.KEY_MONO_ONLY) || key.equals(SettingsManager.KEY_MONO_PREVIEW) || key.equals(SettingsManager.KEY_FRONT_REAR_SWITCHER_VALUE) || key.equals(SettingsManager.KEY_FORCE_AUX) + || key.equals(SettingsManager.KEY_CAMERA_SAVEPATH) || key.equals(SettingsManager.KEY_FACE_DETECTION) || key.equals(SettingsManager.KEY_RECORD_LOCATION) || key.equals(SettingsManager.KEY_CLEARSIGHT) diff --git a/src/com/android/camera/ExtendedFace.java b/src/com/android/camera/ExtendedFace.java index 7b50d4ad1..7b50d4ad1 100755..100644 --- a/src/com/android/camera/ExtendedFace.java +++ b/src/com/android/camera/ExtendedFace.java diff --git a/src/com/android/camera/ListPreference.java b/src/com/android/camera/ListPreference.java index 3acf26e92..3acf26e92 100755..100644 --- a/src/com/android/camera/ListPreference.java +++ b/src/com/android/camera/ListPreference.java diff --git a/src/com/android/camera/LocationManager.java b/src/com/android/camera/LocationManager.java index 2ae901aed..2ae901aed 100755..100644 --- a/src/com/android/camera/LocationManager.java +++ b/src/com/android/camera/LocationManager.java diff --git a/src/com/android/camera/MediaSaveService.java b/src/com/android/camera/MediaSaveService.java index 7c1b050f2..72606766d 100755..100644 --- a/src/com/android/camera/MediaSaveService.java +++ b/src/com/android/camera/MediaSaveService.java @@ -291,8 +291,14 @@ public class MediaSaveService extends Service { // combine to single mpo String path = Storage.generateFilepath(title, pictureFormat); - MpoInterface.writeMpo(mpo, path); - return Storage.addImage(MediaSaveService.this, path); + int size = MpoInterface.writeMpo(mpo, path); + // Try to get the real image size after add exif. + File f = new File(path); + if (f.exists() && f.isFile()) { + size = (int) f.length(); + } + return Storage.addImage(resolver, title, date, loc, orientation, null, + size, path, width, height, pictureFormat); } @Override @@ -378,7 +384,9 @@ public class MediaSaveService extends Service { @Override protected Uri doInBackground(Void... params) { - return Storage.addImage(MediaSaveService.this, path); + return Storage.addHeifImage( + resolver,title,date,loc,orientation,exif,path, + width,height,quality,pictureFormat); } @Override @@ -430,7 +438,8 @@ public class MediaSaveService extends Service { width = options.outWidth; height = options.outHeight; } - return Storage.addImage(MediaSaveService.this, title, exif, data, pictureFormat); + return Storage.addImage( + resolver, title, date, loc, orientation, exif, data, width, height, pictureFormat); } @Override @@ -506,7 +515,8 @@ public class MediaSaveService extends Service { width = options.outWidth; height = options.outHeight; } - return Storage.addImage(MediaSaveService.this, title, exif, data, pictureFormat); + return Storage.addImage( + resolver, title, date, loc, orientation, exif, data, width, height, pictureFormat); } @Override diff --git a/src/com/android/camera/PanoCaptureModule.java b/src/com/android/camera/PanoCaptureModule.java index b333158d9..cea752b25 100644..100755 --- a/src/com/android/camera/PanoCaptureModule.java +++ b/src/com/android/camera/PanoCaptureModule.java @@ -494,7 +494,9 @@ public class PanoCaptureModule implements CameraModule, PhotoController { Log.e(TAG, "Cannot set exif for " + filepath, e); Storage.writeFile(filepath, jpegData); } - return Storage.addImage(mActivity, filepath); + int jpegLength = (int) (new File(filepath).length()); + return Storage.addImage(mContentResolver, filename, timeTaken, loc, orientation, + jpegLength, filepath, width, height, LocalData.MIME_TYPE_JPEG); } return null; } @@ -684,6 +686,11 @@ public class PanoCaptureModule implements CameraModule, PhotoController { } @Override + public void onSwitchSavePath() { + + } + + @Override public void waitingLocationPermissionResult(boolean waiting) { } diff --git a/src/com/android/camera/PauseButton.java b/src/com/android/camera/PauseButton.java index 6511e0e08..6511e0e08 100755..100644 --- a/src/com/android/camera/PauseButton.java +++ b/src/com/android/camera/PauseButton.java diff --git a/src/com/android/camera/PermissionsActivity.java b/src/com/android/camera/PermissionsActivity.java index 19077a3c0..19077a3c0 100755..100644 --- a/src/com/android/camera/PermissionsActivity.java +++ b/src/com/android/camera/PermissionsActivity.java diff --git a/src/com/android/camera/PhotoController.java b/src/com/android/camera/PhotoController.java index 18493755b..18493755b 100755..100644 --- a/src/com/android/camera/PhotoController.java +++ b/src/com/android/camera/PhotoController.java diff --git a/src/com/android/camera/PhotoMenu.java b/src/com/android/camera/PhotoMenu.java index 71d827e3f..4f6c88688 100644 --- a/src/com/android/camera/PhotoMenu.java +++ b/src/com/android/camera/PhotoMenu.java @@ -187,6 +187,7 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_PICTURE_SIZE, CameraSettings.KEY_JPEG_QUALITY, CameraSettings.KEY_TIMER, + CameraSettings.KEY_CAMERA_SAVEPATH, CameraSettings.KEY_LONGSHOT, CameraSettings.KEY_FACE_DETECTION, CameraSettings.KEY_ISO, @@ -216,6 +217,7 @@ public class PhotoMenu extends MenuController CameraSettings.KEY_PICTURE_SIZE, CameraSettings.KEY_JPEG_QUALITY, CameraSettings.KEY_TIMER, + CameraSettings.KEY_CAMERA_SAVEPATH, CameraSettings.KEY_LONGSHOT, CameraSettings.KEY_FACE_DETECTION, CameraSettings.KEY_ISO, diff --git a/src/com/android/camera/PhotoModule.java b/src/com/android/camera/PhotoModule.java index 891a849a2..fb988b431 100644..100755 --- a/src/com/android/camera/PhotoModule.java +++ b/src/com/android/camera/PhotoModule.java @@ -616,6 +616,8 @@ public class PhotoModule mBlurDegreeProgressBar = (SeekBar)mRootView.findViewById(R.id.blur_degree_bar); mBlurDegreeProgressBar.setOnSeekBarChangeListener(mBlurDegreeListener); mBlurDegreeProgressBar.setMax(100); + Storage.setSaveSDCard( + mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); // LGE HDR mode if (mApplicationContext != null) { @@ -909,6 +911,19 @@ public class PhotoModule mUI.setAspectRatio((float) size.width / size.height); } + @Override + public void onSwitchSavePath() { + if (mUI.mMenuInitialized) { + mUI.setPreference(CameraSettings.KEY_CAMERA_SAVEPATH, "1"); + } else { + mPreferences.edit() + .putString(CameraSettings.KEY_CAMERA_SAVEPATH, "1") + .apply(); + } + RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard, + Toast.LENGTH_SHORT).show(); + } + // Snapshots can only be taken after this is called. It should be called // once only. We could have done these things in onCreate() but we want to // make preview screen appear as soon as possible. @@ -1260,6 +1275,7 @@ public class PhotoModule } String dstPath = Storage.DIRECTORY; + File sdCard = android.os.Environment.getExternalStorageDirectory(); File dstFile = new File(dstPath); if (dstFile == null) { Log.e(TAG, "Destination file path invalid"); @@ -4963,6 +4979,13 @@ public class PhotoModule return; } + if (CameraSettings.KEY_CAMERA_SAVEPATH.equals(pref.getKey())) { + Storage.setSaveSDCard( + mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); + mActivity.updateStorageSpaceAndHint(); + updateRemainingPhotos(); + } + if (!CameraSettings.hasChromaFlashScene(mActivity) && CameraSettings.KEY_QC_CHROMA_FLASH.equals(pref.getKey())) { mUI.setPreference(CameraSettings.KEY_ADVANCED_FEATURES, pref.getValue()); diff --git a/src/com/android/camera/PhotoUI.java b/src/com/android/camera/PhotoUI.java index 072294da7..072294da7 100755..100644 --- a/src/com/android/camera/PhotoUI.java +++ b/src/com/android/camera/PhotoUI.java diff --git a/src/com/android/camera/PreviewGestures.java b/src/com/android/camera/PreviewGestures.java index ce4587047..78591eab5 100755..100644 --- a/src/com/android/camera/PreviewGestures.java +++ b/src/com/android/camera/PreviewGestures.java @@ -93,17 +93,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()); if((Math.abs(deltaX) > 40 || Math.abs(deltaY) > 40) && Math.abs(e1.getY()) < 1800) { 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.swipeCameraMode(-1); if (mMultiCameraUI != null) mMultiCameraUI.swipeCameraMode(-1); diff --git a/src/com/android/camera/SDCard.java b/src/com/android/camera/SDCard.java new file mode 100755 index 000000000..836c2db8a --- /dev/null +++ b/src/com/android/camera/SDCard.java @@ -0,0 +1,127 @@ +/* Copyright (c) 2014, The Linux Foundation. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * * Neither the name of The Linux Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.android.camera; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Environment; +import android.os.storage.StorageVolume; +import android.os.storage.StorageManager; +import android.util.Log; + +import java.util.List; + +public class SDCard { + private static final String TAG = "SDCard"; + + private static final int VOLUME_SDCARD_INDEX = 1; + + private StorageManager mStorageManager = null; + private StorageVolume mVolume = null; + private String mPath = null; + private String mRawpath = null; + private static SDCard sSDCard; + + public boolean isWriteable() { + if (mVolume == null) return false; + final String state = getSDCardStorageState(); + if (Environment.MEDIA_MOUNTED.equals(state)) { + return true; + } + return false; + } + + public String getDirectory() { + if (mVolume == null) { + return null; + } + if (mPath == null) { + mPath = mVolume.getDirectory().toString() + "/DCIM/Camera"; + } + return mPath; + } + + public String getRawDirectory() { + if (mVolume == null) { + return null; + } + if (mRawpath == null) { + mRawpath = mVolume.getDirectory().toString() + "/DCIM/Camera/raw"; + } + return mRawpath; + } + + public static void initialize(Context context) { + if (sSDCard == null) { + sSDCard = new SDCard(context); + } + } + + public static synchronized SDCard instance() { + return sSDCard; + } + + private String getSDCardStorageState() { + return mVolume.getState(); + } + + private SDCard(Context context) { + try { + mStorageManager = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); + initVolume(); + registerMediaBroadcastreceiver(context); + } catch (Exception e) { + Log.e(TAG, "couldn't talk to MountService", e); + } + } + + private void initVolume() { + final StorageVolume[] volumes = mStorageManager.getVolumeList(); + mVolume = (volumes.length > VOLUME_SDCARD_INDEX) ? + volumes[VOLUME_SDCARD_INDEX] : null; + mPath = null; + mRawpath = null; + } + + private void registerMediaBroadcastreceiver(Context context) { + IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED); + filter.addAction(Intent.ACTION_MEDIA_UNMOUNTED); + filter.addDataScheme("file"); + context.registerReceiver(mMediaBroadcastReceiver , filter); + } + + private BroadcastReceiver mMediaBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + initVolume(); + } + }; +} diff --git a/src/com/android/camera/SceneModeActivity.java b/src/com/android/camera/SceneModeActivity.java index c6258d9d0..c6258d9d0 100755..100644 --- a/src/com/android/camera/SceneModeActivity.java +++ b/src/com/android/camera/SceneModeActivity.java diff --git a/src/com/android/camera/DisableCameraReceiver.java b/src/com/android/camera/SetActivitiesCameraReceiver.java index 624874184..c2db30511 100644 --- a/src/com/android/camera/DisableCameraReceiver.java +++ b/src/com/android/camera/SetActivitiesCameraReceiver.java @@ -26,11 +26,10 @@ 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 -// this receiver will be disabled, so it will not run again. -public class DisableCameraReceiver extends BroadcastReceiver { - private static final String TAG = "DisableCameraReceiver"; +// Enable or disable camera-related activities based on "camera hal" in every boot up. +// This receiver runs when BOOT_COMPLETED intent is received. +public class SetActivitiesCameraReceiver extends BroadcastReceiver { + private static final String TAG = "SetActivitiesCameraReceiver"; private static final boolean CHECK_BACK_CAMERA_ONLY = false; private static final String ACTIVITIES[] = { "com.android.camera.CameraLauncher", @@ -51,19 +50,17 @@ public class DisableCameraReceiver extends BroadcastReceiver { CameraHolder.setCamera2Mode(context, mCamera2enabled); // Disable camera-related activities if there is no camera. - boolean needCameraActivity = CHECK_BACK_CAMERA_ONLY + int component_state = (CHECK_BACK_CAMERA_ONLY ? hasBackCamera() - : hasCamera(); + : hasCamera()) + ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED; - if (!needCameraActivity) { - Log.i(TAG, "disable all camera activities"); - for (int i = 0; i < ACTIVITIES.length; i++) { - disableComponent(context, ACTIVITIES[i]); - } + Log.i(TAG, "component state is " + component_state); + for (int i = 0; i < ACTIVITIES.length; i++) { + setComponent(context, ACTIVITIES[i], + component_state); } - - // Disable this receiver so it won't run again. - disableComponent(context, "com.android.camera.DisableCameraReceiver"); } private boolean hasCamera() { @@ -78,14 +75,14 @@ public class DisableCameraReceiver extends BroadcastReceiver { return backCameraId != -1; } - private void disableComponent(Context context, String klass) { + private void setComponent(Context context, String klass, final int enabledState) { ComponentName name = new ComponentName(context, klass); PackageManager pm = context.getPackageManager(); // We need the DONT_KILL_APP flag, otherwise we will be killed // immediately because we are in the same app. pm.setComponentEnabledSetting(name, - PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + enabledState, PackageManager.DONT_KILL_APP); } } diff --git a/src/com/android/camera/SettingsActivity.java b/src/com/android/camera/SettingsActivity.java index 1a6702d8e..460385f38 100644..100755 --- a/src/com/android/camera/SettingsActivity.java +++ b/src/com/android/camera/SettingsActivity.java @@ -1175,6 +1175,7 @@ public class SettingsActivity extends PreferenceActivity { updateZslPreference(); updateFormatPreference(); updateEISPreference(); + updateStoragePreference(); updateMfnrPreference(); Map<String, SettingsManager.Values> map = mSettingsManager.getValuesMap(); @@ -1232,6 +1233,18 @@ public class SettingsActivity extends PreferenceActivity { } } + private void updateStoragePreference() { + boolean isWrite = SDCard.instance().isWriteable(); + ListPreference pref = (ListPreference)findPreference(SettingsManager.KEY_CAMERA_SAVEPATH); + if (pref == null) { + return; + } + pref.setEnabled(isWrite); + if (!isWrite) { + updatePreference(SettingsManager.KEY_CAMERA_SAVEPATH); + } + } + private void updateMfnrPreference(){ CaptureModule.CameraMode mode = (CaptureModule.CameraMode) getIntent().getSerializableExtra(CAMERA_MODULE); diff --git a/src/com/android/camera/SettingsManager.java b/src/com/android/camera/SettingsManager.java index 208d5a5c5..cfe42dfcd 100644..100755 --- a/src/com/android/camera/SettingsManager.java +++ b/src/com/android/camera/SettingsManager.java @@ -130,6 +130,7 @@ public class SettingsManager implements ListMenu.SettingsListener { public static final String SCENE_MODE_DUAL_STRING = "100"; public static final String SCENE_MODE_SUNSET_STRING = "10"; public static final String SCENE_MODE_LANDSCAPE_STRING = "4"; + public static final String KEY_CAMERA_SAVEPATH = "pref_camera2_savepath_key"; public static final String KEY_RECORD_LOCATION = "pref_camera2_recordlocation_key"; public static final String KEY_JPEG_QUALITY = "pref_camera2_jpegquality_key"; public static final String KEY_FOCUS_MODE = "pref_camera2_focusmode_key"; @@ -250,6 +251,7 @@ public class SettingsManager implements ListMenu.SettingsListener { private boolean mIsFrontCameraPresent = false; private boolean mHasMultiCamera = false; private boolean mIsHFRSupported = false; + private boolean mIsHFRSupportedOnCurrentResolution = false; private JSONObject mDependency; private int mCameraId; private Set<String> mFilteredKeys; @@ -1076,6 +1078,7 @@ public class SettingsManager implements ListMenu.SettingsListener { } } // filter unsupported preferences + ListPreference savePath = mPreferenceGroup.findPreference(KEY_CAMERA_SAVEPATH); ListPreference forceAUX = mPreferenceGroup.findPreference(KEY_FORCE_AUX); ListPreference whiteBalance = mPreferenceGroup.findPreference(KEY_WHITE_BALANCE); ListPreference flashMode = mPreferenceGroup.findPreference(KEY_FLASH_MODE); @@ -1116,6 +1119,13 @@ public class SettingsManager implements ListMenu.SettingsListener { } buildCameraId(); + if (savePath != null) { + if (filterUnsupportedOptions(savePath, getSupportedSavePaths(cameraId))) { + mFilteredKeys.add(savePath.getKey()); + } + } + + if (whiteBalance != null) { if (filterUnsupportedOptions(whiteBalance, getSupportedWhiteBalanceModes(cameraId))) { mFilteredKeys.add(whiteBalance.getKey()); @@ -1526,6 +1536,10 @@ public class SettingsManager implements ListMenu.SettingsListener { return mIsHFRSupported; } + public boolean isHFRSupportedOnCurrentResolution() { + return mIsHFRSupportedOnCurrentResolution; + } + private void filterVideoEncoderProfileOptions() { ListPreference videoEncoderProfilePref = mPreferenceGroup.findPreference(KEY_VIDEO_ENCODER_PROFILE); @@ -1696,12 +1710,14 @@ public class SettingsManager implements ListMenu.SettingsListener { rate = String.valueOf(r.getUpper()); supported.add("hfr" + rate); supported.add("hsr" + rate); + mIsHFRSupportedOnCurrentResolution = true; } } } } } catch (IllegalArgumentException ex) { Log.w(TAG, "HFR is not supported for this resolution " + ex); + mIsHFRSupportedOnCurrentResolution = false; } if (mExtendedHFRSize != null && mExtendedHFRSize.length >= 3) { for (int i = 0; i < mExtendedHFRSize.length; i += 3) { @@ -2117,6 +2133,16 @@ public class SettingsManager implements ListMenu.SettingsListener { .get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); } + private List<String> getSupportedSavePaths(int cameraId) { + boolean writeable = SDCard.instance().isWriteable(); + List<String> savePaths = new ArrayList<>(); + savePaths.add("" + 0); + if (writeable) { + savePaths.add("" + 1); + } + return savePaths; + } + private List<String> getSupportedWhiteBalanceModes(int cameraId) { try { List<String> modes = new ArrayList<>(); diff --git a/src/com/android/camera/Storage.java b/src/com/android/camera/Storage.java index db37b10a0..9c8f92af4 100644..100755 --- a/src/com/android/camera/Storage.java +++ b/src/com/android/camera/Storage.java @@ -23,11 +23,9 @@ import java.io.IOException; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.ContentValues; -import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.location.Location; -import android.media.MediaScannerConnection; import android.net.Uri; import android.os.Build; import android.os.Environment; @@ -64,6 +62,16 @@ public class Storage { public static final long UNKNOWN_SIZE = -3L; public static final long LOW_STORAGE_THRESHOLD_BYTES = 60 * 1024 * 1024L; + private static boolean sSaveSDCard = false; + + public static boolean isSaveSDCard() { + return sSaveSDCard; + } + + public static void setSaveSDCard(boolean saveSDCard) { + sSaveSDCard = saveSDCard; + } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private static void setImageSize(ContentValues values, int width, int height) { // The two fields are available since ICS but got published in JB @@ -110,12 +118,19 @@ public class Storage { } // Save the image with a given mimeType and add it the MediaStore. - public static Uri addImage(Context context, String title, - ExifInterface exif, byte[] jpeg, String mimeType) { + public static Uri addImage(ContentResolver resolver, String title, long date, + Location location, int orientation, ExifInterface exif, byte[] jpeg, int width, + int height, String mimeType) { String path = generateFilepath(title, mimeType); - writeFile(path, jpeg, exif, mimeType); - return addImage(context, path); + int size = writeFile(path, jpeg, exif, mimeType); + // Try to get the real image size after add exif. + File f = new File(path); + if (f.exists() && f.isFile()) { + size = (int) f.length(); + } + return addImage(resolver, title, date, location, orientation, exif, + size, path, width, height, mimeType); } // Get a ContentValues object for the given photo data @@ -168,15 +183,26 @@ public class Storage { } // Add the image to media store. - public static Uri addImage(Context context, String path) { - // Scan file - MediaScannerConnection.scanFile( - context, - new String[] { path }, - null /* no mimetype, let scanner guess */, - null /* no callback */); - - return Uri.fromFile(new File(path)); + public static Uri addImage(ContentResolver resolver, String title, + long date, Location location, int orientation, ExifInterface exif,int jpegLength, + String path, int width, int height, String mimeType) { + // Insert into MediaStore. + ContentValues values = + getContentValuesForData(title, date, location, orientation, exif, jpegLength, path, + width, height, mimeType); + + return insertImage(resolver, values); + } + + public static Uri addImage(ContentResolver resolver, String title, + long date, Location location, int orientation,int jpegLength, + String path, int width, int height, String mimeType) { + // Insert into MediaStore. + ContentValues values = + getContentValuesForData(title, date, location, orientation, null, jpegLength, + path, width, height, mimeType); + + return insertImage(resolver, values); } public static long addRawImage(String title, byte[] data, @@ -191,20 +217,32 @@ public class Storage { return size; } + public static Uri addHeifImage(ContentResolver resolver, String title, long date, + Location location, int orientation, ExifInterface exif, String path, int width, + int height, int quality, String mimeType) { + File f = new File(path); + int size = 0; + if (f.exists() && f.isFile()) { + size = (int) f.length(); + } + return addImage(resolver, title, date, location, orientation, + size, path, width, height, mimeType); + } + // Overwrites the file and updates the MediaStore, or inserts the image if // one does not already exist. - public static void updateImage(Uri imageUri, Context context, String title, long date, + public static void updateImage(Uri imageUri, ContentResolver resolver, String title, long date, Location location, int orientation, ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) { String path = generateFilepath(title, mimeType); writeFile(path, jpeg, exif, mimeType); - updateImage(imageUri, context, title, date, location, orientation, jpeg.length, path, + updateImage(imageUri, resolver, title, date, location, orientation, jpeg.length, path, width, height, mimeType); } // Updates the image values in MediaStore, or inserts the image if one does // not already exist. - public static void updateImage(Uri imageUri, Context context, String title, + public static void updateImage(Uri imageUri, ContentResolver resolver, String title, long date, Location location, int orientation, int jpegLength, String path, int width, int height, String mimeType) { @@ -213,7 +251,6 @@ public class Storage { width, height, mimeType); // Update the MediaStore - ContentResolver resolver = context.getContentResolver(); int rowsModified = resolver.update(imageUri, values, null, null); if (rowsModified == 0) { @@ -225,13 +262,6 @@ public class Storage { throw new IllegalStateException("Bad number of rows (" + rowsModified + ") updated for uri: " + imageUri); } - - // Scan file - MediaScannerConnection.scanFile( - context, - new String[] { path }, - null /* no mimetype, let scanner guess */, - null /* no callback */); } public static void deleteImage(ContentResolver resolver, Uri uri) { @@ -252,10 +282,18 @@ public class Storage { }else if(pictureFormat.equalsIgnoreCase("heics")) { suffix = ".heics"; } - return DIRECTORY + '/' + title + suffix; + if (isSaveSDCard() && SDCard.instance().isWriteable()) { + return SDCard.instance().getDirectory() + '/' + title + suffix; + } else { + return DIRECTORY + '/' + title + suffix; + } } else if (pictureFormat.equalsIgnoreCase("yuv")){ String suffix = ".yuv"; - return DIRECTORY + '/' + title + suffix; + if (isSaveSDCard() && SDCard.instance().isWriteable()) { + return SDCard.instance().getDirectory() + '/' + title + suffix; + } else { + return DIRECTORY + '/' + title + suffix; + } } else { File dir = new File(RAW_DIRECTORY); dir.mkdirs(); @@ -263,7 +301,22 @@ public class Storage { } } - public static long getAvailableSpace() { + private static long getSDCardAvailableSpace() { + if (SDCard.instance().isWriteable()) { + File dir = new File(SDCard.instance().getDirectory()); + dir.mkdirs(); + try { + StatFs stat = new StatFs(SDCard.instance().getDirectory()); + long ret = stat.getAvailableBlocks() * (long) stat.getBlockSize(); + return ret; + } catch (Exception e) { + } + return UNKNOWN_SIZE; + } + return UNKNOWN_SIZE; + } + + private static long getInternalStorageAvailableSpace() { String state = Environment.getExternalStorageState(); Log.d(TAG, "External storage state=" + state); if (Environment.MEDIA_CHECKING.equals(state)) { @@ -288,6 +341,14 @@ public class Storage { return UNKNOWN_SIZE; } + public static long getAvailableSpace() { + if (isSaveSDCard()) { + return getSDCardAvailableSpace(); + } else { + return getInternalStorageAvailableSpace(); + } + } + public static long getTotalSpace(){ String state = Environment.getExternalStorageState(); Log.d(TAG, "External storage state=" + state); @@ -313,6 +374,16 @@ public class Storage { return UNKNOWN_SIZE; } + public static boolean switchSavePath() { + if (!isSaveSDCard() + && getInternalStorageAvailableSpace() <= LOW_STORAGE_THRESHOLD_BYTES + && getSDCardAvailableSpace() > LOW_STORAGE_THRESHOLD_BYTES) { + setSaveSDCard(true); + return true; + } + return false; + } + /** * OSX requires plugged-in USB storage to have path /DCIM/NNNAAAAA to be * imported. This is a temporary fix for bug#1655552. diff --git a/src/com/android/camera/Thumbnail.java b/src/com/android/camera/Thumbnail.java index 5f8483d6c..5f8483d6c 100755..100644 --- a/src/com/android/camera/Thumbnail.java +++ b/src/com/android/camera/Thumbnail.java diff --git a/src/com/android/camera/TsMakeupManager.java b/src/com/android/camera/TsMakeupManager.java index b58c0c1c0..b58c0c1c0 100755..100644 --- a/src/com/android/camera/TsMakeupManager.java +++ b/src/com/android/camera/TsMakeupManager.java diff --git a/src/com/android/camera/VideoMenu.java b/src/com/android/camera/VideoMenu.java index 6c2918e77..1bde44a8c 100644..100755 --- a/src/com/android/camera/VideoMenu.java +++ b/src/com/android/camera/VideoMenu.java @@ -115,6 +115,7 @@ public class VideoMenu extends MenuController CameraSettings.KEY_VIDEOCAMERA_FLASH_MODE, CameraSettings.KEY_RECORD_LOCATION, CameraSettings.KEY_VIDEO_QUALITY, + CameraSettings.KEY_CAMERA_SAVEPATH, CameraSettings.KEY_EXPOSURE, CameraSettings.KEY_WHITE_BALANCE, CameraSettings.KEY_VIDEOCAMERA_FOCUS_MODE, @@ -129,6 +130,7 @@ public class VideoMenu extends MenuController CameraSettings.KEY_RECORD_LOCATION, CameraSettings.KEY_VIDEO_QUALITY, CameraSettings.KEY_VIDEO_SNAPSHOT_SIZE, + CameraSettings.KEY_CAMERA_SAVEPATH, CameraSettings.KEY_FACE_DETECTION, CameraSettings.KEY_EXPOSURE, CameraSettings.KEY_WHITE_BALANCE, diff --git a/src/com/android/camera/VideoModule.java b/src/com/android/camera/VideoModule.java index d59184592..b74458897 100644..100755 --- a/src/com/android/camera/VideoModule.java +++ b/src/com/android/camera/VideoModule.java @@ -108,6 +108,8 @@ public class VideoModule implements CameraModule, private static final int SCREEN_DELAY = 2 * 60 * 1000; + private static final int SDCARD_SIZE_LIMIT = 4000 * 1024 * 1024; + private static final long SHUTTER_BUTTON_TIMEOUT = 0L; // 0ms /** @@ -135,6 +137,7 @@ public class VideoModule implements CameraModule, private ComboPreferences mPreferences; private PreferenceGroup mPreferenceGroup; + private boolean mSaveToSDCard = false; // Preference must be read before starting preview. We check this before starting // preview. @@ -539,6 +542,9 @@ public class VideoModule implements CameraModule, mContentResolver = mActivity.getContentResolver(); + Storage.setSaveSDCard( + mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); + mSaveToSDCard = Storage.isSaveSDCard(); // Surface texture is from camera screen nail and startPreview needs it. // This must be done before startPreview. mIsVideoCaptureIntent = isVideoCaptureIntent(); @@ -1198,6 +1204,13 @@ public class VideoModule implements CameraModule, } @Override + public void onSwitchSavePath() { + mUI.setPreference(CameraSettings.KEY_CAMERA_SAVEPATH, "1"); + RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard, + Toast.LENGTH_SHORT).show(); + } + + @Override public void installIntentFilter() { if(mReceiver != null) return; @@ -1782,6 +1795,10 @@ public class VideoModule implements CameraModule, maxFileSize = requestedSizeLimit; } + if (Storage.isSaveSDCard() && maxFileSize > SDCARD_SIZE_LIMIT) { + maxFileSize = SDCARD_SIZE_LIMIT; + } + try { mMediaRecorder.setMaxFileSize(maxFileSize); } catch (RuntimeException exception) { @@ -1849,7 +1866,12 @@ public class VideoModule implements CameraModule, // Used when emailing. String filename = title + convertOutputFormatToFileExt(outputFileFormat); String mime = convertOutputFormatToMimeType(outputFileFormat); - String path = Storage.DIRECTORY + '/' + filename; + String path = null; + if (Storage.isSaveSDCard() && SDCard.instance().isWriteable()) { + path = SDCard.instance().getDirectory() + '/' + filename; + } else { + path = Storage.DIRECTORY + '/' + filename; + } mCurrentVideoValues = new ContentValues(9); mCurrentVideoValues.put(Video.Media.TITLE, title); mCurrentVideoValues.put(Video.Media.DISPLAY_NAME, filename); @@ -1927,7 +1949,7 @@ public class VideoModule implements CameraModule, stopVideoRecording(); mUI.showUIafterRecording(); if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { - // We may have run out of storage space. + // We may have run out of space on the sdcard. mActivity.updateStorageSpaceAndHint(); } } @@ -3046,6 +3068,8 @@ public class VideoModule implements CameraModule, setCameraParameters(false); } mRestartPreview = false; + Storage.setSaveSDCard( + mPreferences.getString(CameraSettings.KEY_CAMERA_SAVEPATH, "0").equals("1")); mActivity.updateStorageSpaceAndHint(); mActivity.initPowerShutter(mPreferences); mActivity.initMaxBrightness(mPreferences); diff --git a/src/com/android/camera/VideoUI.java b/src/com/android/camera/VideoUI.java index 712cfeeaf..712cfeeaf 100755..100644 --- a/src/com/android/camera/VideoUI.java +++ b/src/com/android/camera/VideoUI.java diff --git a/src/com/android/camera/WideAnglePanoramaModule.java b/src/com/android/camera/WideAnglePanoramaModule.java index d66acd186..e1edcad6b 100644..100755 --- a/src/com/android/camera/WideAnglePanoramaModule.java +++ b/src/com/android/camera/WideAnglePanoramaModule.java @@ -49,6 +49,7 @@ import com.android.camera.CameraManager.CameraProxy; import com.android.camera.app.OrientationManager; import com.android.camera.data.LocalData; import com.android.camera.exif.ExifInterface; +import com.android.camera.ui.RotateTextToast; import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; import org.codeaurora.snapcam.R; @@ -886,7 +887,9 @@ public class WideAnglePanoramaModule Log.e(TAG, "Cannot set exif for " + filepath, e); Storage.writeFile(filepath, jpegData); } - return Storage.addImage(mActivity, filepath); + int jpegLength = (int) (new File(filepath).length()); + return Storage.addImage(mContentResolver, filename, mTimeTaken, loc, orientation, + jpegLength, filepath, width, height, LocalData.MIME_TYPE_JPEG); } return null; } @@ -1013,6 +1016,13 @@ public class WideAnglePanoramaModule } @Override + public void onSwitchSavePath() { + mPreferences.getGlobal().edit().putString(CameraSettings.KEY_CAMERA_SAVEPATH, "1").apply(); + RotateTextToast.makeText(mActivity, R.string.on_switch_save_path_to_sdcard, + Toast.LENGTH_SHORT).show(); + } + + @Override public void onResumeBeforeSuper() { mPaused = false; mPreferences = ComboPreferences.get(mActivity); diff --git a/src/com/android/camera/WideAnglePanoramaUI.java b/src/com/android/camera/WideAnglePanoramaUI.java index 9a55972c3..9a55972c3 100755..100644 --- a/src/com/android/camera/WideAnglePanoramaUI.java +++ b/src/com/android/camera/WideAnglePanoramaUI.java diff --git a/src/com/android/camera/app/CameraApp.java b/src/com/android/camera/app/CameraApp.java index ef36f2656..dc5825cb1 100644 --- a/src/com/android/camera/app/CameraApp.java +++ b/src/com/android/camera/app/CameraApp.java @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.app.Application; import android.content.Context; +import com.android.camera.SDCard; import com.android.camera.util.CameraUtil; import com.android.camera.util.UsageStatistics; import com.android.camera.SettingsManager; @@ -44,6 +45,7 @@ public class CameraApp extends Application { SettingsManager.createInstance(this); UsageStatistics.initialize(this); CameraUtil.initialize(this); + SDCard.initialize(this); } public static Context getContext() { diff --git a/src/com/android/camera/app/PlaceholderManager.java b/src/com/android/camera/app/PlaceholderManager.java index e9a99a6b1..326f0be80 100644 --- a/src/com/android/camera/app/PlaceholderManager.java +++ b/src/com/android/camera/app/PlaceholderManager.java @@ -147,7 +147,8 @@ public class PlaceholderManager implements ImageTaskManager { } Uri uri = - Storage.addImage(mContext, null, null, null, PLACEHOLDER_MIME_TYPE); + Storage.addImage(mContext.getContentResolver(), title, timestamp, null, 0, null, + placeholder, width, height, PLACEHOLDER_MIME_TYPE); if (uri == null) { return null; @@ -166,7 +167,7 @@ public class PlaceholderManager implements ImageTaskManager { public void replacePlaceholder(Session session, Location location, int orientation, ExifInterface exif, byte[] jpeg, int width, int height, String mimeType) { - Storage.updateImage(session.outputUri, mContext, session.outputTitle, + Storage.updateImage(session.outputUri, mContext.getContentResolver(), session.outputTitle, session.time, location, orientation, exif, jpeg, width, height, mimeType); synchronized (mListenerRefs) { diff --git a/src/com/android/camera/data/Camera2ModeAdapter.java b/src/com/android/camera/data/Camera2ModeAdapter.java index 039180b91..086e9f779 100755..100644 --- a/src/com/android/camera/data/Camera2ModeAdapter.java +++ b/src/com/android/camera/data/Camera2ModeAdapter.java @@ -34,18 +34,22 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; -import android.widget.TextView; + +import com.android.camera.ui.RotateImageView; + import org.codeaurora.snapcam.R; import java.util.List; public class Camera2ModeAdapter extends RecyclerView.Adapter<Camera2ModeAdapter.ViewHolder> { private List<String> mModeList; + private List<Integer> mIcons; private int mSelectedPos = 2; private OnItemClickListener mOnItemClickListener; - public Camera2ModeAdapter(List<String> list) { + public Camera2ModeAdapter(List<String> list, List<Integer> icons) { this.mModeList = list; + this.mIcons = icons; } public void setOnItemClickListener(OnItemClickListener listener) { @@ -62,9 +66,9 @@ public class Camera2ModeAdapter extends RecyclerView.Adapter<Camera2ModeAdapter. @Override public void onBindViewHolder(ViewHolder holder, int position) { - holder.mCameraModeText.setText(mModeList.get(position)); - holder.mCameraModeText.setSelected(mSelectedPos == position); - holder.mCameraModeText.setOnClickListener(new View.OnClickListener() { + holder.mCameraModeIcon.setSelected(mSelectedPos == position); + holder.mCameraModeIcon.setImageResource(mIcons.get(position)); + holder.mCameraModeIcon.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnItemClickListener.onItemClick(position) >= 0) { @@ -90,12 +94,11 @@ public class Camera2ModeAdapter extends RecyclerView.Adapter<Camera2ModeAdapter. } class ViewHolder extends RecyclerView.ViewHolder{ - - protected TextView mCameraModeText; + protected RotateImageView mCameraModeIcon; public ViewHolder(View itemView) { super(itemView); - mCameraModeText = (TextView) itemView.findViewById(R.id.mode_text); + mCameraModeIcon = itemView.findViewById(R.id.mode_icon); } } -}
\ No newline at end of file +} diff --git a/src/com/android/camera/data/CameraDataAdapter.java b/src/com/android/camera/data/CameraDataAdapter.java index 8b6b03e54..95c1ea723 100644..100755 --- a/src/com/android/camera/data/CameraDataAdapter.java +++ b/src/com/android/camera/data/CameraDataAdapter.java @@ -27,6 +27,7 @@ import android.provider.MediaStore; import android.util.Log; import android.view.View; +import com.android.camera.SDCard; import com.android.camera.Storage; import com.android.camera.app.PlaceholderManager; import com.android.camera.ui.FilmStripView.ImageData; @@ -279,7 +280,8 @@ public class CameraDataAdapter implements LocalDataAdapter { } private static String[] getCameraPath() { - String[] cameraPath = { Storage.DIRECTORY + "/%" }; + String[] cameraPath = + {Storage.DIRECTORY + "/%", SDCard.instance().getDirectory() + "/%"}; return cameraPath; } diff --git a/src/com/android/camera/deepportrait/CamGLRenderObserver.java b/src/com/android/camera/deepportrait/CamGLRenderObserver.java index 53faa4543..53faa4543 100755..100644 --- a/src/com/android/camera/deepportrait/CamGLRenderObserver.java +++ b/src/com/android/camera/deepportrait/CamGLRenderObserver.java diff --git a/src/com/android/camera/deepportrait/CamGLRenderer.java b/src/com/android/camera/deepportrait/CamGLRenderer.java index ef8e3b1f4..ef8e3b1f4 100755..100644 --- a/src/com/android/camera/deepportrait/CamGLRenderer.java +++ b/src/com/android/camera/deepportrait/CamGLRenderer.java diff --git a/src/com/android/camera/deepportrait/CamRenderShader.java b/src/com/android/camera/deepportrait/CamRenderShader.java index 5b80ed919..5b80ed919 100755..100644 --- a/src/com/android/camera/deepportrait/CamRenderShader.java +++ b/src/com/android/camera/deepportrait/CamRenderShader.java diff --git a/src/com/android/camera/deepportrait/CamRenderTexture.java b/src/com/android/camera/deepportrait/CamRenderTexture.java index c0b4108b3..c0b4108b3 100755..100644 --- a/src/com/android/camera/deepportrait/CamRenderTexture.java +++ b/src/com/android/camera/deepportrait/CamRenderTexture.java diff --git a/src/com/android/camera/deepportrait/DPImage.java b/src/com/android/camera/deepportrait/DPImage.java index 381270682..381270682 100755..100644 --- a/src/com/android/camera/deepportrait/DPImage.java +++ b/src/com/android/camera/deepportrait/DPImage.java diff --git a/src/com/android/camera/deepportrait/GLCameraPreview.java b/src/com/android/camera/deepportrait/GLCameraPreview.java index 7d62faebf..7d62faebf 100755..100644 --- a/src/com/android/camera/deepportrait/GLCameraPreview.java +++ b/src/com/android/camera/deepportrait/GLCameraPreview.java diff --git a/src/com/android/camera/imageprocessor/FrameProcessor.java b/src/com/android/camera/imageprocessor/FrameProcessor.java index cd1a5f8a2..cd1a5f8a2 100755..100644 --- a/src/com/android/camera/imageprocessor/FrameProcessor.java +++ b/src/com/android/camera/imageprocessor/FrameProcessor.java diff --git a/src/com/android/camera/imageprocessor/PostProcessor.java b/src/com/android/camera/imageprocessor/PostProcessor.java index d12955ada..d12955ada 100755..100644 --- a/src/com/android/camera/imageprocessor/PostProcessor.java +++ b/src/com/android/camera/imageprocessor/PostProcessor.java diff --git a/src/com/android/camera/imageprocessor/ZSLQueue.java b/src/com/android/camera/imageprocessor/ZSLQueue.java index 8bcba71db..8bcba71db 100755..100644 --- a/src/com/android/camera/imageprocessor/ZSLQueue.java +++ b/src/com/android/camera/imageprocessor/ZSLQueue.java diff --git a/src/com/android/camera/imageprocessor/filter/DeepPortraitFilter.java b/src/com/android/camera/imageprocessor/filter/DeepPortraitFilter.java index 6e98824d3..6e98824d3 100755..100644 --- a/src/com/android/camera/imageprocessor/filter/DeepPortraitFilter.java +++ b/src/com/android/camera/imageprocessor/filter/DeepPortraitFilter.java diff --git a/src/com/android/camera/imageprocessor/filter/DeepZoomFilter.java b/src/com/android/camera/imageprocessor/filter/DeepZoomFilter.java index 9e271ae17..9e271ae17 100755..100644 --- a/src/com/android/camera/imageprocessor/filter/DeepZoomFilter.java +++ b/src/com/android/camera/imageprocessor/filter/DeepZoomFilter.java diff --git a/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java b/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java index 0c588b976..0c588b976 100755..100644 --- a/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java +++ b/src/com/android/camera/imageprocessor/filter/UbifocusFilter.java diff --git a/src/com/android/camera/multi/MultiCamera.java b/src/com/android/camera/multi/MultiCamera.java index b63b4ab32..a934ec712 100755..100644 --- a/src/com/android/camera/multi/MultiCamera.java +++ b/src/com/android/camera/multi/MultiCamera.java @@ -1,47 +1,47 @@ -/*
-Copyright (c) 2020, The Linux Foundation. All rights reserved.
-
-Not a Contribution.
-
-Copyright (C) 2013 The Android Open Source Project
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-package com.android.camera.multi;
-
-public interface MultiCamera {
-
- boolean openCamera(String[] id);
-
- void startPreview();
-
- void closeSession();
-
- void closeCamera();
-
- void onShutterButtonClick(String[] ids);
-
- void onVideoButtonClick(String[] ids);
-
- void onPause();
-
- void onResume(String[] ids);
-
- void onButtonPause(String[] ids);
-
- void onButtonContinue(String[] ids);
-
- void onOrientationChanged(int orientation);
-
- boolean isRecordingVideo();
+/* +Copyright (c) 2020, The Linux Foundation. All rights reserved. + +Not a Contribution. + +Copyright (C) 2013 The Android Open Source Project + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package com.android.camera.multi; + +public interface MultiCamera { + + boolean openCamera(String[] id); + + void startPreview(); + + void closeSession(); + + void closeCamera(); + + void onShutterButtonClick(String[] ids); + + void onVideoButtonClick(String[] ids); + + void onPause(); + + void onResume(String[] ids); + + void onButtonPause(String[] ids); + + void onButtonContinue(String[] ids); + + void onOrientationChanged(int orientation); + + boolean isRecordingVideo(); }
\ No newline at end of file diff --git a/src/com/android/camera/multi/MultiCameraModule.java b/src/com/android/camera/multi/MultiCameraModule.java index 9f5745225..10ed17f93 100644 --- a/src/com/android/camera/multi/MultiCameraModule.java +++ b/src/com/android/camera/multi/MultiCameraModule.java @@ -1,655 +1,674 @@ -/*
- * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Not a Contribution.
- *
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.camera.multi;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCharacteristics;
-import android.media.AudioManager;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.view.WindowManager;
-import android.view.View;
-import android.view.KeyEvent;
-import android.view.OrientationEventListener;
-import android.util.Log;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.Set;
-
-import com.android.camera.CameraModule;
-import com.android.camera.CameraActivity;
-import com.android.camera.MediaSaveService;
-import com.android.camera.PhotoController;
-import com.android.camera.util.CameraUtil;
-import com.android.camera.data.Camera2ModeAdapter.OnItemClickListener;
-
-public class MultiCameraModule implements CameraModule, PhotoController {
-
- private static final String TAG = "SnapCam_MultiCameraModule";
- private static final int MAX_NUM_CAM = 16;
-
- private static final int OPEN_CAMERA = 0;
-
- public static final int CLEAR_SCREEN_DELAY = 101;
-
- private View mRootView;
- private MultiCameraUI mUI;
- private CameraActivity mActivity;
- private MultiCamera mMultiCamera;
-
- /**
- * An additional thread for running tasks that shouldn't block the UI.
- */
- private HandlerThread mCameraThread;
- private HandlerThread mCaptureCallbackThread;
-
- private Handler mCameraHandler;
- private Handler mCaptureCallbackHandler;
-
- private final Handler mHandler = new MainHandler();
-
- private int mDisplayRotation;
- private int mDisplayOrientation;
-
- private boolean mCameraModeSwitcherAllowed = true;
- private int mNextModeIndex = 1;
- private int mCurrentModeIndex = 1;
-
- // The degrees of the device rotated clockwise from its natural orientation.
- private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
-
- private boolean mIsMute = false;
-
- private boolean[] mTakingPicture = new boolean[MAX_NUM_CAM];
-
- private ArrayList<SceneModule> mSceneCameraIds = new ArrayList<>();
- private String[] mSelectableModes = {"Video", "Photo"};
- private SceneModule mCurrentSceneMode;
- private String[] mCameraIds = new String[2];
-
- public static int CURRENT_ID = 0;
- public static CameraMode CURRENT_MODE = CameraMode.DEFAULT;
-
- public enum CameraMode {
- VIDEO,
- DEFAULT
- }
-
- public Handler getMainHandler() {
- return mHandler;
- }
-
- public Handler getMyCameraHandler() {
- return mCameraHandler;
- }
-
- public void reinit() {
- }
-
- public List<String> getCameraModeList() {
- ArrayList<String> cameraModes = new ArrayList<>();
- for (SceneModule sceneModule : mSceneCameraIds) {
- cameraModes.add(mSelectableModes[sceneModule.mode.ordinal()]);
- }
- return cameraModes;
- }
-
- public OnItemClickListener getModeItemClickListener() {
- return new OnItemClickListener() {
- @Override
- public int onItemClick(int mode) {
- Log.v(TAG, " onItemClick mode :" + mode);
- if (!getCameraModeSwitcherAllowed()) {
- return -1;
- }
- return selectCameraMode(mode);
- }
- };
- }
-
- public int selectCameraMode(int mode) {
- if (mCurrentSceneMode.mode == mSceneCameraIds.get(mode).mode) {
- return -1;
- }
- setCameraModeSwitcherAllowed(true);
- setNextSceneMode(mode);
- mCurrentSceneMode = mSceneCameraIds.get(mode);
- SceneModule nextSceneMode = mSceneCameraIds.get(mode);
- restartAll();
- return 1;
- }
-
- public void setNextSceneMode(int index) {
- mNextModeIndex = index;
- }
-
- public void setCameraModeSwitcherAllowed(boolean allow) {
- mCameraModeSwitcherAllowed = allow;
- }
-
- public int getCurrentModeIndex() {
- return mCurrentModeIndex;
- }
-
- public void setCurrentSceneModeOnly(int mode) {
- mCurrentSceneMode = mSceneCameraIds.get(mode);
- mCurrentModeIndex = mode;
- CURRENT_ID = mCurrentSceneMode.getCurrentId();
- CURRENT_MODE = mCurrentSceneMode.mode;
- }
-
- public void restartAll() {
- Log.d(TAG, "restart all");
- onPauseBeforeSuper();
- onPauseAfterSuper();
- setCurrentSceneModeOnly(mNextModeIndex);
- updateMultiCamera();
- onResumeBeforeSuper();
- onResumeAfterSuper();
- }
-
- private void updateMultiCamera() {
- mMultiCamera = null;
- if (mCurrentSceneMode.mode == CameraMode.VIDEO) {
- mMultiCamera = new MultiVideoModule(mActivity, mUI, this);
- } else if (mCurrentSceneMode.mode == CameraMode.DEFAULT) {
- mMultiCamera = new MultiCaptureModule(mActivity, mUI, this);
- }
- }
-
- public boolean getCameraModeSwitcherAllowed() {
- return mCameraModeSwitcherAllowed;
- }
-
- public void onVideoButtonClick() {
- Log.d(TAG, "onVideoButtonClick");
- mMultiCamera.onVideoButtonClick(mCameraIds);
- }
-
- public boolean isTakingPicture() {
- for (int i = 0; i < mTakingPicture.length; i++) {
- if (mTakingPicture[i]) return true;
- }
- return false;
- }
-
- public boolean isRecordingVideo() {
- return mMultiCamera.isRecordingVideo();
- }
-
- public void updateTakingPicture() {
- for (int i = 0; i < mTakingPicture.length; i++) {
- mTakingPicture[i] = false;
- }
- }
-
- public void setMute(boolean enable, boolean isValue) {
- AudioManager am = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE);
- am.setMicrophoneMute(enable);
- if (isValue) {
- mIsMute = enable;
- }
- }
-
- public boolean isAudioMute() {
- return mIsMute;
- }
-
- public void onButtonPause() {
- Log.v(TAG, "onButtonPause");
- mMultiCamera.onButtonPause(mCameraIds);
- }
-
- public void onButtonContinue() {
- Log.v(TAG, "onButtonContinue");
- mMultiCamera.onButtonContinue(mCameraIds);
- }
-
- public int[] getOpenCameraIdList() {
- int[] result = new int[2];
- result[0] = 0;
- result[1] = 2;
- return result;
- }
-
- public CameraMode getCurrenCameraMode() {
- if (mCurrentSceneMode == null) {
- Log.w(TAG, "getCurrenCameraMode mCurrentSceneMode is NULL retrun CameraMode.DEFAULT");
- return CameraMode.DEFAULT;
- } else {
- return mCurrentSceneMode.mode;
- }
- }
-
- @Override
- public void init(CameraActivity activity, View parent) {
- SceneModule module;
- Log.v(TAG, " init ");
- for (int i = 0; i < mSelectableModes.length; i++) {
- module = new SceneModule();
- module.mode = CameraMode.values()[i];
- mSceneCameraIds.add(module);
- }
-
- for (int i = 0; i < MAX_NUM_CAM; i++) {
- mTakingPicture[i] = false;
- }
-
- mCurrentSceneMode = mSceneCameraIds.get(1);
-
- mActivity = activity;
- mRootView = parent;
- if (mUI == null) {
- mUI = new MultiCameraUI(activity, this, parent);
- }
- mMultiCamera = new MultiCaptureModule(mActivity, mUI, this);
- startBackgroundThread();
- initCameraIds();
- }
-
- @Override
- public void onPreviewFocusChanged(boolean previewFocused) {
-
- }
-
- @Override
- public void onResumeBeforeSuper() {
- for (int i = 0; i < MAX_NUM_CAM; i++) {
- mTakingPicture[i] = false;
- }
- startBackgroundThread();
- }
-
- @Override
- public void onResumeAfterSuper() {
- Log.d(TAG, " onResumeAfterSuper ");
- mCameraIds[0] = "0";
- mCameraIds[1] = "2";
- mMultiCamera.onResume(mCameraIds);
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- mUI.showRelatedIcons(mCurrentSceneMode.mode);
- }
-
- @Override
- public void onPauseBeforeSuper() {
- Log.d(TAG, " onPauseBeforeSuper ");
- mMultiCamera.onPause();
- }
-
- @Override
- public void onPauseAfterSuper() {
- stopBackgroundThread();
- }
-
- @Override
- public void onConfigurationChanged(Configuration config) {
-
- }
-
- @Override
- public void onStop() {
-
- }
-
- @Override
- public void onDestroy() {
-
- }
-
- @Override
- public void installIntentFilter() {
-
- }
-
- @Override
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
-
- }
-
- @Override
- public boolean onBackPressed() {
- return false;
- }
-
- @Override
- public boolean onKeyDown(int keyCode, KeyEvent event) {
- return false;
- }
-
- @Override
- public boolean onKeyUp(int keyCode, KeyEvent event) {
- return false;
- }
-
- @Override
- public void onSingleTapUp(View view, int x, int y) {
-
- }
-
- @Override
- public void onLongPress(View view, int x, int y) {
- }
-
- @Override
- public void onPreviewTextureCopied() {
-
- }
-
- @Override
- public void onCaptureTextureCopied() {
-
- }
-
- @Override
- public void onUserInteraction() {
-
- }
-
- @Override
- public boolean updateStorageHintOnResume() {
- return false;
- }
-
- @Override
- public void onOrientationChanged(int orientation) {
- // We keep the last known orientation. So if the user first orient
- // the camera then point the camera to floor or sky, we still have
- // the correct orientation.
- if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return;
- int oldOrientation = mOrientation;
- mOrientation = CameraUtil.roundOrientation(orientation, mOrientation);
- if (oldOrientation != mOrientation) {
- mUI.setOrientation(mOrientation, true);
- mMultiCamera.onOrientationChanged(mOrientation);
- }
-
- }
-
- @Override
- public void onShowSwitcherPopup() {
-
- }
-
- @Override
- public void onMediaSaveServiceConnected(MediaSaveService s) {
-
- }
-
- @Override
- public boolean arePreviewControlsVisible() {
- return false;
- }
-
- @Override
- public void resizeForPreviewAspectRatio() {
-
- }
-
- @Override
- public void waitingLocationPermissionResult(boolean waiting) {
-
- }
-
- @Override
- public void enableRecordingLocation(boolean enable) {
-
- }
-
- @Override
- public void setPreferenceForTest(String key, String value) {
-
- }
-
- @Override
- public int onZoomChanged(int requestedZoom) {
- return 0;
- }
-
- @Override
- public void onZoomChanged(float requestedZoom) {
- }
-
- @Override
- public boolean isImageCaptureIntent() {
- return false;
- }
-
- @Override
- public boolean isCameraIdle() {
- return true;
- }
-
- @Override
- public void onCaptureDone() {
-
- }
-
- @Override
- public void onCaptureCancelled() {
-
- }
-
- @Override
- public void onCaptureRetake() {
-
- }
-
- @Override
- public void cancelAutoFocus() {
-
- }
-
- @Override
- public void stopPreview() {
-
- }
-
- @Override
- public int getCameraState() {
- return 0;
- }
-
- @Override
- public void onCountDownFinished() {
- }
-
- @Override
- public void onScreenSizeChanged(int width, int height) {
-
- }
-
- @Override
- public void onPreviewRectChanged(Rect previewRect) {
-
- }
-
- @Override
- public void updateCameraOrientation() {
- if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) {
- setDisplayOrientation();
- }
- }
-
- @Override
- public void onPreviewUIReady() {
- }
-
- @Override
- public void onPreviewUIDestroyed() {
- }
-
- @Override
- public void onShutterButtonClick() {
- Log.d(TAG, "onShutterButtonClick");
- mMultiCamera.onShutterButtonClick(mCameraIds);
- for (String CameraId : mCameraIds) {
- int id = Integer.parseInt(CameraId);
- mTakingPicture[id] = true;
- }
- }
-
- @Override
- public void onShutterButtonLongClick() {
-
- }
-
- @Override
- public void onShutterButtonFocus(boolean pressed) {
-
- }
-
- private class SceneModule {
- CameraMode mode = CameraMode.DEFAULT;
- public int rearCameraId;
- public int frontCameraId;
- public int auxCameraId = 0;
- int getCurrentId() {
- return rearCameraId;
- }
- }
-
- /**
- * This Handler is used to post message back onto the main thread of the
- * application
- */
- private class MainHandler extends Handler {
- public MainHandler() {
- super(Looper.getMainLooper());
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case CLEAR_SCREEN_DELAY: {
- mActivity.getWindow().clearFlags(
- WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- break;
- }
- }
- }
- }
-
- private void initCameraIds() {
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- boolean isFirstDefault = true;
- String[] cameraIdList = null;
- try {
- cameraIdList = manager.getCameraIdList();
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- if (cameraIdList == null || cameraIdList.length == 0) {
- return;
- }
- for (int i = 0; i < cameraIdList.length; i++) {
- String cameraId = cameraIdList[i];
- CameraCharacteristics characteristics;
- try {
- characteristics = manager.getCameraCharacteristics(cameraId);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- continue;
- }
- Set<String> physicalCameraIds = characteristics.getPhysicalCameraIds();
- for (String camId : physicalCameraIds) {
- Log.v(TAG, "initCameraIds physicalCameraIds :" + physicalCameraIds);
- Log.v(TAG, "initCameraIds >>>> camId :" + camId + " cameraId :" + cameraId + ", i :" + i);
- }
- }
- }
-
- /**
- * Starts a background thread and its {@link Handler}.
- */
- private void startBackgroundThread() {
- if (mCameraThread == null) {
- mCameraThread = new HandlerThread("CameraBackground");
- mCameraThread.start();
- }
- if (mCaptureCallbackThread == null) {
- mCaptureCallbackThread = new HandlerThread("CameraCaptureCallback");
- mCaptureCallbackThread.start();
- }
-
- if (mCameraHandler == null) {
- mCameraHandler = new MyCameraHandler(mCameraThread.getLooper());
- }
- if (mCaptureCallbackHandler == null) {
- mCaptureCallbackHandler = new Handler(mCaptureCallbackThread.getLooper());
- }
- }
-
- private void stopBackgroundThread() {
- mCameraThread.quitSafely();
- try {
- mCameraThread.join();
- mCameraThread = null;
- mCameraHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
-
- mCaptureCallbackThread.quitSafely();
- try {
- mCaptureCallbackThread.join();
- mCaptureCallbackThread = null;
- mCaptureCallbackHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private class MyCameraHandler extends Handler {
-
- public MyCameraHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- int id = msg.arg1;
- switch (msg.what) {
- case OPEN_CAMERA:
- if (mMultiCamera != null) {
- mMultiCamera.openCamera(mCameraIds);
- }
- break;
- }
- }
- }
-
- private void setDisplayOrientation() {
- mDisplayRotation = CameraUtil.getDisplayRotation(mActivity);
- mDisplayOrientation = CameraUtil.getDisplayOrientationForCamera2(
- mDisplayRotation, 0);
- }
-
-}
+/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.multi; + +import android.content.Context; +import android.content.Intent; +import android.content.res.Configuration; +import android.content.res.TypedArray; +import android.graphics.Rect; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCharacteristics; +import android.media.AudioManager; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.view.WindowManager; +import android.view.View; +import android.view.KeyEvent; +import android.view.OrientationEventListener; +import android.util.Log; + +import java.util.List; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Set; + +import com.android.camera.CameraModule; +import com.android.camera.CameraActivity; +import com.android.camera.MediaSaveService; +import com.android.camera.PhotoController; +import com.android.camera.util.CameraUtil; +import com.android.camera.data.Camera2ModeAdapter.OnItemClickListener; + +import org.codeaurora.snapcam.R; + +public class MultiCameraModule implements CameraModule, PhotoController { + + private static final String TAG = "SnapCam_MultiCameraModule"; + private static final int MAX_NUM_CAM = 16; + + private static final int OPEN_CAMERA = 0; + + public static final int CLEAR_SCREEN_DELAY = 101; + + private View mRootView; + private MultiCameraUI mUI; + private CameraActivity mActivity; + private MultiCamera mMultiCamera; + + /** + * An additional thread for running tasks that shouldn't block the UI. + */ + private HandlerThread mCameraThread; + private HandlerThread mCaptureCallbackThread; + + private Handler mCameraHandler; + private Handler mCaptureCallbackHandler; + + private final Handler mHandler = new MainHandler(); + + private int mDisplayRotation; + private int mDisplayOrientation; + + private boolean mCameraModeSwitcherAllowed = true; + private int mNextModeIndex = 1; + private int mCurrentModeIndex = 1; + + // The degrees of the device rotated clockwise from its natural orientation. + private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; + + private boolean mIsMute = false; + + private boolean[] mTakingPicture = new boolean[MAX_NUM_CAM]; + + private ArrayList<SceneModule> mSceneCameraIds = new ArrayList<>(); + private String[] mSelectableModes = {"Video", "Photo"}; + private SceneModule mCurrentSceneMode; + private String[] mCameraIds = new String[2]; + + public static int CURRENT_ID = 0; + public static CameraMode CURRENT_MODE = CameraMode.DEFAULT; + + public enum CameraMode { + VIDEO, + DEFAULT + } + + public Handler getMainHandler() { + return mHandler; + } + + public Handler getMyCameraHandler() { + return mCameraHandler; + } + + public void reinit() { + } + + public List<String> getCameraModeList() { + ArrayList<String> cameraModes = new ArrayList<>(); + for (SceneModule sceneModule : mSceneCameraIds) { + cameraModes.add(mSelectableModes[sceneModule.mode.ordinal()]); + } + return cameraModes; + } + + public OnItemClickListener getModeItemClickListener() { + return new OnItemClickListener() { + @Override + public int onItemClick(int mode) { + Log.v(TAG, " onItemClick mode :" + mode); + if (!getCameraModeSwitcherAllowed()) { + return -1; + } + return selectCameraMode(mode); + } + }; + } + + public int selectCameraMode(int mode) { + if (mCurrentSceneMode.mode == mSceneCameraIds.get(mode).mode) { + return -1; + } + setCameraModeSwitcherAllowed(true); + setNextSceneMode(mode); + mCurrentSceneMode = mSceneCameraIds.get(mode); + SceneModule nextSceneMode = mSceneCameraIds.get(mode); + restartAll(); + return 1; + } + + public void setNextSceneMode(int index) { + mNextModeIndex = index; + } + + public void setCameraModeSwitcherAllowed(boolean allow) { + mCameraModeSwitcherAllowed = allow; + } + + public int getCurrentModeIndex() { + return mCurrentModeIndex; + } + + public List<Integer> getCameraModeIconList() { + ArrayList<Integer> cameraModeIcons = new ArrayList<>(); + TypedArray ic = mActivity.getResources() + .obtainTypedArray(R.array.camera_modes_front); + for (SceneModule sceneModule : mSceneCameraIds) { + cameraModeIcons.add(ic.getResourceId(sceneModule.mode.ordinal(), 0)); + } + return cameraModeIcons; + } + + public void setCurrentSceneModeOnly(int mode) { + mCurrentSceneMode = mSceneCameraIds.get(mode); + mCurrentModeIndex = mode; + CURRENT_ID = mCurrentSceneMode.getCurrentId(); + CURRENT_MODE = mCurrentSceneMode.mode; + } + + public void restartAll() { + Log.d(TAG, "restart all"); + onPauseBeforeSuper(); + onPauseAfterSuper(); + setCurrentSceneModeOnly(mNextModeIndex); + updateMultiCamera(); + onResumeBeforeSuper(); + onResumeAfterSuper(); + } + + private void updateMultiCamera() { + mMultiCamera = null; + if (mCurrentSceneMode.mode == CameraMode.VIDEO) { + mMultiCamera = new MultiVideoModule(mActivity, mUI, this); + } else if (mCurrentSceneMode.mode == CameraMode.DEFAULT) { + mMultiCamera = new MultiCaptureModule(mActivity, mUI, this); + } + } + + public boolean getCameraModeSwitcherAllowed() { + return mCameraModeSwitcherAllowed; + } + + public void onVideoButtonClick() { + Log.d(TAG, "onVideoButtonClick"); + mMultiCamera.onVideoButtonClick(mCameraIds); + } + + public boolean isTakingPicture() { + for (int i = 0; i < mTakingPicture.length; i++) { + if (mTakingPicture[i]) return true; + } + return false; + } + + public boolean isRecordingVideo() { + return mMultiCamera.isRecordingVideo(); + } + + public void updateTakingPicture() { + for (int i = 0; i < mTakingPicture.length; i++) { + mTakingPicture[i] = false; + } + } + + public void setMute(boolean enable, boolean isValue) { + AudioManager am = (AudioManager) mActivity.getSystemService(Context.AUDIO_SERVICE); + am.setMicrophoneMute(enable); + if (isValue) { + mIsMute = enable; + } + } + + public boolean isAudioMute() { + return mIsMute; + } + + public void onButtonPause() { + Log.v(TAG, "onButtonPause"); + mMultiCamera.onButtonPause(mCameraIds); + } + + public void onButtonContinue() { + Log.v(TAG, "onButtonContinue"); + mMultiCamera.onButtonContinue(mCameraIds); + } + + public int[] getOpenCameraIdList() { + int[] result = new int[2]; + result[0] = 0; + result[1] = 2; + return result; + } + + public CameraMode getCurrenCameraMode() { + if (mCurrentSceneMode == null) { + Log.w(TAG, "getCurrenCameraMode mCurrentSceneMode is NULL retrun CameraMode.DEFAULT"); + return CameraMode.DEFAULT; + } else { + return mCurrentSceneMode.mode; + } + } + + @Override + public void init(CameraActivity activity, View parent) { + SceneModule module; + Log.v(TAG, " init "); + for (int i = 0; i < mSelectableModes.length; i++) { + module = new SceneModule(); + module.mode = CameraMode.values()[i]; + mSceneCameraIds.add(module); + } + + for (int i = 0; i < MAX_NUM_CAM; i++) { + mTakingPicture[i] = false; + } + + mCurrentSceneMode = mSceneCameraIds.get(1); + + mActivity = activity; + mRootView = parent; + if (mUI == null) { + mUI = new MultiCameraUI(activity, this, parent); + } + mMultiCamera = new MultiCaptureModule(mActivity, mUI, this); + startBackgroundThread(); + initCameraIds(); + } + + @Override + public void onPreviewFocusChanged(boolean previewFocused) { + + } + + @Override + public void onResumeBeforeSuper() { + for (int i = 0; i < MAX_NUM_CAM; i++) { + mTakingPicture[i] = false; + } + startBackgroundThread(); + } + + @Override + public void onResumeAfterSuper() { + Log.d(TAG, " onResumeAfterSuper "); + mCameraIds[0] = "0"; + mCameraIds[1] = "2"; + mMultiCamera.onResume(mCameraIds); + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + mUI.showRelatedIcons(mCurrentSceneMode.mode); + } + + @Override + public void onPauseBeforeSuper() { + Log.d(TAG, " onPauseBeforeSuper "); + mMultiCamera.onPause(); + } + + @Override + public void onPauseAfterSuper() { + stopBackgroundThread(); + } + + @Override + public void onConfigurationChanged(Configuration config) { + + } + + @Override + public void onStop() { + + } + + @Override + public void onDestroy() { + + } + + @Override + public void installIntentFilter() { + + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + + } + + @Override + public boolean onBackPressed() { + return false; + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + return false; + } + + @Override + public boolean onKeyUp(int keyCode, KeyEvent event) { + return false; + } + + @Override + public void onSingleTapUp(View view, int x, int y) { + + } + + @Override + public void onLongPress(View view, int x, int y) { + } + + @Override + public void onPreviewTextureCopied() { + + } + + @Override + public void onCaptureTextureCopied() { + + } + + @Override + public void onUserInteraction() { + + } + + @Override + public boolean updateStorageHintOnResume() { + return false; + } + + @Override + public void onOrientationChanged(int orientation) { + // We keep the last known orientation. So if the user first orient + // the camera then point the camera to floor or sky, we still have + // the correct orientation. + if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) return; + int oldOrientation = mOrientation; + mOrientation = CameraUtil.roundOrientation(orientation, mOrientation); + if (oldOrientation != mOrientation) { + mUI.setOrientation(mOrientation, true); + mMultiCamera.onOrientationChanged(mOrientation); + } + + } + + @Override + public void onShowSwitcherPopup() { + + } + + @Override + public void onMediaSaveServiceConnected(MediaSaveService s) { + + } + + @Override + public boolean arePreviewControlsVisible() { + return false; + } + + @Override + public void resizeForPreviewAspectRatio() { + + } + + @Override + public void onSwitchSavePath() { + + } + + @Override + public void waitingLocationPermissionResult(boolean waiting) { + + } + + @Override + public void enableRecordingLocation(boolean enable) { + + } + + @Override + public void setPreferenceForTest(String key, String value) { + + } + + @Override + public int onZoomChanged(int requestedZoom) { + return 0; + } + + @Override + public void onZoomChanged(float requestedZoom) { + } + + @Override + public boolean isImageCaptureIntent() { + return false; + } + + @Override + public boolean isCameraIdle() { + return true; + } + + @Override + public void onCaptureDone() { + + } + + @Override + public void onCaptureCancelled() { + + } + + @Override + public void onCaptureRetake() { + + } + + @Override + public void cancelAutoFocus() { + + } + + @Override + public void stopPreview() { + + } + + @Override + public int getCameraState() { + return 0; + } + + @Override + public void onCountDownFinished() { + } + + @Override + public void onScreenSizeChanged(int width, int height) { + + } + + @Override + public void onPreviewRectChanged(Rect previewRect) { + + } + + @Override + public void updateCameraOrientation() { + if (mDisplayRotation != CameraUtil.getDisplayRotation(mActivity)) { + setDisplayOrientation(); + } + } + + @Override + public void onPreviewUIReady() { + } + + @Override + public void onPreviewUIDestroyed() { + } + + @Override + public void onShutterButtonClick() { + Log.d(TAG, "onShutterButtonClick"); + mMultiCamera.onShutterButtonClick(mCameraIds); + for (String CameraId : mCameraIds) { + int id = Integer.parseInt(CameraId); + mTakingPicture[id] = true; + } + } + + @Override + public void onShutterButtonLongClick() { + + } + + @Override + public void onShutterButtonFocus(boolean pressed) { + + } + + private class SceneModule { + CameraMode mode = CameraMode.DEFAULT; + public int rearCameraId; + public int frontCameraId; + public int auxCameraId = 0; + int getCurrentId() { + return rearCameraId; + } + } + + /** + * This Handler is used to post message back onto the main thread of the + * application + */ + private class MainHandler extends Handler { + public MainHandler() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case CLEAR_SCREEN_DELAY: { + mActivity.getWindow().clearFlags( + WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + break; + } + } + } + } + + private void initCameraIds() { + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + boolean isFirstDefault = true; + String[] cameraIdList = null; + try { + cameraIdList = manager.getCameraIdList(); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + if (cameraIdList == null || cameraIdList.length == 0) { + return; + } + for (int i = 0; i < cameraIdList.length; i++) { + String cameraId = cameraIdList[i]; + CameraCharacteristics characteristics; + try { + characteristics = manager.getCameraCharacteristics(cameraId); + } catch (CameraAccessException e) { + e.printStackTrace(); + continue; + } + Set<String> physicalCameraIds = characteristics.getPhysicalCameraIds(); + for (String camId : physicalCameraIds) { + Log.v(TAG, "initCameraIds physicalCameraIds :" + physicalCameraIds); + Log.v(TAG, "initCameraIds >>>> camId :" + camId + " cameraId :" + cameraId + ", i :" + i); + } + } + } + + /** + * Starts a background thread and its {@link Handler}. + */ + private void startBackgroundThread() { + if (mCameraThread == null) { + mCameraThread = new HandlerThread("CameraBackground"); + mCameraThread.start(); + } + if (mCaptureCallbackThread == null) { + mCaptureCallbackThread = new HandlerThread("CameraCaptureCallback"); + mCaptureCallbackThread.start(); + } + + if (mCameraHandler == null) { + mCameraHandler = new MyCameraHandler(mCameraThread.getLooper()); + } + if (mCaptureCallbackHandler == null) { + mCaptureCallbackHandler = new Handler(mCaptureCallbackThread.getLooper()); + } + } + + private void stopBackgroundThread() { + mCameraThread.quitSafely(); + try { + mCameraThread.join(); + mCameraThread = null; + mCameraHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + + mCaptureCallbackThread.quitSafely(); + try { + mCaptureCallbackThread.join(); + mCaptureCallbackThread = null; + mCaptureCallbackHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private class MyCameraHandler extends Handler { + + public MyCameraHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + int id = msg.arg1; + switch (msg.what) { + case OPEN_CAMERA: + if (mMultiCamera != null) { + mMultiCamera.openCamera(mCameraIds); + } + break; + } + } + } + + private void setDisplayOrientation() { + mDisplayRotation = CameraUtil.getDisplayRotation(mActivity); + mDisplayOrientation = CameraUtil.getDisplayOrientationForCamera2( + mDisplayRotation, 0); + } + +} diff --git a/src/com/android/camera/multi/MultiCameraUI.java b/src/com/android/camera/multi/MultiCameraUI.java index df4c54341..8391f8bae 100755..100644 --- a/src/com/android/camera/multi/MultiCameraUI.java +++ b/src/com/android/camera/multi/MultiCameraUI.java @@ -1,539 +1,549 @@ -/*
- * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved.
- * Not a Contribution.
- *
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.camera.multi;
-
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.drawable.AnimationDrawable;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.view.SurfaceHolder;
-import android.util.Log;
-
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-
-import com.android.camera.CameraActivity;
-import com.android.camera.PauseButton;
-import com.android.camera.PreviewGestures;
-import com.android.camera.data.Camera2ModeAdapter;
-import com.android.camera.ShutterButton;
-import com.android.camera.ui.AutoFitSurfaceView;
-import com.android.camera.ui.CameraControls;
-import com.android.camera.ui.Camera2FaceView;
-import com.android.camera.ui.CountDownView;
-import com.android.camera.ui.FlashToggleButton;
-import com.android.camera.ui.OneUICameraControls;
-import com.android.camera.ui.RenderOverlay;
-import com.android.camera.ui.RotateImageView;
-import com.android.camera.ui.RotateLayout;
-
-import org.codeaurora.snapcam.R;
-
-import java.util.ArrayList;
-
-public class MultiCameraUI implements PreviewGestures.SingleTapListener,
- PauseButton.OnPauseButtonListener {
-
- private static final String TAG = "SnapCam_MultiCameraUI";
-
- private static final int MAX_NUM_CAM = 16;
-
- private static final int PREVIEW_WIDTH = 1024;
- private static final int PREVIEW_HIEGHT = 768;
-
- private CameraActivity mActivity;
- private View mRootView;
- private MultiCameraModule mModule;
- private PreviewGestures mGestures;
-
- /**
- * An {@link AutoFitTextureView} for camera preview.
- */
- private AutoFitSurfaceView mMainPreviewSurface;
- private AutoFitSurfaceView mFirstPreviewSurface;
- private AutoFitSurfaceView mSecondPreviewSurface;
- private AutoFitSurfaceView mPreviewSurface;
- private ArrayList<AutoFitSurfaceView> mSurfaceViewList = new ArrayList();
-
- private SurfaceHolder mMainSurfaceHolder;
- private SurfaceHolder mFirstSurfaceHolder;
- private SurfaceHolder mSecondSurfaceHolder;
-
- private RenderOverlay mRenderOverlay;
- private ShutterButton mShutterButton;
- private PauseButton mPauseButton;
- private RotateImageView mMuteButton;
- private ImageView mVideoButton;
- private ImageView mThumbnail;
- private ImageView mSettingsIcon;
-
- private CountDownView mCountDownView;
- private OneUICameraControls mCameraControls;
- private Camera2FaceView mFaceView;
-
- private TextView mRecordingTimeView;
- private View mTimeLapseLabel;
- private RotateLayout mRecordingTimeRect;
-
- private FlashToggleButton mFlashButton;
- private View mFilterModeSwitcher;
- private View mSceneModeSwitcher;
-
- private int mOrientation;
-
- private RecyclerView mModeSelectLayout;
- private Camera2ModeAdapter mCameraModeAdapter;
-
- private int mPreviewWidths[] = new int[MAX_NUM_CAM];
- private int mPreviewHeights[] = new int[MAX_NUM_CAM];
-
- public MultiCameraUI(CameraActivity activity, final MultiCameraModule module, View parent) {
- mActivity = activity;
- mModule = module;
- mRootView = parent;
- mActivity.getLayoutInflater().inflate(R.layout.multi_camera_module,
- (ViewGroup) mRootView, true);
-
- initPreviewSurface();
- initCameraControls();
- initShutterButton();
- initVideoButton();
- initVideoMuteButton();
- initializeThumbnail();
- hideMenuButton();
- initPauseButton();
- initSettingsMenu();
- initModeSelectLayout();
- }
-
- @Override
- public void onButtonPause() {
- mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds(
- R.drawable.ic_pausing_indicator, 0, 0, 0);
- mModule.onButtonPause();
- }
-
- @Override
- public void onButtonContinue() {
- mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds(
- R.drawable.ic_recording_indicator, 0, 0, 0);
- mModule.onButtonContinue();
- }
-
- public void showSurfaceView(int index) {
- Log.d(TAG, "showSurfaceView" + mPreviewWidths[index] + " " + mPreviewHeights[index]);
- mSurfaceViewList.get(index).getHolder().setFixedSize(mPreviewWidths[index], mPreviewHeights[index]);
- mSurfaceViewList.get(index).setAspectRatio(mPreviewHeights[index], mPreviewWidths[index]);
- mSurfaceViewList.get(index).setVisibility(View.VISIBLE);
- }
-
- public boolean setPreviewSize(int index, int width, int height) {
- Log.d(TAG, "setPreviewSize " + width + " " + height);
- boolean changed = (width != mPreviewWidths[index]) || (height != mPreviewHeights[index]);
- mPreviewWidths[index] = width;
- mPreviewHeights[index] = height;
- if (changed) {
- showSurfaceView(index);
- }
- return changed;
- }
-
- private void initPreviewSurface() {
- // Multi camera preview
- mMainPreviewSurface = (AutoFitSurfaceView) mRootView.findViewById(R.id.main_preview_content);
- mFirstPreviewSurface = (AutoFitSurfaceView) mRootView.findViewById(R.id.first_preview_content);
- mSecondPreviewSurface = (AutoFitSurfaceView) mRootView.findViewById(R.id.second_preview_content);
-
- mSurfaceViewList.add(mMainPreviewSurface);
- mSurfaceViewList.add(mFirstPreviewSurface);
- mSurfaceViewList.add(mSecondPreviewSurface);
-
- mMainSurfaceHolder = mMainPreviewSurface.getHolder();
- mMainSurfaceHolder.addCallback(mMainSurfaceHolderCallback);
- mFirstSurfaceHolder = mFirstPreviewSurface.getHolder();
- mFirstSurfaceHolder.addCallback(mFirstHolderCallback);
- mSecondSurfaceHolder = mSecondPreviewSurface.getHolder();
- mSecondSurfaceHolder.addCallback(mSecondHolderCallback);
-
- mMainPreviewSurface.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
- @Override
- public void onLayoutChange(View v, int left, int top, int right,
- int bottom, int oldLeft, int oldTop, int oldRight,
- int oldBottom) {
- int width = right - left;
- int height = bottom - top;
- }
- });
- }
-
- private void initShutterButton() {
- mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button);
- mShutterButton.setOnShutterButtonListener(mModule);
- mShutterButton.setImageResource(R.drawable.btn_new_shutter);
- }
-
- private void initializeThumbnail() {
- if (mThumbnail == null) {
- mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb);
- }
- mActivity.updateThumbnail(mThumbnail);
- mThumbnail.setVisibility(View.INVISIBLE);
- mThumbnail.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if (!CameraControls.isAnimating() && !mModule.isTakingPicture() &&
- !mModule.isRecordingVideo()) {
- mActivity.gotoGallery();
- }
- }
- });
- }
-
- private void initModeSelectLayout() {
- mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay);
- mModeSelectLayout = (RecyclerView) mRootView.findViewById(R.id.mode_select_layout);
- mModeSelectLayout.setLayoutManager(new LinearLayoutManager(mActivity,
- LinearLayoutManager.HORIZONTAL, false));
- mCameraModeAdapter = new Camera2ModeAdapter(mModule.getCameraModeList());
- mCameraModeAdapter.setSelectedPosition(1);
- mCameraModeAdapter.setOnItemClickListener(mModule.getModeItemClickListener());
- mModeSelectLayout.setAdapter(mCameraModeAdapter);
- mModeSelectLayout.setVisibility(View.VISIBLE);
-
- if (mGestures == null) {
- // this will handle gesture disambiguation and dispatching
- mGestures = new PreviewGestures(mActivity, this, null, null, null);
- mRenderOverlay.setGestures(mGestures);
- }
-
- mGestures.setRenderOverlay(mRenderOverlay);
- mRenderOverlay.requestLayout();
- mActivity.setPreviewGestures(mGestures);
- }
-
- private void initCameraControls() {
- mCameraControls = (OneUICameraControls) mRootView.findViewById(R.id.camera_controls);
- mFaceView = (Camera2FaceView) mRootView.findViewById(R.id.face_view);
- mFaceView.initMode();
- }
-
- private void initSettingsMenu() {
- mSettingsIcon = (ImageView) mRootView.findViewById(R.id.settings);
- mSettingsIcon.setImageResource(R.drawable.settings);
- mSettingsIcon.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- openSettingsMenu();
- }
- });
- }
-
- private void initPauseButton() {
- mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time);
- mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.multi_recording_time_rect);
- mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label);
-
- mPauseButton = (PauseButton) mRootView.findViewById(R.id.video_pause);
- mPauseButton.setOnPauseButtonListener(this);
- }
-
- private void hideMenuButton() {
- if (mFlashButton == null) {
- mFlashButton = (FlashToggleButton) mRootView.findViewById(R.id.flash_button);
- }
- if (mFilterModeSwitcher == null) {
- mFilterModeSwitcher = mRootView.findViewById(R.id.filter_mode_switcher);
- }
- if (mSceneModeSwitcher == null) {
- mSceneModeSwitcher = mRootView.findViewById(R.id.scene_mode_switcher);
- }
- mFlashButton.setVisibility(View.INVISIBLE);
- mFilterModeSwitcher.setVisibility(View.INVISIBLE);
- mSceneModeSwitcher.setVisibility(View.INVISIBLE);
- }
-
- private void initVideoButton() {
- mVideoButton = (ImageView) mRootView.findViewById(R.id.video_button);
- mVideoButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- cancelCountDown();
- mModule.onVideoButtonClick();
- }
- });
- }
-
- private void initVideoMuteButton() {
- mMuteButton = (RotateImageView)mRootView.findViewById(R.id.mute_button);
- mMuteButton.setVisibility(View.VISIBLE);
- setMuteButtonResource(!mModule.isAudioMute());
- mMuteButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- boolean isEnabled = !mModule.isAudioMute();
- mModule.setMute(isEnabled, true);
- setMuteButtonResource(!isEnabled);
- }
- });
- }
-
- 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);
- }
-
- private void setMuteButtonResource(boolean isUnMute) {
- if(isUnMute) {
- mMuteButton.setImageResource(R.drawable.ic_unmuted_button);
- } else {
- mMuteButton.setImageResource(R.drawable.ic_muted_button);
- }
- }
-
- public void setRecordingTime(String text) {
- mRecordingTimeView.setText(text);
- }
-
- public void setRecordingTimeTextColor(int color) {
- mRecordingTimeView.setTextColor(color);
- }
-
- public void resetPauseButton() {
- mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds(
- R.drawable.ic_recording_indicator, 0, 0, 0);
- mPauseButton.setPaused(false);
- }
-
- public void showRecordingUI(boolean recording) {
- if (recording) {
- mCameraControls.setVideoMode(true);
- mVideoButton.setImageResource(R.drawable.video_stop);
- mPauseButton.setVisibility(View.VISIBLE);
- mRecordingTimeView.setText("00:00");
- mRecordingTimeRect.setVisibility(View.VISIBLE);
- mMuteButton.setVisibility(View.INVISIBLE);
- setMuteButtonResource(!mModule.isAudioMute());
- showTimeLapseUI(false);
- mShutterButton.setVisibility(View.VISIBLE);
- mSettingsIcon.setVisibility(View.INVISIBLE);
- } else {
- //mFlashButton.setVisibility(View.VISIBLE);
- //mFlashButton.init(true);
- mCameraControls.setVideoMode(false);
- mPauseButton.setVisibility(View.INVISIBLE);
- mVideoButton.setImageResource(R.drawable.btn_new_shutter_video);
- mRecordingTimeRect.setVisibility(View.GONE);
- mMuteButton.setVisibility(View.INVISIBLE);
- mShutterButton.setVisibility(View.INVISIBLE);
- mSettingsIcon.setVisibility(View.VISIBLE);
- }
- }
-
- public void showTimeLapseUI(boolean enable) {
- if (mTimeLapseLabel != null) {
- mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE);
- }
- }
-
- /**
- * Enables or disables the shutter button.
- */
- public void enableShutter(boolean enabled) {
- if (mShutterButton != null) {
- mShutterButton.setEnabled(enabled);
- }
- }
-
- public void showRelatedIcons(MultiCameraModule.CameraMode mode) {
- //common settings
- mShutterButton.setVisibility(View.VISIBLE);
- //settings for each mode
- switch (mode) {
- case DEFAULT:
- mCameraControls.setVideoMode(false);
- mVideoButton.setVisibility(View.INVISIBLE);
- mMuteButton.setVisibility(View.INVISIBLE);
- mPauseButton.setVisibility(View.INVISIBLE);
- break;
- case VIDEO:
- mVideoButton.setVisibility(View.VISIBLE);
- mShutterButton.setVisibility(View.INVISIBLE);
- break;
- default:
- break;
- }
- }
-
- public void setOrientation(int orientation, boolean animation) {
- mOrientation = orientation;
- mCameraControls.setOrientation(orientation, animation);
- if (mRecordingTimeRect != null) {
- mRecordingTimeView.setRotation(-orientation);
- }
- if (mCountDownView != null)
- mCountDownView.setOrientation(orientation);
-
- }
-
- public int getOrientation() {
- return mOrientation;
- }
-
- public void cancelCountDown() {
- if (mCountDownView == null) return;
- mCountDownView.cancelCountDown();
- }
-
- public void initCountDownView() {
- if (mCountDownView == null) {
- initializeCountDown();
- } else {
- mCountDownView.initSoundPool();
- }
- }
-
- public void onCameraOpened(int cameraId) {
- mGestures.setMultiCameraUI(this);
- }
-
- public void swipeCameraMode(int move) {
- if (!mModule.getCameraModeSwitcherAllowed()) {
- return;
- }
- int index = mModule.getCurrentModeIndex() + move;
- int modeListSize = mModule.getCameraModeList().size();
- if (index >= modeListSize || index == -1) {
- return;
- }
- int mode = index % modeListSize;
- mModule.setCameraModeSwitcherAllowed(false);
- mCameraModeAdapter.setSelectedPosition(mode);
- mModeSelectLayout.smoothScrollToPosition(mode);
- mModule.selectCameraMode(mode);
- }
-
- public boolean isShutterEnabled() {
- return mShutterButton.isEnabled();
- }
-
- public ArrayList<AutoFitSurfaceView> getSurfaceViewList () {
- return mSurfaceViewList;
- }
-
-
- private void openSettingsMenu() {
- Intent intent = new Intent(mActivity, MultiSettingsActivity.class);
- intent.putExtra(MultiSettingsActivity.CAMERA_MODULE, mModule.getCurrenCameraMode());
- intent.putExtra(MultiSettingsActivity.CAMERA_ID_LISTS, mModule.getOpenCameraIdList());
- mActivity.startActivity(intent);
- }
-
- private void previewUIReady() {
- if((mMainSurfaceHolder != null && mMainSurfaceHolder.getSurface().isValid())) {
- mModule.onPreviewUIReady();
- if (mModule.isRecordingVideo() && mThumbnail != null){
- mThumbnail.setVisibility(View.INVISIBLE);
- mThumbnail = null;
- mActivity.updateThumbnail(mThumbnail);
- } else if (!mModule.isRecordingVideo()){
- if (mThumbnail == null)
- mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb);
- mActivity.updateThumbnail(mThumbnail);
- }
- }
- }
-
- private SurfaceHolder.Callback mMainSurfaceHolderCallback = new SurfaceHolder.Callback() {
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Log.v(TAG, "surfaceChanged: width =" + width + ", height = " + height);
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- Log.v(TAG, "mMainSurfaceHolderCallback surfaceCreated");
- mMainSurfaceHolder = holder;
- previewUIReady();
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- Log.v(TAG, "mMainSurfaceHolderCallback surfaceDestroyed");
- mMainSurfaceHolder = null;
- }
- };
-
- private SurfaceHolder.Callback mFirstHolderCallback = new SurfaceHolder.Callback() {
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Log.v(TAG, "mFirstHolderCallback surfaceChanged: w : h =" + width + "x" + height);
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- Log.v(TAG, "mFirstHolderCallback surfaceCreated");
- mFirstSurfaceHolder = holder;
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- Log.v(TAG, "mFirstHolderCallback surfaceDestroyed");
- mFirstSurfaceHolder = null;
- }
- };
-
- private SurfaceHolder.Callback mSecondHolderCallback = new SurfaceHolder.Callback() {
-
- @Override
- public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
- Log.v(TAG, "mSecondHolderCallback surfaceChanged: w : h =" + width + "x" + height);
- }
-
- @Override
- public void surfaceCreated(SurfaceHolder holder) {
- Log.v(TAG, "mSecondHolderCallback surfaceCreated");
- mSecondSurfaceHolder = holder;
- }
-
- @Override
- public void surfaceDestroyed(SurfaceHolder holder) {
- Log.v(TAG, "mSecondHolderCallback surfaceDestroyed");
- mSecondSurfaceHolder = null;
- }
- };
-
- @Override
- public void onSingleTapUp(View view, int x, int y) {
- mModule.onSingleTapUp(view, x, y);
- }
-
- @Override
- public void onLongPress(View view, int x, int y) {
- mModule.onLongPress(view, x, y);
- }
+/* + * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.multi; + +import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.AnimationDrawable; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.view.SurfaceHolder; +import android.util.Log; + +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; + +import com.android.camera.CameraActivity; +import com.android.camera.PauseButton; +import com.android.camera.PreviewGestures; +import com.android.camera.data.Camera2ModeAdapter; +import com.android.camera.ShutterButton; +import com.android.camera.ui.AutoFitSurfaceView; +import com.android.camera.ui.CameraControls; +import com.android.camera.ui.Camera2FaceView; +import com.android.camera.ui.CountDownView; +import com.android.camera.ui.FlashToggleButton; +import com.android.camera.ui.OneUICameraControls; +import com.android.camera.ui.RenderOverlay; +import com.android.camera.ui.RotateImageView; +import com.android.camera.ui.RotateLayout; + +import org.codeaurora.snapcam.R; + +import java.util.ArrayList; + +public class MultiCameraUI implements PreviewGestures.SingleTapListener, + PauseButton.OnPauseButtonListener { + + private static final String TAG = "SnapCam_MultiCameraUI"; + + private static final int MAX_NUM_CAM = 16; + + private static final int PREVIEW_WIDTH = 1024; + private static final int PREVIEW_HIEGHT = 768; + + private CameraActivity mActivity; + private View mRootView; + private MultiCameraModule mModule; + private PreviewGestures mGestures; + + /** + * An {@link AutoFitTextureView} for camera preview. + */ + private AutoFitSurfaceView mMainPreviewSurface; + private AutoFitSurfaceView mFirstPreviewSurface; + private AutoFitSurfaceView mSecondPreviewSurface; + private AutoFitSurfaceView mPreviewSurface; + private ArrayList<AutoFitSurfaceView> mSurfaceViewList = new ArrayList(); + + private SurfaceHolder mMainSurfaceHolder; + private SurfaceHolder mFirstSurfaceHolder; + private SurfaceHolder mSecondSurfaceHolder; + + private RenderOverlay mRenderOverlay; + private ShutterButton mShutterButton; + private PauseButton mPauseButton; + private RotateImageView mMuteButton; + private ImageView mVideoButton; + private ImageView mThumbnail; + private ImageView mSettingsIcon; + + private CountDownView mCountDownView; + private OneUICameraControls mCameraControls; + private Camera2FaceView mFaceView; + + private TextView mRecordingTimeView; + private View mTimeLapseLabel; + private RotateLayout mRecordingTimeRect; + + private FlashToggleButton mFlashButton; + private View mFilterModeSwitcher; + private View mSceneModeSwitcher; + + private int mOrientation; + + private RecyclerView mModeSelectLayout; + private Camera2ModeAdapter mCameraModeAdapter; + + private int mPreviewWidths[] = new int[MAX_NUM_CAM]; + private int mPreviewHeights[] = new int[MAX_NUM_CAM]; + + public MultiCameraUI(CameraActivity activity, final MultiCameraModule module, View parent) { + mActivity = activity; + mModule = module; + mRootView = parent; + mActivity.getLayoutInflater().inflate(R.layout.multi_camera_module, + (ViewGroup) mRootView, true); + + initPreviewSurface(); + initCameraControls(); + initShutterButton(); + initVideoButton(); + initVideoMuteButton(); + initFlashButton(); + initializeThumbnail(); + hideMenuButton(); + initPauseButton(); + initSettingsMenu(); + initModeSelectLayout(); + } + + @Override + public void onButtonPause() { + mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_pausing_indicator, 0, 0, 0); + mModule.onButtonPause(); + } + + @Override + public void onButtonContinue() { + mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_recording_indicator, 0, 0, 0); + mModule.onButtonContinue(); + } + + public void showSurfaceView(int index) { + Log.d(TAG, "showSurfaceView" + mPreviewWidths[index] + " " + mPreviewHeights[index]); + mSurfaceViewList.get(index).getHolder().setFixedSize(mPreviewWidths[index], mPreviewHeights[index]); + mSurfaceViewList.get(index).setAspectRatio(mPreviewHeights[index], mPreviewWidths[index]); + mSurfaceViewList.get(index).setVisibility(View.VISIBLE); + } + + public boolean setPreviewSize(int index, int width, int height) { + Log.d(TAG, "setPreviewSize " + width + " " + height); + boolean changed = (width != mPreviewWidths[index]) || (height != mPreviewHeights[index]); + mPreviewWidths[index] = width; + mPreviewHeights[index] = height; + if (changed) { + showSurfaceView(index); + } + return changed; + } + + private void initPreviewSurface() { + // Multi camera preview + mMainPreviewSurface = (AutoFitSurfaceView) mRootView.findViewById(R.id.main_preview_content); + mFirstPreviewSurface = (AutoFitSurfaceView) mRootView.findViewById(R.id.first_preview_content); + mSecondPreviewSurface = (AutoFitSurfaceView) mRootView.findViewById(R.id.second_preview_content); + + mSurfaceViewList.add(mMainPreviewSurface); + mSurfaceViewList.add(mFirstPreviewSurface); + mSurfaceViewList.add(mSecondPreviewSurface); + + mMainSurfaceHolder = mMainPreviewSurface.getHolder(); + mMainSurfaceHolder.addCallback(mMainSurfaceHolderCallback); + mFirstSurfaceHolder = mFirstPreviewSurface.getHolder(); + mFirstSurfaceHolder.addCallback(mFirstHolderCallback); + mSecondSurfaceHolder = mSecondPreviewSurface.getHolder(); + mSecondSurfaceHolder.addCallback(mSecondHolderCallback); + + mMainPreviewSurface.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, + int bottom, int oldLeft, int oldTop, int oldRight, + int oldBottom) { + int width = right - left; + int height = bottom - top; + } + }); + } + + private void initShutterButton() { + mShutterButton = (ShutterButton) mRootView.findViewById(R.id.shutter_button); + mShutterButton.setOnShutterButtonListener(mModule); + mShutterButton.setImageResource(R.drawable.btn_new_shutter); + } + + private void initializeThumbnail() { + if (mThumbnail == null) { + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + } + mActivity.updateThumbnail(mThumbnail); + mThumbnail.setVisibility(View.INVISIBLE); + mThumbnail.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!CameraControls.isAnimating() && !mModule.isTakingPicture() && + !mModule.isRecordingVideo()) { + mActivity.gotoGallery(); + } + } + }); + } + + private void initModeSelectLayout() { + mRenderOverlay = (RenderOverlay) mRootView.findViewById(R.id.render_overlay); + mModeSelectLayout = (RecyclerView) mRootView.findViewById(R.id.mode_select_layout); + mModeSelectLayout.setLayoutManager(new LinearLayoutManager(mActivity, + LinearLayoutManager.HORIZONTAL, false)); + mCameraModeAdapter = new Camera2ModeAdapter(mModule.getCameraModeList(), + mModule.getCameraModeIconList()); + mCameraModeAdapter.setSelectedPosition(1); + mCameraModeAdapter.setOnItemClickListener(mModule.getModeItemClickListener()); + mModeSelectLayout.setAdapter(mCameraModeAdapter); + mModeSelectLayout.setVisibility(View.INVISIBLE); + + if (mGestures == null) { + // this will handle gesture disambiguation and dispatching + mGestures = new PreviewGestures(mActivity, this, null, null, null); + mRenderOverlay.setGestures(mGestures); + } + + mGestures.setRenderOverlay(mRenderOverlay); + mRenderOverlay.requestLayout(); + mActivity.setPreviewGestures(mGestures); + } + + private void initCameraControls() { + mCameraControls = (OneUICameraControls) mRootView.findViewById(R.id.camera_controls); + mFaceView = (Camera2FaceView) mRootView.findViewById(R.id.face_view); + mFaceView.initMode(); + } + + private void initSettingsMenu() { + mSettingsIcon = (ImageView) mRootView.findViewById(R.id.settings); + mSettingsIcon.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + openSettingsMenu(); + } + }); + } + + private void initPauseButton() { + mRecordingTimeView = (TextView) mRootView.findViewById(R.id.recording_time); + mRecordingTimeRect = (RotateLayout) mRootView.findViewById(R.id.multi_recording_time_rect); + mTimeLapseLabel = mRootView.findViewById(R.id.time_lapse_label); + + mPauseButton = (PauseButton) mRootView.findViewById(R.id.video_pause); + mPauseButton.setOnPauseButtonListener(this); + } + + private void initFlashButton() { + if (mFlashButton == null) { + mFlashButton = (FlashToggleButton) mRootView.findViewById(R.id.flash_button); + } + mFlashButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + mFlashButton.handleClick(); + } + }); + } + + private void hideMenuButton() { + if (mFilterModeSwitcher == null) { + mFilterModeSwitcher = mRootView.findViewById(R.id.filter_mode_switcher); + } + if (mSceneModeSwitcher == null) { + mSceneModeSwitcher = mRootView.findViewById(R.id.scene_mode_switcher); + } + mFlashButton.setVisibility(View.INVISIBLE); + mFilterModeSwitcher.setVisibility(View.INVISIBLE); + mSceneModeSwitcher.setVisibility(View.INVISIBLE); + } + + private void initVideoButton() { + mVideoButton = (ImageView) mRootView.findViewById(R.id.video_button); + mVideoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + cancelCountDown(); + mModule.onVideoButtonClick(); + } + }); + } + + private void initVideoMuteButton() { + mMuteButton = (RotateImageView)mRootView.findViewById(R.id.mute_button); + mMuteButton.setVisibility(View.VISIBLE); + setMuteButtonResource(!mModule.isAudioMute()); + mMuteButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + boolean isEnabled = !mModule.isAudioMute(); + mModule.setMute(isEnabled, true); + setMuteButtonResource(!isEnabled); + } + }); + } + + 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); + } + + private void setMuteButtonResource(boolean isUnMute) { + if(isUnMute) { + mMuteButton.setImageResource(R.drawable.ic_unmuted_button); + } else { + mMuteButton.setImageResource(R.drawable.ic_muted_button); + } + } + + public void setRecordingTime(String text) { + mRecordingTimeView.setText(text); + } + + public void setRecordingTimeTextColor(int color) { + mRecordingTimeView.setTextColor(color); + } + + public void resetPauseButton() { + mRecordingTimeView.setCompoundDrawablesWithIntrinsicBounds( + R.drawable.ic_recording_indicator, 0, 0, 0); + mPauseButton.setPaused(false); + } + + public void showRecordingUI(boolean recording) { + if (recording) { + mCameraControls.setVideoMode(true); + mVideoButton.setImageResource(R.drawable.shutter_button_video_stop); + mPauseButton.setVisibility(View.VISIBLE); + mRecordingTimeView.setText("00:00"); + mRecordingTimeRect.setVisibility(View.VISIBLE); + mMuteButton.setVisibility(View.INVISIBLE); + setMuteButtonResource(!mModule.isAudioMute()); + showTimeLapseUI(false); + mShutterButton.setVisibility(View.VISIBLE); + mSettingsIcon.setVisibility(View.INVISIBLE); + } else { + //mFlashButton.setVisibility(View.VISIBLE); + //mFlashButton.init(true); + mCameraControls.setVideoMode(false); + mPauseButton.setVisibility(View.INVISIBLE); + mVideoButton.setImageResource(R.drawable.btn_new_shutter_video); + mRecordingTimeRect.setVisibility(View.GONE); + mMuteButton.setVisibility(View.INVISIBLE); + mShutterButton.setVisibility(View.INVISIBLE); + mSettingsIcon.setVisibility(View.VISIBLE); + } + } + + public void showTimeLapseUI(boolean enable) { + if (mTimeLapseLabel != null) { + mTimeLapseLabel.setVisibility(enable ? View.VISIBLE : View.GONE); + } + } + + /** + * Enables or disables the shutter button. + */ + public void enableShutter(boolean enabled) { + if (mShutterButton != null) { + mShutterButton.setEnabled(enabled); + } + } + + public void showRelatedIcons(MultiCameraModule.CameraMode mode) { + //common settings + mShutterButton.setVisibility(View.VISIBLE); + //settings for each mode + switch (mode) { + case DEFAULT: + mCameraControls.setVideoMode(false); + mVideoButton.setVisibility(View.INVISIBLE); + mMuteButton.setVisibility(View.INVISIBLE); + mPauseButton.setVisibility(View.INVISIBLE); + break; + case VIDEO: + mVideoButton.setVisibility(View.VISIBLE); + mShutterButton.setVisibility(View.INVISIBLE); + break; + default: + break; + } + } + + public void setOrientation(int orientation, boolean animation) { + mOrientation = orientation; + mCameraControls.setOrientation(orientation, animation); + if (mRecordingTimeRect != null) { + mRecordingTimeView.setRotation(-orientation); + } + if (mCountDownView != null) + mCountDownView.setOrientation(orientation); + + } + + public int getOrientation() { + return mOrientation; + } + + public void cancelCountDown() { + if (mCountDownView == null) return; + mCountDownView.cancelCountDown(); + } + + public void initCountDownView() { + if (mCountDownView == null) { + initializeCountDown(); + } else { + mCountDownView.initSoundPool(); + } + } + + public void onCameraOpened(int cameraId) { + mGestures.setMultiCameraUI(this); + } + + public void swipeCameraMode(int move) { + if (!mModule.getCameraModeSwitcherAllowed()) { + return; + } + int index = mModule.getCurrentModeIndex() + move; + int modeListSize = mModule.getCameraModeList().size(); + if (index >= modeListSize || index == -1) { + return; + } + int mode = index % modeListSize; + mModule.setCameraModeSwitcherAllowed(false); + mCameraModeAdapter.setSelectedPosition(mode); + mModeSelectLayout.smoothScrollToPosition(mode); + mModule.selectCameraMode(mode); + } + + public boolean isShutterEnabled() { + return mShutterButton.isEnabled(); + } + + public ArrayList<AutoFitSurfaceView> getSurfaceViewList () { + return mSurfaceViewList; + } + + + private void openSettingsMenu() { + Intent intent = new Intent(mActivity, MultiSettingsActivity.class); + intent.putExtra(MultiSettingsActivity.CAMERA_MODULE, mModule.getCurrenCameraMode()); + intent.putExtra(MultiSettingsActivity.CAMERA_ID_LISTS, mModule.getOpenCameraIdList()); + mActivity.startActivity(intent); + } + + private void previewUIReady() { + if((mMainSurfaceHolder != null && mMainSurfaceHolder.getSurface().isValid())) { + mModule.onPreviewUIReady(); + if (mModule.isRecordingVideo() && mThumbnail != null){ + mThumbnail.setVisibility(View.INVISIBLE); + mThumbnail = null; + mActivity.updateThumbnail(mThumbnail); + } else if (!mModule.isRecordingVideo()){ + if (mThumbnail == null) + mThumbnail = (ImageView) mRootView.findViewById(R.id.preview_thumb); + mActivity.updateThumbnail(mThumbnail); + } + } + } + + private SurfaceHolder.Callback mMainSurfaceHolderCallback = new SurfaceHolder.Callback() { + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.v(TAG, "surfaceChanged: width =" + width + ", height = " + height); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.v(TAG, "mMainSurfaceHolderCallback surfaceCreated"); + mMainSurfaceHolder = holder; + previewUIReady(); + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.v(TAG, "mMainSurfaceHolderCallback surfaceDestroyed"); + mMainSurfaceHolder = null; + } + }; + + private SurfaceHolder.Callback mFirstHolderCallback = new SurfaceHolder.Callback() { + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.v(TAG, "mFirstHolderCallback surfaceChanged: w : h =" + width + "x" + height); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.v(TAG, "mFirstHolderCallback surfaceCreated"); + mFirstSurfaceHolder = holder; + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.v(TAG, "mFirstHolderCallback surfaceDestroyed"); + mFirstSurfaceHolder = null; + } + }; + + private SurfaceHolder.Callback mSecondHolderCallback = new SurfaceHolder.Callback() { + + @Override + public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { + Log.v(TAG, "mSecondHolderCallback surfaceChanged: w : h =" + width + "x" + height); + } + + @Override + public void surfaceCreated(SurfaceHolder holder) { + Log.v(TAG, "mSecondHolderCallback surfaceCreated"); + mSecondSurfaceHolder = holder; + } + + @Override + public void surfaceDestroyed(SurfaceHolder holder) { + Log.v(TAG, "mSecondHolderCallback surfaceDestroyed"); + mSecondSurfaceHolder = null; + } + }; + + @Override + public void onSingleTapUp(View view, int x, int y) { + mModule.onSingleTapUp(view, x, y); + } + + @Override + public void onLongPress(View view, int x, int y) { + mModule.onLongPress(view, x, y); + } } diff --git a/src/com/android/camera/multi/MultiCaptureModule.java b/src/com/android/camera/multi/MultiCaptureModule.java index cf629daa1..34065150f 100755..100644 --- a/src/com/android/camera/multi/MultiCaptureModule.java +++ b/src/com/android/camera/multi/MultiCaptureModule.java @@ -1,824 +1,824 @@ -/*
- * Copyright (c) 2019 The Linux Foundation. All rights reserved.
- * Not a Contribution.
- *
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.camera.multi;
-
-import android.content.Context;
-import android.content.ContentResolver;
-import android.content.SharedPreferences;
-import android.graphics.ImageFormat;
-import android.graphics.Point;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.media.CameraProfile;
-import android.media.Image;
-import android.media.ImageReader;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-import android.util.Size;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.widget.Toast;
-
-import java.nio.ByteBuffer;
-import java.util.concurrent.Executor;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.List;
-
-import com.android.camera.CameraActivity;
-import com.android.camera.CaptureModule;
-import com.android.camera.ComboPreferences;
-import com.android.camera.MediaSaveService;
-import com.android.camera.Exif;
-import com.android.camera.SoundClips;
-import com.android.camera.exif.ExifInterface;
-import com.android.camera.PhotoModule.NamedImages;
-import com.android.camera.PhotoModule.NamedImages.NamedEntity;
-import com.android.camera.util.CameraUtil;
-import com.android.camera.util.PersistUtil;
-import org.codeaurora.snapcam.R;
-
-public class MultiCaptureModule implements MultiCamera {
-
- private static final String TAG = "SnapCam_MultiCaptureModule";
-
- private static final boolean DEBUG =
- (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_LOG) ||
- (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_ALL);
-
- private static final int WAIT_SURFACE = 0;
- private static final int OPEN_CAMERA = 1;
-
- private int mCameraListIndex = 0;
-
- private static final int MAX_NUM_CAM = 16;
-
- private CameraActivity mActivity;
- private MultiCameraUI mMultiCameraUI;
- private MultiCameraModule mMultiCameraModule;
- private CameraDevice[] mCameraDevices = new CameraDevice[MAX_NUM_CAM];
- private SharedPreferences mLocalSharedPref;
- private ArrayList<CameraCharacteristics> mCharacteristics;
-
- private ArrayList<String> mCameraIDList = new ArrayList<>();
- private CameraCaptureSession[] mCameraCaptureSessions = new CameraCaptureSession[MAX_NUM_CAM];
- private ImageReader[] mImageReaders = new ImageReader[MAX_NUM_CAM];
-
- private Size mPreviewSizes[] = new Size[MAX_NUM_CAM];
- private Size mPictureSizes[] = new Size[MAX_NUM_CAM];
- private int[][] mMaxPreviewSize = new int[MAX_NUM_CAM][];
-
- private Handler mCameraHandler;
- private HandlerThread mCameraThread;
-
- private static final CaptureRequest.Key<Byte> override_resource_cost_validation =
- new CaptureRequest.Key<>(
- "org.codeaurora.qcamera3.sessionParameters.overrideResourceCostValidation",
- byte.class);
-
- /**
- * {@link CaptureRequest.Builder} for the camera preview
- */
- private CaptureRequest.Builder[] mPreviewRequestBuilders = new CaptureRequest.Builder[MAX_NUM_CAM];
- /**
- * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder}
- */
- private CaptureRequest mPreviewRequest;
- /**
- * A {@link Semaphore} make sure the camera open callback happens first before closing the
- * camera.
- */
- private Semaphore mCameraOpenCloseLock = new Semaphore(3);
-
- /**
- * Orientation of the camera sensor
- */
- private int mSensorOrientation;
-
- private long mCaptureStartTime;
- private NamedImages mNamedImages;
- private ContentResolver mContentResolver;
- private SoundClips.Player mSoundPlayer;
-
- public MultiCaptureModule(CameraActivity activity, MultiCameraUI ui, MultiCameraModule module) {
- mActivity = activity;
- mMultiCameraUI = ui;
- mMultiCameraModule = module;
- mContentResolver = mActivity.getContentResolver();
- mNamedImages = new NamedImages();
- mLocalSharedPref = mActivity.getSharedPreferences(
- ComboPreferences.getLocalSharedPreferencesName(mActivity,
- "multi" + mMultiCameraModule.getCurrenCameraMode()), Context.MODE_PRIVATE);
- initCameraCharacteristics();
- startBackgroundThread();
- }
-
- @Override
- public void onResume(String[] ids) {
- for (String id : ids) {
- mCameraIDList.add(id);
- }
- // Set up sound playback for shutter button, video record and video stop
- if (mSoundPlayer == null) {
- mSoundPlayer = SoundClips.getPlayer(mActivity);
- }
- startBackgroundThread();
- initCameraCharacteristics();
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- updatePictureSize(cameraId);
- int index = mCameraIDList.indexOf(id);
- mMultiCameraUI.setPreviewSize(index, mPreviewSizes[cameraId].getWidth(),
- mPreviewSizes[cameraId].getHeight());
- }
- }
-
- @Override
- public void onPause() {
- if (mSoundPlayer != null) {
- mSoundPlayer.release();
- mSoundPlayer = null;
- }
- closeCamera();
- if (mCameraIDList != null) {
- mCameraIDList.clear();
- }
- mCameraListIndex = 0;
- stopBackgroundThread();
- }
-
- @Override
- public boolean openCamera(String[] ids) {
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- return true;
- }
-
- @Override
- public void startPreview() {
-
- }
-
- @Override
- public void closeSession() {
-
- }
-
- @Override
- public void closeCamera() {
- Log.d(TAG, "closeCamera");
- /* no need to set this in the callback and handle asynchronously. This is the same
- reason as why we release the semaphore here, not in camera close callback function
- as we don't have to protect the case where camera open() gets called during camera
- close(). The low level framework/HAL handles the synchronization for open()
- happens after close() */
- try {
- // Close camera starting with AUX first
- for (int i = MAX_NUM_CAM - 1; i >= 0; i--) {
- if (null != mCameraDevices[i]) {
- if (!mCameraOpenCloseLock.tryAcquire(2000, TimeUnit.MILLISECONDS)) {
- Log.d(TAG, "Time out waiting to lock camera closing.");
- throw new RuntimeException("Time out waiting to lock camera closing");
- }
- Log.d(TAG, "Closing camera: " + mCameraDevices[i].getId());
- mCameraDevices[i].close();
- mCameraDevices[i] = null;
- mCameraCaptureSessions[i] = null;
- }
- if (null != mImageReaders[i]) {
- mImageReaders[i].close();
- mImageReaders[i] = null;
- }
- }
- } catch (InterruptedException e) {
- mCameraOpenCloseLock.release();
- throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } finally {
- mCameraOpenCloseLock.release();
- }
- }
-
- private void openCameraInSequence(String id) {
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- CameraCharacteristics characteristics = null;
- try {
- characteristics = manager.getCameraCharacteristics(id);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- Log.d(TAG, "openCameraInSequence " + id + ", mSensorOrientation :" + mSensorOrientation);
- try {
- if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
- Log.d(TAG, "Time out waiting to lock camera opening.");
- throw new RuntimeException("Time out waiting to lock camera opening");
- }
- manager.openCamera(id, mStateCallback, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- manager = null;
- characteristics = null;
- }
-
- private void startBackgroundThread() {
- if (mCameraThread == null) {
- mCameraThread = new HandlerThread("CameraBackground");
- mCameraThread.start();
- }
- if (mCameraHandler == null) {
- mCameraHandler = new MyCameraHandler(mCameraThread.getLooper());
- }
- }
-
- private void stopBackgroundThread() {
- mCameraThread.quitSafely();
- try {
- mCameraThread.join();
- mCameraThread = null;
- mCameraHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private Size[] getSupportedOutputSize(int cameraId, Class cl) {
- StreamConfigurationMap map = mCharacteristics.get(cameraId).get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- Size[] normal = map.getOutputSizes(cl);
- Size[] high = map.getHighResolutionOutputSizes(ImageFormat.PRIVATE);
- Size[] ret = new Size[normal.length + high.length];
- System.arraycopy(normal, 0, ret, 0, normal.length);
- System.arraycopy(high, 0, ret, normal.length, high.length);
- return ret;
- }
-
- private Size getOptimalPreviewSize(int id, Size pictureSize, Size[] prevSizes) {
- Point[] points = new Point[prevSizes.length];
-
- double targetRatio = (double) pictureSize.getWidth() / pictureSize.getHeight();
- int index = 0;
- int point_max[] = mMaxPreviewSize[id];
- int max_size = -1;
- if (point_max != null){
- max_size = point_max[0] * point_max[1];
- }
- for (Size s : prevSizes) {
- if (max_size != -1){
- int size = s.getWidth() * s.getHeight();
- if (s.getWidth() == s.getHeight()){
- if (s.getWidth() > Math.max(point_max[0],point_max[1]))
- continue;
- } else if (size > max_size || size == 0) {
- continue;
- }
- }
- points[index++] = new Point(s.getWidth(), s.getHeight());
- }
-
- int optimalPickIndex = CameraUtil.getOptimalPreviewSize(mActivity, points, targetRatio);
- return (optimalPickIndex == -1) ? null :
- new Size(points[optimalPickIndex].x,points[optimalPickIndex].y);
- }
-
- private class MyCameraHandler extends Handler {
-
- public MyCameraHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case WAIT_SURFACE:
- int id = msg.arg1;
- int index = mCameraIDList.indexOf(String.valueOf(id));
- Log.v(TAG, "WAIT_SURFACE id :" + id + ", index :" + index);
- if (index == -1) {
- break;
- }
- Surface surface = mMultiCameraUI.getSurfaceViewList().get(index)
- .getHolder().getSurface();
- if (surface.isValid()) {
- createCaptureSessions(id, surface);
- } else {
- mCameraHandler.sendMessageDelayed(msg, 200);
- Log.v(TAG, "Surface is invalid, wait more 200ms surfaceCreated");
- }
- break;
- case OPEN_CAMERA:
- if (mCameraListIndex == mCameraIDList.size()) {
- mCameraListIndex = 0;
- } else {
- String cameraId = mCameraIDList.get(mCameraListIndex);
- openCameraInSequence(cameraId);
- mCameraListIndex ++;
- Log.v(TAG, " OPEN_CAMERA cameraId :" + cameraId + ", mCameraListIndex :"
- + mCameraListIndex);
- }
- break;
- }
- }
- }
-
- private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
-
- @Override
- public void onOpened(CameraDevice cameraDevice) {
- final int id = Integer.parseInt(cameraDevice.getId());
- mCameraDevices[id] = cameraDevice;
- Log.d(TAG, "onOpened " + id);
- mCameraOpenCloseLock.release();
- //updatePictureSize(id);
- createCameraPreviewSession(id);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMultiCameraUI.onCameraOpened(id);
- }
- });
- }
-
- @Override
- public void onDisconnected(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.d(TAG, "onDisconnected " + id);
- mCameraOpenCloseLock.release();
- mCameraDevices[id] = null;
- }
-
- @Override
- public void onError(CameraDevice cameraDevice, int error) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.e(TAG, "onError " + id + " " + error);
- mCameraOpenCloseLock.release();
-
- if (null != mActivity) {
- Toast.makeText(mActivity,"open camera error id =" + id,
- Toast.LENGTH_LONG).show();
- mActivity.finish();
- }
- }
-
- @Override
- public void onClosed(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.d(TAG, "onClosed " + id);
- mCameraOpenCloseLock.release();
- mCameraDevices[id] = null;
- }
- };
-
- /**
- * Creates a new {@link CameraCaptureSession} for camera preview.
- */
- private void createCameraPreviewSession(int id) {
- // This is the output Surface we need to start preview.
- int index = mCameraIDList.indexOf(String.valueOf(id));
- Log.v(TAG, "createCameraPreviewSession id :" + id + ", index :" + index);
- Surface surface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder().getSurface();
- if (surface.isValid()) {
- createCaptureSessions(id, surface);
- } else {
- Message msg = Message.obtain();
- msg.what = WAIT_SURFACE;
- msg.arg1 = id;
- mCameraHandler.sendMessageDelayed(msg, 200);
- Log.v(TAG, "Surface is invalid, wait 200ms surfaceCreated");
- }
- }
-
- private void createCaptureSessions(final int id, Surface surface) {
- try {
- // We set up a CaptureRequest.Builder with the output Surface.
- mPreviewRequestBuilders[id]
- = mCameraDevices[id].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- mPreviewRequestBuilders[id].addTarget(surface);
- mPreviewRequestBuilders[id].setTag(id);
- CameraCaptureSession.StateCallback stateCallback =
- new CameraCaptureSession.StateCallback() {
- @Override
- public void onConfigured(CameraCaptureSession cameraCaptureSession) {
- // The camera is already closed
- if (null == mCameraDevices[id]) {
- return;
- }
- Log.v(TAG, " CameraCaptureSession onConfigured id :" + id);
- // When the session is ready, we start displaying the preview.
- mCameraCaptureSessions[id] = cameraCaptureSession;
- try {
- // Auto focus should be continuous for camera preview.
- mPreviewRequestBuilders[id].set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- // Finally, we start displaying the camera preview.
- mCameraCaptureSessions[id].setRepeatingRequest(
- mPreviewRequestBuilders[id].build(),
- mCaptureCallback, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
-
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- }
-
- @Override
- public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
- showToast("onConfigureFailed");
- }
- };
-
- try {
- final byte enable = 1;
- mPreviewRequestBuilders[id].set(override_resource_cost_validation, enable);
- Log.v(TAG, " set" + override_resource_cost_validation + " is 1");
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
-
- List<OutputConfiguration> outConfigurations = new ArrayList<>(2);
- outConfigurations.add(new OutputConfiguration(surface));
- outConfigurations.add(new OutputConfiguration(mImageReaders[id].getSurface()));
-
- SessionConfiguration sessionConfiguration = new SessionConfiguration(
- SessionConfiguration.SESSION_REGULAR, outConfigurations,
- new HandlerExecutor(mCameraHandler), stateCallback);
- sessionConfiguration.setSessionParameters(mPreviewRequestBuilders[id].build());
- mCameraDevices[id].createCaptureSession(sessionConfiguration);
- try {
- CaptureRequest captureRequest = sessionConfiguration.getSessionParameters();
- Log.v(TAG, " override_resource_cost_validation result: " +
- captureRequest.get(override_resource_cost_validation));
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private class HandlerExecutor implements Executor {
- private final Handler ihandler;
-
- public HandlerExecutor(Handler handler) {
- ihandler = handler;
- }
-
- @Override
- public void execute(Runnable runCmd) {
- ihandler.post(runCmd);
- }
- }
-
- /**
- * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
- */
- private CameraCaptureSession.CaptureCallback mCaptureCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- private void process(CaptureResult result) {
- Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
- Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
- if (DEBUG) {
- Log.v(TAG, "process afState :" + afState + ", aeState :" + aeState);
- }
- }
-
- @Override
- public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
- CaptureResult partialResult) {
- process(partialResult);
- }
-
- @Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
- TotalCaptureResult result) {
- process(result);
- }
-
- };
-
- private void updatePictureSize(int id) {
- String defaultSize = mActivity.getString(R.string.pref_multi_camera_picturesize_default);
- int index = mCameraIDList.indexOf(String.valueOf(id));
- String pictureSize = mLocalSharedPref.getString(
- MultiSettingsActivity.KEY_PICTURE_SIZES.get(index), defaultSize);
- mPictureSizes[id] = parsePictureSize(pictureSize);
- Log.v(TAG, " updatePictureSize size :" + mPictureSizes[id].getWidth() + "x"
- + mPictureSizes[id].getHeight());
- mImageReaders[id] = ImageReader.newInstance(mPictureSizes[id].getWidth(),
- mPictureSizes[id].getHeight(), ImageFormat.JPEG, /*maxImages*/2);
- mImageReaders[id].setOnImageAvailableListener(
- mOnImageAvailableListener, mMultiCameraModule.getMyCameraHandler());
-
- Size[] prevSizes = getSupportedOutputSize(id, SurfaceHolder.class);
- mPreviewSizes[id] = getOptimalPreviewSize(id, mPictureSizes[id], prevSizes);
- }
-
- private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
- = new ImageReader.OnImageAvailableListener() {
-
- @Override
- public void onImageAvailable(ImageReader reader) {
- Log.v(TAG, "onImageAvailable ...");
- Image image = reader.acquireNextImage();
- mCaptureStartTime = System.currentTimeMillis();
- mNamedImages.nameNewImage(mCaptureStartTime);
- NamedEntity name = mNamedImages.getNextNameEntity();
- String title = (name == null) ? null : name.title;
- long date = (name == null) ? -1 : name.date;
-
- byte[] bytes = getJpegData(image);
- Log.i(TAG, "image format:" + image.getFormat());
- if (image.getFormat() == ImageFormat.RAW10) {
- mActivity.getMediaSaveService().addRawImage(bytes, title,
- "raw");
- image.close();
- } else if (image.getFormat() == ImageFormat.YUV_420_888) {
- Log.d(TAG, "YUV buffer received" );
- image.close();
- } else {
- int orientation = 0;
- ExifInterface exif = null;
- if (image.getFormat() != ImageFormat.HEIC) {
- exif = Exif.getExif(bytes);
- orientation = Exif.getOrientation(exif);
- }
-
- String pictureFormat = "jpeg";
- if (image.getFormat() == ImageFormat.HEIC) {
- pictureFormat = "heic";
- }
- mActivity.getMediaSaveService().addImage(bytes, title, date,
- null, image.getWidth(), image.getHeight(), orientation, exif,
- mOnMediaSavedListener, mContentResolver, pictureFormat);
-
- if (image.getFormat() != ImageFormat.HEIC) {
- mActivity.updateThumbnail(bytes);
- }
- image.close();
- mMultiCameraModule.updateTakingPicture();
- }
- }
-
- };
-
- /**
- * Shows a {@link Toast} on the UI thread.
- * @param text The message to show
- */
- private void showToast(final String text) {
- if (mActivity != null) {
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
-
- private byte[] getJpegData(Image image) {
- ByteBuffer buffer = image.getPlanes()[0].getBuffer();
- byte[] bytes = new byte[buffer.remaining()];
- buffer.get(bytes);
- return bytes;
- }
-
- @Override
- public void onShutterButtonClick(String[] ids) {
- checkAndPlayShutterSound();
- mMultiCameraUI.enableShutter(false);
- boolean halZSLCheck = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_HAL_ZAL, false);
- for (String id : ids) {
- try {
- int cameraId = Integer.parseInt(id);
- final CaptureRequest.Builder captureBuilder =
- mCameraDevices[cameraId].createCaptureRequest(
- CameraDevice.TEMPLATE_STILL_CAPTURE);
- captureBuilder.addTarget(mImageReaders[cameraId].getSurface());
- int index = mCameraIDList.indexOf(String.valueOf(cameraId));
- captureBuilder.addTarget(mMultiCameraUI.getSurfaceViewList().get(
- index).getHolder().getSurface());
- applySettingsForCapture(captureBuilder, cameraId);
- // Use the same AE and AF modes as the preview.
- captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- if (halZSLCheck) {
- captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, true);
- } else {
- captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, false);
- }
- setAutoFlash(captureBuilder);
- // Orientation
- int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
- CameraUtil.getJpegRotation(cameraId, rotation));
- mCameraCaptureSessions[cameraId].capture(captureBuilder.build(),
- mCaptureStillCallback, mMultiCameraModule.getMyCameraHandler());
- Log.d(TAG, " cameraCaptureSession" + id + " captured ");
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public void onVideoButtonClick(String[] ids) {
-
- }
-
- @Override
- public void onButtonPause(String[] ids) {
-
- }
-
- @Override
- public void onButtonContinue(String[] ids) {
-
- }
-
- @Override
- public void onOrientationChanged(int orientation) {
-
- }
-
- @Override
- public boolean isRecordingVideo() {
- return false;
- }
-
- private void checkAndPlayShutterSound() {
- boolean isPlay = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_SHUTTER_SOUND, true);
- if (mSoundPlayer != null && isPlay) {
- mSoundPlayer.play(SoundClips.SHUTTER_CLICK);
- }
- }
-
- private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
- @Override
- public void onMediaSaved(Uri uri) {
- if (uri != null) {
- mActivity.notifyNewMedia(uri);
- }
- }
- };
-
- private CameraCaptureSession.CaptureCallback mCaptureStillCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- @Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
- TotalCaptureResult result) {
- Log.d(TAG, " mCaptureCallback onCaptureCompleted ");
- mMultiCameraModule.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, " enable Shutter " );
- mMultiCameraUI.enableShutter(true);
- }
- });
- }
-
- @Override
- public void onCaptureFailed(CameraCaptureSession session,
- CaptureRequest request,
- CaptureFailure result) {
- Log.d(TAG, " mCaptureCallback onCaptureFailed " );
- }
-
-
- @Override
- public void onCaptureSequenceCompleted(CameraCaptureSession session, int
- sequenceId, long frameNumber) {
- Log.d(TAG, " mCaptureCallback onCaptureSequenceCompleted ");
- }
- };
-
- private void applySettingsForCapture(CaptureRequest.Builder builder, int id) {
- builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
- applyJpegQuality(builder);
- }
-
- private void applyJpegQuality(CaptureRequest.Builder request) {
- String defaultValue = mActivity.getString(R.string.pref_camera_jpegquality_default);
- String value = mLocalSharedPref.getString(MultiSettingsActivity.KEY_PICTURE_QUALITY,
- defaultValue);
- int jpegQuality = getQualityNumber(value);
- request.set(CaptureRequest.JPEG_QUALITY, (byte) jpegQuality);
- }
-
- private int getQualityNumber(String jpegQuality) {
- if (jpegQuality == null) {
- return 85;
- }
- try {
- int qualityPercentile = Integer.parseInt(jpegQuality);
- if (qualityPercentile >= 0 && qualityPercentile <= 100)
- return qualityPercentile;
- else
- return 85;
- } catch (NumberFormatException nfe) {
- //chosen quality is not a number, continue
- }
- int value = 0;
- switch (jpegQuality) {
- case "superfine":
- value = CameraProfile.QUALITY_HIGH;
- break;
- case "fine":
- value = CameraProfile.QUALITY_MEDIUM;
- break;
- case "normal":
- value = CameraProfile.QUALITY_LOW;
- break;
- default:
- return 85;
- }
- return CameraProfile.getJpegEncodingQualityParameter(value);
- }
-
- private Size parsePictureSize(String value) {
- int indexX = value.indexOf('x');
- int width = Integer.parseInt(value.substring(0, indexX));
- int height = Integer.parseInt(value.substring(indexX + 1));
- return new Size(width, height);
- }
-
- private void initCameraCharacteristics() {
- mCharacteristics = new ArrayList<>();
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- try {
- String[] cameraIdList = manager.getCameraIdList();
- Log.d(TAG, "cameraIdList size =" + cameraIdList.length);
- for (int i = 0; i < cameraIdList.length; i++) {
- String cameraId = cameraIdList[i];
- CameraCharacteristics characteristics
- = manager.getCameraCharacteristics(cameraId);
- mCharacteristics.add(i, characteristics);
- try {
- mMaxPreviewSize[i] = characteristics.get(CaptureModule.max_preview_size);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "getMaxPreviewSize no vendorTag max_preview_size:");
- }
- if (mMaxPreviewSize[i] != null) {
- Log.d(TAG, " init cameraId :" + cameraId + ", i :" + i +
- ", maxPreviewSize :" + mMaxPreviewSize[i][0]+ "x" +
- mMaxPreviewSize[i][1]);
- }
- }
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
- if (true) {
- requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
- }
- }
+/* + * Copyright (c) 2019 The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.multi; + +import android.content.Context; +import android.content.ContentResolver; +import android.content.SharedPreferences; +import android.graphics.ImageFormat; +import android.graphics.Point; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CameraProfile; +import android.media.Image; +import android.media.ImageReader; +import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.util.Log; +import android.util.Size; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.widget.Toast; + +import java.nio.ByteBuffer; +import java.util.concurrent.Executor; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; + +import com.android.camera.CameraActivity; +import com.android.camera.CaptureModule; +import com.android.camera.ComboPreferences; +import com.android.camera.MediaSaveService; +import com.android.camera.Exif; +import com.android.camera.SoundClips; +import com.android.camera.exif.ExifInterface; +import com.android.camera.PhotoModule.NamedImages; +import com.android.camera.PhotoModule.NamedImages.NamedEntity; +import com.android.camera.util.CameraUtil; +import com.android.camera.util.PersistUtil; +import org.codeaurora.snapcam.R; + +public class MultiCaptureModule implements MultiCamera { + + private static final String TAG = "SnapCam_MultiCaptureModule"; + + private static final boolean DEBUG = + (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_LOG) || + (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_ALL); + + private static final int WAIT_SURFACE = 0; + private static final int OPEN_CAMERA = 1; + + private int mCameraListIndex = 0; + + private static final int MAX_NUM_CAM = 16; + + private CameraActivity mActivity; + private MultiCameraUI mMultiCameraUI; + private MultiCameraModule mMultiCameraModule; + private CameraDevice[] mCameraDevices = new CameraDevice[MAX_NUM_CAM]; + private SharedPreferences mLocalSharedPref; + private ArrayList<CameraCharacteristics> mCharacteristics; + + private ArrayList<String> mCameraIDList = new ArrayList<>(); + private CameraCaptureSession[] mCameraCaptureSessions = new CameraCaptureSession[MAX_NUM_CAM]; + private ImageReader[] mImageReaders = new ImageReader[MAX_NUM_CAM]; + + private Size mPreviewSizes[] = new Size[MAX_NUM_CAM]; + private Size mPictureSizes[] = new Size[MAX_NUM_CAM]; + private int[][] mMaxPreviewSize = new int[MAX_NUM_CAM][]; + + private Handler mCameraHandler; + private HandlerThread mCameraThread; + + private static final CaptureRequest.Key<Byte> override_resource_cost_validation = + new CaptureRequest.Key<>( + "org.codeaurora.qcamera3.sessionParameters.overrideResourceCostValidation", + byte.class); + + /** + * {@link CaptureRequest.Builder} for the camera preview + */ + private CaptureRequest.Builder[] mPreviewRequestBuilders = new CaptureRequest.Builder[MAX_NUM_CAM]; + /** + * {@link CaptureRequest} generated by {@link #mPreviewRequestBuilder} + */ + private CaptureRequest mPreviewRequest; + /** + * A {@link Semaphore} make sure the camera open callback happens first before closing the + * camera. + */ + private Semaphore mCameraOpenCloseLock = new Semaphore(3); + + /** + * Orientation of the camera sensor + */ + private int mSensorOrientation; + + private long mCaptureStartTime; + private NamedImages mNamedImages; + private ContentResolver mContentResolver; + private SoundClips.Player mSoundPlayer; + + public MultiCaptureModule(CameraActivity activity, MultiCameraUI ui, MultiCameraModule module) { + mActivity = activity; + mMultiCameraUI = ui; + mMultiCameraModule = module; + mContentResolver = mActivity.getContentResolver(); + mNamedImages = new NamedImages(); + mLocalSharedPref = mActivity.getSharedPreferences( + ComboPreferences.getLocalSharedPreferencesName(mActivity, + "multi" + mMultiCameraModule.getCurrenCameraMode()), Context.MODE_PRIVATE); + initCameraCharacteristics(); + startBackgroundThread(); + } + + @Override + public void onResume(String[] ids) { + for (String id : ids) { + mCameraIDList.add(id); + } + // Set up sound playback for shutter button, video record and video stop + if (mSoundPlayer == null) { + mSoundPlayer = SoundClips.getPlayer(mActivity); + } + startBackgroundThread(); + initCameraCharacteristics(); + for (String id : ids) { + int cameraId = Integer.parseInt(id); + updatePictureSize(cameraId); + int index = mCameraIDList.indexOf(id); + mMultiCameraUI.setPreviewSize(index, mPreviewSizes[cameraId].getWidth(), + mPreviewSizes[cameraId].getHeight()); + } + } + + @Override + public void onPause() { + if (mSoundPlayer != null) { + mSoundPlayer.release(); + mSoundPlayer = null; + } + closeCamera(); + if (mCameraIDList != null) { + mCameraIDList.clear(); + } + mCameraListIndex = 0; + stopBackgroundThread(); + } + + @Override + public boolean openCamera(String[] ids) { + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + return true; + } + + @Override + public void startPreview() { + + } + + @Override + public void closeSession() { + + } + + @Override + public void closeCamera() { + Log.d(TAG, "closeCamera"); + /* no need to set this in the callback and handle asynchronously. This is the same + reason as why we release the semaphore here, not in camera close callback function + as we don't have to protect the case where camera open() gets called during camera + close(). The low level framework/HAL handles the synchronization for open() + happens after close() */ + try { + // Close camera starting with AUX first + for (int i = MAX_NUM_CAM - 1; i >= 0; i--) { + if (null != mCameraDevices[i]) { + if (!mCameraOpenCloseLock.tryAcquire(2000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting to lock camera closing."); + throw new RuntimeException("Time out waiting to lock camera closing"); + } + Log.d(TAG, "Closing camera: " + mCameraDevices[i].getId()); + mCameraDevices[i].close(); + mCameraDevices[i] = null; + mCameraCaptureSessions[i] = null; + } + if (null != mImageReaders[i]) { + mImageReaders[i].close(); + mImageReaders[i] = null; + } + } + } catch (InterruptedException e) { + mCameraOpenCloseLock.release(); + throw new RuntimeException("Interrupted while trying to lock camera closing.", e); + } catch (IllegalStateException e) { + e.printStackTrace(); + } finally { + mCameraOpenCloseLock.release(); + } + } + + private void openCameraInSequence(String id) { + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + CameraCharacteristics characteristics = null; + try { + characteristics = manager.getCameraCharacteristics(id); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + Log.d(TAG, "openCameraInSequence " + id + ", mSensorOrientation :" + mSensorOrientation); + try { + if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting to lock camera opening."); + throw new RuntimeException("Time out waiting to lock camera opening"); + } + manager.openCamera(id, mStateCallback, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + manager = null; + characteristics = null; + } + + private void startBackgroundThread() { + if (mCameraThread == null) { + mCameraThread = new HandlerThread("CameraBackground"); + mCameraThread.start(); + } + if (mCameraHandler == null) { + mCameraHandler = new MyCameraHandler(mCameraThread.getLooper()); + } + } + + private void stopBackgroundThread() { + mCameraThread.quitSafely(); + try { + mCameraThread.join(); + mCameraThread = null; + mCameraHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private Size[] getSupportedOutputSize(int cameraId, Class cl) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] normal = map.getOutputSizes(cl); + Size[] high = map.getHighResolutionOutputSizes(ImageFormat.PRIVATE); + Size[] ret = new Size[normal.length + high.length]; + System.arraycopy(normal, 0, ret, 0, normal.length); + System.arraycopy(high, 0, ret, normal.length, high.length); + return ret; + } + + private Size getOptimalPreviewSize(int id, Size pictureSize, Size[] prevSizes) { + Point[] points = new Point[prevSizes.length]; + + double targetRatio = (double) pictureSize.getWidth() / pictureSize.getHeight(); + int index = 0; + int point_max[] = mMaxPreviewSize[id]; + int max_size = -1; + if (point_max != null){ + max_size = point_max[0] * point_max[1]; + } + for (Size s : prevSizes) { + if (max_size != -1){ + int size = s.getWidth() * s.getHeight(); + if (s.getWidth() == s.getHeight()){ + if (s.getWidth() > Math.max(point_max[0],point_max[1])) + continue; + } else if (size > max_size || size == 0) { + continue; + } + } + points[index++] = new Point(s.getWidth(), s.getHeight()); + } + + int optimalPickIndex = CameraUtil.getOptimalPreviewSize(mActivity, points, targetRatio); + return (optimalPickIndex == -1) ? null : + new Size(points[optimalPickIndex].x,points[optimalPickIndex].y); + } + + private class MyCameraHandler extends Handler { + + public MyCameraHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case WAIT_SURFACE: + int id = msg.arg1; + int index = mCameraIDList.indexOf(String.valueOf(id)); + Log.v(TAG, "WAIT_SURFACE id :" + id + ", index :" + index); + if (index == -1) { + break; + } + Surface surface = mMultiCameraUI.getSurfaceViewList().get(index) + .getHolder().getSurface(); + if (surface.isValid()) { + createCaptureSessions(id, surface); + } else { + mCameraHandler.sendMessageDelayed(msg, 200); + Log.v(TAG, "Surface is invalid, wait more 200ms surfaceCreated"); + } + break; + case OPEN_CAMERA: + if (mCameraListIndex == mCameraIDList.size()) { + mCameraListIndex = 0; + } else { + String cameraId = mCameraIDList.get(mCameraListIndex); + openCameraInSequence(cameraId); + mCameraListIndex ++; + Log.v(TAG, " OPEN_CAMERA cameraId :" + cameraId + ", mCameraListIndex :" + + mCameraListIndex); + } + break; + } + } + } + + private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(CameraDevice cameraDevice) { + final int id = Integer.parseInt(cameraDevice.getId()); + mCameraDevices[id] = cameraDevice; + Log.d(TAG, "onOpened " + id); + mCameraOpenCloseLock.release(); + //updatePictureSize(id); + createCameraPreviewSession(id); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mMultiCameraUI.onCameraOpened(id); + } + }); + } + + @Override + public void onDisconnected(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.d(TAG, "onDisconnected " + id); + mCameraOpenCloseLock.release(); + mCameraDevices[id] = null; + } + + @Override + public void onError(CameraDevice cameraDevice, int error) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.e(TAG, "onError " + id + " " + error); + mCameraOpenCloseLock.release(); + + if (null != mActivity) { + Toast.makeText(mActivity,"open camera error id =" + id, + Toast.LENGTH_LONG).show(); + mActivity.finish(); + } + } + + @Override + public void onClosed(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.d(TAG, "onClosed " + id); + mCameraOpenCloseLock.release(); + mCameraDevices[id] = null; + } + }; + + /** + * Creates a new {@link CameraCaptureSession} for camera preview. + */ + private void createCameraPreviewSession(int id) { + // This is the output Surface we need to start preview. + int index = mCameraIDList.indexOf(String.valueOf(id)); + Log.v(TAG, "createCameraPreviewSession id :" + id + ", index :" + index); + Surface surface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder().getSurface(); + if (surface.isValid()) { + createCaptureSessions(id, surface); + } else { + Message msg = Message.obtain(); + msg.what = WAIT_SURFACE; + msg.arg1 = id; + mCameraHandler.sendMessageDelayed(msg, 200); + Log.v(TAG, "Surface is invalid, wait 200ms surfaceCreated"); + } + } + + private void createCaptureSessions(final int id, Surface surface) { + try { + // We set up a CaptureRequest.Builder with the output Surface. + mPreviewRequestBuilders[id] + = mCameraDevices[id].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilders[id].addTarget(surface); + mPreviewRequestBuilders[id].setTag(id); + CameraCaptureSession.StateCallback stateCallback = + new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == mCameraDevices[id]) { + return; + } + Log.v(TAG, " CameraCaptureSession onConfigured id :" + id); + // When the session is ready, we start displaying the preview. + mCameraCaptureSessions[id] = cameraCaptureSession; + try { + // Auto focus should be continuous for camera preview. + mPreviewRequestBuilders[id].set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + // Finally, we start displaying the camera preview. + mCameraCaptureSessions[id].setRepeatingRequest( + mPreviewRequestBuilders[id].build(), + mCaptureCallback, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + showToast("onConfigureFailed"); + } + }; + + try { + final byte enable = 1; + mPreviewRequestBuilders[id].set(override_resource_cost_validation, enable); + Log.v(TAG, " set" + override_resource_cost_validation + " is 1"); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + + List<OutputConfiguration> outConfigurations = new ArrayList<>(2); + outConfigurations.add(new OutputConfiguration(surface)); + outConfigurations.add(new OutputConfiguration(mImageReaders[id].getSurface())); + + SessionConfiguration sessionConfiguration = new SessionConfiguration( + SessionConfiguration.SESSION_REGULAR, outConfigurations, + new HandlerExecutor(mCameraHandler), stateCallback); + sessionConfiguration.setSessionParameters(mPreviewRequestBuilders[id].build()); + mCameraDevices[id].createCaptureSession(sessionConfiguration); + try { + CaptureRequest captureRequest = sessionConfiguration.getSessionParameters(); + Log.v(TAG, " override_resource_cost_validation result: " + + captureRequest.get(override_resource_cost_validation)); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private class HandlerExecutor implements Executor { + private final Handler ihandler; + + public HandlerExecutor(Handler handler) { + ihandler = handler; + } + + @Override + public void execute(Runnable runCmd) { + ihandler.post(runCmd); + } + } + + /** + * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. + */ + private CameraCaptureSession.CaptureCallback mCaptureCallback + = new CameraCaptureSession.CaptureCallback() { + + private void process(CaptureResult result) { + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + if (DEBUG) { + Log.v(TAG, "process afState :" + afState + ", aeState :" + aeState); + } + } + + @Override + public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, + CaptureResult partialResult) { + process(partialResult); + } + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + process(result); + } + + }; + + private void updatePictureSize(int id) { + String defaultSize = mActivity.getString(R.string.pref_multi_camera_picturesize_default); + int index = mCameraIDList.indexOf(String.valueOf(id)); + String pictureSize = mLocalSharedPref.getString( + MultiSettingsActivity.KEY_PICTURE_SIZES.get(index), defaultSize); + mPictureSizes[id] = parsePictureSize(pictureSize); + Log.v(TAG, " updatePictureSize size :" + mPictureSizes[id].getWidth() + "x" + + mPictureSizes[id].getHeight()); + mImageReaders[id] = ImageReader.newInstance(mPictureSizes[id].getWidth(), + mPictureSizes[id].getHeight(), ImageFormat.JPEG, /*maxImages*/2); + mImageReaders[id].setOnImageAvailableListener( + mOnImageAvailableListener, mMultiCameraModule.getMyCameraHandler()); + + Size[] prevSizes = getSupportedOutputSize(id, SurfaceHolder.class); + mPreviewSizes[id] = getOptimalPreviewSize(id, mPictureSizes[id], prevSizes); + } + + private final ImageReader.OnImageAvailableListener mOnImageAvailableListener + = new ImageReader.OnImageAvailableListener() { + + @Override + public void onImageAvailable(ImageReader reader) { + Log.v(TAG, "onImageAvailable ..."); + Image image = reader.acquireNextImage(); + mCaptureStartTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(mCaptureStartTime); + NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + long date = (name == null) ? -1 : name.date; + + byte[] bytes = getJpegData(image); + Log.i(TAG, "image format:" + image.getFormat()); + if (image.getFormat() == ImageFormat.RAW10) { + mActivity.getMediaSaveService().addRawImage(bytes, title, + "raw"); + image.close(); + } else if (image.getFormat() == ImageFormat.YUV_420_888) { + Log.d(TAG, "YUV buffer received" ); + image.close(); + } else { + int orientation = 0; + ExifInterface exif = null; + if (image.getFormat() != ImageFormat.HEIC) { + exif = Exif.getExif(bytes); + orientation = Exif.getOrientation(exif); + } + + String pictureFormat = "jpeg"; + if (image.getFormat() == ImageFormat.HEIC) { + pictureFormat = "heic"; + } + mActivity.getMediaSaveService().addImage(bytes, title, date, + null, image.getWidth(), image.getHeight(), orientation, exif, + mOnMediaSavedListener, mContentResolver, pictureFormat); + + if (image.getFormat() != ImageFormat.HEIC) { + mActivity.updateThumbnail(bytes); + } + image.close(); + mMultiCameraModule.updateTakingPicture(); + } + } + + }; + + /** + * Shows a {@link Toast} on the UI thread. + * @param text The message to show + */ + private void showToast(final String text) { + if (mActivity != null) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + private byte[] getJpegData(Image image) { + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + return bytes; + } + + @Override + public void onShutterButtonClick(String[] ids) { + checkAndPlayShutterSound(); + mMultiCameraUI.enableShutter(false); + boolean halZSLCheck = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_HAL_ZAL, false); + for (String id : ids) { + try { + int cameraId = Integer.parseInt(id); + final CaptureRequest.Builder captureBuilder = + mCameraDevices[cameraId].createCaptureRequest( + CameraDevice.TEMPLATE_STILL_CAPTURE); + captureBuilder.addTarget(mImageReaders[cameraId].getSurface()); + int index = mCameraIDList.indexOf(String.valueOf(cameraId)); + captureBuilder.addTarget(mMultiCameraUI.getSurfaceViewList().get( + index).getHolder().getSurface()); + applySettingsForCapture(captureBuilder, cameraId); + // Use the same AE and AF modes as the preview. + captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + if (halZSLCheck) { + captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, true); + } else { + captureBuilder.set(CaptureRequest.CONTROL_ENABLE_ZSL, false); + } + setAutoFlash(captureBuilder); + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, + CameraUtil.getJpegRotation(cameraId, rotation)); + mCameraCaptureSessions[cameraId].capture(captureBuilder.build(), + mCaptureStillCallback, mMultiCameraModule.getMyCameraHandler()); + Log.d(TAG, " cameraCaptureSession" + id + " captured "); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onVideoButtonClick(String[] ids) { + + } + + @Override + public void onButtonPause(String[] ids) { + + } + + @Override + public void onButtonContinue(String[] ids) { + + } + + @Override + public void onOrientationChanged(int orientation) { + + } + + @Override + public boolean isRecordingVideo() { + return false; + } + + private void checkAndPlayShutterSound() { + boolean isPlay = mLocalSharedPref.getBoolean(MultiSettingsActivity.KEY_SHUTTER_SOUND, true); + if (mSoundPlayer != null && isPlay) { + mSoundPlayer.play(SoundClips.SHUTTER_CLICK); + } + } + + private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = + new MediaSaveService.OnMediaSavedListener() { + @Override + public void onMediaSaved(Uri uri) { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } + } + }; + + private CameraCaptureSession.CaptureCallback mCaptureStillCallback + = new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, " mCaptureCallback onCaptureCompleted "); + mMultiCameraModule.getMainHandler().post(new Runnable() { + @Override + public void run() { + Log.d(TAG, " enable Shutter " ); + mMultiCameraUI.enableShutter(true); + } + }); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, " mCaptureCallback onCaptureFailed " ); + } + + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, " mCaptureCallback onCaptureSequenceCompleted "); + } + }; + + private void applySettingsForCapture(CaptureRequest.Builder builder, int id) { + builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_IDLE); + applyJpegQuality(builder); + } + + private void applyJpegQuality(CaptureRequest.Builder request) { + String defaultValue = mActivity.getString(R.string.pref_camera_jpegquality_default); + String value = mLocalSharedPref.getString(MultiSettingsActivity.KEY_PICTURE_QUALITY, + defaultValue); + int jpegQuality = getQualityNumber(value); + request.set(CaptureRequest.JPEG_QUALITY, (byte) jpegQuality); + } + + private int getQualityNumber(String jpegQuality) { + if (jpegQuality == null) { + return 85; + } + try { + int qualityPercentile = Integer.parseInt(jpegQuality); + if (qualityPercentile >= 0 && qualityPercentile <= 100) + return qualityPercentile; + else + return 85; + } catch (NumberFormatException nfe) { + //chosen quality is not a number, continue + } + int value = 0; + switch (jpegQuality) { + case "superfine": + value = CameraProfile.QUALITY_HIGH; + break; + case "fine": + value = CameraProfile.QUALITY_MEDIUM; + break; + case "normal": + value = CameraProfile.QUALITY_LOW; + break; + default: + return 85; + } + return CameraProfile.getJpegEncodingQualityParameter(value); + } + + private Size parsePictureSize(String value) { + int indexX = value.indexOf('x'); + int width = Integer.parseInt(value.substring(0, indexX)); + int height = Integer.parseInt(value.substring(indexX + 1)); + return new Size(width, height); + } + + private void initCameraCharacteristics() { + mCharacteristics = new ArrayList<>(); + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + try { + String[] cameraIdList = manager.getCameraIdList(); + Log.d(TAG, "cameraIdList size =" + cameraIdList.length); + for (int i = 0; i < cameraIdList.length; i++) { + String cameraId = cameraIdList[i]; + CameraCharacteristics characteristics + = manager.getCameraCharacteristics(cameraId); + mCharacteristics.add(i, characteristics); + try { + mMaxPreviewSize[i] = characteristics.get(CaptureModule.max_preview_size); + } catch (IllegalArgumentException e) { + Log.e(TAG, "getMaxPreviewSize no vendorTag max_preview_size:"); + } + if (mMaxPreviewSize[i] != null) { + Log.d(TAG, " init cameraId :" + cameraId + ", i :" + i + + ", maxPreviewSize :" + mMaxPreviewSize[i][0]+ "x" + + mMaxPreviewSize[i][1]); + } + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private void setAutoFlash(CaptureRequest.Builder requestBuilder) { + if (true) { + requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); + } + } }
\ No newline at end of file diff --git a/src/com/android/camera/multi/MultiSettingsActivity.java b/src/com/android/camera/multi/MultiSettingsActivity.java index a3bd75f80..a3bd75f80 100755..100644 --- a/src/com/android/camera/multi/MultiSettingsActivity.java +++ b/src/com/android/camera/multi/MultiSettingsActivity.java diff --git a/src/com/android/camera/multi/MultiVideoModule.java b/src/com/android/camera/multi/MultiVideoModule.java index b07e7ca18..0caf30d30 100644 --- a/src/com/android/camera/multi/MultiVideoModule.java +++ b/src/com/android/camera/multi/MultiVideoModule.java @@ -1,1332 +1,1338 @@ -/*
- * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved.
- * Not a Contribution.
- *
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.camera.multi;
-
-import android.content.Context;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.ImageFormat;
-import android.graphics.Point;
-import android.hardware.camera2.CameraAccessException;
-import android.hardware.camera2.CameraCaptureSession;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.TotalCaptureResult;
-import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.params.SessionConfiguration;
-import android.hardware.camera2.params.StreamConfigurationMap;
-import android.location.Location;
-import android.media.AudioManager;
-import android.media.CamcorderProfile;
-import android.media.Image;
-import android.media.ImageReader;
-import android.media.MediaRecorder;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Looper;
-import android.os.Message;
-import android.os.SystemClock;
-import android.os.ParcelFileDescriptor;
-import android.provider.MediaStore;
-import android.util.Log;
-import android.util.Size;
-import android.view.OrientationEventListener;
-import android.util.SparseIntArray;
-import android.view.Surface;
-import android.view.SurfaceHolder;
-import android.view.WindowManager;
-import android.widget.Toast;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.text.SimpleDateFormat;
-import java.util.Arrays;
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.List;
-import java.util.Date;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-import com.android.camera.CameraActivity;
-import com.android.camera.CaptureModule;
-import com.android.camera.CameraSettings;
-import com.android.camera.ComboPreferences;
-import com.android.camera.Exif;
-import com.android.camera.exif.ExifInterface;
-import com.android.camera.LocationManager;
-import com.android.camera.MediaSaveService;
-import com.android.camera.PhotoModule.NamedImages;
-import com.android.camera.PhotoModule.NamedImages.NamedEntity;
-import com.android.camera.SoundClips;
-import com.android.camera.Storage;
-import com.android.camera.Thumbnail;
-import com.android.camera.util.CameraUtil;
-import com.android.camera.util.SettingTranslation;
-import com.android.camera.util.PersistUtil;
-import com.android.camera.ui.RotateTextToast;
-
-import org.codeaurora.snapcam.R;
-
-public class MultiVideoModule implements MultiCamera, LocationManager.Listener,
- MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {
-
- private static final String TAG = "SnapCam_MultiVideoModule";
-
- private static final boolean DEBUG =
- (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_LOG) ||
- (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_ALL);
-
- private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
- private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
- private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
- private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
-
- private static final int WAIT_SURFACE = 0;
- private static final int OPEN_CAMERA = 1;
-
- private static final int SCREEN_DELAY = 2 * 60 * 1000;
-
- private static final int MAX_NUM_CAM = 16;
-
- private int mCameraListIndex = 0;
-
- private static final CaptureRequest.Key<Byte> override_resource_cost_validation =
- new CaptureRequest.Key<>(
- "org.codeaurora.qcamera3.sessionParameters.overrideResourceCostValidation",
- byte.class);
-
- private CameraActivity mActivity;
- private MultiCameraUI mMultiCameraUI;
- private MultiCameraModule mMultiCameraModule;
- private SharedPreferences mLocalSharedPref;
- private ArrayList<CameraCharacteristics> mCharacteristics;
- private CameraDevice[] mCameraDevices = new CameraDevice[MAX_NUM_CAM];
- private CameraCaptureSession[] mCameraPreviewSessions = new CameraCaptureSession[MAX_NUM_CAM];
- private ContentValues[] mCurrentVideoValues = new ContentValues[MAX_NUM_CAM];
- private ImageReader[] mImageReaders = new ImageReader[MAX_NUM_CAM];
- private MediaRecorder[] mMediaRecorders = new MediaRecorder[MAX_NUM_CAM];
- private String[] mNextVideoAbsolutePaths = new String[MAX_NUM_CAM];
- private boolean mPaused = true;
-
- private NamedImages mNamedImages;
-
- private Uri mCurrentVideoUri;
-
- private boolean mMediaRecorderPausing = false;
-
- private boolean mRecordingTimeCountsDown = false;
-
- private LocationManager mLocationManager;
- private CamcorderProfile mProfile;
-
- private Size[] mVideoSize = new Size[MAX_NUM_CAM];
- private Size mPreviewSizes[] = new Size[MAX_NUM_CAM];
- private int[][] mMaxPreviewSize = new int[MAX_NUM_CAM][];
-
- private boolean mCaptureTimeLapse = false;
- // Default 0. If it is larger than 0, the camcorder is in time lapse mode.
- private int mTimeBetweenTimeLapseFrameCaptureMs = 0;
-
- private String[] mVideoFilenames = new String[MAX_NUM_CAM];
-
- private long mRecordingStartTime;
- private long mRecordingTotalTime;
-
- private ParcelFileDescriptor mVideoFileDescriptor;
-
- // The video duration limit. 0 means no limit.
- private int mMaxVideoDurationInMs;
-
- private int mAudioEncoder;
- private String mVideoRotation;
-
- private SoundClips.Player mSoundPlayer;
-
- /**
- * Whether the app is recording video now
- */
- private boolean[] mIsRecordingVideos = new boolean[MAX_NUM_CAM];
-
- private String[] mCameraIds;
- private ArrayList<String> mCameraIDList = new ArrayList<>();
-
- /**
- * Orientation of the camera sensor
- */
- private int mSensorOrientation;
- private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN;
-
- /**
- * {@link CaptureRequest.Builder} for the camera preview
- */
- private CaptureRequest.Builder[] mPreviewRequestBuilders = new CaptureRequest.Builder[MAX_NUM_CAM];
-
- private Handler mCameraHandler;
- private HandlerThread mCameraThread;
-
- private ContentResolver mContentResolver;
- /**
- * A {@link Semaphore} make sure the camera open callback happens first before closing the
- * camera.
- */
- private Semaphore mCameraOpenCloseLock = new Semaphore(3);
-
- public MultiVideoModule(CameraActivity activity, MultiCameraUI ui, MultiCameraModule module) {
- mActivity = activity;
- mMultiCameraUI = ui;
- mMultiCameraModule = module;
- mContentResolver = mActivity.getContentResolver();
- mLocationManager = new LocationManager(mActivity, this);
- mNamedImages = new NamedImages();
- mLocalSharedPref = mActivity.getSharedPreferences(
- ComboPreferences.getLocalSharedPreferencesName(mActivity,
- "multi" + mMultiCameraModule.getCurrenCameraMode()), Context.MODE_PRIVATE);
- startBackgroundThread();
- initCameraCharacteristics();
- }
-
- @Override
- public void onResume(String[] ids) {
- for (String id : ids) {
- mCameraIDList.add(id);
- }
- // Set up sound playback for video record and video stop
- if (mSoundPlayer == null) {
- mSoundPlayer = SoundClips.getPlayer(mActivity);
- }
- mPaused = false;
- initializeValues();
- startBackgroundThread();
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- updateVideoSize(cameraId);
- int index = mCameraIDList.indexOf(id);
- mMultiCameraUI.setPreviewSize(index, mPreviewSizes[cameraId].getWidth(),
- mPreviewSizes[cameraId].getHeight());
- }
- }
-
- @Override
- public void onPause() {
- mPaused = true;
- if (mSoundPlayer != null) {
- mSoundPlayer.release();
- mSoundPlayer = null;
- }
- for (String id : mCameraIds) {
- int cameraId = Integer.parseInt(id);
- Log.d(TAG, " onPause id :" + cameraId + " recording is :" +
- (mIsRecordingVideos[cameraId] ? "STOPED" : "START"));
- if (mIsRecordingVideos[cameraId]) {
- stopRecordingVideo(cameraId);
- }
- }
-
- if (mCameraIDList != null) {
- mCameraIDList.clear();
- }
- mCameraListIndex = 0;
- closeCamera();
- stopBackgroundThread();
- }
-
- @Override
- public boolean openCamera(String[] ids) {
- mCameraIds = ids;
- for (String id : ids) {
- mCameraIDList.add(id);
- }
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- return true;
- }
-
- @Override
- public void startPreview() {
-
- }
-
- @Override
- public void closeSession() {
-
- }
-
- @Override
- public void closeCamera() {
- Log.d(TAG, "closeCamera");
- /* no need to set this in the callback and handle asynchronously. This is the same
- reason as why we release the semaphore here, not in camera close callback function
- as we don't have to protect the case where camera open() gets called during camera
- close(). The low level framework/HAL handles the synchronization for open()
- happens after close() */
- try {
- // Close camera starting with AUX first
- for (int i = MAX_NUM_CAM - 1; i >= 0; i--) {
- if (null != mCameraDevices[i]) {
- if (!mCameraOpenCloseLock.tryAcquire(2000, TimeUnit.MILLISECONDS)) {
- Log.d(TAG, "Time out waiting to lock camera closing.");
- throw new RuntimeException("Time out waiting to lock camera closing");
- }
- Log.d(TAG, "Closing camera: " + mCameraDevices[i].getId());
- mCameraDevices[i].close();
- mCameraDevices[i] = null;
- mCameraPreviewSessions[i] = null;
- }
- if (null != mImageReaders[i]) {
- mImageReaders[i].close();
- mImageReaders[i] = null;
- }
- }
- } catch (InterruptedException e) {
- mCameraOpenCloseLock.release();
- throw new RuntimeException("Interrupted while trying to lock camera closing.", e);
- } catch (IllegalStateException e) {
- e.printStackTrace();
- } finally {
- mCameraOpenCloseLock.release();
- }
- }
-
- @Override
- public void onVideoButtonClick(String[] ids) {
- checkAndPlayShutterSound(mIsRecordingVideos[0]);
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- Log.d(TAG, " onVideoButtonClick id :" + cameraId + " recording is :" +
- (mIsRecordingVideos[cameraId] ? "STOPED" : "START"));
- if (mIsRecordingVideos[cameraId]) {
- stopRecordingVideo(cameraId);
- } else {
- startRecordingVideo(cameraId);
- }
- }
- }
-
- @Override
- public void onShutterButtonClick(String[] ids) {
- checkAndPlayCaptureSound();
- mMultiCameraUI.enableShutter(false);
- for (String id : ids) {
- Log.d(TAG, "onShutterButtonClick id :" + id);
- try {
- int cameraId = Integer.parseInt(id);
- if (null == mActivity || null == mCameraDevices[cameraId]) {
- warningToast("Camera is not ready yet to take a video snapshot.");
- return;
- }
- final CaptureRequest.Builder captureBuilder =
- mCameraDevices[cameraId].createCaptureRequest(
- CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
- captureBuilder.addTarget(mImageReaders[cameraId].getSurface());
- int index = mCameraIDList.indexOf(String.valueOf(cameraId));
- captureBuilder.addTarget(mMultiCameraUI.getSurfaceViewList().get(
- index).getHolder().getSurface());
- // Use the same AE and AF modes as the preview.
- captureBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- captureBuilder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY, (byte) 80);
- captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
- captureBuilder.set(CaptureRequest.CONTROL_AE_MODE,
- CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
-
- // Orientation
- int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation();
- captureBuilder.set(CaptureRequest.JPEG_ORIENTATION,
- CameraUtil.getJpegRotation(cameraId, rotation));
- mCameraPreviewSessions[cameraId].capture(captureBuilder.build(),
- mCaptureStillCallback, mMultiCameraModule.getMyCameraHandler());
- Log.d(TAG, " cameraCaptureSession" + id + " captured ");
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- public void onButtonPause(String[] ids) {
- mRecordingTotalTime += SystemClock.uptimeMillis() - mRecordingStartTime;
- mMediaRecorderPausing = true;
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- mMediaRecorders[cameraId].pause();
- }
- }
-
- @Override
- public void onButtonContinue(String[] ids) {
- mMediaRecorderPausing = false;
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- mMediaRecorders[cameraId].resume();
- mRecordingStartTime = SystemClock.uptimeMillis();
- updateRecordingTime(cameraId);
- }
- }
-
- @Override
- public void onErrorListener(int error) {
- enableRecordingLocation(false);
- }
-
- // from MediaRecorder.OnErrorListener
- @Override
- public void onError(MediaRecorder mr, int what, int extra) {
- Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra);
- String[] ids = {"0", "1"};
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- stopRecordingVideo(cameraId);
- }
- if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) {
- // We may have run out of space on the sdcard.
- mActivity.updateStorageSpaceAndHint();
- } else {
- warningToast("MediaRecorder error. what=" + what + ". extra=" + extra);
- }
- }
-
- // from MediaRecorder.OnInfoListener
- @Override
- public void onInfo(MediaRecorder mr, int what, int extra) {
- String[] ids = {"0", "1"};
- if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- if (mIsRecordingVideos[cameraId]) {
- stopRecordingVideo(cameraId);
- }
- }
- } else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
- for (String id : ids) {
- int cameraId = Integer.parseInt(id);
- if (mIsRecordingVideos[cameraId]) {
- stopRecordingVideo(cameraId);
- }
- }
- // Show the toast.
- RotateTextToast.makeText(mActivity, R.string.video_reach_size_limit,
- Toast.LENGTH_LONG).show();
- }
- }
-
- @Override
- public void onOrientationChanged(int orientation) {
- mOrientation = orientation;
- }
-
- @Override
- public boolean isRecordingVideo() {
- for (int i = 0; i < mIsRecordingVideos.length; i++) {
- if (mIsRecordingVideos[i]) return true;
- }
- return false;
- }
-
- private void openCameraInSequence(String id) {
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- CameraCharacteristics characteristics = null;
- try {
- characteristics = manager.getCameraCharacteristics(id);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- Log.d(TAG, "openCameraInSequence " + id + ", mSensorOrientation :" + mSensorOrientation);
- try {
- if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
- Log.d(TAG, "Time out waiting to lock camera opening.");
- throw new RuntimeException("Time out waiting to lock camera opening");
- }
- manager.openCamera(id, mStateCallback, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- manager = null;
- characteristics = null;
- }
-
- private void initCameraCharacteristics() {
- mCharacteristics = new ArrayList<>();
- CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE);
- try {
- String[] cameraIdList = manager.getCameraIdList();
- Log.d(TAG, "cameraIdList size =" + cameraIdList.length);
- for (int i = 0; i < cameraIdList.length; i++) {
- String cameraId = cameraIdList[i];
- CameraCharacteristics characteristics
- = manager.getCameraCharacteristics(cameraId);
- mCharacteristics.add(i, characteristics);
- try {
- mMaxPreviewSize[i] = characteristics.get(CaptureModule.max_preview_size);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "getMaxPreviewSize no vendorTag max_preview_size:");
- }
- if (mMaxPreviewSize[i] != null) {
- Log.d(TAG, " init cameraId :" + cameraId + ", i :" + i +
- ", maxPreviewSize :" + mMaxPreviewSize[i][0]+ "x" +
- mMaxPreviewSize[i][1]);
- }
- }
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private void startBackgroundThread() {
- if (mCameraThread == null) {
- mCameraThread = new HandlerThread("CameraBackground");
- mCameraThread.start();
- }
- if (mCameraHandler == null) {
- mCameraHandler = new MyCameraHandler(mCameraThread.getLooper());
- }
- }
-
- private void stopBackgroundThread() {
- mCameraThread.quitSafely();
- try {
- mCameraThread.join();
- mCameraThread = null;
- mCameraHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
-
- private class MyCameraHandler extends Handler {
-
- public MyCameraHandler(Looper looper) {
- super(looper);
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case WAIT_SURFACE:
- int id = msg.arg1;
- int index = mCameraIDList.indexOf(String.valueOf(id));
- Log.v(TAG, "WAIT_SURFACE id :" + id + ", index :" + index);
- if (index == -1) {
- break;
- }
- Surface surface = mMultiCameraUI.getSurfaceViewList().get(index)
- .getHolder().getSurface();
- if (surface.isValid()) {
- createCaptureSessions(id, surface);
- } else {
- mCameraHandler.sendMessageDelayed(msg, 200);
- Log.v(TAG, "Surface is invalid, wait more 200ms surfaceCreated");
- }
- break;
- case OPEN_CAMERA:
- if (mCameraListIndex == mCameraIDList.size()) {
- mCameraListIndex = 0;
- } else {
- String cameraId = mCameraIDList.get(mCameraListIndex);
- openCameraInSequence(cameraId);
- mCameraListIndex ++;
- Log.v(TAG, " OPEN_CAMERA cameraId :" + cameraId + ", mCameraListIndex :"
- + mCameraListIndex);
- }
- break;
- }
- }
- }
-
- private void createVideoSnapshotImageReader(int id) {
- if (mImageReaders[id] != null) {
- mImageReaders[id].close();
- }
- mImageReaders[id] = ImageReader.newInstance(mVideoSize[id].getWidth(),
- mVideoSize[id].getHeight(),
- ImageFormat.JPEG, /*maxImages*/2);
- mImageReaders[id].setOnImageAvailableListener(
- mOnImageAvailableListener, mMultiCameraModule.getMyCameraHandler());
- }
-
- private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
-
- @Override
- public void onOpened(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- mCameraDevices[id] = cameraDevice;
- Log.d(TAG, "onOpened " + id);
- mCameraOpenCloseLock.release();
- createCameraPreviewSession(id);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mMultiCameraUI.onCameraOpened(id);
- }
- });
- }
-
- @Override
- public void onDisconnected(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.d(TAG, "onDisconnected " + id);
- mCameraOpenCloseLock.release();
- mCameraDevices[id] = null;
- }
-
- @Override
- public void onError(CameraDevice cameraDevice, int error) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.e(TAG, "onError " + id + " " + error);
- mCameraOpenCloseLock.release();
-
- if (null != mActivity) {
- Toast.makeText(mActivity,"open camera error id =" + id,
- Toast.LENGTH_LONG).show();
- mActivity.finish();
- }
- }
-
- @Override
- public void onClosed(CameraDevice cameraDevice) {
- int id = Integer.parseInt(cameraDevice.getId());
- Log.d(TAG, "onClosed " + id);
- mCameraOpenCloseLock.release();
- mCameraDevices[id] = null;
- }
- };
-
- private CameraCaptureSession.CaptureCallback mCaptureStillCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- @Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
- TotalCaptureResult result) {
- Log.d(TAG, " mCaptureCallback onCaptureCompleted ");
- mMultiCameraModule.getMainHandler().post(new Runnable() {
- @Override
- public void run() {
- Log.d(TAG, " enable Shutter " );
- mMultiCameraUI.enableShutter(true);
- }
- });
- }
-
- @Override
- public void onCaptureFailed(CameraCaptureSession session,
- CaptureRequest request,
- CaptureFailure result) {
- Log.d(TAG, " mCaptureCallback onCaptureFailed " );
- }
-
-
- @Override
- public void onCaptureSequenceCompleted(CameraCaptureSession session, int
- sequenceId, long frameNumber) {
- Log.d(TAG, " mCaptureCallback onCaptureSequenceCompleted ");
- }
- };
-
- private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
- @Override
- public void onMediaSaved(Uri uri) {
- if (uri != null) {
- mActivity.notifyNewMedia(uri);
- }
- }
- };
-
- public void enableRecordingLocation(boolean enable) {
- mLocationManager.recordLocation(enable);
- }
-
- private Size parsePictureSize(String value) {
- int indexX = value.indexOf('x');
- int width = Integer.parseInt(value.substring(0, indexX));
- int height = Integer.parseInt(value.substring(indexX + 1));
- return new Size(width, height);
- }
-
- private void updateVideoSize(int id) {
- String defaultSize = mActivity.getString(R.string.pref_multi_camera_video_quality_default);
- int index = mCameraIDList.indexOf(String.valueOf(id));
- String videoSize = mLocalSharedPref.getString(
- MultiSettingsActivity.KEY_VIDEO_SIZES.get(index), defaultSize);
- Size size = parsePictureSize(videoSize);
- mVideoSize[id] = size;
- Log.v(TAG, " updateVideoSize size :" + mVideoSize[id].getWidth() + "x" +
- mVideoSize[id].getHeight());
-
- Size[] prevSizes = getSupportedOutputSize(id, MediaRecorder.class);
- mPreviewSizes[id] = getOptimalVideoPreviewSize(id, mVideoSize[id], prevSizes);
- }
-
- private Size[] getSupportedOutputSize(int cameraId, Class cl) {
- StreamConfigurationMap map = mCharacteristics.get(cameraId).get(
- CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- Size[] normal = map.getOutputSizes(cl);
- Size[] high = map.getHighResolutionOutputSizes(ImageFormat.PRIVATE);
- Size[] ret = new Size[normal.length + high.length];
- System.arraycopy(normal, 0, ret, 0, normal.length);
- System.arraycopy(high, 0, ret, normal.length, high.length);
- return ret;
- }
-
- private Size getOptimalVideoPreviewSize(int id, Size VideoSize, Size[] prevSizes) {
- Point[] points = new Point[prevSizes.length];
-
- int index = 0;
- int point_max[] = mMaxPreviewSize[id];
- int max_size = -1;
- if (point_max != null) {
- max_size = point_max[0] * point_max[1];
- }
- for (Size s : prevSizes) {
- if (max_size != -1) {
- int size = s.getWidth() * s.getHeight();
- if (s.getWidth() == s.getHeight()) {
- if (s.getWidth() > Math.max(point_max[0], point_max[1]))
- continue;
- } else if (size > max_size || size == 0) {
- continue;
- }
- }
- points[index++] = new Point(s.getWidth(), s.getHeight());
- }
-
- int optimalPickIndex = CameraUtil.getOptimalVideoPreviewSize(mActivity, points, VideoSize);
- return (optimalPickIndex == -1) ? null :
- new Size(points[optimalPickIndex].x, points[optimalPickIndex].y);
- }
-
- /**
- * Creates a new {@link CameraCaptureSession} for camera preview.
- */
- private void createCameraPreviewSession(int id) {
- // This is the output Surface we need to start preview.
- int index = mCameraIDList.indexOf(String.valueOf(id));
- Log.v(TAG, "createCameraPreviewSession id :" + id + ", index :" + index);
- Surface surface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder().getSurface();
-
- if (surface.isValid()) {
- createCaptureSessions(id, surface);
- } else {
- Message msg = Message.obtain();
- msg.what = WAIT_SURFACE;
- msg.arg1 = id;
- mCameraHandler.sendMessageDelayed(msg, 200);
- Log.v(TAG, "Surface is invalid, wait 200ms surfaceCreated");
- }
-
- }
-
- private void createCaptureSessions(int id, Surface surface) {
- try {
- // We set up a CaptureRequest.Builder with the output Surface.
- mPreviewRequestBuilders[id]
- = mCameraDevices[id].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- mPreviewRequestBuilders[id].addTarget(surface);
- mPreviewRequestBuilders[id].setTag(id);
-
- CameraCaptureSession.StateCallback stateCallback =
- new CameraCaptureSession.StateCallback() {
- @Override
- public void onConfigured(CameraCaptureSession cameraCaptureSession) {
- // The camera is already closed
- if (null == mCameraDevices[id]) {
- return;
- }
- Log.v(TAG, " CameraCaptureSession onConfigured id :" + id);
- // When the session is ready, we start displaying the preview.
- mCameraPreviewSessions[id] = cameraCaptureSession;
- try {
- // Auto focus should be continuous for camera preview.
- mPreviewRequestBuilders[id].set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- // Finally, we start displaying the camera preview.
- mCameraPreviewSessions[id].setRepeatingRequest(
- mPreviewRequestBuilders[id].build(),
- mCaptureCallback, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
-
- if (mCameraDevices[mCameraListIndex] == null) {
- Message msg = Message.obtain();
- msg.what = OPEN_CAMERA;
- if (mCameraHandler != null) {
- mCameraHandler.sendMessage(msg);
- }
- }
- }
-
- @Override
- public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
- showToast("onConfigureFailed");
- }
- };
-
- try {
- final byte enable = 1;
- mPreviewRequestBuilders[id].set(override_resource_cost_validation, enable);
- Log.v(TAG, " set" + override_resource_cost_validation + " is 1");
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- }
-
- List<OutputConfiguration> outConfigurations = new ArrayList<>(1);
- outConfigurations.add(new OutputConfiguration(surface));
-
- SessionConfiguration sessionConfiguration = new SessionConfiguration(
- SessionConfiguration.SESSION_REGULAR, outConfigurations,
- new HandlerExecutor(mCameraHandler), stateCallback);
- sessionConfiguration.setSessionParameters(mPreviewRequestBuilders[id].build());
- mCameraDevices[id].createCaptureSession(sessionConfiguration);
-
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private class HandlerExecutor implements Executor {
- private final Handler ihandler;
-
- public HandlerExecutor(Handler handler) {
- ihandler = handler;
- }
-
- @Override
- public void execute(Runnable runCmd) {
- ihandler.post(runCmd);
- }
- }
-
- /**
- * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture.
- */
- private CameraCaptureSession.CaptureCallback mCaptureCallback
- = new CameraCaptureSession.CaptureCallback() {
-
- private void process(CaptureResult result) {
- Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
- Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
- if (DEBUG) {
- Log.v(TAG, "process afState :" + afState + ", aeState :" + aeState);
- }
- }
-
- @Override
- public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request,
- CaptureResult partialResult) {
- process(partialResult);
- }
-
- @Override
- public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
- TotalCaptureResult result) {
- process(result);
- }
-
- };
-
- private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
- = new ImageReader.OnImageAvailableListener() {
-
- @Override
- public void onImageAvailable(ImageReader reader) {
- Log.v(TAG, " onImageAvailable ...");
- Image image = reader.acquireNextImage();
- long imageTime = System.currentTimeMillis();
- mNamedImages.nameNewImage(imageTime);
- NamedEntity name = mNamedImages.getNextNameEntity();
- String title = (name == null) ? null : name.title;
- long date = (name == null) ? -1 : name.date;
-
- ByteBuffer buffer = image.getPlanes()[0].getBuffer();
- byte[] bytes = new byte[buffer.remaining()];
- buffer.get(bytes);
-
- int orientation = 0;
- ExifInterface exif = Exif.getExif(bytes);
- orientation = Exif.getOrientation(exif);
- String saveFormat = "jpeg";
- mActivity.getMediaSaveService().addImage(bytes, title, date,
- null, image.getWidth(), image.getHeight(), orientation, exif,
- mOnMediaSavedListener, mContentResolver, saveFormat);
- mActivity.updateThumbnail(bytes);
- image.close();
- mMultiCameraModule.updateTakingPicture();
- }
- };
-
- private void initializeValues() {
- updateAudioEncoder();
- updateVideoRotation();
- }
-
- private void checkAndPlayShutterSound(boolean isStarted) {
- if (mSoundPlayer != null) {
- mSoundPlayer.play(isStarted? SoundClips.STOP_VIDEO_RECORDING
- : SoundClips.START_VIDEO_RECORDING);
- }
- }
-
- private void checkAndPlayCaptureSound() {
- if (mSoundPlayer != null) {
- mSoundPlayer.play(SoundClips.SHUTTER_CLICK);
- }
- }
-
- private void closePreviewSession(int id) {
- if (mCameraPreviewSessions[id] != null) {
- Log.v(TAG, "closePreviewSession id :" + id);
- mCameraPreviewSessions[id].close();
- mCameraPreviewSessions[id] = null;
- }
- }
-
- private void startRecordingVideo(final int id) {
- int index = mCameraIDList.indexOf(String.valueOf(id));
- if (null == mCameraDevices[id] ||
- !mMultiCameraUI.getSurfaceViewList().get(index).isEnabled()) {
- return;
- }
- Log.v(TAG, " startRecordingVideo " + id);
- try {
- closePreviewSession(id);
- setUpMediaRecorder(id);
- createVideoSnapshotImageReader(id);
- mPreviewRequestBuilders[id] = mCameraDevices[id].createCaptureRequest(
- CameraDevice.TEMPLATE_RECORD);
- if (true) {
- mPreviewRequestBuilders[id].set(CaptureRequest.NOISE_REDUCTION_MODE,
- CaptureRequest.NOISE_REDUCTION_MODE_FAST);
- } else {
- mPreviewRequestBuilders[id].set(CaptureRequest.NOISE_REDUCTION_MODE,
- CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY);
- }
- List<Surface> surfaces = new ArrayList<>();
-
- // Set up Surface for the camera preview
- Surface previewSurface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder()
- .getSurface();
- surfaces.add(previewSurface);
- mPreviewRequestBuilders[id].addTarget(previewSurface);
-
- // Set up Surface for the MediaRecorder
- Surface recorderSurface = mMediaRecorders[id].getSurface();
- surfaces.add(recorderSurface);
- mPreviewRequestBuilders[id].addTarget(recorderSurface);
- surfaces.add(mImageReaders[id].getSurface());
-
- // Start a capture session
- // Once the session starts, we can update the UI and start recording
- mCameraDevices[id].createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
-
- @Override
- public void onConfigured(CameraCaptureSession cameraCaptureSession) {
- mCameraPreviewSessions[id] = cameraCaptureSession;
- updatePreview(id);
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mIsRecordingVideos[id] = true;
- // Start recording
- mMediaRecorders[id].start();
- requestAudioFocus();
- mRecordingTotalTime = 0L;
- mRecordingStartTime = SystemClock.uptimeMillis();
- mMediaRecorderPausing = false;
- mMultiCameraUI.resetPauseButton();
- mMultiCameraUI.showRecordingUI(true);
- updateRecordingTime(id);
- keepScreenOn();
- Log.v(TAG, " startRecordingVideo done " + id);
- }
- });
- }
-
- @Override
- public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {
- if (null != mActivity) {
- Toast.makeText(mActivity, "Configure Failed", Toast.LENGTH_SHORT).show();
- }
- }
- }, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException | IllegalStateException | IOException e) {
- e.printStackTrace();
- }
- }
-
- public void stopRecordingVideo(int id) {
- Log.v(TAG, " stopRecordingVideo " + id);
- mIsRecordingVideos[id] = false;
- try {
- mMediaRecorders[id].setOnErrorListener(null);
- mMediaRecorders[id].setOnInfoListener(null);
- // Stop recording
- mMediaRecorders[id].stop();
- mMediaRecorders[id].reset();
- saveVideo(id);
- keepScreenOnAwhile();
- // release media recorder
- releaseMediaRecorder(id);
- releaseAudioFocus();
- } catch (RuntimeException e) {
- Log.w(TAG, "MediaRecoder stop fail", e);
- if (mVideoFilenames[id] != null) deleteVideoFile(mVideoFilenames[id]);
- }
-
- mMultiCameraUI.showRecordingUI(false);
- if (null != mActivity) {
- Toast.makeText(mActivity, "Video saved: " + mNextVideoAbsolutePaths[id],
- Toast.LENGTH_SHORT).show();
- Log.d(TAG, "Video saved: " + mNextVideoAbsolutePaths[id]);
- }
- mNextVideoAbsolutePaths[id] = null;
- if(!mPaused) {
- createCameraPreviewSession(id);
- }
- }
-
- private final MediaSaveService.OnMediaSavedListener mOnVideoSavedListener =
- new MediaSaveService.OnMediaSavedListener() {
- @Override
- public void onMediaSaved(Uri uri) {
- if (uri != null) {
- mActivity.notifyNewMedia(uri);
- mCurrentVideoUri = uri;
- }
- }
- };
-
- private void keepScreenOn() {
- mMultiCameraModule.getMainHandler().removeMessages(MultiCameraModule.CLEAR_SCREEN_DELAY);
- mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- }
-
- private void keepScreenOnAwhile() {
- mMultiCameraModule.getMainHandler().removeMessages(MultiCameraModule.CLEAR_SCREEN_DELAY);
- mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
- mMultiCameraModule.getMainHandler().sendEmptyMessageDelayed(
- MultiCameraModule.CLEAR_SCREEN_DELAY, SCREEN_DELAY);
- }
-
- private void releaseMediaRecorder(int id) {
- Log.v(TAG, "Releasing media recorder.");
- cleanupEmptyFile(id);
- if (mMediaRecorders[id] != null) {
- try{
- mMediaRecorders[id].reset();
- mMediaRecorders[id].release();
- }catch (RuntimeException e) {
- e.printStackTrace();
- }
- mMediaRecorders[id] = null;
- }
- }
-
- /*
- * Make sure we're not recording music playing in the background, ask the
- * MediaPlaybackService to pause playback.
- */
- private void requestAudioFocus() {
- AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE);
- // Send request to obtain audio focus. This will stop other
- // music stream.
- int result = am.requestAudioFocus(null, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
- if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
- Log.v(TAG, "Audio focus request failed");
- }
- }
-
- private void releaseAudioFocus() {
- AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE);
- int result = am.abandonAudioFocus(null);
- if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) {
- Log.v(TAG, "Audio focus release failed");
- }
- }
-
- private void cleanupEmptyFile(int id) {
- if (mVideoFilenames[id] != null) {
- File f = new File(mVideoFilenames[id]);
- if (f.length() == 0 && f.delete()) {
- Log.v(TAG, "Empty video file deleted: " + mVideoFilenames[id]);
- mVideoFilenames[id] = null;
- }
- }
- }
-
- private void updateAudioEncoder() {
- String audioEncoderStr = mActivity.getResources().getString(
- R.string.pref_camera_audioencoder_default);
- if (mLocalSharedPref != null) {
- audioEncoderStr = mLocalSharedPref.getString(MultiSettingsActivity.KEY_AUDIO_ENCODER,
- audioEncoderStr);
- }
- mAudioEncoder = SettingTranslation.getAudioEncoder(audioEncoderStr);
- }
-
- private void updateVideoRotation() {
- String defaultValue = mActivity.getResources().getString(
- R.string.pref_camera_video_rotation_default);
- if (mLocalSharedPref != null) {
- mVideoRotation = mLocalSharedPref.getString(MultiSettingsActivity.KEY_VIDEO_ROTATION,
- defaultValue);
- }
- }
-
- private void deleteVideoFile(String fileName) {
- Log.v(TAG, "Deleting video " + fileName);
- File f = new File(fileName);
- if (!f.delete()) {
- Log.v(TAG, "Could not delete " + fileName);
- }
- }
-
- private void saveVideo(int id) {
- File origFile = new File(mVideoFilenames[id]);
- if (!origFile.exists() || origFile.length() <= 0) {
- Log.e(TAG, "Invalid file");
- mCurrentVideoValues[id] = null;
- return;
- }
-
- long duration = 0L;
- MediaMetadataRetriever retriever = new MediaMetadataRetriever();
- try {
- retriever.setDataSource(mVideoFilenames[id]);
- duration = Long.valueOf(retriever.extractMetadata(
- MediaMetadataRetriever.METADATA_KEY_DURATION));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "cannot access the file");
- }
- retriever.release();
- mActivity.getMediaSaveService().addVideo(mVideoFilenames[id],
- duration, mCurrentVideoValues[id],
- mOnVideoSavedListener, mContentResolver);
- Log.v(TAG, "saveVideo mVideoFilenames[id] :" + mVideoFilenames[id]);
- mCurrentVideoValues[id] = null;
- }
-
- private void updateRecordingTime(int id) {
- if (!mIsRecordingVideos[id] || id == 0) {
- return;
- }
-
- if (mMediaRecorderPausing) {
- return;
- }
-
- long now = SystemClock.uptimeMillis();
- long delta = now - mRecordingStartTime + mRecordingTotalTime;
-
- // Starting a minute before reaching the max duration
- // limit, we'll countdown the remaining time instead.
- boolean countdownRemainingTime = (mMaxVideoDurationInMs != 0
- && delta >= mMaxVideoDurationInMs - 60000);
-
- long deltaAdjusted = delta;
- if (countdownRemainingTime) {
- deltaAdjusted = Math.max(0, mMaxVideoDurationInMs - deltaAdjusted) + 999;
- }
- String text;
- long targetNextUpdateDelay;
- if (!mCaptureTimeLapse) {
- text = CameraUtil.millisecondToTimeString(deltaAdjusted, false);
- targetNextUpdateDelay = 1000;
- } else {
- // The length of time lapse video is different from the length
- // of the actual wall clock time elapsed. Display the video length
- // only in format hh:mm:ss.dd, where dd are the centi seconds.
- text = CameraUtil.millisecondToTimeString(getTimeLapseVideoLength(delta), true);
- targetNextUpdateDelay = mTimeBetweenTimeLapseFrameCaptureMs;
- }
- mMultiCameraUI.setRecordingTime(text);
- if (mRecordingTimeCountsDown != countdownRemainingTime) {
- // Avoid setting the color on every update, do it only
- // when it needs changing.
- mRecordingTimeCountsDown = countdownRemainingTime;
-
- int color = mActivity.getResources().getColor(countdownRemainingTime
- ? R.color.recording_time_remaining_text
- : R.color.recording_time_elapsed_text);
-
- mMultiCameraUI.setRecordingTimeTextColor(color);
- }
- long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay);
- mMultiCameraModule.getMainHandler().postDelayed(new Runnable() {
- @Override
- public void run() {
- updateRecordingTime(id);
- }
- }, actualNextUpdateDelay);
- }
-
- private long getTimeLapseVideoLength(long deltaMs) {
- // For better approximation calculate fractional number of frames captured.
- // This will update the video time at a higher resolution.
- double numberOfFrames = (double) deltaMs / mTimeBetweenTimeLapseFrameCaptureMs;
- return (long) (numberOfFrames / mProfile.videoFrameRate * 1000);
- }
-
- /**
- * Update the camera preview. {@link #startPreview()} needs to be called in advance.
- */
- private void updatePreview(int id) {
- if (null == mCameraDevices[id]) {
- return;
- }
- try {
- setUpCaptureRequestBuilder(mPreviewRequestBuilders[id]);
- mCameraPreviewSessions[id].setRepeatingRequest(mPreviewRequestBuilders[id].build(),
- null, mMultiCameraModule.getMyCameraHandler());
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- }
-
- private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
- builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
- }
-
- private String generateVideoFilename(int outputFileFormat, int id) {
- long dateTaken = System.currentTimeMillis();
- String title = createName(dateTaken);
- String filename = title + "_"+ id + CameraUtil.convertOutputFormatToFileExt(outputFileFormat);
- String mime = CameraUtil.convertOutputFormatToMimeType(outputFileFormat);
- String path = Storage.DIRECTORY + '/' + filename;
- mCurrentVideoValues[id] = new ContentValues(9);
- mCurrentVideoValues[id].put(MediaStore.Video.Media.TITLE, title);
- mCurrentVideoValues[id].put(MediaStore.Video.Media.DISPLAY_NAME, filename);
- mCurrentVideoValues[id].put(MediaStore.Video.Media.DATE_TAKEN, dateTaken);
- mCurrentVideoValues[id].put(MediaStore.MediaColumns.DATE_MODIFIED, dateTaken / 1000);
- mCurrentVideoValues[id].put(MediaStore.Video.Media.MIME_TYPE, mime);
- mCurrentVideoValues[id].put(MediaStore.Video.Media.DATA, path);
- mCurrentVideoValues[id].put(MediaStore.Video.Media.RESOLUTION,
- "" + mVideoSize[id].getWidth() + "x" + mVideoSize[id].getHeight());
- Location loc = mLocationManager.getCurrentLocation();
- if (loc != null) {
- mCurrentVideoValues[id].put(MediaStore.Video.Media.LATITUDE, loc.getLatitude());
- mCurrentVideoValues[id].put(MediaStore.Video.Media.LONGITUDE, loc.getLongitude());
- }
- mVideoFilenames[id] = path;
- return path;
- }
-
- private String createName(long dateTaken) {
- Date date = new Date(dateTaken);
- SimpleDateFormat dateFormat = new SimpleDateFormat(
- mActivity.getString(R.string.video_file_name_format));
- return dateFormat.format(date);
- }
-
- private void setUpMediaRecorder(int id) throws IOException {
- if (null == mActivity) {
- return;
- }
- Log.v(TAG, " setUpMediaRecorder " + id);
- int size = CameraSettings.VIDEO_QUALITY_TABLE.get(mVideoSize[id].getWidth() + "x"
- + mVideoSize[id].getHeight());
- if (CamcorderProfile.hasProfile(id, size)) {
- mProfile = CamcorderProfile.get(id, size);
- } else {
- warningToast(R.string.error_app_unsupported_profile);
- throw new IllegalArgumentException("error_app_unsupported_profile");
- }
-
- if (mMediaRecorders[id] == null) {
- mMediaRecorders[id] = new MediaRecorder();
- }
- mMediaRecorders[id].setAudioSource(MediaRecorder.AudioSource.MIC);
- mMediaRecorders[id].setVideoSource(MediaRecorder.VideoSource.SURFACE);
- mMediaRecorders[id].setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- if (mNextVideoAbsolutePaths[id] == null || mNextVideoAbsolutePaths[id].isEmpty()) {
- mNextVideoAbsolutePaths[id] = generateVideoFilename(mProfile.fileFormat, id);
- }
-
- mMediaRecorders[id].setMaxDuration(mMaxVideoDurationInMs);
- mMediaRecorders[id].setOutputFile(mNextVideoAbsolutePaths[id]);
- mMediaRecorders[id].setVideoEncodingBitRate(10000000);
- mMediaRecorders[id].setVideoFrameRate(30);
- mMediaRecorders[id].setVideoSize(mVideoSize[id].getWidth(), mVideoSize[id].getHeight());
- mMediaRecorders[id].setVideoEncoder(MediaRecorder.VideoEncoder.H264);
- mMediaRecorders[id].setAudioEncoder(mAudioEncoder);
- int rotation = CameraUtil.getJpegRotation(id, mOrientation);
- if (mVideoRotation != null) {
- rotation += Integer.parseInt(mVideoRotation);
- rotation = rotation % 360;
- }
- mMediaRecorders[id].setOrientationHint(rotation);
- mMediaRecorders[id].prepare();
- mMediaRecorders[id].setOnErrorListener(this);
- mMediaRecorders[id].setOnInfoListener(this);
- }
-
- private void warningToast(final String msg) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- RotateTextToast.makeText(mActivity, msg,
- Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- private void warningToast(final int sourceId) {
- warningToast(sourceId, true);
- }
-
- private void warningToast(final int sourceId, boolean isLongShow) {
- mActivity.runOnUiThread(new Runnable() {
- public void run() {
- RotateTextToast.makeText(mActivity, sourceId,
- isLongShow ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show();
- }
- });
- }
-
- /**
- * Shows a {@link Toast} on the UI thread.
- * @param text The message to show
- */
- private void showToast(final String text) {
- if (mActivity != null) {
- mActivity.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show();
- }
- });
- }
- }
-}
+/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Not a Contribution. + * + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.camera.multi; + +import android.content.Context; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.ImageFormat; +import android.graphics.Point; +import android.hardware.camera2.CameraAccessException; +import android.hardware.camera2.CameraCaptureSession; +import android.hardware.camera2.CameraCharacteristics; +import android.hardware.camera2.CameraDevice; +import android.hardware.camera2.CaptureFailure; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraMetadata; +import android.hardware.camera2.CaptureRequest; +import android.hardware.camera2.CaptureResult; +import android.hardware.camera2.TotalCaptureResult; +import android.hardware.camera2.params.OutputConfiguration; +import android.hardware.camera2.params.SessionConfiguration; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.location.Location; +import android.media.AudioManager; +import android.media.CamcorderProfile; +import android.media.Image; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.media.MediaMetadataRetriever; +import android.net.Uri; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Message; +import android.os.SystemClock; +import android.os.ParcelFileDescriptor; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Size; +import android.view.OrientationEventListener; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.WindowManager; +import android.widget.Toast; + +import java.io.File; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.concurrent.Executor; +import java.util.List; +import java.util.Date; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import com.android.camera.CameraActivity; +import com.android.camera.CaptureModule; +import com.android.camera.CameraSettings; +import com.android.camera.ComboPreferences; +import com.android.camera.Exif; +import com.android.camera.exif.ExifInterface; +import com.android.camera.LocationManager; +import com.android.camera.MediaSaveService; +import com.android.camera.PhotoModule.NamedImages; +import com.android.camera.PhotoModule.NamedImages.NamedEntity; +import com.android.camera.SDCard; +import com.android.camera.SoundClips; +import com.android.camera.Storage; +import com.android.camera.Thumbnail; +import com.android.camera.util.CameraUtil; +import com.android.camera.util.SettingTranslation; +import com.android.camera.util.PersistUtil; +import com.android.camera.ui.RotateTextToast; + +import org.codeaurora.snapcam.R; + +public class MultiVideoModule implements MultiCamera, LocationManager.Listener, + MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener { + + private static final String TAG = "SnapCam_MultiVideoModule"; + + private static final boolean DEBUG = + (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_LOG) || + (PersistUtil.getCamera2Debug() == PersistUtil.CAMERA2_DEBUG_DUMP_ALL); + + private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90; + private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270; + private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray(); + private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray(); + + private static final int WAIT_SURFACE = 0; + private static final int OPEN_CAMERA = 1; + + private static final int SCREEN_DELAY = 2 * 60 * 1000; + + private static final int MAX_NUM_CAM = 16; + + private int mCameraListIndex = 0; + + private static final CaptureRequest.Key<Byte> override_resource_cost_validation = + new CaptureRequest.Key<>( + "org.codeaurora.qcamera3.sessionParameters.overrideResourceCostValidation", + byte.class); + + private CameraActivity mActivity; + private MultiCameraUI mMultiCameraUI; + private MultiCameraModule mMultiCameraModule; + private SharedPreferences mLocalSharedPref; + private ArrayList<CameraCharacteristics> mCharacteristics; + private CameraDevice[] mCameraDevices = new CameraDevice[MAX_NUM_CAM]; + private CameraCaptureSession[] mCameraPreviewSessions = new CameraCaptureSession[MAX_NUM_CAM]; + private ContentValues[] mCurrentVideoValues = new ContentValues[MAX_NUM_CAM]; + private ImageReader[] mImageReaders = new ImageReader[MAX_NUM_CAM]; + private MediaRecorder[] mMediaRecorders = new MediaRecorder[MAX_NUM_CAM]; + private String[] mNextVideoAbsolutePaths = new String[MAX_NUM_CAM]; + private boolean mPaused = true; + + private NamedImages mNamedImages; + + private Uri mCurrentVideoUri; + + private boolean mMediaRecorderPausing = false; + + private boolean mRecordingTimeCountsDown = false; + + private LocationManager mLocationManager; + private CamcorderProfile mProfile; + + private Size[] mVideoSize = new Size[MAX_NUM_CAM]; + private Size mPreviewSizes[] = new Size[MAX_NUM_CAM]; + private int[][] mMaxPreviewSize = new int[MAX_NUM_CAM][]; + + private boolean mCaptureTimeLapse = false; + // Default 0. If it is larger than 0, the camcorder is in time lapse mode. + private int mTimeBetweenTimeLapseFrameCaptureMs = 0; + + private String[] mVideoFilenames = new String[MAX_NUM_CAM]; + + private long mRecordingStartTime; + private long mRecordingTotalTime; + + private ParcelFileDescriptor mVideoFileDescriptor; + + // The video duration limit. 0 means no limit. + private int mMaxVideoDurationInMs; + + private int mAudioEncoder; + private String mVideoRotation; + + private SoundClips.Player mSoundPlayer; + + /** + * Whether the app is recording video now + */ + private boolean[] mIsRecordingVideos = new boolean[MAX_NUM_CAM]; + + private String[] mCameraIds; + private ArrayList<String> mCameraIDList = new ArrayList<>(); + + /** + * Orientation of the camera sensor + */ + private int mSensorOrientation; + private int mOrientation = OrientationEventListener.ORIENTATION_UNKNOWN; + + /** + * {@link CaptureRequest.Builder} for the camera preview + */ + private CaptureRequest.Builder[] mPreviewRequestBuilders = new CaptureRequest.Builder[MAX_NUM_CAM]; + + private Handler mCameraHandler; + private HandlerThread mCameraThread; + + private ContentResolver mContentResolver; + /** + * A {@link Semaphore} make sure the camera open callback happens first before closing the + * camera. + */ + private Semaphore mCameraOpenCloseLock = new Semaphore(3); + + public MultiVideoModule(CameraActivity activity, MultiCameraUI ui, MultiCameraModule module) { + mActivity = activity; + mMultiCameraUI = ui; + mMultiCameraModule = module; + mContentResolver = mActivity.getContentResolver(); + mLocationManager = new LocationManager(mActivity, this); + mNamedImages = new NamedImages(); + mLocalSharedPref = mActivity.getSharedPreferences( + ComboPreferences.getLocalSharedPreferencesName(mActivity, + "multi" + mMultiCameraModule.getCurrenCameraMode()), Context.MODE_PRIVATE); + startBackgroundThread(); + initCameraCharacteristics(); + } + + @Override + public void onResume(String[] ids) { + for (String id : ids) { + mCameraIDList.add(id); + } + // Set up sound playback for video record and video stop + if (mSoundPlayer == null) { + mSoundPlayer = SoundClips.getPlayer(mActivity); + } + mPaused = false; + initializeValues(); + startBackgroundThread(); + for (String id : ids) { + int cameraId = Integer.parseInt(id); + updateVideoSize(cameraId); + int index = mCameraIDList.indexOf(id); + mMultiCameraUI.setPreviewSize(index, mPreviewSizes[cameraId].getWidth(), + mPreviewSizes[cameraId].getHeight()); + } + } + + @Override + public void onPause() { + mPaused = true; + if (mSoundPlayer != null) { + mSoundPlayer.release(); + mSoundPlayer = null; + } + for (String id : mCameraIds) { + int cameraId = Integer.parseInt(id); + Log.d(TAG, " onPause id :" + cameraId + " recording is :" + + (mIsRecordingVideos[cameraId] ? "STOPED" : "START")); + if (mIsRecordingVideos[cameraId]) { + stopRecordingVideo(cameraId); + } + } + + if (mCameraIDList != null) { + mCameraIDList.clear(); + } + mCameraListIndex = 0; + closeCamera(); + stopBackgroundThread(); + } + + @Override + public boolean openCamera(String[] ids) { + mCameraIds = ids; + for (String id : ids) { + mCameraIDList.add(id); + } + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + return true; + } + + @Override + public void startPreview() { + + } + + @Override + public void closeSession() { + + } + + @Override + public void closeCamera() { + Log.d(TAG, "closeCamera"); + /* no need to set this in the callback and handle asynchronously. This is the same + reason as why we release the semaphore here, not in camera close callback function + as we don't have to protect the case where camera open() gets called during camera + close(). The low level framework/HAL handles the synchronization for open() + happens after close() */ + try { + // Close camera starting with AUX first + for (int i = MAX_NUM_CAM - 1; i >= 0; i--) { + if (null != mCameraDevices[i]) { + if (!mCameraOpenCloseLock.tryAcquire(2000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting to lock camera closing."); + throw new RuntimeException("Time out waiting to lock camera closing"); + } + Log.d(TAG, "Closing camera: " + mCameraDevices[i].getId()); + mCameraDevices[i].close(); + mCameraDevices[i] = null; + mCameraPreviewSessions[i] = null; + } + if (null != mImageReaders[i]) { + mImageReaders[i].close(); + mImageReaders[i] = null; + } + } + } catch (InterruptedException e) { + mCameraOpenCloseLock.release(); + throw new RuntimeException("Interrupted while trying to lock camera closing.", e); + } catch (IllegalStateException e) { + e.printStackTrace(); + } finally { + mCameraOpenCloseLock.release(); + } + } + + @Override + public void onVideoButtonClick(String[] ids) { + checkAndPlayShutterSound(mIsRecordingVideos[0]); + for (String id : ids) { + int cameraId = Integer.parseInt(id); + Log.d(TAG, " onVideoButtonClick id :" + cameraId + " recording is :" + + (mIsRecordingVideos[cameraId] ? "STOPED" : "START")); + if (mIsRecordingVideos[cameraId]) { + stopRecordingVideo(cameraId); + } else { + startRecordingVideo(cameraId); + } + } + } + + @Override + public void onShutterButtonClick(String[] ids) { + checkAndPlayCaptureSound(); + mMultiCameraUI.enableShutter(false); + for (String id : ids) { + Log.d(TAG, "onShutterButtonClick id :" + id); + try { + int cameraId = Integer.parseInt(id); + if (null == mActivity || null == mCameraDevices[cameraId]) { + warningToast("Camera is not ready yet to take a video snapshot."); + return; + } + final CaptureRequest.Builder captureBuilder = + mCameraDevices[cameraId].createCaptureRequest( + CameraDevice.TEMPLATE_VIDEO_SNAPSHOT); + captureBuilder.addTarget(mImageReaders[cameraId].getSurface()); + int index = mCameraIDList.indexOf(String.valueOf(cameraId)); + captureBuilder.addTarget(mMultiCameraUI.getSurfaceViewList().get( + index).getHolder().getSurface()); + // Use the same AE and AF modes as the preview. + captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + captureBuilder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY, (byte) 80); + captureBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO); + captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, + CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH); + + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, + CameraUtil.getJpegRotation(cameraId, rotation)); + mCameraPreviewSessions[cameraId].capture(captureBuilder.build(), + mCaptureStillCallback, mMultiCameraModule.getMyCameraHandler()); + Log.d(TAG, " cameraCaptureSession" + id + " captured "); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + } + + @Override + public void onButtonPause(String[] ids) { + mRecordingTotalTime += SystemClock.uptimeMillis() - mRecordingStartTime; + mMediaRecorderPausing = true; + for (String id : ids) { + int cameraId = Integer.parseInt(id); + mMediaRecorders[cameraId].pause(); + } + } + + @Override + public void onButtonContinue(String[] ids) { + mMediaRecorderPausing = false; + for (String id : ids) { + int cameraId = Integer.parseInt(id); + mMediaRecorders[cameraId].resume(); + mRecordingStartTime = SystemClock.uptimeMillis(); + updateRecordingTime(cameraId); + } + } + + @Override + public void onErrorListener(int error) { + enableRecordingLocation(false); + } + + // from MediaRecorder.OnErrorListener + @Override + public void onError(MediaRecorder mr, int what, int extra) { + Log.e(TAG, "MediaRecorder error. what=" + what + ". extra=" + extra); + String[] ids = {"0", "1"}; + for (String id : ids) { + int cameraId = Integer.parseInt(id); + stopRecordingVideo(cameraId); + } + if (what == MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN) { + // We may have run out of space on the sdcard. + mActivity.updateStorageSpaceAndHint(); + } else { + warningToast("MediaRecorder error. what=" + what + ". extra=" + extra); + } + } + + // from MediaRecorder.OnInfoListener + @Override + public void onInfo(MediaRecorder mr, int what, int extra) { + String[] ids = {"0", "1"}; + if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) { + for (String id : ids) { + int cameraId = Integer.parseInt(id); + if (mIsRecordingVideos[cameraId]) { + stopRecordingVideo(cameraId); + } + } + } else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) { + for (String id : ids) { + int cameraId = Integer.parseInt(id); + if (mIsRecordingVideos[cameraId]) { + stopRecordingVideo(cameraId); + } + } + // Show the toast. + RotateTextToast.makeText(mActivity, R.string.video_reach_size_limit, + Toast.LENGTH_LONG).show(); + } + } + + @Override + public void onOrientationChanged(int orientation) { + mOrientation = orientation; + } + + @Override + public boolean isRecordingVideo() { + for (int i = 0; i < mIsRecordingVideos.length; i++) { + if (mIsRecordingVideos[i]) return true; + } + return false; + } + + private void openCameraInSequence(String id) { + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + CameraCharacteristics characteristics = null; + try { + characteristics = manager.getCameraCharacteristics(id); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); + Log.d(TAG, "openCameraInSequence " + id + ", mSensorOrientation :" + mSensorOrientation); + try { + if (!mCameraOpenCloseLock.tryAcquire(5000, TimeUnit.MILLISECONDS)) { + Log.d(TAG, "Time out waiting to lock camera opening."); + throw new RuntimeException("Time out waiting to lock camera opening"); + } + manager.openCamera(id, mStateCallback, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + manager = null; + characteristics = null; + } + + private void initCameraCharacteristics() { + mCharacteristics = new ArrayList<>(); + CameraManager manager = (CameraManager) mActivity.getSystemService(Context.CAMERA_SERVICE); + try { + String[] cameraIdList = manager.getCameraIdList(); + Log.d(TAG, "cameraIdList size =" + cameraIdList.length); + for (int i = 0; i < cameraIdList.length; i++) { + String cameraId = cameraIdList[i]; + CameraCharacteristics characteristics + = manager.getCameraCharacteristics(cameraId); + mCharacteristics.add(i, characteristics); + try { + mMaxPreviewSize[i] = characteristics.get(CaptureModule.max_preview_size); + } catch (IllegalArgumentException e) { + Log.e(TAG, "getMaxPreviewSize no vendorTag max_preview_size:"); + } + if (mMaxPreviewSize[i] != null) { + Log.d(TAG, " init cameraId :" + cameraId + ", i :" + i + + ", maxPreviewSize :" + mMaxPreviewSize[i][0]+ "x" + + mMaxPreviewSize[i][1]); + } + } + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private void startBackgroundThread() { + if (mCameraThread == null) { + mCameraThread = new HandlerThread("CameraBackground"); + mCameraThread.start(); + } + if (mCameraHandler == null) { + mCameraHandler = new MyCameraHandler(mCameraThread.getLooper()); + } + } + + private void stopBackgroundThread() { + mCameraThread.quitSafely(); + try { + mCameraThread.join(); + mCameraThread = null; + mCameraHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + private class MyCameraHandler extends Handler { + + public MyCameraHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case WAIT_SURFACE: + int id = msg.arg1; + int index = mCameraIDList.indexOf(String.valueOf(id)); + Log.v(TAG, "WAIT_SURFACE id :" + id + ", index :" + index); + if (index == -1) { + break; + } + Surface surface = mMultiCameraUI.getSurfaceViewList().get(index) + .getHolder().getSurface(); + if (surface.isValid()) { + createCaptureSessions(id, surface); + } else { + mCameraHandler.sendMessageDelayed(msg, 200); + Log.v(TAG, "Surface is invalid, wait more 200ms surfaceCreated"); + } + break; + case OPEN_CAMERA: + if (mCameraListIndex == mCameraIDList.size()) { + mCameraListIndex = 0; + } else { + String cameraId = mCameraIDList.get(mCameraListIndex); + openCameraInSequence(cameraId); + mCameraListIndex ++; + Log.v(TAG, " OPEN_CAMERA cameraId :" + cameraId + ", mCameraListIndex :" + + mCameraListIndex); + } + break; + } + } + } + + private void createVideoSnapshotImageReader(int id) { + if (mImageReaders[id] != null) { + mImageReaders[id].close(); + } + mImageReaders[id] = ImageReader.newInstance(mVideoSize[id].getWidth(), + mVideoSize[id].getHeight(), + ImageFormat.JPEG, /*maxImages*/2); + mImageReaders[id].setOnImageAvailableListener( + mOnImageAvailableListener, mMultiCameraModule.getMyCameraHandler()); + } + + private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { + + @Override + public void onOpened(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + mCameraDevices[id] = cameraDevice; + Log.d(TAG, "onOpened " + id); + mCameraOpenCloseLock.release(); + createCameraPreviewSession(id); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mMultiCameraUI.onCameraOpened(id); + } + }); + } + + @Override + public void onDisconnected(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.d(TAG, "onDisconnected " + id); + mCameraOpenCloseLock.release(); + mCameraDevices[id] = null; + } + + @Override + public void onError(CameraDevice cameraDevice, int error) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.e(TAG, "onError " + id + " " + error); + mCameraOpenCloseLock.release(); + + if (null != mActivity) { + Toast.makeText(mActivity,"open camera error id =" + id, + Toast.LENGTH_LONG).show(); + mActivity.finish(); + } + } + + @Override + public void onClosed(CameraDevice cameraDevice) { + int id = Integer.parseInt(cameraDevice.getId()); + Log.d(TAG, "onClosed " + id); + mCameraOpenCloseLock.release(); + mCameraDevices[id] = null; + } + }; + + private CameraCaptureSession.CaptureCallback mCaptureStillCallback + = new CameraCaptureSession.CaptureCallback() { + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + Log.d(TAG, " mCaptureCallback onCaptureCompleted "); + mMultiCameraModule.getMainHandler().post(new Runnable() { + @Override + public void run() { + Log.d(TAG, " enable Shutter " ); + mMultiCameraUI.enableShutter(true); + } + }); + } + + @Override + public void onCaptureFailed(CameraCaptureSession session, + CaptureRequest request, + CaptureFailure result) { + Log.d(TAG, " mCaptureCallback onCaptureFailed " ); + } + + + @Override + public void onCaptureSequenceCompleted(CameraCaptureSession session, int + sequenceId, long frameNumber) { + Log.d(TAG, " mCaptureCallback onCaptureSequenceCompleted "); + } + }; + + private MediaSaveService.OnMediaSavedListener mOnMediaSavedListener = + new MediaSaveService.OnMediaSavedListener() { + @Override + public void onMediaSaved(Uri uri) { + if (uri != null) { + mActivity.notifyNewMedia(uri); + } + } + }; + + public void enableRecordingLocation(boolean enable) { + mLocationManager.recordLocation(enable); + } + + private Size parsePictureSize(String value) { + int indexX = value.indexOf('x'); + int width = Integer.parseInt(value.substring(0, indexX)); + int height = Integer.parseInt(value.substring(indexX + 1)); + return new Size(width, height); + } + + private void updateVideoSize(int id) { + String defaultSize = mActivity.getString(R.string.pref_multi_camera_video_quality_default); + int index = mCameraIDList.indexOf(String.valueOf(id)); + String videoSize = mLocalSharedPref.getString( + MultiSettingsActivity.KEY_VIDEO_SIZES.get(index), defaultSize); + Size size = parsePictureSize(videoSize); + mVideoSize[id] = size; + Log.v(TAG, " updateVideoSize size :" + mVideoSize[id].getWidth() + "x" + + mVideoSize[id].getHeight()); + + Size[] prevSizes = getSupportedOutputSize(id, MediaRecorder.class); + mPreviewSizes[id] = getOptimalVideoPreviewSize(id, mVideoSize[id], prevSizes); + } + + private Size[] getSupportedOutputSize(int cameraId, Class cl) { + StreamConfigurationMap map = mCharacteristics.get(cameraId).get( + CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + Size[] normal = map.getOutputSizes(cl); + Size[] high = map.getHighResolutionOutputSizes(ImageFormat.PRIVATE); + Size[] ret = new Size[normal.length + high.length]; + System.arraycopy(normal, 0, ret, 0, normal.length); + System.arraycopy(high, 0, ret, normal.length, high.length); + return ret; + } + + private Size getOptimalVideoPreviewSize(int id, Size VideoSize, Size[] prevSizes) { + Point[] points = new Point[prevSizes.length]; + + int index = 0; + int point_max[] = mMaxPreviewSize[id]; + int max_size = -1; + if (point_max != null) { + max_size = point_max[0] * point_max[1]; + } + for (Size s : prevSizes) { + if (max_size != -1) { + int size = s.getWidth() * s.getHeight(); + if (s.getWidth() == s.getHeight()) { + if (s.getWidth() > Math.max(point_max[0], point_max[1])) + continue; + } else if (size > max_size || size == 0) { + continue; + } + } + points[index++] = new Point(s.getWidth(), s.getHeight()); + } + + int optimalPickIndex = CameraUtil.getOptimalVideoPreviewSize(mActivity, points, VideoSize); + return (optimalPickIndex == -1) ? null : + new Size(points[optimalPickIndex].x, points[optimalPickIndex].y); + } + + /** + * Creates a new {@link CameraCaptureSession} for camera preview. + */ + private void createCameraPreviewSession(int id) { + // This is the output Surface we need to start preview. + int index = mCameraIDList.indexOf(String.valueOf(id)); + Log.v(TAG, "createCameraPreviewSession id :" + id + ", index :" + index); + Surface surface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder().getSurface(); + + if (surface.isValid()) { + createCaptureSessions(id, surface); + } else { + Message msg = Message.obtain(); + msg.what = WAIT_SURFACE; + msg.arg1 = id; + mCameraHandler.sendMessageDelayed(msg, 200); + Log.v(TAG, "Surface is invalid, wait 200ms surfaceCreated"); + } + + } + + private void createCaptureSessions(int id, Surface surface) { + try { + // We set up a CaptureRequest.Builder with the output Surface. + mPreviewRequestBuilders[id] + = mCameraDevices[id].createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + mPreviewRequestBuilders[id].addTarget(surface); + mPreviewRequestBuilders[id].setTag(id); + + CameraCaptureSession.StateCallback stateCallback = + new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == mCameraDevices[id]) { + return; + } + Log.v(TAG, " CameraCaptureSession onConfigured id :" + id); + // When the session is ready, we start displaying the preview. + mCameraPreviewSessions[id] = cameraCaptureSession; + try { + // Auto focus should be continuous for camera preview. + mPreviewRequestBuilders[id].set(CaptureRequest.CONTROL_AF_MODE, + CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); + // Finally, we start displaying the camera preview. + mCameraPreviewSessions[id].setRepeatingRequest( + mPreviewRequestBuilders[id].build(), + mCaptureCallback, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + + if (mCameraDevices[mCameraListIndex] == null) { + Message msg = Message.obtain(); + msg.what = OPEN_CAMERA; + if (mCameraHandler != null) { + mCameraHandler.sendMessage(msg); + } + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + showToast("onConfigureFailed"); + } + }; + + try { + final byte enable = 1; + mPreviewRequestBuilders[id].set(override_resource_cost_validation, enable); + Log.v(TAG, " set" + override_resource_cost_validation + " is 1"); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } + + List<OutputConfiguration> outConfigurations = new ArrayList<>(1); + outConfigurations.add(new OutputConfiguration(surface)); + + SessionConfiguration sessionConfiguration = new SessionConfiguration( + SessionConfiguration.SESSION_REGULAR, outConfigurations, + new HandlerExecutor(mCameraHandler), stateCallback); + sessionConfiguration.setSessionParameters(mPreviewRequestBuilders[id].build()); + mCameraDevices[id].createCaptureSession(sessionConfiguration); + + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private class HandlerExecutor implements Executor { + private final Handler ihandler; + + public HandlerExecutor(Handler handler) { + ihandler = handler; + } + + @Override + public void execute(Runnable runCmd) { + ihandler.post(runCmd); + } + } + + /** + * A {@link CameraCaptureSession.CaptureCallback} that handles events related to JPEG capture. + */ + private CameraCaptureSession.CaptureCallback mCaptureCallback + = new CameraCaptureSession.CaptureCallback() { + + private void process(CaptureResult result) { + Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); + Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); + if (DEBUG) { + Log.v(TAG, "process afState :" + afState + ", aeState :" + aeState); + } + } + + @Override + public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, + CaptureResult partialResult) { + process(partialResult); + } + + @Override + public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, + TotalCaptureResult result) { + process(result); + } + + }; + + private final ImageReader.OnImageAvailableListener mOnImageAvailableListener + = new ImageReader.OnImageAvailableListener() { + + @Override + public void onImageAvailable(ImageReader reader) { + Log.v(TAG, " onImageAvailable ..."); + Image image = reader.acquireNextImage(); + long imageTime = System.currentTimeMillis(); + mNamedImages.nameNewImage(imageTime); + NamedEntity name = mNamedImages.getNextNameEntity(); + String title = (name == null) ? null : name.title; + long date = (name == null) ? -1 : name.date; + + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.remaining()]; + buffer.get(bytes); + + int orientation = 0; + ExifInterface exif = Exif.getExif(bytes); + orientation = Exif.getOrientation(exif); + String saveFormat = "jpeg"; + mActivity.getMediaSaveService().addImage(bytes, title, date, + null, image.getWidth(), image.getHeight(), orientation, exif, + mOnMediaSavedListener, mContentResolver, saveFormat); + mActivity.updateThumbnail(bytes); + image.close(); + mMultiCameraModule.updateTakingPicture(); + } + }; + + private void initializeValues() { + updateAudioEncoder(); + updateVideoRotation(); + } + + private void checkAndPlayShutterSound(boolean isStarted) { + if (mSoundPlayer != null) { + mSoundPlayer.play(isStarted? SoundClips.STOP_VIDEO_RECORDING + : SoundClips.START_VIDEO_RECORDING); + } + } + + private void checkAndPlayCaptureSound() { + if (mSoundPlayer != null) { + mSoundPlayer.play(SoundClips.SHUTTER_CLICK); + } + } + + private void closePreviewSession(int id) { + if (mCameraPreviewSessions[id] != null) { + Log.v(TAG, "closePreviewSession id :" + id); + mCameraPreviewSessions[id].close(); + mCameraPreviewSessions[id] = null; + } + } + + private void startRecordingVideo(final int id) { + int index = mCameraIDList.indexOf(String.valueOf(id)); + if (null == mCameraDevices[id] || + !mMultiCameraUI.getSurfaceViewList().get(index).isEnabled()) { + return; + } + Log.v(TAG, " startRecordingVideo " + id); + try { + closePreviewSession(id); + setUpMediaRecorder(id); + createVideoSnapshotImageReader(id); + mPreviewRequestBuilders[id] = mCameraDevices[id].createCaptureRequest( + CameraDevice.TEMPLATE_RECORD); + if (true) { + mPreviewRequestBuilders[id].set(CaptureRequest.NOISE_REDUCTION_MODE, + CaptureRequest.NOISE_REDUCTION_MODE_FAST); + } else { + mPreviewRequestBuilders[id].set(CaptureRequest.NOISE_REDUCTION_MODE, + CaptureRequest.NOISE_REDUCTION_MODE_HIGH_QUALITY); + } + List<Surface> surfaces = new ArrayList<>(); + + // Set up Surface for the camera preview + Surface previewSurface = mMultiCameraUI.getSurfaceViewList().get(index).getHolder() + .getSurface(); + surfaces.add(previewSurface); + mPreviewRequestBuilders[id].addTarget(previewSurface); + + // Set up Surface for the MediaRecorder + Surface recorderSurface = mMediaRecorders[id].getSurface(); + surfaces.add(recorderSurface); + mPreviewRequestBuilders[id].addTarget(recorderSurface); + surfaces.add(mImageReaders[id].getSurface()); + + // Start a capture session + // Once the session starts, we can update the UI and start recording + mCameraDevices[id].createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { + + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + mCameraPreviewSessions[id] = cameraCaptureSession; + updatePreview(id); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mIsRecordingVideos[id] = true; + // Start recording + mMediaRecorders[id].start(); + requestAudioFocus(); + mRecordingTotalTime = 0L; + mRecordingStartTime = SystemClock.uptimeMillis(); + mMediaRecorderPausing = false; + mMultiCameraUI.resetPauseButton(); + mMultiCameraUI.showRecordingUI(true); + updateRecordingTime(id); + keepScreenOn(); + Log.v(TAG, " startRecordingVideo done " + id); + } + }); + } + + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + if (null != mActivity) { + Toast.makeText(mActivity, "Configure Failed", Toast.LENGTH_SHORT).show(); + } + } + }, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException | IllegalStateException | IOException e) { + e.printStackTrace(); + } + } + + public void stopRecordingVideo(int id) { + Log.v(TAG, " stopRecordingVideo " + id); + mIsRecordingVideos[id] = false; + try { + mMediaRecorders[id].setOnErrorListener(null); + mMediaRecorders[id].setOnInfoListener(null); + // Stop recording + mMediaRecorders[id].stop(); + mMediaRecorders[id].reset(); + saveVideo(id); + keepScreenOnAwhile(); + // release media recorder + releaseMediaRecorder(id); + releaseAudioFocus(); + } catch (RuntimeException e) { + Log.w(TAG, "MediaRecoder stop fail", e); + if (mVideoFilenames[id] != null) deleteVideoFile(mVideoFilenames[id]); + } + + mMultiCameraUI.showRecordingUI(false); + if (null != mActivity) { + Toast.makeText(mActivity, "Video saved: " + mNextVideoAbsolutePaths[id], + Toast.LENGTH_SHORT).show(); + Log.d(TAG, "Video saved: " + mNextVideoAbsolutePaths[id]); + } + mNextVideoAbsolutePaths[id] = null; + if(!mPaused) { + createCameraPreviewSession(id); + } + } + + private final MediaSaveService.OnMediaSavedListener mOnVideoSavedListener = + new MediaSaveService.OnMediaSavedListener() { + @Override + public void onMediaSaved(Uri uri) { + if (uri != null) { + mActivity.notifyNewMedia(uri); + mCurrentVideoUri = uri; + } + } + }; + + private void keepScreenOn() { + mMultiCameraModule.getMainHandler().removeMessages(MultiCameraModule.CLEAR_SCREEN_DELAY); + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + } + + private void keepScreenOnAwhile() { + mMultiCameraModule.getMainHandler().removeMessages(MultiCameraModule.CLEAR_SCREEN_DELAY); + mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + mMultiCameraModule.getMainHandler().sendEmptyMessageDelayed( + MultiCameraModule.CLEAR_SCREEN_DELAY, SCREEN_DELAY); + } + + private void releaseMediaRecorder(int id) { + Log.v(TAG, "Releasing media recorder."); + cleanupEmptyFile(id); + if (mMediaRecorders[id] != null) { + try{ + mMediaRecorders[id].reset(); + mMediaRecorders[id].release(); + }catch (RuntimeException e) { + e.printStackTrace(); + } + mMediaRecorders[id] = null; + } + } + + /* + * Make sure we're not recording music playing in the background, ask the + * MediaPlaybackService to pause playback. + */ + private void requestAudioFocus() { + AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE); + // Send request to obtain audio focus. This will stop other + // music stream. + int result = am.requestAudioFocus(null, AudioManager.STREAM_MUSIC, + AudioManager.AUDIOFOCUS_GAIN_TRANSIENT); + if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { + Log.v(TAG, "Audio focus request failed"); + } + } + + private void releaseAudioFocus() { + AudioManager am = (AudioManager)mActivity.getSystemService(Context.AUDIO_SERVICE); + int result = am.abandonAudioFocus(null); + if (result == AudioManager.AUDIOFOCUS_REQUEST_FAILED) { + Log.v(TAG, "Audio focus release failed"); + } + } + + private void cleanupEmptyFile(int id) { + if (mVideoFilenames[id] != null) { + File f = new File(mVideoFilenames[id]); + if (f.length() == 0 && f.delete()) { + Log.v(TAG, "Empty video file deleted: " + mVideoFilenames[id]); + mVideoFilenames[id] = null; + } + } + } + + private void updateAudioEncoder() { + String audioEncoderStr = mActivity.getResources().getString( + R.string.pref_camera_audioencoder_default); + if (mLocalSharedPref != null) { + audioEncoderStr = mLocalSharedPref.getString(MultiSettingsActivity.KEY_AUDIO_ENCODER, + audioEncoderStr); + } + mAudioEncoder = SettingTranslation.getAudioEncoder(audioEncoderStr); + } + + private void updateVideoRotation() { + String defaultValue = mActivity.getResources().getString( + R.string.pref_camera_video_rotation_default); + if (mLocalSharedPref != null) { + mVideoRotation = mLocalSharedPref.getString(MultiSettingsActivity.KEY_VIDEO_ROTATION, + defaultValue); + } + } + + private void deleteVideoFile(String fileName) { + Log.v(TAG, "Deleting video " + fileName); + File f = new File(fileName); + if (!f.delete()) { + Log.v(TAG, "Could not delete " + fileName); + } + } + + private void saveVideo(int id) { + File origFile = new File(mVideoFilenames[id]); + if (!origFile.exists() || origFile.length() <= 0) { + Log.e(TAG, "Invalid file"); + mCurrentVideoValues[id] = null; + return; + } + + long duration = 0L; + MediaMetadataRetriever retriever = new MediaMetadataRetriever(); + try { + retriever.setDataSource(mVideoFilenames[id]); + duration = Long.valueOf(retriever.extractMetadata( + MediaMetadataRetriever.METADATA_KEY_DURATION)); + } catch (IllegalArgumentException e) { + Log.e(TAG, "cannot access the file"); + } + retriever.release(); + mActivity.getMediaSaveService().addVideo(mVideoFilenames[id], + duration, mCurrentVideoValues[id], + mOnVideoSavedListener, mContentResolver); + Log.v(TAG, "saveVideo mVideoFilenames[id] :" + mVideoFilenames[id]); + mCurrentVideoValues[id] = null; + } + + private void updateRecordingTime(int id) { + if (!mIsRecordingVideos[id] || id == 0) { + return; + } + + if (mMediaRecorderPausing) { + return; + } + + long now = SystemClock.uptimeMillis(); + long delta = now - mRecordingStartTime + mRecordingTotalTime; + + // Starting a minute before reaching the max duration + // limit, we'll countdown the remaining time instead. + boolean countdownRemainingTime = (mMaxVideoDurationInMs != 0 + && delta >= mMaxVideoDurationInMs - 60000); + + long deltaAdjusted = delta; + if (countdownRemainingTime) { + deltaAdjusted = Math.max(0, mMaxVideoDurationInMs - deltaAdjusted) + 999; + } + String text; + long targetNextUpdateDelay; + if (!mCaptureTimeLapse) { + text = CameraUtil.millisecondToTimeString(deltaAdjusted, false); + targetNextUpdateDelay = 1000; + } else { + // The length of time lapse video is different from the length + // of the actual wall clock time elapsed. Display the video length + // only in format hh:mm:ss.dd, where dd are the centi seconds. + text = CameraUtil.millisecondToTimeString(getTimeLapseVideoLength(delta), true); + targetNextUpdateDelay = mTimeBetweenTimeLapseFrameCaptureMs; + } + mMultiCameraUI.setRecordingTime(text); + if (mRecordingTimeCountsDown != countdownRemainingTime) { + // Avoid setting the color on every update, do it only + // when it needs changing. + mRecordingTimeCountsDown = countdownRemainingTime; + + int color = mActivity.getResources().getColor(countdownRemainingTime + ? R.color.recording_time_remaining_text + : R.color.recording_time_elapsed_text); + + mMultiCameraUI.setRecordingTimeTextColor(color); + } + long actualNextUpdateDelay = targetNextUpdateDelay - (delta % targetNextUpdateDelay); + mMultiCameraModule.getMainHandler().postDelayed(new Runnable() { + @Override + public void run() { + updateRecordingTime(id); + } + }, actualNextUpdateDelay); + } + + private long getTimeLapseVideoLength(long deltaMs) { + // For better approximation calculate fractional number of frames captured. + // This will update the video time at a higher resolution. + double numberOfFrames = (double) deltaMs / mTimeBetweenTimeLapseFrameCaptureMs; + return (long) (numberOfFrames / mProfile.videoFrameRate * 1000); + } + + /** + * Update the camera preview. {@link #startPreview()} needs to be called in advance. + */ + private void updatePreview(int id) { + if (null == mCameraDevices[id]) { + return; + } + try { + setUpCaptureRequestBuilder(mPreviewRequestBuilders[id]); + mCameraPreviewSessions[id].setRepeatingRequest(mPreviewRequestBuilders[id].build(), + null, mMultiCameraModule.getMyCameraHandler()); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) { + builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + } + + private String generateVideoFilename(int outputFileFormat, int id) { + long dateTaken = System.currentTimeMillis(); + String title = createName(dateTaken); + String filename = title + "_"+ id + CameraUtil.convertOutputFormatToFileExt(outputFileFormat); + String mime = CameraUtil.convertOutputFormatToMimeType(outputFileFormat); + String path; + if (Storage.isSaveSDCard() && SDCard.instance().isWriteable()) { + path = SDCard.instance().getDirectory() + '/' + filename; + } else { + path = Storage.DIRECTORY + '/' + filename; + } + mCurrentVideoValues[id] = new ContentValues(9); + mCurrentVideoValues[id].put(MediaStore.Video.Media.TITLE, title); + mCurrentVideoValues[id].put(MediaStore.Video.Media.DISPLAY_NAME, filename); + mCurrentVideoValues[id].put(MediaStore.Video.Media.DATE_TAKEN, dateTaken); + mCurrentVideoValues[id].put(MediaStore.MediaColumns.DATE_MODIFIED, dateTaken / 1000); + mCurrentVideoValues[id].put(MediaStore.Video.Media.MIME_TYPE, mime); + mCurrentVideoValues[id].put(MediaStore.Video.Media.DATA, path); + mCurrentVideoValues[id].put(MediaStore.Video.Media.RESOLUTION, + "" + mVideoSize[id].getWidth() + "x" + mVideoSize[id].getHeight()); + Location loc = mLocationManager.getCurrentLocation(); + if (loc != null) { + mCurrentVideoValues[id].put(MediaStore.Video.Media.LATITUDE, loc.getLatitude()); + mCurrentVideoValues[id].put(MediaStore.Video.Media.LONGITUDE, loc.getLongitude()); + } + mVideoFilenames[id] = path; + return path; + } + + private String createName(long dateTaken) { + Date date = new Date(dateTaken); + SimpleDateFormat dateFormat = new SimpleDateFormat( + mActivity.getString(R.string.video_file_name_format)); + return dateFormat.format(date); + } + + private void setUpMediaRecorder(int id) throws IOException { + if (null == mActivity) { + return; + } + Log.v(TAG, " setUpMediaRecorder " + id); + int size = CameraSettings.VIDEO_QUALITY_TABLE.get(mVideoSize[id].getWidth() + "x" + + mVideoSize[id].getHeight()); + if (CamcorderProfile.hasProfile(id, size)) { + mProfile = CamcorderProfile.get(id, size); + } else { + warningToast(R.string.error_app_unsupported_profile); + throw new IllegalArgumentException("error_app_unsupported_profile"); + } + + if (mMediaRecorders[id] == null) { + mMediaRecorders[id] = new MediaRecorder(); + } + mMediaRecorders[id].setAudioSource(MediaRecorder.AudioSource.MIC); + mMediaRecorders[id].setVideoSource(MediaRecorder.VideoSource.SURFACE); + mMediaRecorders[id].setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + if (mNextVideoAbsolutePaths[id] == null || mNextVideoAbsolutePaths[id].isEmpty()) { + mNextVideoAbsolutePaths[id] = generateVideoFilename(mProfile.fileFormat, id); + } + + mMediaRecorders[id].setMaxDuration(mMaxVideoDurationInMs); + mMediaRecorders[id].setOutputFile(mNextVideoAbsolutePaths[id]); + mMediaRecorders[id].setVideoEncodingBitRate(10000000); + mMediaRecorders[id].setVideoFrameRate(30); + mMediaRecorders[id].setVideoSize(mVideoSize[id].getWidth(), mVideoSize[id].getHeight()); + mMediaRecorders[id].setVideoEncoder(MediaRecorder.VideoEncoder.H264); + mMediaRecorders[id].setAudioEncoder(mAudioEncoder); + int rotation = CameraUtil.getJpegRotation(id, mOrientation); + if (mVideoRotation != null) { + rotation += Integer.parseInt(mVideoRotation); + rotation = rotation % 360; + } + mMediaRecorders[id].setOrientationHint(rotation); + mMediaRecorders[id].prepare(); + mMediaRecorders[id].setOnErrorListener(this); + mMediaRecorders[id].setOnInfoListener(this); + } + + private void warningToast(final String msg) { + mActivity.runOnUiThread(new Runnable() { + public void run() { + RotateTextToast.makeText(mActivity, msg, + Toast.LENGTH_SHORT).show(); + } + }); + } + + private void warningToast(final int sourceId) { + warningToast(sourceId, true); + } + + private void warningToast(final int sourceId, boolean isLongShow) { + mActivity.runOnUiThread(new Runnable() { + public void run() { + RotateTextToast.makeText(mActivity, sourceId, + isLongShow ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT).show(); + } + }); + } + + /** + * Shows a {@link Toast} on the UI thread. + * @param text The message to show + */ + private void showToast(final String text) { + if (mActivity != null) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + Toast.makeText(mActivity, text, Toast.LENGTH_SHORT).show(); + } + }); + } + } +} diff --git a/src/com/android/camera/ui/Camera2FaceView.java b/src/com/android/camera/ui/Camera2FaceView.java index ced3245af..ced3245af 100755..100644 --- a/src/com/android/camera/ui/Camera2FaceView.java +++ b/src/com/android/camera/ui/Camera2FaceView.java diff --git a/src/com/android/camera/ui/CountDownView.java b/src/com/android/camera/ui/CountDownView.java index 069d0b3d8..069d0b3d8 100755..100644 --- a/src/com/android/camera/ui/CountDownView.java +++ b/src/com/android/camera/ui/CountDownView.java diff --git a/src/com/android/camera/ui/FaceView.java b/src/com/android/camera/ui/FaceView.java index 37b7c8c51..37b7c8c51 100755..100644 --- a/src/com/android/camera/ui/FaceView.java +++ b/src/com/android/camera/ui/FaceView.java diff --git a/src/com/android/camera/ui/FlashToggleButton.java b/src/com/android/camera/ui/FlashToggleButton.java index 18bd26d31..a1e3f9ba9 100755..100644 --- a/src/com/android/camera/ui/FlashToggleButton.java +++ b/src/com/android/camera/ui/FlashToggleButton.java @@ -30,6 +30,7 @@ package com.android.camera.ui; import android.content.Context; +import android.content.res.TypedArray; import android.util.AttributeSet; import android.util.Log; import android.view.View; @@ -42,8 +43,8 @@ import org.codeaurora.snapcam.R; public class FlashToggleButton extends RotateImageView { private SettingsManager mSettingsManager; - private int[] cameraFlashIcon = {R.drawable.flash_off, R.drawable.flash_auto, R.drawable.flash}; - private int[] videoFlashIcon = {R.drawable.flash_off, R.drawable.flash}; + TypedArray cameraFlashIcon; + TypedArray videoFlashIcon; private int mIndex; private boolean mIsVideoFlash; private Context mContext; @@ -51,11 +52,13 @@ public class FlashToggleButton extends RotateImageView { public FlashToggleButton(Context context) { super(context); mContext = context; + initFlashIcons(); } public FlashToggleButton(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; + initFlashIcons(); } public void init(boolean videoFlash) { @@ -85,32 +88,37 @@ public class FlashToggleButton extends RotateImageView { } update(); - this.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - int[] icons; - String key; - if (mIsVideoFlash) { - icons = videoFlashIcon; - key = SettingsManager.KEY_VIDEO_FLASH_MODE; - } else { - icons = cameraFlashIcon; - key = SettingsManager.KEY_FLASH_MODE; - } - mIndex = (mIndex + 1) % icons.length; - mSettingsManager.setValueIndex(key, mIndex); - update(); - } - }); + } + + public void handleClick() { + TypedArray icons; + String key; + if (mIsVideoFlash) { + icons = videoFlashIcon; + key = SettingsManager.KEY_VIDEO_FLASH_MODE; + } else { + icons = cameraFlashIcon; + key = SettingsManager.KEY_FLASH_MODE; + } + mIndex = (mIndex + 1) % icons.length(); + mSettingsManager.setValueIndex(key, mIndex); + update(); + } + + private void initFlashIcons() { + cameraFlashIcon = mContext.getResources() + .obtainTypedArray(R.array.flash_modes_camera); + videoFlashIcon = mContext.getResources() + .obtainTypedArray(R.array.flash_modes_video); } private void update() { - int[] icons; + TypedArray icons; if (mIsVideoFlash) { icons = videoFlashIcon; } else { icons = cameraFlashIcon; } - setImageResource(icons[mIndex]); + setImageResource(icons.getResourceId(mIndex, 0)); } } diff --git a/src/com/android/camera/ui/ModuleSwitcher.java b/src/com/android/camera/ui/ModuleSwitcher.java index 3bcb494cb..3bcb494cb 100755..100644 --- a/src/com/android/camera/ui/ModuleSwitcher.java +++ b/src/com/android/camera/ui/ModuleSwitcher.java diff --git a/src/com/android/camera/ui/OneUICameraControls.java b/src/com/android/camera/ui/OneUICameraControls.java index 669565d52..b84f9065a 100755..100644 --- a/src/com/android/camera/ui/OneUICameraControls.java +++ b/src/com/android/camera/ui/OneUICameraControls.java @@ -25,17 +25,21 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.Point; +import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.TypedValue; +import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.Display; import android.widget.FrameLayout; +import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.SeekBar; import android.widget.TextView; import com.android.camera.CaptureModule; +import com.android.camera.CaptureUI; import com.android.camera.Storage; import com.android.camera.imageprocessor.filter.BeautificationFilter; @@ -45,7 +49,7 @@ public class OneUICameraControls extends RotatableLayout { private static final String TAG = "CAM_Controls"; - private static final float TOP_PANEL_SPACE_NUM = 4f; + private static final float TOP_PANEL_SPACE_NUM = 5f; private static final float BOTTOM_PANEL_SPACE_NUM = 5f; private static final float PANEL_INDEX_0 = 0f; private static final float PANEL_INDEX_1 = 1f; @@ -68,9 +72,11 @@ public class OneUICameraControls extends RotatableLayout { private View mMakeupSeekBarHighText; private View mMakeupSeekBarLayout; private View mCancelButton; + private View mModeSwitcher; private ViewGroup mProModeLayout; private View mSettingsButton; + private RecyclerView mModeSelectLayout; private ArrowTextView mRefocusToast; private static final int WIDTH_GRID = 5; @@ -110,6 +116,8 @@ public class OneUICameraControls extends RotatableLayout { private RotateLayout mIsoRotateLayout; private RotateLayout mZoomSeekBarLayout; + private CaptureUI mCUI; + public OneUICameraControls(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); @@ -167,6 +175,7 @@ public class OneUICameraControls extends RotatableLayout { mRemainingPhotos = (LinearLayout) findViewById(R.id.remaining_photos); mRemainingPhotosText = (TextView) findViewById(R.id.remaining_photos_text); mCancelButton = findViewById(R.id.cancel_button); + mModeSwitcher = findViewById(R.id.mode_switcher); mProModeLayout = (ViewGroup) findViewById(R.id.pro_mode_layout); mExposureText = (TextView) findViewById(R.id.exposure_value); @@ -183,75 +192,13 @@ public class OneUICameraControls extends RotatableLayout { mIsoRotateLayout = (RotateLayout) findViewById(R.id.iso_rotate_layout); mZoomSeekBarLayout = (RotateLayout) findViewById(R.id.zoom_seekbar_layout); - mExposureText.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - resetProModeIcons(); - int mode = mProMode.getMode(); - if (mode == ProMode.EXPOSURE_MODE) { - mProMode.setMode(ProMode.NO_MODE); - } else { - mExposureText.setSelected(true); - mProMode.setMode(ProMode.EXPOSURE_MODE); - } - } - }); - mManualText.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - resetProModeIcons(); - int mode = mProMode.getMode(); - if (mode == ProMode.MANUAL_MODE) { - mProMode.setMode(ProMode.NO_MODE); - } else { - mManualText.setSelected(true); - mProMode.setMode(ProMode.MANUAL_MODE); - } - } - }); - mWhiteBalanceText.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - resetProModeIcons(); - int mode = mProMode.getMode(); - if (mode == ProMode.WHITE_BALANCE_MODE) { - mProMode.setMode(ProMode.NO_MODE); - } else { - mWhiteBalanceText.setSelected(true); - mProMode.setMode(ProMode.WHITE_BALANCE_MODE); - } - } - }); - mIsoText.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - resetProModeIcons(); - int mode = mProMode.getMode(); - if (mode == ProMode.ISO_MODE) { - mProMode.setMode(ProMode.NO_MODE); - } else { - mIsoText.setSelected(true); - mProMode.setMode(ProMode.ISO_MODE); - } - } - }); - mZoomSeekbarText.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - resetProModeIcons(); - int mode = mProMode.getMode(); - if (mode == ProMode.ZOOM_MODE) { - mProMode.setMode(ProMode.NO_MODE); - } else { - mZoomSeekbarText.setSelected(true); - mProMode.setMode(ProMode.ZOOM_MODE); - } - } - }); + mModeSelectLayout = (RecyclerView) findViewById(R.id.mode_select_layout); + mModeSelectLayout.setVisibility(View.INVISIBLE); + mViews = new View[]{ mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, - mFlashButton, - mPreview, mPauseButton, mCancelButton, mSettingsButton + mFlashButton, mPreview, mPauseButton, mCancelButton, + mSettingsButton, mModeSwitcher }; mBottomLargeSize = getResources().getDimensionPixelSize( R.dimen.one_ui_bottom_large); @@ -320,24 +267,80 @@ public class OneUICameraControls extends RotatableLayout { } } + public void onExposureTextClick() { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.EXPOSURE_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mExposureText.setSelected(true); + mProMode.setMode(ProMode.EXPOSURE_MODE); + } + } + + public void onManualTextClick() { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.MANUAL_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mManualText.setSelected(true); + mProMode.setMode(ProMode.MANUAL_MODE); + } + } + + public void onWhiteBalanceTextClick() { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.WHITE_BALANCE_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mWhiteBalanceText.setSelected(true); + mProMode.setMode(ProMode.WHITE_BALANCE_MODE); + } + } + + public void onIsoTextClick() { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.ISO_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mIsoText.setSelected(true); + mProMode.setMode(ProMode.ISO_MODE); + } + } + + public void onZoomSeekbarTextClick() { + resetProModeIcons(); + int mode = mProMode.getMode(); + if (mode == ProMode.ZOOM_MODE) { + mProMode.setMode(ProMode.NO_MODE); + } else { + mZoomSeekbarText.setSelected(true); + mProMode.setMode(ProMode.ZOOM_MODE); + } + } + + public void initialize(CaptureUI cUI) { + mCUI = cUI; + } + + public void closeModeSwitcher(boolean animate) { + mCUI.closeModeSwitcher(animate); + } + private void setLocation(View v, boolean top, float idx) { if(v == null) { return; } - int w = v.getMeasuredWidth(); int h = v.getMeasuredHeight(); if (top) { v.setY((mTop - h) / 2); } else { v.setY(mHeight - mBottom + (mBottom - h) / 2); } - float bW; - if (top) { - bW = mWidth / TOP_PANEL_SPACE_NUM; - } else { - bW = mWidth / BOTTOM_PANEL_SPACE_NUM; - } - v.setX(bW * idx + (bW - w) / 2); + v.setX(getLocationX(v, top, idx)); } private void setLocationCustomBottom(View v, float x, float y) { @@ -352,10 +355,32 @@ public class OneUICameraControls extends RotatableLayout { v.setX(bW * x); } + private float getLocationX(View v, boolean top, float idx) { + int w = v.getMeasuredWidth(); + float bW; + if (top) { + bW = mWidth / TOP_PANEL_SPACE_NUM; + } else { + bW = mWidth / BOTTOM_PANEL_SPACE_NUM; + } + return (bW * idx + (bW - w) / 2); + } + + private void setLocationCameraModePanel() { + if(mModeSelectLayout == null) { + return; + } + mModeSelectLayout.setY(mModeSwitcher.getY() - mModeSelectLayout.getMeasuredHeight() + + mModeSwitcher.getMeasuredHeight()); + mModeSelectLayout.setX(getLocationX(mModeSelectLayout, true, 4f)); + } + private void setLocation(int w, int h) { int rotation = getUnifiedRotation(); setLocation(mSceneModeSwitcher, true, PANEL_INDEX_0); setLocation(mFilterModeSwitcher, true, PANEL_INDEX_1); + setLocation(mModeSwitcher, false, 4f); + setLocationCameraModePanel(); if (mIsVideoMode) { setLocation(mMute, true, PANEL_INDEX_1); setLocation(mFlashButton, true, PANEL_INDEX_2); @@ -365,9 +390,9 @@ public class OneUICameraControls extends RotatableLayout { setLocation(mVideoShutter, false, PANEL_INDEX_2); setLocation(mExitBestPhotpMode ,false, PANEL_INDEX_4); } else { - setLocation(mFlashButton, true, PANEL_INDEX_2); - setLocation(mSettingsButton,true, PANEL_INDEX_3); - setLocation(mFrontBackSwitcher, false, 3.15f); + setLocation(mFlashButton, true, PANEL_INDEX_3); + setLocation(mSettingsButton,true, PANEL_INDEX_4); + setLocation(mFrontBackSwitcher, true, PANEL_INDEX_2); if (mIntentMode == CaptureModule.INTENT_MODE_CAPTURE) { setLocation(mShutter, false, PANEL_INDEX_2); setLocation(mCancelButton, false, 0.85f); @@ -518,7 +543,7 @@ public class OneUICameraControls extends RotatableLayout { mSceneModeSwitcher, mFilterModeSwitcher, mFrontBackSwitcher, mFlashButton, mSettingsButton, mPreview, mMute, mMakeupSeekBarLowText, mMakeupSeekBarHighText, - mPauseButton, mExitBestPhotpMode + mPauseButton, mExitBestPhotpMode, mModeSwitcher }; for (View v : views) { @@ -532,6 +557,13 @@ public class OneUICameraControls extends RotatableLayout { mIsoRotateLayout.setOrientation(orientation, animation); mZoomSeekBarLayout.setOrientation(orientation, animation); mProMode.setOrientation(orientation); + + // Reorient the camera modes + for (int i = 0; i < ((ViewGroup) mModeSelectLayout).getChildCount(); i++) { + ((Rotatable) ((ViewGroup) mModeSelectLayout.getChildAt(i)) + .getChildAt(0)).setOrientation(orientation, animation); + } + layoutRemaingPhotos(); } diff --git a/src/com/android/camera/ui/PieRenderer.java b/src/com/android/camera/ui/PieRenderer.java index d9e947dab..d9e947dab 100755..100644 --- a/src/com/android/camera/ui/PieRenderer.java +++ b/src/com/android/camera/ui/PieRenderer.java diff --git a/src/com/android/camera/ui/ProMode.java b/src/com/android/camera/ui/ProMode.java index d1ad0fade..3ff9ae2be 100755..100644 --- a/src/com/android/camera/ui/ProMode.java +++ b/src/com/android/camera/ui/ProMode.java @@ -62,10 +62,9 @@ public class ProMode extends View { private static final int BLUE = 0xff4693fb; private static final int SELECTED_DOT_SIZE = 20; private static final int DOT_SIZE = 10; - private static final int[] wbIcons = {R.drawable.auto, R.drawable.incandecent, - R.drawable.fluorescent, R.drawable.sunlight, R.drawable.cloudy}; - private static final int[] wbIconsBlue = {R.drawable.auto_blue, R.drawable.incandecent_blue, - R.drawable.fluorescent_blue, R.drawable.sunlight_blue, R.drawable.cloudy_blue}; + private static final int[] wbIcons = {R.drawable.ic_scene_mode_auto_wb, + R.drawable.ic_scene_mode_backlight, R.drawable.ic_scene_mode_fluorescent, + R.drawable.ic_scene_mode_sunset, R.drawable.ic_scene_mode_cloudy}; private static final int WB_ICON_SIZE = 80; private PathMeasure mCurveMeasure; private int mCurveLeft; @@ -350,7 +349,7 @@ public class ProMode extends View { ((TextView) v).setTextColor(Color.WHITE); } else if (v instanceof ImageView) { if (mMode == WHITE_BALANCE_MODE) { - ((ImageView) v).setImageResource(wbIcons[mIndex]); + ((ImageView) v).setColorFilter(null); } } } @@ -362,7 +361,7 @@ public class ProMode extends View { ((TextView) v).setTextColor(BLUE); } else if (v instanceof ImageView) { if (mMode == WHITE_BALANCE_MODE) { - ((ImageView) v).setImageResource(wbIconsBlue[mIndex]); + ((ImageView) v).setColorFilter(BLUE); } } if (key != null) mSettingsManager.setValueIndex(key, mIndex); @@ -383,6 +382,7 @@ public class ProMode extends View { @Override public boolean onTouchEvent(MotionEvent event) { + mUI.closeModeSwitcher(true); if (mMode == MANUAL_MODE) { float slider = getSlider(event.getX(), event.getY()); if (slider >= 0) { diff --git a/src/com/android/camera/ui/TouchTrackFocusRenderer.java b/src/com/android/camera/ui/TouchTrackFocusRenderer.java index 60c27d389..60c27d389 100755..100644 --- a/src/com/android/camera/ui/TouchTrackFocusRenderer.java +++ b/src/com/android/camera/ui/TouchTrackFocusRenderer.java diff --git a/src/com/android/camera/ui/ZoomRenderer.java b/src/com/android/camera/ui/ZoomRenderer.java index c9678f008..c9678f008 100755..100644 --- a/src/com/android/camera/ui/ZoomRenderer.java +++ b/src/com/android/camera/ui/ZoomRenderer.java diff --git a/src/com/android/camera/util/ApiHelper.java b/src/com/android/camera/util/ApiHelper.java index d1144956f..d1144956f 100755..100644 --- a/src/com/android/camera/util/ApiHelper.java +++ b/src/com/android/camera/util/ApiHelper.java diff --git a/src/com/android/camera/util/AutoTestUtil.java b/src/com/android/camera/util/AutoTestUtil.java index 4d8cc107e..4d8cc107e 100755..100644 --- a/src/com/android/camera/util/AutoTestUtil.java +++ b/src/com/android/camera/util/AutoTestUtil.java diff --git a/src/com/android/camera/util/CameraUtil.java b/src/com/android/camera/util/CameraUtil.java index 89e877087..89e877087 100755..100644 --- a/src/com/android/camera/util/CameraUtil.java +++ b/src/com/android/camera/util/CameraUtil.java diff --git a/src/com/android/camera/util/PersistUtil.java b/src/com/android/camera/util/PersistUtil.java index ce4466d4a..d2165ce42 100644..100755 --- a/src/com/android/camera/util/PersistUtil.java +++ b/src/com/android/camera/util/PersistUtil.java @@ -91,6 +91,8 @@ public class PersistUtil { getInt("persist.sys.camera.perf.memlimit", 120); private static final boolean PERSIST_CAMERA_UI_AUTO_TEST_ENABLED = getBoolean("persist.sys.camera.ui.auto_test", false); + private static final boolean PERSIST_CAMERA_SAVE_IN_SD_ENABLED = + getBoolean("persist.sys.env.camera.saveinsd", false); private static final boolean PERSIST_LONG_SAVE_ENABLED = getBoolean("persist.sys.camera.longshot.save", false); private static final boolean PERSIST_CAMERA_PREVIEW_RESTART_ENABLED = @@ -315,6 +317,10 @@ public class PersistUtil { return PERSIST_CAMERA_UI_AUTO_TEST_ENABLED; } + public static boolean isSaveInSdEnabled(){ + return PERSIST_CAMERA_SAVE_IN_SD_ENABLED; + } + public static boolean isLongSaveEnabled(){ return PERSIST_LONG_SAVE_ENABLED; } diff --git a/src/com/android/camera/util/SettingTranslation.java b/src/com/android/camera/util/SettingTranslation.java index f873799d6..f873799d6 100755..100644 --- a/src/com/android/camera/util/SettingTranslation.java +++ b/src/com/android/camera/util/SettingTranslation.java diff --git a/src/com/android/camera/util/VendorTagUtil.java b/src/com/android/camera/util/VendorTagUtil.java index 1c3160312..1c3160312 100755..100644 --- a/src/com/android/camera/util/VendorTagUtil.java +++ b/src/com/android/camera/util/VendorTagUtil.java |
