Repository: hzitoun/android-camera2-secret-picture-taker Branch: master Commit: 885408d834f2 Files: 29 Total size: 43.2 KB Directory structure: gitextract_b0o9xqpx/ ├── .gitignore ├── .idea/ │ ├── gradle.xml │ └── runConfigurations.xml ├── .travis.yml ├── LICENSE ├── README.md ├── app/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── hzitoun/ │ │ └── camera2SecretPictureTaker/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── hzitoun/ │ │ │ └── camera2SecretPictureTaker/ │ │ │ ├── activities/ │ │ │ │ └── MainActivity.java │ │ │ ├── listeners/ │ │ │ │ └── PictureCapturingListener.java │ │ │ └── services/ │ │ │ ├── APictureCapturingService.java │ │ │ └── PictureCapturingServiceImpl.java │ │ └── res/ │ │ ├── layout/ │ │ │ └── activity_main.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ └── test/ │ └── java/ │ └── com/ │ └── hzitoun/ │ └── camera2SecretPictureTaker/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat └── settings.gradle ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.iml .gradle /local.properties /.idea/workspace.xml /.idea/libraries .DS_Store /build /captures .externalNativeBuild # User-specific configurations .idea/libraries/ .idea/workspace.xml .idea/tasks.xml .idea/.name .idea/compiler.xml .idea/copyright/profiles_settings.xml .idea/encodings.xml .idea/misc.xml .idea/modules.xml .idea/scopes/scope_settings.xml .idea/dictionaries .idea/vcs.xml .idea/jsLibraryMappings.xml .idea/datasources.xml .idea/dataSources.ids .idea/sqlDataSources.xml .idea/dynamic.xml .idea/uiDesigner.xml ================================================ FILE: .idea/gradle.xml ================================================ ================================================ FILE: .idea/runConfigurations.xml ================================================ ================================================ FILE: .travis.yml ================================================ language: android jdk: oraclejdk8 env: android: components: - tools - build-tools-25.0.2 - android-25 - platform-tools - extra-android-m2repository before_install: - chmod +x gradlew install: - echo y | android update sdk -u -a -t tools - echo y | android update sdk -u -a -t platform-tools - echo y | android update sdk -u -a -t build-tools-25.0.2 - echo y | android update sdk -u -a -t android-25 - echo y | android update sdk -u -a -t extra-google-m2repository - echo y | android update sdk -u -a -t extra-android-m2repository script: - ./gradlew assembleRelease ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2019 Hamed ZITOUN Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ [![Build Status](https://travis-ci.org/hzitoun/android-camera2-secret-picture-taker.svg?branch=master)](https://travis-ci.org/hzitoun/android-camera2-secret-picture-taker) # 📸 Android Camera2 Secret Picture Taker (AC2SPT) Take pictures secretly (without preview or launching device's camera app) from all available cameras using Android CAMERA2 API. The Camera2 API replaces the deprecated Camera class. ___ ## How can I support this project? - If you have enjoyed the project and it helped you creating a project, building an app, starting a business. You could encourage and support me on patreon https://www.patreon.com/hzitoun 🤗 ! - Star this GitHub repo :star: - Create pull requests, submit bugs, suggest new features or documentation updates :wrench: ## Usage 1. Implement the interface ```PictureCapturingListener``` (your capture listener) and override the following methods: - **void onDoneCapturingAllPhotos(TreeMap picturesTaken)** which is called when we've done taking pictures from ALL available cameras OR when NO camera was detected on the device; - **void onCaptureDone(String pictureUrl, byte[] pictureData)** to get a couple (picture Url, picture Data). Use this method if you don't want to wait for ALL pictures to be ready; 2. Create a new instance of ```APictureCapturingService``` using ```PictureCapturingServiceImpl#getInstance()``` method; 3. **Start capture** by calling the method ```APictureCapturingService#startCapturing(PictureCapturingListener listener) ``` and pass the listener you've just implemented (**step 1**) ## Sample Here, I've chosen to just display the two pictures taken within a vertical linear layout. Here is a code snippet of how to use the service: ```java public class MainActivity extends AppCompatActivity implements PictureCapturingListener, ActivityCompat.OnRequestPermissionsResultCallback { private APictureCapturingService pictureService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //check for camera and external storage permissions checkPermissions(); final Button btn = (Button) findViewById(R.id.startCaptureBtn); pictureService = PictureCapturingServiceImpl.getInstance(this); //start capturing when clicking on the button btn.setOnClickListener(v -> pictureService.startCapturing(this) ); } @Override public void onDoneCapturingAllPhotos(TreeMap picturesTaken) { if (picturesTaken != null && !picturesTaken.isEmpty()) { picturesTaken.forEach((pictureUrl, pictureData) -> { //convert the byte array 'pictureData' to a bitmap (no need to read the file from the external storage) but in case you //You can also use 'pictureUrl' which stores the picture's location on the device final Bitmap bitmap = BitmapFactory.decodeByteArray(pictureData, 0, pictureData.length); }); showToast("Done capturing all photos!"); return; } showToast("No camera detected!"); } @Override public void onCaptureDone(String pictureUrl, byte[] pictureData) { if (pictureData != null && pictureUrl != null) { runOnUiThread(() -> { //convert byte array 'pictureData' to a bitmap (no need to read the file from the external storage) final Bitmap bitmap = BitmapFactory.decodeByteArray(pictureData, 0, pictureData.length); //scale image to avoid POTENTIAL "Bitmap too large to be uploaded into a texture" when displaying into an ImageView final int nh = (int) (bitmap.getHeight() * (512.0 / bitmap.getWidth())); final Bitmap scaled = Bitmap.createScaledBitmap(bitmap, 512, nh, true); //do whatever you want with the bitmap or the scaled one... }); showToast("Picture saved to " + pictureUrl); } } private void showToast(final String text) { runOnUiThread(() -> Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show() ); } } ``` Thanks to [maaudrana](https://github.com/maaudrana) for the logo :) ## Contributors Hamed ZITOUN ## Help If you run into issues, please don't hesitate to find help on the GitHub project. ## License The android-camera2-secret-picture-taker is covered by the MIT License. The MIT License (MIT) Copyright (c) 2022 Hamed ZITOUN and contributors to the android-camera2-secret-picture-taker project. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.hzitoun.camera2secretpicturetaker" minSdkVersion 21 targetSdkVersion 25 versionCode 2 versionName "1.0.1" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" jackOptions { enabled true } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.0' testCompile 'junit:junit:4.12' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in C:\Users\hamed\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/hzitoun/camera2SecretPictureTaker/ExampleInstrumentedTest.java ================================================ package com.hzitoun.camera2SecretPictureTaker; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.hzitoun.camera2secretpicturetaker", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/hzitoun/camera2SecretPictureTaker/activities/MainActivity.java ================================================ package com.hzitoun.camera2SecretPictureTaker.activities; import android.Manifest; import android.annotation.TargetApi; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.ImageView; import android.widget.Toast; import com.hzitoun.camera2SecretPictureTaker.R; import com.hzitoun.camera2SecretPictureTaker.listeners.PictureCapturingListener; import com.hzitoun.camera2SecretPictureTaker.services.APictureCapturingService; import com.hzitoun.camera2SecretPictureTaker.services.PictureCapturingServiceImpl; import java.util.ArrayList; import java.util.List; import java.util.TreeMap; /** * App's Main Activity showing a simple usage of the picture taking service. * @author hzitoun (zitoun.hamed@gmail.com) */ public class MainActivity extends AppCompatActivity implements PictureCapturingListener, ActivityCompat.OnRequestPermissionsResultCallback { private static final String[] requiredPermissions = { Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA, }; private static final int MY_PERMISSIONS_REQUEST_ACCESS_CODE = 1; private ImageView uploadBackPhoto; private ImageView uploadFrontPhoto; //The capture service private APictureCapturingService pictureService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); checkPermissions(); uploadBackPhoto = (ImageView) findViewById(R.id.backIV); uploadFrontPhoto = (ImageView) findViewById(R.id.frontIV); final Button btn = (Button) findViewById(R.id.startCaptureBtn); // getting instance of the Service from PictureCapturingServiceImpl pictureService = PictureCapturingServiceImpl.getInstance(this); btn.setOnClickListener(v -> { showToast("Starting capture!"); pictureService.startCapturing(this); }); } private void showToast(final String text) { runOnUiThread(() -> Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT).show() ); } /** * We've finished taking pictures from all phone's cameras */ @Override public void onDoneCapturingAllPhotos(TreeMap picturesTaken) { if (picturesTaken != null && !picturesTaken.isEmpty()) { showToast("Done capturing all photos!"); return; } showToast("No camera detected!"); } /** * Displaying the pictures taken. */ @Override public void onCaptureDone(String pictureUrl, byte[] pictureData) { if (pictureData != null && pictureUrl != null) { runOnUiThread(() -> { final Bitmap bitmap = BitmapFactory.decodeByteArray(pictureData, 0, pictureData.length); final int nh = (int) (bitmap.getHeight() * (512.0 / bitmap.getWidth())); final Bitmap scaled = Bitmap.createScaledBitmap(bitmap, 512, nh, true); if (pictureUrl.contains("0_pic.jpg")) { uploadBackPhoto.setImageBitmap(scaled); } else if (pictureUrl.contains("1_pic.jpg")) { uploadFrontPhoto.setImageBitmap(scaled); } }); showToast("Picture saved to " + pictureUrl); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case MY_PERMISSIONS_REQUEST_ACCESS_CODE: { if (!(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)) { checkPermissions(); } } } } /** * checking permissions at Runtime. */ @TargetApi(Build.VERSION_CODES.M) private void checkPermissions() { final List neededPermissions = new ArrayList<>(); for (final String permission : requiredPermissions) { if (ContextCompat.checkSelfPermission(getApplicationContext(), permission) != PackageManager.PERMISSION_GRANTED) { neededPermissions.add(permission); } } if (!neededPermissions.isEmpty()) { requestPermissions(neededPermissions.toArray(new String[]{}), MY_PERMISSIONS_REQUEST_ACCESS_CODE); } } } ================================================ FILE: app/src/main/java/com/hzitoun/camera2SecretPictureTaker/listeners/PictureCapturingListener.java ================================================ package com.hzitoun.camera2SecretPictureTaker.listeners; import java.util.TreeMap; /** * Picture capturing listener * * @author hzitoun (zitoun.hamed@gmail.com) */ public interface PictureCapturingListener { /** * a callback called when we've done taking a picture from a single camera * (use this method if you don't want to wait for ALL taken pictures to be ready @see onDoneCapturingAllPhotos) * * @param pictureUrl taken picture's location on the device * @param pictureData taken picture's data as a byte array */ void onCaptureDone(String pictureUrl, byte[] pictureData); /** * a callback called when we've done taking pictures from ALL AVAILABLE cameras * OR when NO camera was detected on the device * * @param picturesTaken : a Map */ void onDoneCapturingAllPhotos(TreeMap picturesTaken); } ================================================ FILE: app/src/main/java/com/hzitoun/camera2SecretPictureTaker/services/APictureCapturingService.java ================================================ package com.hzitoun.camera2SecretPictureTaker.services; import android.app.Activity; import android.content.Context; import android.hardware.camera2.CameraManager; import android.util.SparseIntArray; import android.view.Surface; import com.hzitoun.camera2SecretPictureTaker.listeners.PictureCapturingListener; /** * Abstract Picture Taking Service. * * @author hzitoun (zitoun.hamed@gmail.com) */ public abstract class APictureCapturingService { private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); 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); } private final Activity activity; final Context context; final CameraManager manager; /*** * constructor. * * @param activity the activity used to get display manager and the application context */ APictureCapturingService(final Activity activity) { this.activity = activity; this.context = activity.getApplicationContext(); this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE); } /*** * @return orientation */ int getOrientation() { final int rotation = this.activity.getWindowManager().getDefaultDisplay().getRotation(); return ORIENTATIONS.get(rotation); } /** * starts pictures capturing process. * * @param listener picture capturing listener */ public abstract void startCapturing(final PictureCapturingListener listener); } ================================================ FILE: app/src/main/java/com/hzitoun/camera2SecretPictureTaker/services/PictureCapturingServiceImpl.java ================================================ package com.hzitoun.camera2SecretPictureTaker.services; import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.pm.PackageManager; import android.graphics.ImageFormat; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.TotalCaptureResult; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.ImageReader; import android.os.Build; import android.os.Environment; import android.os.Handler; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.util.Log; import android.util.Size; import android.view.Surface; import com.hzitoun.camera2SecretPictureTaker.listeners.PictureCapturingListener; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.TreeMap; import java.util.UUID; /** * The aim of this service is to secretly take pictures (without preview or opening device's camera app) * from all available cameras using Android Camera 2 API * * @author hzitoun (zitoun.hamed@gmail.com) */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) //NOTE: camera 2 api was added in API level 21 public class PictureCapturingServiceImpl extends APictureCapturingService { private static final String TAG = PictureCapturingServiceImpl.class.getSimpleName(); private CameraDevice cameraDevice; private ImageReader imageReader; /*** * camera ids queue. */ private Queue cameraIds; private String currentCameraId; private boolean cameraClosed; /** * stores a sorted map of (pictureUrlOnDisk, PictureData). */ private TreeMap picturesTaken; private PictureCapturingListener capturingListener; /*** * private constructor, meant to force the use of {@link #getInstance} method */ private PictureCapturingServiceImpl(final Activity activity) { super(activity); } /** * @param activity the activity used to get the app's context and the display manager * @return a new instance */ public static APictureCapturingService getInstance(final Activity activity) { return new PictureCapturingServiceImpl(activity); } /** * Starts pictures capturing treatment. * * @param listener picture capturing listener */ @Override public void startCapturing(final PictureCapturingListener listener) { this.picturesTaken = new TreeMap<>(); this.capturingListener = listener; this.cameraIds = new LinkedList<>(); try { final String[] cameraIds = manager.getCameraIdList(); if (cameraIds.length > 0) { this.cameraIds.addAll(Arrays.asList(cameraIds)); this.currentCameraId = this.cameraIds.poll(); openCamera(); } else { //No camera detected! capturingListener.onDoneCapturingAllPhotos(picturesTaken); } } catch (final CameraAccessException e) { Log.e(TAG, "Exception occurred while accessing the list of cameras", e); } } private void openCamera() { Log.d(TAG, "opening camera " + currentCameraId); try { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { manager.openCamera(currentCameraId, stateCallback, null); } } catch (final CameraAccessException e) { Log.e(TAG, " exception occurred while opening camera " + currentCameraId, e); } } private final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); if (picturesTaken.lastEntry() != null) { capturingListener.onCaptureDone(picturesTaken.lastEntry().getKey(), picturesTaken.lastEntry().getValue()); Log.i(TAG, "done taking picture from camera " + cameraDevice.getId()); } closeCamera(); } }; private final ImageReader.OnImageAvailableListener onImageAvailableListener = (ImageReader imReader) -> { final Image image = imReader.acquireLatestImage(); final ByteBuffer buffer = image.getPlanes()[0].getBuffer(); final byte[] bytes = new byte[buffer.capacity()]; buffer.get(bytes); saveImageToDisk(bytes); image.close(); }; private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { cameraClosed = false; Log.d(TAG, "camera " + camera.getId() + " opened"); cameraDevice = camera; Log.i(TAG, "Taking picture from camera " + camera.getId()); //Take the picture after some delay. It may resolve getting a black dark photos. new Handler().postDelayed(() -> { try { takePicture(); } catch (final CameraAccessException e) { Log.e(TAG, " exception occurred while taking picture from " + currentCameraId, e); } }, 500); } @Override public void onDisconnected(@NonNull CameraDevice camera) { Log.d(TAG, " camera " + camera.getId() + " disconnected"); if (cameraDevice != null && !cameraClosed) { cameraClosed = true; cameraDevice.close(); } } @Override public void onClosed(@NonNull CameraDevice camera) { cameraClosed = true; Log.d(TAG, "camera " + camera.getId() + " closed"); //once the current camera has been closed, start taking another picture if (!cameraIds.isEmpty()) { takeAnotherPicture(); } else { capturingListener.onDoneCapturingAllPhotos(picturesTaken); } } @Override public void onError(@NonNull CameraDevice camera, int error) { Log.e(TAG, "camera in error, int code " + error); if (cameraDevice != null && !cameraClosed) { cameraDevice.close(); } } }; private void takePicture() throws CameraAccessException { if (null == cameraDevice) { Log.e(TAG, "cameraDevice is null"); return; } final CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId()); Size[] jpegSizes = null; StreamConfigurationMap streamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); if (streamConfigurationMap != null) { jpegSizes = streamConfigurationMap.getOutputSizes(ImageFormat.JPEG); } final boolean jpegSizesNotEmpty = jpegSizes != null && 0 < jpegSizes.length; int width = jpegSizesNotEmpty ? jpegSizes[0].getWidth() : 640; int height = jpegSizesNotEmpty ? jpegSizes[0].getHeight() : 480; final ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1); final List outputSurfaces = new ArrayList<>(); outputSurfaces.add(reader.getSurface()); final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(reader.getSurface()); captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation()); reader.setOnImageAvailableListener(onImageAvailableListener, null); cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() { @Override public void onConfigured(@NonNull CameraCaptureSession session) { try { session.capture(captureBuilder.build(), captureListener, null); } catch (final CameraAccessException e) { Log.e(TAG, " exception occurred while accessing " + currentCameraId, e); } } @Override public void onConfigureFailed(@NonNull CameraCaptureSession session) { } } , null); } private void saveImageToDisk(final byte[] bytes) { final String cameraId = this.cameraDevice == null ? UUID.randomUUID().toString() : this.cameraDevice.getId(); final File file = new File(Environment.getExternalStorageDirectory() + "/" + cameraId + "_pic.jpg"); try (final OutputStream output = new FileOutputStream(file)) { output.write(bytes); this.picturesTaken.put(file.getPath(), bytes); } catch (final IOException e) { Log.e(TAG, "Exception occurred while saving picture to external storage ", e); } } private void takeAnotherPicture() { this.currentCameraId = this.cameraIds.poll(); openCamera(); } private void closeCamera() { Log.d(TAG, "closing camera " + cameraDevice.getId()); if (null != cameraDevice && !cameraClosed) { cameraDevice.close(); cameraDevice = null; } if (null != imageReader) { imageReader.close(); imageReader = null; } } } ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================