From f3c4aa5ab4bba7920a1cc16a00c17a41aa3b07c1 Mon Sep 17 00:00:00 2001 From: Jeevaka Prabu Badrappan Date: Wed, 18 Sep 2019 10:52:19 +0530 Subject: [PATCH 01/13] Capture and recording support with supported resolutions - Image capture in various supported resolutions - Video recording of 1080p,720p and 480p - Multicamera capture and recording - Added clang-format and also coding-style file - Fixed CtsVerifier Camera Intents failure Tracked-On: OAM-84763 Signed-off-by: Nikumbh, Mayur NimbaX Signed-off-by: Jeevaka Prabu Badrappan --- camera/.clang-format | 151 +++ camera/MultiCameraApplication/Android.mk | 41 +- .../AndroidManifest.xml | 41 +- .../ic_launcher-web.png | Bin 0 -> 16659 bytes .../intel/multicamera/AutoFitTextureView.java | 67 + .../com/intel/multicamera/BotmLeftCam.java | 557 +++++++++ .../com/intel/multicamera/BotmRightCam.java | 557 +++++++++ .../com/intel/multicamera/GlobalVariable.java | 27 + .../com/intel/multicamera/MainActivity.java | 1084 +++++------------ .../intel/multicamera/SettingsActivity.java | 485 ++++++++ .../com/intel/multicamera/TopLeftCam.java | 609 +++++++++ .../com/intel/multicamera/TopRightCam.java | 557 +++++++++ .../java/com/intel/multicamera/Utils.java | 192 +++ .../drawable-v24/ic_launcher_foreground.xml | 68 +- .../res/drawable/ic_launcher_background.xml | 244 ++-- .../res/drawable/ic_launcher_foreground.xml | 13 + .../res/drawable/ic_settings_black_24dp.xml | 5 + .../res/layout/activity_main.xml | 108 +- .../res/layout/content_main.xml | 169 +++ .../res/layout/settings_activity.xml | 9 + .../res/menu/menu_main.xml | 12 + .../res/mipmap-anydpi-v26/ic_launcher.xml | 8 +- .../mipmap-anydpi-v26/ic_launcher_round.xml | 8 +- .../res/mipmap-hdpi/ic_launcher.png | Bin 3056 -> 1490 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 5024 -> 3377 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 2096 -> 1378 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 2858 -> 2134 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 4569 -> 2789 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 7098 -> 5250 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 6464 -> 3755 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 10676 -> 7642 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 9250 -> 5778 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 15523 -> 11835 bytes .../res/values/arrays.xml | 66 + .../res/values/colors.xml | 12 +- .../res/values/dimens.xml | 3 + .../res/values/strings.xml | 65 +- .../res/values/styles.xml | 29 +- .../res/xml/root_preferences.xml | 57 + camera/coding_style.txt | 4 + 40 files changed, 4127 insertions(+), 1121 deletions(-) create mode 100644 camera/.clang-format create mode 100644 camera/MultiCameraApplication/ic_launcher-web.png create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/AutoFitTextureView.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/GlobalVariable.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/SettingsActivity.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java create mode 100644 camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java create mode 100644 camera/MultiCameraApplication/res/drawable/ic_launcher_foreground.xml create mode 100644 camera/MultiCameraApplication/res/drawable/ic_settings_black_24dp.xml create mode 100644 camera/MultiCameraApplication/res/layout/content_main.xml create mode 100644 camera/MultiCameraApplication/res/layout/settings_activity.xml create mode 100644 camera/MultiCameraApplication/res/menu/menu_main.xml create mode 100644 camera/MultiCameraApplication/res/values/arrays.xml create mode 100644 camera/MultiCameraApplication/res/values/dimens.xml create mode 100644 camera/MultiCameraApplication/res/xml/root_preferences.xml create mode 100644 camera/coding_style.txt diff --git a/camera/.clang-format b/camera/.clang-format new file mode 100644 index 0000000..77b4a86 --- /dev/null +++ b/camera/.clang-format @@ -0,0 +1,151 @@ +--- +Language: Java +BasedOnStyle: Google +AccessModifierOffset: -1 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: true +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: true +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Attach +BreakBeforeInheritanceComma: false +BreakInheritanceList: BeforeColon +BreakBeforeTernaryOperators: true +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeColon +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: true +ColumnLimit: 100 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: true +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DerivePointerAlignment: true +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '^' + Priority: 2 + - Regex: '^<.*\.h>' + Priority: 1 + - Regex: '^<.*' + Priority: 2 + - Regex: '.*' + Priority: 3 +IncludeIsMainRegex: '([-_](test|unittest))?$' +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Never +ObjCBlockIndentWidth: 4 +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Left +RawStringFormats: + - Language: Cpp + Delimiters: + - cc + - CC + - cpp + - Cpp + - CPP + - 'c++' + - 'C++' + CanonicalDelimiter: '' + BasedOnStyle: google + - Language: TextProto + Delimiters: + - pb + - PB + - proto + - PROTO + EnclosingFunctions: + - EqualsProto + - EquivToProto + - PARSE_PARTIAL_TEXT_PROTO + - PARSE_TEST_PROTO + - PARSE_TEXT_PROTO + - ParseTextOrDie + - ParseTextProtoOrDie + CanonicalDelimiter: '' + BasedOnStyle: google +ReflowComments: true +SortIncludes: true +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 2 +SpacesInAngles: false +SpacesInContainerLiterals: true +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Auto +StatementMacros: + - Q_UNUSED + - QT_REQUIRE_VERSION +TabWidth: 8 +UseTab: Never +... + diff --git a/camera/MultiCameraApplication/Android.mk b/camera/MultiCameraApplication/Android.mk index ebd5fc5..6fc2b5b 100644 --- a/camera/MultiCameraApplication/Android.mk +++ b/camera/MultiCameraApplication/Android.mk @@ -1,18 +1,37 @@ LOCAL_PATH:= $(call my-dir) - include $(CLEAR_VARS) -LOCAL_MODULE_TAGS := optional -LOCAL_CERTIFICATE := platform -LOCAL_SDK_VERSION := current +LOCAL_PACKAGE_NAME := MultiCameraApp +LOCAL_MODULE_TAGS := optional +LOCAL_DEX_PREOPT := false +LOCAL_CERTIFICATE := platform +LOCAL_SDK_VERSION := current LOCAL_MIN_SDK_VERSION := 27 -LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res -LOCAL_SRC_FILES := \ - $(call all-java-files-under, java) -LOCAL_DEX_PREOPT := false -LOCAL_PACKAGE_NAME := MultiCameraApp +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res \ -include $(BUILD_PACKAGE) +LOCAL_SRC_FILES := $(call all-java-files-under, java) + + +LOCAL_AIDL_INCLUDES := \ + frameworks/native/aidl/gui -include $(call all-makefiles-under, $(LOCAL_PATH)) +#LOCAL_PROGUARD_FLAG_FILES := ../../../frameworks/support/design/proguard-rules.pro + +LOCAL_USE_AAPT2 := true + +LOCAL_STATIC_JAVA_LIBRARIES = \ + androidx-constraintlayout_constraintlayout-solver + +LOCAL_PROPRIETARY_MODULE := true + +LOCAL_STATIC_ANDROID_LIBRARIES := \ + androidx-constraintlayout_constraintlayout \ + androidx.preference_preference \ + androidx.cardview_cardview \ + com.google.android.material_material \ + androidx.legacy_legacy-support-v13 \ + androidx.legacy_legacy-support-v4 \ + androidx.appcompat_appcompat + +include $(BUILD_PACKAGE) diff --git a/camera/MultiCameraApplication/AndroidManifest.xml b/camera/MultiCameraApplication/AndroidManifest.xml index 862e909..2919c63 100644 --- a/camera/MultiCameraApplication/AndroidManifest.xml +++ b/camera/MultiCameraApplication/AndroidManifest.xml @@ -2,26 +2,51 @@ + + + + + + + + + - + android:theme="@style/AppTheme" + android:requestLegacyExternalStorage="true"> + + + + + + + + + + + + + - - - - diff --git a/camera/MultiCameraApplication/ic_launcher-web.png b/camera/MultiCameraApplication/ic_launcher-web.png new file mode 100644 index 0000000000000000000000000000000000000000..0c90ffee5938eff24e8ebac206fac4f524714a2e GIT binary patch literal 16659 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%0AD%F z>0vC26~mTJ(neF<(3A%j+=KQ=^Fj9G-0;4XTNWFc8EJAg}0M(ILhd15TvGiq0{4~TwqPK?dR#xiv zNcP)DEN+*cEVE$YfTWAT6T4z|=6vOFqV~O_VwME zEM}CB@v+`k8@zezA(Qd5E&B#@0xTQv5gIyYN(^Xyl?3sTOZbIXm^%En-KBm?iJ953 zJ}cOCFxoQ|eg9V9abc-*jf7HIN<{N;R$<lU0=^+5r+dcK2UWkf7k%= zYCUY@Ews0x@ODdbsp6wOh4llD|I|j*%^YpDRI?Q(=xr~%i z_$6Py{aXPKX6~fJl07~791o|vweWVfGKItl{jQ@M&1?p5%PsYv#XQCw<6Txzo{#Kh z!siE<6>j$JD`JWoUg(+m`%H z_X1pQY}kPc8NF%u(JzW@LL literal 0 HcmV?d00001 diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/AutoFitTextureView.java b/camera/MultiCameraApplication/java/com/intel/multicamera/AutoFitTextureView.java new file mode 100644 index 0000000..b3d28b9 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/AutoFitTextureView.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.intel.multicamera; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.TextureView; + +/** + * A {@link TextureView} that can be adjusted to a specified aspect ratio. + */ +public class AutoFitTextureView extends TextureView { + private int mRatioWidth = 0; + private int mRatioHeight = 0; + public AutoFitTextureView(Context context) { this(context, null); } + public AutoFitTextureView(Context context, AttributeSet attrs) { this(context, attrs, 0); } + public AutoFitTextureView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Sets the aspect ratio for this view. The size of the view will be measured based + * on the ratio calculated from the parameters. Note that the actual sizes of parameters + * don't matter, that is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the + * same result. + * @param width Relative horizontal size + * @param height Relative vertical size + */ + public void setAspectRatio(int width, int height) { + if (width < 0 || height < 0) { + throw new IllegalArgumentException("Size cannot be negative."); + } + mRatioWidth = width; + mRatioHeight = height; + requestLayout(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = MeasureSpec.getSize(widthMeasureSpec); + int height = MeasureSpec.getSize(heightMeasureSpec); + if (0 == mRatioWidth || 0 == mRatioHeight) { + setMeasuredDimension(width, height); + } else { + if (width < height * mRatioWidth / mRatioHeight) { + setMeasuredDimension(width, width * mRatioHeight / mRatioWidth); + } else { + setMeasuredDimension(height * mRatioWidth / mRatioHeight, height); + } + } + } +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java new file mode 100644 index 0000000..8b2e628 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmLeftCam.java @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * 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.intel.multicamera; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.*; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; +import android.media.Image; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Size; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.preference.PreferenceManager; +import java.io.*; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class BotmLeftCam { + Activity mActivity; + private static final String TAG = "BotmLeftCam"; + private String mNextVideoAbsolutePath; + private CamcorderProfile mProfile; + /** + * An {@link AutoFitTextureView} for camera preview. + */ + private AutoFitTextureView textureView; + private Button takePictureButton, TakeVideoButton; + + private MediaRecorder mMediaRecorder; + private String cameraId; + protected CameraDevice cameraDevice; + protected CameraCaptureSession cameraCaptureSessions; + protected CaptureRequest captureRequest; + protected CaptureRequest.Builder captureRequestBuilder; + private Size imageDimension; + private ImageReader imageReader; + private File file; + private static final int REQUEST_CAMERA_PERMISSION = 200; + private final int PERMISSIONS_REQUEST_SNAPSHOT = 3; + + private Handler mBackgroundHandler; + private HandlerThread mBackgroundThread; + private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private SharedPreferences settings; + /** + * Whether the app is recording video now + */ + private boolean mIsRecordingVideo; + + // The video file that the hardware camera is about to record into + // (or is recording into. + private String mVideoFilename, mPictureFilename; + private ContentValues mCurrentVideoValues, mCurrentPictureValues; + byte[] jpegLength; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + + public BotmLeftCam(Activity activity, AutoFitTextureView textureView, Button PictureButton, + Button RecordButton) { + Log.e(TAG, "constructor called"); + this.mActivity = activity; + this.textureView = textureView; + this.textureView.setSurfaceTextureListener(textureListener); + this.ClickListeners(PictureButton, RecordButton); + this.settings = PreferenceManager.getDefaultSharedPreferences(activity); + } + + public void ClickListeners(Button PictureButton, Button RecordButton) { + TakePicureOnClicked(PictureButton); + + StartVideoRecording(RecordButton); + } + + private void TakePicureOnClicked(Button PictureButton) { + takePictureButton = PictureButton; + assert takePictureButton != null; + + takePictureButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + takePicture(); + Utils.broadcastNewPicture(mActivity.getApplicationContext(), mCurrentPictureValues); + } + }); + } + + private void StartVideoRecording(Button RecordButton) { + TakeVideoButton = RecordButton; + TakeVideoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Intent i = new Intent(HomeScreenActivity.this, CameraActivity.class); + System.out.println(" onCreate Record0"); + if (mIsRecordingVideo) { + stopRecordingVideo(); + Utils.broadcastNewVideo(mActivity.getApplicationContext(), mCurrentVideoValues); + takePictureButton.setVisibility(View.VISIBLE); + } else { + startRecordingVideo(); + takePictureButton.setVisibility(View.GONE); + } + } + }); + } + + TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + // open your camera here + openCamera(); + } + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // Transform you image captured size according to the surface width and height + } + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + return false; + } + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) {} + }; + + public void openCamera() { + CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE); + Log.e(TAG, "is camera open"); + try { + cameraId = manager.getCameraIdList()[2]; + Log.e(TAG, "is camera open ID" + cameraId); + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + StreamConfigurationMap map = + characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + assert map != null; + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, + new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_CAMERA_PERMISSION); + return; + } + + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + String videoQuality = settings.getString("video_list", "medium"); + + int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality); + Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality); + + mProfile = CamcorderProfile.get(0, quality); + + Log.d(TAG, "camera1 final Video width:" + mProfile.videoFrameWidth + " " + + "final Video height: " + mProfile.videoFrameHeight); + + Log.d(TAG, "camera1 final preview width:" + imageDimension.getWidth() + " " + + "final preview height: " + imageDimension.getHeight()); + + mMediaRecorder = new MediaRecorder(); + + startBackgroundThread(); + + manager.openCamera(cameraId, stateCallback, null); + + } catch (CameraAccessException e) { + e.printStackTrace(); + } + Log.e(TAG, "openCamera X"); + } + + private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { + @Override + public void onOpened(CameraDevice camera) { + // This is called when the camera is open + Log.e(TAG, "onOpened"); + cameraDevice = camera; + createCameraPreview(); + } + @Override + public void onDisconnected(CameraDevice camera) { + cameraDevice.close(); + } + @Override + public void onError(CameraDevice camera, int error) { + cameraDevice.close(); + cameraDevice = null; + } + }; + + protected void createCameraPreview() { + try { + closePreviewSession(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight()); + Surface surface = new Surface(texture); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + captureRequestBuilder.addTarget(surface); + cameraDevice.createCaptureSession( + Arrays.asList(surface), new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == cameraDevice) { + return; + } + // When the session is ready, we start displaying the preview. + cameraCaptureSessions = cameraCaptureSession; + updatePreview(); + } + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Configuration change", Toast.LENGTH_SHORT) + .show(); + } + }, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public void closeCamera() { + if (null != cameraDevice) { + cameraDevice.close(); + cameraDevice = null; + } + if (null != imageReader) { + imageReader.close(); + imageReader = null; + } + if (null != mMediaRecorder) { + mMediaRecorder.release(); + mMediaRecorder = null; + } + closePreviewSession(); + stopBackgroundThread(); + } + + /** + * Starts a background thread and its {@link Handler}. + */ + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("Camera-$cameraId"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + /** + * Stops the background thread and its {@link Handler}. + */ + private void stopBackgroundThread() { + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + protected void updatePreview() { + if (null == cameraDevice) { + Log.e(TAG, "updatePreview error, return"); + } + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + HandlerThread thread = new HandlerThread("Camera Preview"); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + try { + cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, handler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + protected void takePicture() { + if (null == cameraDevice) { + Log.e(TAG, "cameraDevice is null"); + return; + } + + try { + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + ImageReader reader = ImageReader.newInstance( + imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1); + List outputSurfaces = new ArrayList(2); + outputSurfaces.add(reader.getSurface()); + outputSurfaces.add(new Surface(textureView.getSurfaceTexture())); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); + captureRequestBuilder.addTarget(reader.getSurface()); + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, + CameraMetadata.CONTROL_MODE_AUTO); + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST_SNAPSHOT); + return; + } + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mPictureFilename = fileDetails[3]; + mCurrentPictureValues = + Utils.getContentValues(Utils.MEDIA_TYPE_IMAGE, fileDetails, + imageDimension.getWidth(), imageDimension.getHeight()); + + file = new File(mPictureFilename); + + ImageReader.OnImageAvailableListener readerListener = + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = null; + try { + image = reader.acquireLatestImage(); + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.capacity()]; + buffer.get(bytes); + jpegLength = bytes; + mCurrentPictureValues.put(MediaStore.Images.ImageColumns.SIZE, + jpegLength); + + save(bytes); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (image != null) { + image.close(); + } + } + } + + private void save(byte[] bytes) throws IOException { + OutputStream output = null; + try { + output = new FileOutputStream(file); + output.write(bytes); + } finally { + if (null != output) { + output.close(); + } + } + } + }; + reader.setOnImageAvailableListener(readerListener, mBackgroundHandler); + final CameraCaptureSession.CaptureCallback captureListener = + new CameraCaptureSession.CaptureCallback() { + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + super.onCaptureCompleted(session, request, result); + Toast.makeText(mActivity, "Saved:" + file, Toast.LENGTH_SHORT).show(); + + createCameraPreview(); + } + }; + cameraDevice.createCaptureSession( + outputSurfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession session) { + try { + session.capture(captureRequestBuilder.build(), captureListener, + mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) {} + }, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /* Recording Start*/ + private void startRecordingVideo() { + if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) { + return; + } + try { + closePreviewSession(); + setUpMediaRecorder(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + + captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); + List surfaces = new ArrayList<>(); + + // Set up Surface for the camera preview + Surface previewSurface = new Surface(texture); + surfaces.add(previewSurface); + captureRequestBuilder.addTarget(previewSurface); + + // Set up Surface for the MediaRecorder + Surface recorderSurface = mMediaRecorder.getSurface(); + surfaces.add(recorderSurface); + captureRequestBuilder.addTarget(recorderSurface); + + // Start a capture session + // Once the session starts, we can update the UI and start recording + cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(@NonNull CameraCaptureSession camCaptureSession) { + cameraCaptureSessions = camCaptureSession; + updatePreview(); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + // UI + TakeVideoButton.setText(R.string.stop); + mIsRecordingVideo = true; + + // Start recording + mMediaRecorder.start(); + } + }); + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + if (null != mActivity) { + Toast.makeText(mActivity, "Failed", Toast.LENGTH_SHORT).show(); + } + } + }, mBackgroundHandler); + } catch (CameraAccessException | IOException e) { + e.printStackTrace(); + } + } + + private void setUpMediaRecorder() throws IOException { + if (null == mActivity) { + return; + } + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); + mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + mMediaRecorder.setVideoFrameRate(30); + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mVideoFilename = fileDetails[3]; + mCurrentVideoValues = + Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth, + mProfile.videoFrameHeight); + + /** + * set output file in media recorder + */ + mMediaRecorder.setOutputFile(mVideoFilename); + mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); + mMediaRecorder.setVideoEncodingBitRate(10000000); + + mMediaRecorder.prepare(); + } + + private void closePreviewSession() { + System.out.println(" closePreviewSession"); + if (cameraCaptureSessions != null) { + cameraCaptureSessions.close(); + cameraCaptureSessions = null; + } + } + + private void stopRecordingVideo() { + mIsRecordingVideo = false; + TakeVideoButton.setText(R.string.record); + // Stop recording + mMediaRecorder.stop(); + mMediaRecorder.reset(); + + if (null != mActivity) { + Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show(); + Log.d(TAG, "Video saved: " + mVideoFilename); + } + mVideoFilename = null; + + createCameraPreview(); + } +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java new file mode 100644 index 0000000..4d22b42 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/BotmRightCam.java @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * 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.intel.multicamera; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.*; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; +import android.media.Image; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Size; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.preference.PreferenceManager; +import java.io.*; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class BotmRightCam { + Activity mActivity; + private static final String TAG = "BotmRightCam"; + private String mNextVideoAbsolutePath; + private CamcorderProfile mProfile; + /** + * An {@link AutoFitTextureView} for camera preview. + */ + private AutoFitTextureView textureView; + private Button takePictureButton, TakeVideoButton; + + private MediaRecorder mMediaRecorder; + private String cameraId; + protected CameraDevice cameraDevice; + protected CameraCaptureSession cameraCaptureSessions; + protected CaptureRequest captureRequest; + protected CaptureRequest.Builder captureRequestBuilder; + private Size imageDimension; + private ImageReader imageReader; + private File file; + private static final int REQUEST_CAMERA_PERMISSION = 200; + private final int PERMISSIONS_REQUEST_SNAPSHOT = 3; + + private Handler mBackgroundHandler; + private HandlerThread mBackgroundThread; + private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private SharedPreferences settings; + /** + * Whether the app is recording video now + */ + private boolean mIsRecordingVideo; + + // The video file that the hardware camera is about to record into + // (or is recording into. + private String mVideoFilename, mPictureFilename; + private ContentValues mCurrentVideoValues, mCurrentPictureValues; + byte[] jpegLength; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + + public BotmRightCam(Activity activity, AutoFitTextureView textureView, Button PictureButton, + Button RecordButton) { + Log.e(TAG, "constructor called"); + this.mActivity = activity; + this.textureView = textureView; + this.textureView.setSurfaceTextureListener(textureListener); + this.ClickListeners(PictureButton, RecordButton); + this.settings = PreferenceManager.getDefaultSharedPreferences(activity); + } + + public void ClickListeners(Button PictureButton, Button RecordButton) { + TakePicureOnClicked(PictureButton); + + StartVideoRecording(RecordButton); + } + + private void TakePicureOnClicked(Button PictureButton) { + takePictureButton = PictureButton; + assert takePictureButton != null; + + takePictureButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + takePicture(); + Utils.broadcastNewPicture(mActivity.getApplicationContext(), mCurrentPictureValues); + } + }); + } + + private void StartVideoRecording(Button RecordButton) { + TakeVideoButton = RecordButton; + TakeVideoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Intent i = new Intent(HomeScreenActivity.this, CameraActivity.class); + System.out.println(" onCreate Record0"); + if (mIsRecordingVideo) { + stopRecordingVideo(); + Utils.broadcastNewVideo(mActivity.getApplicationContext(), mCurrentVideoValues); + takePictureButton.setVisibility(View.VISIBLE); + } else { + startRecordingVideo(); + takePictureButton.setVisibility(View.GONE); + } + } + }); + } + + TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + // open your camera here + openCamera(); + } + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // Transform you image captured size according to the surface width and height + } + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + return false; + } + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) {} + }; + + public void openCamera() { + CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE); + Log.e(TAG, "is camera open"); + try { + cameraId = manager.getCameraIdList()[3]; + Log.e(TAG, "is camera open ID" + cameraId); + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + StreamConfigurationMap map = + characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + assert map != null; + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, + new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_CAMERA_PERMISSION); + return; + } + + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + String videoQuality = settings.getString("video_list", "medium"); + + int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality); + Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality); + + mProfile = CamcorderProfile.get(0, quality); + + Log.d(TAG, "camera1 final Video width:" + mProfile.videoFrameWidth + " " + + "final Video height: " + mProfile.videoFrameHeight); + + Log.d(TAG, "camera1 final preview width:" + imageDimension.getWidth() + " " + + "final preview height: " + imageDimension.getHeight()); + + mMediaRecorder = new MediaRecorder(); + + startBackgroundThread(); + + manager.openCamera(cameraId, stateCallback, null); + + } catch (CameraAccessException e) { + e.printStackTrace(); + } + Log.e(TAG, "openCamera X"); + } + + private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { + @Override + public void onOpened(CameraDevice camera) { + // This is called when the camera is open + Log.e(TAG, "onOpened"); + cameraDevice = camera; + createCameraPreview(); + } + @Override + public void onDisconnected(CameraDevice camera) { + cameraDevice.close(); + } + @Override + public void onError(CameraDevice camera, int error) { + cameraDevice.close(); + cameraDevice = null; + } + }; + + protected void createCameraPreview() { + try { + closePreviewSession(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight()); + Surface surface = new Surface(texture); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + captureRequestBuilder.addTarget(surface); + cameraDevice.createCaptureSession( + Arrays.asList(surface), new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == cameraDevice) { + return; + } + // When the session is ready, we start displaying the preview. + cameraCaptureSessions = cameraCaptureSession; + updatePreview(); + } + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Configuration change", Toast.LENGTH_SHORT) + .show(); + } + }, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public void closeCamera() { + if (null != cameraDevice) { + cameraDevice.close(); + cameraDevice = null; + } + if (null != imageReader) { + imageReader.close(); + imageReader = null; + } + if (null != mMediaRecorder) { + mMediaRecorder.release(); + mMediaRecorder = null; + } + closePreviewSession(); + stopBackgroundThread(); + } + + /** + * Starts a background thread and its {@link Handler}. + */ + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("Camera-$cameraId"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + /** + * Stops the background thread and its {@link Handler}. + */ + private void stopBackgroundThread() { + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + protected void updatePreview() { + if (null == cameraDevice) { + Log.e(TAG, "updatePreview error, return"); + } + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + HandlerThread thread = new HandlerThread("Camera Preview"); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + try { + cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, handler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + protected void takePicture() { + if (null == cameraDevice) { + Log.e(TAG, "cameraDevice is null"); + return; + } + + try { + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + ImageReader reader = ImageReader.newInstance( + imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1); + List outputSurfaces = new ArrayList(2); + outputSurfaces.add(reader.getSurface()); + outputSurfaces.add(new Surface(textureView.getSurfaceTexture())); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); + captureRequestBuilder.addTarget(reader.getSurface()); + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, + CameraMetadata.CONTROL_MODE_AUTO); + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST_SNAPSHOT); + return; + } + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mPictureFilename = fileDetails[3]; + mCurrentPictureValues = + Utils.getContentValues(Utils.MEDIA_TYPE_IMAGE, fileDetails, + imageDimension.getWidth(), imageDimension.getHeight()); + + file = new File(mPictureFilename); + + ImageReader.OnImageAvailableListener readerListener = + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = null; + try { + image = reader.acquireLatestImage(); + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.capacity()]; + buffer.get(bytes); + jpegLength = bytes; + mCurrentPictureValues.put(MediaStore.Images.ImageColumns.SIZE, + jpegLength); + + save(bytes); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (image != null) { + image.close(); + } + } + } + + private void save(byte[] bytes) throws IOException { + OutputStream output = null; + try { + output = new FileOutputStream(file); + output.write(bytes); + } finally { + if (null != output) { + output.close(); + } + } + } + }; + reader.setOnImageAvailableListener(readerListener, mBackgroundHandler); + final CameraCaptureSession.CaptureCallback captureListener = + new CameraCaptureSession.CaptureCallback() { + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + super.onCaptureCompleted(session, request, result); + Toast.makeText(mActivity, "Saved:" + file, Toast.LENGTH_SHORT).show(); + + createCameraPreview(); + } + }; + cameraDevice.createCaptureSession( + outputSurfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession session) { + try { + session.capture(captureRequestBuilder.build(), captureListener, + mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) {} + }, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /* Recording Start*/ + private void startRecordingVideo() { + if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) { + return; + } + try { + closePreviewSession(); + setUpMediaRecorder(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + + captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); + List surfaces = new ArrayList<>(); + + // Set up Surface for the camera preview + Surface previewSurface = new Surface(texture); + surfaces.add(previewSurface); + captureRequestBuilder.addTarget(previewSurface); + + // Set up Surface for the MediaRecorder + Surface recorderSurface = mMediaRecorder.getSurface(); + surfaces.add(recorderSurface); + captureRequestBuilder.addTarget(recorderSurface); + + // Start a capture session + // Once the session starts, we can update the UI and start recording + cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(@NonNull CameraCaptureSession camCaptureSession) { + cameraCaptureSessions = camCaptureSession; + updatePreview(); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + // UI + TakeVideoButton.setText(R.string.stop); + mIsRecordingVideo = true; + + // Start recording + mMediaRecorder.start(); + } + }); + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + if (null != mActivity) { + Toast.makeText(mActivity, "Failed", Toast.LENGTH_SHORT).show(); + } + } + }, mBackgroundHandler); + } catch (CameraAccessException | IOException e) { + e.printStackTrace(); + } + } + + private void setUpMediaRecorder() throws IOException { + if (null == mActivity) { + return; + } + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); + mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + mMediaRecorder.setVideoFrameRate(30); + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mVideoFilename = fileDetails[3]; + mCurrentVideoValues = + Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth, + mProfile.videoFrameHeight); + + /** + * set output file in media recorder + */ + mMediaRecorder.setOutputFile(mVideoFilename); + mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); + mMediaRecorder.setVideoEncodingBitRate(10000000); + + mMediaRecorder.prepare(); + } + + private void closePreviewSession() { + System.out.println(" closePreviewSession"); + if (cameraCaptureSessions != null) { + cameraCaptureSessions.close(); + cameraCaptureSessions = null; + } + } + + private void stopRecordingVideo() { + mIsRecordingVideo = false; + TakeVideoButton.setText(R.string.record); + // Stop recording + mMediaRecorder.stop(); + mMediaRecorder.reset(); + + if (null != mActivity) { + Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show(); + Log.d(TAG, "Video saved: " + mVideoFilename); + } + mVideoFilename = null; + + createCameraPreview(); + } +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/GlobalVariable.java b/camera/MultiCameraApplication/java/com/intel/multicamera/GlobalVariable.java new file mode 100644 index 0000000..e6e5415 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/GlobalVariable.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * 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.intel.multicamera; + +import android.util.Size; +import java.util.List; + +public class GlobalVariable { + public static String message; + public static int numOfCameras; + public static List SupportedSizes; + public static String camerId; +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java b/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java index ac00b64..aba96ab 100644 --- a/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/MainActivity.java @@ -1,794 +1,290 @@ -package com.intel.multicamera; - -import android.Manifest; -import android.app.Activity; -import android.content.Context; -import android.content.pm.PackageManager; -import android.graphics.ImageFormat; -import android.graphics.Matrix; -import android.graphics.Point; -import android.graphics.RectF; -import android.graphics.SurfaceTexture; -import android.hardware.camera2.CameraCaptureSession; -import android.hardware.camera2.CameraCharacteristics; -import android.hardware.camera2.CameraDevice; -import android.hardware.camera2.CameraManager; -import android.hardware.camera2.CameraMetadata; -import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.params.StreamConfigurationMap; -import android.media.Image; -import android.os.Bundle; -import android.os.Handler; -import android.os.HandlerThread; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.Size; -import android.view.Surface; -import android.view.TextureView; -import android.view.ViewGroup; -import android.widget.LinearLayout; - -import java.util.Arrays; -import java.util.concurrent.Semaphore; -import java.util.concurrent.TimeUnit; - -public class MainActivity extends Activity { - private String TAG = "multicamera"; - private Size previewsize,previewsize1,previewsize2,previewsize3; - private int preview_width =640; - private int preview_height =480; - private Semaphore mCameraOpenCloseLock = new Semaphore(1); - private int vwidth; - private int vheight; - private static final int PERMISSIONS_REQUEST_CAMERA = 1; - private Size jpegSizes[] = null; - - private TextureView textureView,textureView1,textureView2,textureView3; - private CameraDevice cameraDevice,cameraDevice1,cameraDevice2,cameraDevice3; - private CaptureRequest.Builder previewBuilder,previewBuilder1,previewBuilder2,previewBuilder3; - private CameraCaptureSession previewSession,previewSession1,previewSession2,previewSession3; - - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - - DisplayMetrics displayMetrics = new DisplayMetrics(); - getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); - - LinearLayout layout = findViewById(R.id.lay1); - ViewGroup.LayoutParams params1 = layout.getLayoutParams(); - - params1.height = displayMetrics.heightPixels; - params1.width = displayMetrics.widthPixels; - layout.setLayoutParams(params1); - Log.d(TAG,"params1.height: "+params1.height+" params1.width: "+ params1.width); - - - vheight = displayMetrics.heightPixels/2; - vwidth = displayMetrics.widthPixels/2; - - Log.d(TAG,"vwidth1: "+vwidth+" vheight1: "+vheight); - - - android.widget.FrameLayout.LayoutParams params = new android.widget.FrameLayout.LayoutParams(vwidth, vheight); - - textureView = (TextureView) findViewById(R.id.textureview1); - textureView1 = (TextureView) findViewById(R.id.textureview2); - textureView2 = (TextureView) findViewById(R.id.textureview3); - textureView3 = (TextureView) findViewById(R.id.textureview4); - - textureView.setSurfaceTextureListener(surfaceTextureListener); - textureView1.setSurfaceTextureListener(surfaceTextureListener1); - textureView2.setSurfaceTextureListener(surfaceTextureListener2); - textureView3.setSurfaceTextureListener(surfaceTextureListener3); - - textureView.setLayoutParams(params); - textureView1.setLayoutParams(params); - textureView2.setLayoutParams(params); - textureView3.setLayoutParams(params); - - } - - - @Override - public void onRequestPermissionsResult (int requestCode, String[] permissions, - int[] grantResults) { - if (requestCode == PERMISSIONS_REQUEST_CAMERA) { - if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - openCamera(); - } else { - finish(); - } - } - - } - - - - private TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Log.d(TAG,"Inside surfaceTextureListener onSurfaceTextureAvailable:"); - openCamera(); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - }; - - - private TextureView.SurfaceTextureListener surfaceTextureListener1 = new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Log.d(TAG,"Inside surfaceTextureListener1 onSurfaceTextureAvailable:"); - openCamera1(); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - }; - - private TextureView.SurfaceTextureListener surfaceTextureListener2 = new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Log.d(TAG,"Inside surfaceTextureListener2 onSurfaceTextureAvailable:"); - openCamera2(); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - }; - - private TextureView.SurfaceTextureListener surfaceTextureListener3 = new TextureView.SurfaceTextureListener() { - @Override - public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { - Log.d(TAG,"Inside surfaceTextureListener3 onSurfaceTextureAvailable:"); - openCamera3(); - } - - @Override - public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { - - } - - @Override - public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { - return false; - } - - @Override - public void onSurfaceTextureUpdated(SurfaceTexture surface) { - - } - }; - - public void openCamera() { - CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); - - try { - Log.d(TAG,"Inside openCamera() Total Cameras: "+manager.getCameraIdList().length); - String camerId = manager.getCameraIdList()[0]; - CameraCharacteristics characteristics = manager.getCameraCharacteristics(camerId); - StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); - int total_psizes = map.getOutputSizes(ImageFormat.JPEG).length; - Log.d(TAG,"openCamera() total_psizes: "+total_psizes); - - for (int i=0;i(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG))); + + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public static class SettingsFragment + extends PreferenceFragment implements OnSharedPreferenceChangeListener { + public String TAG = "SettingsFragment"; + public static final String SIZE_LARGE = "large"; + public static final String SIZE_MEDIUM = "medium"; + public static final String SIZE_SMALL = "small"; + + public String[] mCamcorderProfileNames; + private static final String SIZE_SETTING_STRING_DIMENSION_DELIMITER = "x"; + + public static SparseArray sCachedSelectedVideoQualities = + new SparseArray(3); + + /** The selected {@link CamcorderProfile} qualities. */ + public static class SelectedVideoQualities { + public int large = -1; + public int medium = -1; + public int small = -1; + + public int getFromSetting(String sizeSetting) { + // Sanitize the value to be either small, medium or large. Default + // to the latter. + if (!SIZE_SMALL.equals(sizeSetting) && !SIZE_MEDIUM.equals(sizeSetting)) { + sizeSetting = SIZE_LARGE; + } + + if (SIZE_LARGE.equals(sizeSetting)) { + return large; + } else if (SIZE_MEDIUM.equals(sizeSetting)) { + return medium; + } else { + return small; + } + } + } + + /** Video qualities sorted by size. */ + public static int[] sVideoQualities = + new int[] {// CamcorderProfile.QUALITY_HIGH, + CamcorderProfile.QUALITY_1080P, CamcorderProfile.QUALITY_720P, + CamcorderProfile.QUALITY_480P, CamcorderProfile.QUALITY_CIF, + CamcorderProfile.QUALITY_QVGA, CamcorderProfile.QUALITY_QCIF, + CamcorderProfile.QUALITY_LOW}; + + static SelectedVideoQualities VideoQualities; + public int numOfCameras = GlobalVariable.numOfCameras; + public String camerId = GlobalVariable.camerId; + ; + public List PictureSizes = GlobalVariable.SupportedSizes; + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + setPreferencesFromResource(R.xml.root_preferences, rootKey); + mCamcorderProfileNames = getResources().getStringArray(R.array.camcorder_profile_names); + } + + @Override + public void onResume() { + super.onResume(); + // final Activity activity = this.getActivity(); + + Log.d(TAG, "SettingsFragment onResume"); + + /* Put in the summaries for the currently set values. + final PreferenceScreen Multi_Cam = + (PreferenceScreen) findPreference("multi_usb_cam_list");*/ + + setVisibilities(); + + if (numOfCameras > 0) { + VideoQualities = getSelectedVideoQualities(0); + + // Put in the summaries for the currently set values. + final PreferenceGroup Prf_Resolution = + (PreferenceGroup)findPreference("pref_resolution"); + + fillEntriesAndSummaries(Prf_Resolution); + } + + Log.d(TAG, "SettingsFragment onResume end"); + + getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener( + this); + } + + @Override + public void onPause() { + super.onPause(); + getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener( + this); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String Key) { + setSummary(findPreference(Key)); + } + + /** + * Set the summary for the given preference. The given preference needs + * to be a {@link ListPreference}. + */ + private void setSummary(Preference preference) { + if (!(preference instanceof ListPreference)) { + return; + } + + ListPreference listPreference = (ListPreference)preference; + if (listPreference.getKey().equals("capture_list")) { + setSummaryForSelection(PictureSizes, listPreference); + } else if (listPreference.getKey().equals("video_list")) { + setSummaryForSelection(VideoQualities, listPreference); + } else { + listPreference.setSummary(listPreference.getEntry()); + } + } + + /** + * This is used to serialize a size to a string for storage in settings + * + * @param size The size to serialize. + * @return the string to be saved in preferences + */ + private static String sizeToSettingString(Size size) { + return size.getWidth() + SIZE_SETTING_STRING_DIMENSION_DELIMITER + size.getHeight(); + } + + /** + * Sets the summary for the given list preference. + * + * @param displayableSizes The human readable preferred sizes + * @param preference The preference for which to set the summary. + */ + private void setSummaryForSelection(List displayableSizes, + ListPreference preference) { + String setting = preference.getValue(); + if (setting == null || !setting.contains("x")) { + return; + } + Size settingSize = sizeFromSettingString(setting); + if (settingSize == null) { + return; + } + preference.setSummary(getSizeSummaryString(settingSize)); + } + + /** + * Sets the summary for the given list preference. + * + * @param selectedQualities The selected video qualities. + * @param preference The preference for which to set the summary. + */ + private void setSummaryForSelection(SelectedVideoQualities selectedQualities, + ListPreference preference) { + if (selectedQualities == null) { + return; + } + + int selectedQuality = selectedQualities.getFromSetting(preference.getValue()); + Log.d(TAG, "SettingsFragment selectedQuality " + selectedQuality); + preference.setSummary(mCamcorderProfileNames[selectedQuality]); + } + + /** + * This parses a setting string and returns the representative size. + * + * @param sizeSettingString The string that stored in settings to represent a size. + * @return the represented Size. + */ + public static Size sizeFromSettingString(String sizeSettingString) { + if (sizeSettingString == null) { + return null; + } + String[] parts = sizeSettingString.split(SIZE_SETTING_STRING_DIMENSION_DELIMITER); + if (parts.length != 2) { + return null; + } + + try { + int width = Integer.parseInt(parts[0]); + int height = Integer.parseInt(parts[1]); + return new Size(width, height); + } catch (NumberFormatException ex) { + return null; + } + } + + /** + * @param size The photo resolution. + * @return A human readable and translated string for labeling the + * picture size in megapixels. + */ + private String getSizeSummaryString(Size size) { + int width = size.getWidth(); + int height = size.getHeight(); + String result = + getResources().getString(R.string.setting_summary_width_and_height, width, height); + return result; + } + + /** + * Depending on camera availability on the device, this removes settings + * for cameras the device doesn't have. + */ + private void setVisibilities() { + Log.d(TAG, "SettingsFragment setVisibilities"); + + PreferenceGroup Prf_Resolution = (PreferenceGroup)findPreference("pref_resolution"); + PreferenceGroup Prf_Src = (PreferenceGroup)findPreference("pref_Source"); + if (numOfCameras == 0) { + recursiveDelete(Prf_Resolution, findPreference("capture_list")); + recursiveDelete(Prf_Resolution, findPreference("video_list")); + + recursiveDelete(Prf_Src, findPreference("multi_usb_cam_list")); + } + } + + /** + * Recursively go through settings and fill entries and summaries of our + * preferences. + */ + + private void fillEntriesAndSummaries(PreferenceGroup group) { + for (int i = 0; i < group.getPreferenceCount(); ++i) { + Preference pref = group.getPreference(i); + if (pref instanceof PreferenceGroup) { + fillEntriesAndSummaries((PreferenceGroup)pref); + } + setSummary(pref); + setEntries(pref); + } + } + + /** + * Recursively traverses the tree from the given group as the route and + * tries to delete the preference. Traversal stops once the preference + * was found and removed. + */ + private boolean recursiveDelete(PreferenceGroup group, Preference preference) { + Log.d(TAG, "SettingsFragment recursiveDelete"); + + if (group == null) { + Log.d(TAG, "attempting to delete from null preference group"); + return false; + } + if (preference == null) { + Log.d(TAG, "attempting to delete null preference"); + return false; + } + if (group.removePreference(preference)) { + Log.d(TAG, "Removal was successful."); + return true; + } + + for (int i = 0; i < group.getPreferenceCount(); ++i) { + Preference pref = group.getPreference(i); + if (pref instanceof PreferenceGroup) { + if (recursiveDelete((PreferenceGroup)pref, preference)) { + return true; + } + } + } + return false; + } + + /** + * Set the entries for the given preference. The given preference needs + * to be a {@link ListPreference} + */ + private void setEntries(Preference preference) { + if (!(preference instanceof ListPreference)) { + return; + } + + ListPreference listPreference = (ListPreference)preference; + if (listPreference.getKey().equals("capture_list")) { + setEntriesForSelection(PictureSizes, listPreference); + } else if (listPreference.getKey().equals("video_list")) { + setEntriesForSelection(VideoQualities, listPreference); + } + } + + /** + * Sets the entries for the given list preference. + * + * @param selectedSizes The possible S,M,L entries the user can choose + * from. + * @param preference The preference to set the entries for. + */ + private void setEntriesForSelection(List selectedSizes, ListPreference preference) { + if (selectedSizes == null) { + return; + } + + String[] entries = new String[selectedSizes.size()]; + String[] entryValues = new String[selectedSizes.size()]; + for (int i = 0; i < selectedSizes.size(); i++) { + Size size = selectedSizes.get(i); + entries[i] = getSizeSummaryString(size); + entryValues[i] = sizeToSettingString(size); + } + preference.setEntries(entries); + preference.setEntryValues(entryValues); + } + + /** + * Sets the entries for the given list preference. + * + * @param selectedQualities The possible S,M,L entries the user can + * choose from. + * @param preference The preference to set the entries for. + */ + private void setEntriesForSelection(SelectedVideoQualities selectedQualities, + ListPreference preference) { + if (selectedQualities == null) { + return; + } + + // Avoid adding double entries at the bottom of the list which + // indicates that not at least 3 qualities are supported. + ArrayList entries = new ArrayList(); + entries.add(mCamcorderProfileNames[selectedQualities.large]); + if (selectedQualities.medium != selectedQualities.large) { + entries.add(mCamcorderProfileNames[selectedQualities.medium]); + } + if (selectedQualities.small != selectedQualities.medium) { + entries.add(mCamcorderProfileNames[selectedQualities.small]); + } + preference.setEntries(entries.toArray(new String[0])); + } + + /** + * Determines the video quality for large/medium/small for the given camera. + * Returns the one matching the given setting. Defaults to 'large' of the + * qualitySetting does not match either large. medium or small. + * + * @param qualitySetting One of 'large', 'medium', 'small'. + * @param cameraId The ID of the camera for which to get the quality + * setting. + * @return The CamcorderProfile quality setting.} + */ + + static int getVideoQuality(int cameraId, String qualitySetting) { + return getSelectedVideoQualities(cameraId).getFromSetting(qualitySetting); + } + + static SelectedVideoQualities getSelectedVideoQualities(int cameraId) { + if (sCachedSelectedVideoQualities.get(cameraId) != null) { + return sCachedSelectedVideoQualities.get(cameraId); + } + + // Go through the sizes in descending order, see if they are supported, + // and set large/medium/small accordingly. + // If no quality is supported at all, the first call to + // getNextSupportedQuality will throw an exception. + // If only one quality is supported, then all three selected qualities + // will be the same. + int largeIndex = getNextSupportedVideoQualityIndex(cameraId, -1); + int mediumIndex = getNextSupportedVideoQualityIndex(cameraId, largeIndex); + int smallIndex = getNextSupportedVideoQualityIndex(cameraId, mediumIndex); + VideoQualities = new SelectedVideoQualities(); + VideoQualities.large = sVideoQualities[largeIndex]; + VideoQualities.medium = sVideoQualities[mediumIndex]; + VideoQualities.small = sVideoQualities[smallIndex]; + sCachedSelectedVideoQualities.put(cameraId, VideoQualities); + return VideoQualities; + } + + /* + * Starting from 'start' this method returns the next supported video + * quality. + */ + static int getNextSupportedVideoQualityIndex(int cameraId, int start) { + for (int i = start + 1; i < sVideoQualities.length; ++i) { + if (CamcorderProfile.hasProfile(cameraId, sVideoQualities[i])) { + // We found a new supported quality. + return i; + } + } + + // Failed to find another supported quality. + if (start < 0 || start >= sVideoQualities.length) { + // This means we couldn't find any supported quality. + throw new IllegalArgumentException("Could not find supported video qualities."); + } + + // We previously found a larger supported size. In this edge case, just + // return the same index as the previous size. + return start; + } + } +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java new file mode 100644 index 0000000..e7d3522 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/TopLeftCam.java @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * 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.intel.multicamera; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.*; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; +import android.media.Image; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Size; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.preference.PreferenceManager; +import java.io.*; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class TopLeftCam { + Activity mActivity; + private static final String TAG = "TopLeftCam"; + private String mNextVideoAbsolutePath; + private CamcorderProfile mProfile; + /** + * An {@link AutoFitTextureView} for camera preview. + */ + private AutoFitTextureView textureView; + private Button takePictureButton, TakeVideoButton; + + private MediaRecorder mMediaRecorder; + private String cameraId; + protected CameraDevice cameraDevice; + protected CameraCaptureSession cameraCaptureSessions; + protected CaptureRequest captureRequest; + protected CaptureRequest.Builder captureRequestBuilder; + private Size imageDimension; + private ImageReader imageReader; + private File file; + private static final int REQUEST_CAMERA_PERMISSION = 200; + private final int PERMISSIONS_REQUEST_SNAPSHOT = 3; + + private Handler mBackgroundHandler; + private HandlerThread mBackgroundThread; + private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private SharedPreferences settings; + /** + * Whether the app is recording video now + */ + private boolean mIsRecordingVideo, onDoneClicked; + + // The video file that the hardware camera is about to record into + // (or is recording into. + private String mVideoFilename, mPictureFilename; + private ContentValues mCurrentVideoValues, mCurrentPictureValues; + byte[] jpegLength; + + private boolean mIsVideoCaptureIntent, mIsImageCaptureIntent, mIsonDoneClicked; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + + public TopLeftCam(Activity activity, AutoFitTextureView textureView, Button PictureButton, + Button RecordButton) { + Log.e(TAG, "constructor called"); + this.mActivity = activity; + + onDoneClicked = false; + mIsRecordingVideo = false; + + mIsVideoCaptureIntent = isVideoCaptureIntent(); + mIsImageCaptureIntent = isImageCaptureIntent(); + + this.textureView = textureView; + this.textureView.setSurfaceTextureListener(textureListener); + this.ClickListeners(PictureButton, RecordButton); + this.settings = PreferenceManager.getDefaultSharedPreferences(activity); + } + + public boolean isVideoCaptureIntent() { + String action = mActivity.getIntent().getAction(); + return (MediaStore.ACTION_VIDEO_CAPTURE.equals(action)); + } + + public boolean isImageCaptureIntent() { + String action = mActivity.getIntent().getAction(); + return (MediaStore.ACTION_IMAGE_CAPTURE.equals(action)); + } + + public void ClickListeners(Button PictureButton, Button RecordButton) { + TakePictureOnClicked(PictureButton); + + StartVideoRecording(RecordButton); + } + + private void TakePictureOnClicked(Button PictureButton) { + takePictureButton = PictureButton; + assert takePictureButton != null; + + takePictureButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (!onDoneClicked) { + onDoneClicked = false; + + takePicture(); + + if (mIsImageCaptureIntent) { + onDoneClicked = true; + takePictureButton.setText(R.string.done); + } else { + Utils.broadcastNewPicture(mActivity.getApplicationContext(), + mCurrentPictureValues); + } + + } else if (mIsImageCaptureIntent) { + mIsImageCaptureIntent = false; + onDoneClicked = false; + onDoneClicked(); + } + } + }); + } + + private void StartVideoRecording(Button RecordButton) { + TakeVideoButton = RecordButton; + TakeVideoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + if (mIsonDoneClicked) { + mIsonDoneClicked = false; + onDoneClicked(); + } else if (mIsRecordingVideo == true) { + stopRecordingVideo(); + + if (!mIsVideoCaptureIntent) takePictureButton.setVisibility(View.VISIBLE); + + if (mIsVideoCaptureIntent) { + mIsVideoCaptureIntent = false; + TakeVideoButton.setText(R.string.done); + mIsonDoneClicked = true; + } else { + Utils.broadcastNewVideo(mActivity.getApplicationContext(), + mCurrentVideoValues); + } + + } else if (mIsRecordingVideo == false) { + startRecordingVideo(); + takePictureButton.setVisibility(View.GONE); + } + } + }); + } + + public void onDoneClicked() { doReturnToCaller(true); } + + private void doReturnToCaller(boolean valid) { mActivity.finish(); } + + TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + // open your camera here + openCamera(); + } + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // Transform you image captured size according to the surface width and height + } + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + return false; + } + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) {} + }; + + public void openCamera() { + CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE); + Log.e(TAG, "is camera open"); + try { + cameraId = manager.getCameraIdList()[0]; + Log.e(TAG, "is camera open ID" + cameraId); + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + StreamConfigurationMap map = + characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + assert map != null; + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, + new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_CAMERA_PERMISSION); + return; + } + + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + String videoQuality = settings.getString("video_list", "medium"); + + int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality); + Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality); + + mProfile = CamcorderProfile.get(0, quality); + + Log.d(TAG, "camera1 final Video width:" + mProfile.videoFrameWidth + " " + + "final Video height: " + mProfile.videoFrameHeight); + + Log.d(TAG, "camera1 final preview width:" + imageDimension.getWidth() + " " + + "final preview height: " + imageDimension.getHeight()); + + mMediaRecorder = new MediaRecorder(); + + startBackgroundThread(); + + manager.openCamera(cameraId, stateCallback, null); + + } catch (CameraAccessException e) { + e.printStackTrace(); + } + Log.e(TAG, "openCamera X"); + } + + private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { + @Override + public void onOpened(CameraDevice camera) { + // This is called when the camera is open + Log.e(TAG, "onOpened"); + cameraDevice = camera; + createCameraPreview(); + } + @Override + public void onDisconnected(CameraDevice camera) { + cameraDevice.close(); + } + @Override + public void onError(CameraDevice camera, int error) { + cameraDevice.close(); + cameraDevice = null; + } + }; + + protected void createCameraPreview() { + try { + closePreviewSession(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight()); + Surface surface = new Surface(texture); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + captureRequestBuilder.addTarget(surface); + cameraDevice.createCaptureSession( + Arrays.asList(surface), new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == cameraDevice) { + return; + } + // When the session is ready, we start displaying the preview. + cameraCaptureSessions = cameraCaptureSession; + updatePreview(); + } + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Configuration change", Toast.LENGTH_SHORT) + .show(); + } + }, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public void closeCamera() { + if (null != cameraDevice) { + cameraDevice.close(); + cameraDevice = null; + } + if (null != imageReader) { + imageReader.close(); + imageReader = null; + } + if (null != mMediaRecorder) { + mMediaRecorder.release(); + mMediaRecorder = null; + } + closePreviewSession(); + stopBackgroundThread(); + } + + /** + * Starts a background thread and its {@link Handler}. + */ + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("Camera-$cameraId"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + /** + * Stops the background thread and its {@link Handler}. + */ + private void stopBackgroundThread() { + if (mBackgroundThread != null) { + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + protected void updatePreview() { + if (null == cameraDevice) { + Log.e(TAG, "updatePreview error, return"); + } + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + HandlerThread thread = new HandlerThread("Camera Preview"); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + try { + cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, handler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + protected void takePicture() { + if (null == cameraDevice) { + Log.e(TAG, "cameraDevice is null"); + return; + } + + try { + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + ImageReader reader = ImageReader.newInstance( + imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1); + List outputSurfaces = new ArrayList(2); + outputSurfaces.add(reader.getSurface()); + outputSurfaces.add(new Surface(textureView.getSurfaceTexture())); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); + captureRequestBuilder.addTarget(reader.getSurface()); + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, + CameraMetadata.CONTROL_MODE_AUTO); + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST_SNAPSHOT); + return; + } + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mPictureFilename = fileDetails[3]; + mCurrentPictureValues = + Utils.getContentValues(Utils.MEDIA_TYPE_IMAGE, fileDetails, + imageDimension.getWidth(), imageDimension.getHeight()); + + file = new File(mPictureFilename); + + ImageReader.OnImageAvailableListener readerListener = + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = null; + try { + image = reader.acquireLatestImage(); + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.capacity()]; + buffer.get(bytes); + jpegLength = bytes; + mCurrentPictureValues.put(MediaStore.Images.ImageColumns.SIZE, + jpegLength); + + save(bytes); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (image != null) { + image.close(); + } + } + } + + private void save(byte[] bytes) throws IOException { + OutputStream output = null; + try { + output = new FileOutputStream(file); + output.write(bytes); + } finally { + if (null != output) { + output.close(); + } + } + } + }; + reader.setOnImageAvailableListener(readerListener, mBackgroundHandler); + final CameraCaptureSession.CaptureCallback captureListener = + new CameraCaptureSession.CaptureCallback() { + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + super.onCaptureCompleted(session, request, result); + Toast.makeText(mActivity, "Saved:" + file, Toast.LENGTH_SHORT).show(); + + createCameraPreview(); + } + }; + cameraDevice.createCaptureSession( + outputSurfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession session) { + try { + session.capture(captureRequestBuilder.build(), captureListener, + mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) {} + }, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /* Recording Start*/ + private void startRecordingVideo() { + if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) { + return; + } + try { + closePreviewSession(); + setUpMediaRecorder(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + + captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); + List surfaces = new ArrayList<>(); + + // Set up Surface for the camera preview + Surface previewSurface = new Surface(texture); + surfaces.add(previewSurface); + captureRequestBuilder.addTarget(previewSurface); + + // Set up Surface for the MediaRecorder + Surface recorderSurface = mMediaRecorder.getSurface(); + surfaces.add(recorderSurface); + captureRequestBuilder.addTarget(recorderSurface); + + // Start a capture session + // Once the session starts, we can update the UI and start recording + cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(@NonNull CameraCaptureSession camCaptureSession) { + cameraCaptureSessions = camCaptureSession; + updatePreview(); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + // UI + TakeVideoButton.setText(R.string.stop); + mIsRecordingVideo = true; + + // Start recording + mMediaRecorder.start(); + } + }); + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + if (null != mActivity) { + Toast.makeText(mActivity, "Failed", Toast.LENGTH_SHORT).show(); + } + } + }, mBackgroundHandler); + } catch (CameraAccessException | IOException e) { + e.printStackTrace(); + } + } + + private void setUpMediaRecorder() throws IOException { + if (null == mActivity) { + return; + } + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); + mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + mMediaRecorder.setVideoFrameRate(30); + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mVideoFilename = fileDetails[3]; + mCurrentVideoValues = + Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth, + mProfile.videoFrameHeight); + + /** + * set output file in media recorder + */ + mMediaRecorder.setOutputFile(mVideoFilename); + mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); + mMediaRecorder.setVideoEncodingBitRate(10000000); + + mMediaRecorder.prepare(); + } + + private void closePreviewSession() { + System.out.println(" closePreviewSession"); + if (cameraCaptureSessions != null) { + cameraCaptureSessions.close(); + cameraCaptureSessions = null; + } + } + + private void stopRecordingVideo() { + mIsRecordingVideo = false; + TakeVideoButton.setText(R.string.record); + // Stop recording + mMediaRecorder.stop(); + mMediaRecorder.reset(); + + if (null != mActivity) { + Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show(); + Log.d(TAG, "Video saved: " + mVideoFilename); + } + mVideoFilename = null; + + createCameraPreview(); + } +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java b/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java new file mode 100644 index 0000000..97e5e01 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/TopRightCam.java @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * 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.intel.multicamera; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.graphics.SurfaceTexture; +import android.hardware.camera2.*; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; +import android.media.Image; +import android.media.ImageReader; +import android.media.MediaRecorder; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Size; +import android.util.SparseIntArray; +import android.view.Surface; +import android.view.TextureView; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; +import androidx.preference.PreferenceManager; +import java.io.*; +import java.nio.ByteBuffer; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class TopRightCam { + Activity mActivity; + private static final String TAG = "TopRightCam"; + private String mNextVideoAbsolutePath; + private CamcorderProfile mProfile; + /** + * An {@link AutoFitTextureView} for camera preview. + */ + private AutoFitTextureView textureView; + private Button takePictureButton, TakeVideoButton; + + private MediaRecorder mMediaRecorder; + private String cameraId; + protected CameraDevice cameraDevice; + protected CameraCaptureSession cameraCaptureSessions; + protected CaptureRequest captureRequest; + protected CaptureRequest.Builder captureRequestBuilder; + private Size imageDimension; + private ImageReader imageReader; + private File file; + private static final int REQUEST_CAMERA_PERMISSION = 200; + private final int PERMISSIONS_REQUEST_SNAPSHOT = 3; + + private Handler mBackgroundHandler; + private HandlerThread mBackgroundThread; + private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); + private SharedPreferences settings; + /** + * Whether the app is recording video now + */ + private boolean mIsRecordingVideo; + + // The video file that the hardware camera is about to record into + // (or is recording into. + private String mVideoFilename, mPictureFilename; + private ContentValues mCurrentVideoValues, mCurrentPictureValues; + byte[] jpegLength; + + static { + ORIENTATIONS.append(Surface.ROTATION_0, 90); + ORIENTATIONS.append(Surface.ROTATION_90, 0); + ORIENTATIONS.append(Surface.ROTATION_180, 270); + ORIENTATIONS.append(Surface.ROTATION_270, 180); + } + + public TopRightCam(Activity activity, AutoFitTextureView textureView, Button PictureButton, + Button RecordButton) { + Log.e(TAG, "constructor called"); + this.mActivity = activity; + this.textureView = textureView; + this.textureView.setSurfaceTextureListener(textureListener); + this.ClickListeners(PictureButton, RecordButton); + this.settings = PreferenceManager.getDefaultSharedPreferences(activity); + } + + public void ClickListeners(Button PictureButton, Button RecordButton) { + TakePicureOnClicked(PictureButton); + + StartVideoRecording(RecordButton); + } + + private void TakePicureOnClicked(Button PictureButton) { + takePictureButton = PictureButton; + assert takePictureButton != null; + + takePictureButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + takePicture(); + Utils.broadcastNewPicture(mActivity.getApplicationContext(), mCurrentPictureValues); + } + }); + } + + private void StartVideoRecording(Button RecordButton) { + TakeVideoButton = RecordButton; + TakeVideoButton.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + // Intent i = new Intent(HomeScreenActivity.this, CameraActivity.class); + System.out.println(" onCreate Record0"); + if (mIsRecordingVideo) { + stopRecordingVideo(); + Utils.broadcastNewVideo(mActivity.getApplicationContext(), mCurrentVideoValues); + takePictureButton.setVisibility(View.VISIBLE); + } else { + startRecordingVideo(); + takePictureButton.setVisibility(View.GONE); + } + } + }); + } + + TextureView.SurfaceTextureListener textureListener = new TextureView.SurfaceTextureListener() { + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { + // open your camera here + openCamera(); + } + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { + // Transform you image captured size according to the surface width and height + } + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { + return false; + } + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surface) {} + }; + + public void openCamera() { + CameraManager manager = (CameraManager)mActivity.getSystemService(Context.CAMERA_SERVICE); + Log.e(TAG, "is camera open"); + try { + cameraId = manager.getCameraIdList()[1]; + Log.e(TAG, "is camera open ID" + cameraId); + CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId); + StreamConfigurationMap map = + characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); + assert map != null; + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.CAMERA) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED && + ActivityCompat.checkSelfPermission(mActivity, Manifest.permission.RECORD_AUDIO) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, + new String[] {Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, + Manifest.permission.WRITE_EXTERNAL_STORAGE}, + REQUEST_CAMERA_PERMISSION); + return; + } + + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + String videoQuality = settings.getString("video_list", "medium"); + + int quality = SettingsActivity.SettingsFragment.getVideoQuality(0, videoQuality); + Log.d(TAG, "Selected video quality for '" + videoQuality + "' is " + quality); + + mProfile = CamcorderProfile.get(0, quality); + + Log.d(TAG, "camera1 final Video width:" + mProfile.videoFrameWidth + " " + + "final Video height: " + mProfile.videoFrameHeight); + + Log.d(TAG, "camera1 final preview width:" + imageDimension.getWidth() + " " + + "final preview height: " + imageDimension.getHeight()); + + mMediaRecorder = new MediaRecorder(); + + startBackgroundThread(); + + manager.openCamera(cameraId, stateCallback, null); + + } catch (CameraAccessException e) { + e.printStackTrace(); + } + Log.e(TAG, "openCamera X"); + } + + private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { + @Override + public void onOpened(CameraDevice camera) { + // This is called when the camera is open + Log.e(TAG, "onOpened"); + cameraDevice = camera; + createCameraPreview(); + } + @Override + public void onDisconnected(CameraDevice camera) { + cameraDevice.close(); + } + @Override + public void onError(CameraDevice camera, int error) { + cameraDevice.close(); + cameraDevice = null; + } + }; + + protected void createCameraPreview() { + try { + closePreviewSession(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(imageDimension.getWidth(), imageDimension.getHeight()); + Surface surface = new Surface(texture); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); + captureRequestBuilder.addTarget(surface); + cameraDevice.createCaptureSession( + Arrays.asList(surface), new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession cameraCaptureSession) { + // The camera is already closed + if (null == cameraDevice) { + return; + } + // When the session is ready, we start displaying the preview. + cameraCaptureSessions = cameraCaptureSession; + updatePreview(); + } + @Override + public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) { + Toast.makeText(mActivity, "Configuration change", Toast.LENGTH_SHORT) + .show(); + } + }, null); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + public void closeCamera() { + if (null != cameraDevice) { + cameraDevice.close(); + cameraDevice = null; + } + if (null != imageReader) { + imageReader.close(); + imageReader = null; + } + if (null != mMediaRecorder) { + mMediaRecorder.release(); + mMediaRecorder = null; + } + closePreviewSession(); + stopBackgroundThread(); + } + + /** + * Starts a background thread and its {@link Handler}. + */ + private void startBackgroundThread() { + mBackgroundThread = new HandlerThread("Camera-$cameraId"); + mBackgroundThread.start(); + mBackgroundHandler = new Handler(mBackgroundThread.getLooper()); + } + + /** + * Stops the background thread and its {@link Handler}. + */ + private void stopBackgroundThread() { + mBackgroundThread.quitSafely(); + try { + mBackgroundThread.join(); + mBackgroundThread = null; + mBackgroundHandler = null; + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + protected void updatePreview() { + if (null == cameraDevice) { + Log.e(TAG, "updatePreview error, return"); + } + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); + HandlerThread thread = new HandlerThread("Camera Preview"); + thread.start(); + Handler handler = new Handler(thread.getLooper()); + try { + cameraCaptureSessions.setRepeatingRequest(captureRequestBuilder.build(), null, handler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + protected void takePicture() { + if (null == cameraDevice) { + Log.e(TAG, "cameraDevice is null"); + return; + } + + try { + imageDimension = SettingsActivity.SettingsFragment.sizeFromSettingString( + settings.getString("capture_list", "640x480")); + + ImageReader reader = ImageReader.newInstance( + imageDimension.getWidth(), imageDimension.getHeight(), ImageFormat.JPEG, 1); + List outputSurfaces = new ArrayList(2); + outputSurfaces.add(reader.getSurface()); + outputSurfaces.add(new Surface(textureView.getSurfaceTexture())); + captureRequestBuilder = + cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); + captureRequestBuilder.addTarget(reader.getSurface()); + captureRequestBuilder.set(CaptureRequest.CONTROL_MODE, + CameraMetadata.CONTROL_MODE_AUTO); + // Orientation + int rotation = mActivity.getWindowManager().getDefaultDisplay().getRotation(); + captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); + + // Add permission for camera and let user grant the permission + if (ActivityCompat.checkSelfPermission(mActivity, + Manifest.permission.WRITE_EXTERNAL_STORAGE) != + PackageManager.PERMISSION_GRANTED) { + ActivityCompat.requestPermissions( + mActivity, new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, + PERMISSIONS_REQUEST_SNAPSHOT); + return; + } + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_IMAGE); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mPictureFilename = fileDetails[3]; + mCurrentPictureValues = + Utils.getContentValues(Utils.MEDIA_TYPE_IMAGE, fileDetails, + imageDimension.getWidth(), imageDimension.getHeight()); + + file = new File(mPictureFilename); + + ImageReader.OnImageAvailableListener readerListener = + new ImageReader.OnImageAvailableListener() { + @Override + public void onImageAvailable(ImageReader reader) { + Image image = null; + try { + image = reader.acquireLatestImage(); + ByteBuffer buffer = image.getPlanes()[0].getBuffer(); + byte[] bytes = new byte[buffer.capacity()]; + buffer.get(bytes); + jpegLength = bytes; + mCurrentPictureValues.put(MediaStore.Images.ImageColumns.SIZE, + jpegLength); + + save(bytes); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + if (image != null) { + image.close(); + } + } + } + + private void save(byte[] bytes) throws IOException { + OutputStream output = null; + try { + output = new FileOutputStream(file); + output.write(bytes); + } finally { + if (null != output) { + output.close(); + } + } + } + }; + reader.setOnImageAvailableListener(readerListener, mBackgroundHandler); + final CameraCaptureSession.CaptureCallback captureListener = + new CameraCaptureSession.CaptureCallback() { + @Override + public void onCaptureCompleted(CameraCaptureSession session, + CaptureRequest request, + TotalCaptureResult result) { + super.onCaptureCompleted(session, request, result); + Toast.makeText(mActivity, "Saved:" + file, Toast.LENGTH_SHORT).show(); + + createCameraPreview(); + } + }; + cameraDevice.createCaptureSession( + outputSurfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(CameraCaptureSession session) { + try { + session.capture(captureRequestBuilder.build(), captureListener, + mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + @Override + public void onConfigureFailed(CameraCaptureSession session) {} + }, mBackgroundHandler); + } catch (CameraAccessException e) { + e.printStackTrace(); + } + } + + /* Recording Start*/ + private void startRecordingVideo() { + if (null == cameraDevice || !textureView.isAvailable() || null == mProfile) { + return; + } + try { + closePreviewSession(); + setUpMediaRecorder(); + SurfaceTexture texture = textureView.getSurfaceTexture(); + assert texture != null; + texture.setDefaultBufferSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + + captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD); + List surfaces = new ArrayList<>(); + + // Set up Surface for the camera preview + Surface previewSurface = new Surface(texture); + surfaces.add(previewSurface); + captureRequestBuilder.addTarget(previewSurface); + + // Set up Surface for the MediaRecorder + Surface recorderSurface = mMediaRecorder.getSurface(); + surfaces.add(recorderSurface); + captureRequestBuilder.addTarget(recorderSurface); + + // Start a capture session + // Once the session starts, we can update the UI and start recording + cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() { + @Override + public void onConfigured(@NonNull CameraCaptureSession camCaptureSession) { + cameraCaptureSessions = camCaptureSession; + updatePreview(); + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + // UI + TakeVideoButton.setText(R.string.stop); + mIsRecordingVideo = true; + + // Start recording + mMediaRecorder.start(); + } + }); + } + + @Override + public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { + if (null != mActivity) { + Toast.makeText(mActivity, "Failed", Toast.LENGTH_SHORT).show(); + } + } + }, mBackgroundHandler); + } catch (CameraAccessException | IOException e) { + e.printStackTrace(); + } + } + + private void setUpMediaRecorder() throws IOException { + if (null == mActivity) { + return; + } + mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); + mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); + mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); + mMediaRecorder.setVideoSize(mProfile.videoFrameWidth, mProfile.videoFrameHeight); + mMediaRecorder.setVideoFrameRate(30); + + String fileDetails[] = Utils.generateFileDetails(Utils.MEDIA_TYPE_VIDEO); + if (fileDetails == null || fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return; + } + mVideoFilename = fileDetails[3]; + mCurrentVideoValues = + Utils.getContentValues(Utils.MEDIA_TYPE_VIDEO, fileDetails, mProfile.videoFrameWidth, + mProfile.videoFrameHeight); + + /** + * set output file in media recorder + */ + mMediaRecorder.setOutputFile(mVideoFilename); + mMediaRecorder.setVideoFrameRate(mProfile.videoFrameRate); + mMediaRecorder.setVideoEncodingBitRate(10000000); + + mMediaRecorder.prepare(); + } + + private void closePreviewSession() { + System.out.println(" closePreviewSession"); + if (cameraCaptureSessions != null) { + cameraCaptureSessions.close(); + cameraCaptureSessions = null; + } + } + + private void stopRecordingVideo() { + mIsRecordingVideo = false; + TakeVideoButton.setText(R.string.record); + // Stop recording + mMediaRecorder.stop(); + mMediaRecorder.reset(); + + if (null != mActivity) { + Toast.makeText(mActivity, "Video saved: " + mVideoFilename, Toast.LENGTH_SHORT).show(); + Log.d(TAG, "Video saved: " + mVideoFilename); + } + mVideoFilename = null; + + createCameraPreview(); + } +} diff --git a/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java b/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java new file mode 100644 index 0000000..ae9e6f1 --- /dev/null +++ b/camera/MultiCameraApplication/java/com/intel/multicamera/Utils.java @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2019 Intel Corporation. + * + * 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.intel.multicamera; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.*; +import android.content.pm.PackageManager; +import android.graphics.ImageFormat; +import android.hardware.camera2.*; +import android.hardware.camera2.params.StreamConfigurationMap; +import android.media.CamcorderProfile; +import android.net.Uri; +import android.os.Environment; +import android.provider.MediaStore; +import android.util.Log; +import android.util.Size; +import android.util.SparseIntArray; +import java.io.File; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class Utils { + private static final String TAG = "Utils"; + + public static final int MEDIA_TYPE_IMAGE = 0; + public static final int MEDIA_TYPE_VIDEO = 1; + + public static final String IMAGE_FILE_NAME_FORMAT = "'IMG'_yyyyMMdd_HHmmss"; + public static final String VIDEO_FILE_NAME_FORMAT = "'VID'_yyyyMMdd_HHmmss"; + + /** See android.hardware.Camera.ACTION_NEW_PICTURE. */ + public static final String ACTION_NEW_PICTURE = "android.hardware.action.NEW_PICTURE"; + /** See android.hardware.Camera.ACTION_NEW_VIDEO. */ + public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO"; + + private static final String VIDEO_BASE_URI = "content://media/external/video/media"; + + @SuppressLint("SimpleDateFormat") + public static File createOutputmediaStorageDir() { + // To be safe, you should check that the SDCard is mounted + // using Environment.getExternalStorageState() before doing this. + + String state = Environment.getExternalStorageState(); + if (!Environment.MEDIA_MOUNTED.equals(state)) { + Log.e(TAG, "getExternalStorageState failed"); + return null; + } + + File mediaStorageDir = + new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), + "MultiCamera"); + // This location works best if you want the created images to be shared + // between applications and persist after your app has been uninstalled. + + // Create the storage directory if it does not exist + if (!mediaStorageDir.exists()) { + if (!mediaStorageDir.mkdirs()) { + Log.e(TAG, "Failed to create directory for DCIM/MultiCamera"); + return null; + } + } + + return mediaStorageDir; + } + + public static void broadcastNewPicture(Context context, ContentValues values) { + Uri uri = null; + ContentResolver resolver = context.getContentResolver(); + try { + uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, + new ContentValues(values)); + } catch (Throwable th) { + // This can happen when the external volume is already mounted, but + // MediaScanner has not notify MediaProvider to add that volume. + // The picture is still safe and MediaScanner will find it and + // insert it into MediaProvider. The only problem is that the user + // cannot click the thumbnail to review the picture. + Log.e(TAG, "Failed to write MediaStore" + th); + } finally { + Log.v(TAG, "Current Picture URI: " + uri); + } + + context.sendBroadcast(new Intent(ACTION_NEW_PICTURE, uri)); + } + + public static void broadcastNewVideo(Context context, ContentValues values) { + Uri uri = null; + ContentResolver resolver = context.getContentResolver(); + try { + Uri videoTable = Uri.parse(VIDEO_BASE_URI); + uri = resolver.insert(videoTable, new ContentValues(values)); + } catch (Exception e) { + // We failed to insert into the database. This can happen if + // the SD card is unmounted. + Log.e(TAG, "failed to add video to media store", e); + uri = null; + } finally { + Log.v(TAG, "Current video URI: " + uri); + } + + context.sendBroadcast(new Intent(ACTION_NEW_VIDEO, uri)); + } + + public static String[] generateFileDetails(int type) { + File mediaStorageDir = createOutputmediaStorageDir(); + if (mediaStorageDir == null) { + Log.e(TAG, "createOutputmediaStorageDir failed"); + return null; + } + + long dateTaken = System.currentTimeMillis(); + Date date = new Date(dateTaken); + SimpleDateFormat dateFormat; + String fileDetails[] = new String[5]; + if (type == MEDIA_TYPE_IMAGE) { + dateFormat = new SimpleDateFormat(IMAGE_FILE_NAME_FORMAT); + fileDetails[0] = dateFormat.format(date); + fileDetails[1] = fileDetails[0] + ".jpg"; + fileDetails[2] = "image/jpeg"; + } else if (type == MEDIA_TYPE_VIDEO) { + dateFormat = new SimpleDateFormat(VIDEO_FILE_NAME_FORMAT); + fileDetails[0] = dateFormat.format(date); + fileDetails[1] = fileDetails[0] + ".mp4"; + fileDetails[2] = "video/mp4"; + } else { + Log.e(TAG, "Invalid Media Type: " + type); + return null; + } + + fileDetails[3] = mediaStorageDir.getPath() + '/' + fileDetails[1]; + fileDetails[4] = Long.toString(dateTaken); + Log.v(TAG, "Generated filename: " + fileDetails[3]); + return fileDetails; + } + + public static ContentValues getContentValues(int type, String fileDetails[], int width, + int height) { + if (fileDetails.length < 5) { + Log.e(TAG, "Invalid file details"); + return null; + } + + File file = new File(fileDetails[3]); + long dateModifiedSeconds = TimeUnit.MILLISECONDS.toSeconds(file.lastModified()); + + ContentValues contentValue = null; + if (MEDIA_TYPE_IMAGE == type) { + contentValue = new ContentValues(9); + contentValue.put(MediaStore.Images.ImageColumns.TITLE, fileDetails[0]); + contentValue.put(MediaStore.Images.ImageColumns.DISPLAY_NAME, fileDetails[1]); + contentValue.put(MediaStore.Images.ImageColumns.DATE_TAKEN, + Long.valueOf(fileDetails[4])); + contentValue.put(MediaStore.Images.ImageColumns.MIME_TYPE, fileDetails[2]); + contentValue.put(MediaStore.Images.ImageColumns.DATE_MODIFIED, dateModifiedSeconds); + contentValue.put(MediaStore.Images.ImageColumns.DATA, fileDetails[3]); + contentValue.put(MediaStore.MediaColumns.WIDTH, width); + contentValue.put(MediaStore.MediaColumns.HEIGHT, height); + } else if (MEDIA_TYPE_VIDEO == type) { + contentValue = new ContentValues(9); + contentValue.put(MediaStore.Video.Media.TITLE, fileDetails[0]); + contentValue.put(MediaStore.Video.Media.DISPLAY_NAME, fileDetails[1]); + contentValue.put(MediaStore.Video.Media.DATE_TAKEN, Long.valueOf(fileDetails[4])); + contentValue.put(MediaStore.MediaColumns.DATE_MODIFIED, + Long.valueOf(fileDetails[4]) / 1000); + contentValue.put(MediaStore.Video.Media.MIME_TYPE, fileDetails[2]); + contentValue.put(MediaStore.Video.Media.DATA, fileDetails[3]); + contentValue.put(MediaStore.MediaColumns.WIDTH, width); + contentValue.put(MediaStore.MediaColumns.HEIGHT, height); + } + return contentValue; + } +} diff --git a/camera/MultiCameraApplication/res/drawable-v24/ic_launcher_foreground.xml b/camera/MultiCameraApplication/res/drawable-v24/ic_launcher_foreground.xml index ddb26ad..1f6bb29 100644 --- a/camera/MultiCameraApplication/res/drawable-v24/ic_launcher_foreground.xml +++ b/camera/MultiCameraApplication/res/drawable-v24/ic_launcher_foreground.xml @@ -1,34 +1,34 @@ - - - - - - - - - - - + + + + + + + + + + + diff --git a/camera/MultiCameraApplication/res/drawable/ic_launcher_background.xml b/camera/MultiCameraApplication/res/drawable/ic_launcher_background.xml index 3a37cf6..2408e30 100644 --- a/camera/MultiCameraApplication/res/drawable/ic_launcher_background.xml +++ b/camera/MultiCameraApplication/res/drawable/ic_launcher_background.xml @@ -1,170 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/camera/MultiCameraApplication/res/drawable/ic_launcher_foreground.xml b/camera/MultiCameraApplication/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..f8e8dca --- /dev/null +++ b/camera/MultiCameraApplication/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,13 @@ + + + + + diff --git a/camera/MultiCameraApplication/res/drawable/ic_settings_black_24dp.xml b/camera/MultiCameraApplication/res/drawable/ic_settings_black_24dp.xml new file mode 100644 index 0000000..7f2f06c --- /dev/null +++ b/camera/MultiCameraApplication/res/drawable/ic_settings_black_24dp.xml @@ -0,0 +1,5 @@ + + + diff --git a/camera/MultiCameraApplication/res/layout/activity_main.xml b/camera/MultiCameraApplication/res/layout/activity_main.xml index ee611bc..33bb882 100644 --- a/camera/MultiCameraApplication/res/layout/activity_main.xml +++ b/camera/MultiCameraApplication/res/layout/activity_main.xml @@ -1,79 +1,29 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/camera/MultiCameraApplication/res/layout/content_main.xml b/camera/MultiCameraApplication/res/layout/content_main.xml new file mode 100644 index 0000000..f11fad7 --- /dev/null +++ b/camera/MultiCameraApplication/res/layout/content_main.xml @@ -0,0 +1,169 @@ + + + + + + + + + + + + + +