├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── mipmap-hdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── colors.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ └── styles.xml
│ │ │ ├── values-w820dp
│ │ │ │ └── dimens.xml
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ └── activity_display.xml
│ │ ├── AndroidManifest.xml
│ │ └── java
│ │ │ └── me
│ │ │ └── aflak
│ │ │ └── libraries
│ │ │ ├── DisplayActivity.java
│ │ │ └── MainActivity.java
│ ├── test
│ │ └── java
│ │ │ └── me
│ │ │ └── aflak
│ │ │ └── libraries
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── me
│ │ └── aflak
│ │ └── libraries
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── ezcam
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ ├── java
│ │ │ └── me
│ │ │ │ └── aflak
│ │ │ │ └── ezcam
│ │ │ │ ├── EZCamCallback.java
│ │ │ │ ├── CompareSizesByArea.java
│ │ │ │ └── EZCam.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── me
│ │ │ └── aflak
│ │ │ └── ezcam
│ │ │ └── ExampleUnitTest.java
│ └── androidTest
│ │ └── java
│ │ └── me
│ │ └── aflak
│ │ └── ezcam
│ │ └── ApplicationTest.java
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── dictionaries
│ └── Omar.xml
├── vcs.xml
├── runConfigurations.xml
├── compiler.xml
├── modules.xml
├── gradle.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── gradlew.bat
├── README.md
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/ezcam/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':ezcam'
2 |
--------------------------------------------------------------------------------
/ezcam/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EZCam
3 |
4 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omaraflak/Android-Camera2-Library/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omaraflak/Android-Camera2-Library/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omaraflak/Android-Camera2-Library/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omaraflak/Android-Camera2-Library/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omaraflak/Android-Camera2-Library/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/omaraflak/Android-Camera2-Library/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | EZCam
3 |
4 | Return
5 |
6 |
--------------------------------------------------------------------------------
/.idea/dictionaries/Omar.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | autofit
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Apr 07 23:05:24 EEST 2019
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip
7 |
--------------------------------------------------------------------------------
/ezcam/src/main/java/me/aflak/ezcam/EZCamCallback.java:
--------------------------------------------------------------------------------
1 | package me.aflak.ezcam;
2 |
3 | import android.media.Image;
4 |
5 | /**
6 | * Camera callback
7 | *
8 | * @author Omar
9 | * @since 23/02/2017
10 | */
11 |
12 | public interface EZCamCallback {
13 | void onCameraReady();
14 | void onPicture(Image image);
15 | void onError(String message);
16 | void onCameraDisconnected();
17 | }
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/ezcam/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/ezcam/src/test/java/me/aflak/ezcam/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package me.aflak.ezcam;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/ezcam/src/main/java/me/aflak/ezcam/CompareSizesByArea.java:
--------------------------------------------------------------------------------
1 | package me.aflak.ezcam;
2 |
3 | import android.util.Size;
4 |
5 | import java.util.Comparator;
6 |
7 | public class CompareSizesByArea implements Comparator {
8 | @Override
9 | public int compare(Size lhs, Size rhs) {
10 | return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
11 | }
12 | }
--------------------------------------------------------------------------------
/app/src/test/java/me/aflak/libraries/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package me.aflak.libraries;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * To work on unit tests, switch the Test Artifact in the Build Variants view.
9 | */
10 | public class ExampleUnitTest {
11 | @Test
12 | public void addition_isCorrect() throws Exception {
13 | assertEquals(4, 2 + 2);
14 | }
15 | }
--------------------------------------------------------------------------------
/ezcam/src/androidTest/java/me/aflak/ezcam/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package me.aflak.ezcam;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/me/aflak/libraries/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package me.aflak.libraries;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Users\Omar\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/ezcam/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in E:\Users\Omar\AppData\Local\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
11 |
12 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_display.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
15 |
22 |
23 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 | defaultConfig {
7 | applicationId "me.aflak.libraries"
8 | minSdkVersion 21
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | testImplementation 'junit:junit:4.12'
24 | implementation 'com.android.support:appcompat-v7:28.0.0'
25 | implementation project(path: ':ezcam')
26 |
27 | // dexter
28 | implementation 'com.karumi:dexter:5.0.0'
29 |
30 | // glide
31 | implementation 'com.github.bumptech.glide:glide:4.9.0'
32 | annotationProcessor 'com.github.bumptech.glide:compiler:4.9.0'
33 | }
34 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/me/aflak/libraries/DisplayActivity.java:
--------------------------------------------------------------------------------
1 | package me.aflak.libraries;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.os.Bundle;
6 | import android.view.View;
7 | import android.widget.ImageView;
8 |
9 | import com.bumptech.glide.Glide;
10 | import java.io.File;
11 |
12 | /**
13 | * Created by Omar on 25/02/2017.
14 | */
15 |
16 | public class DisplayActivity extends Activity implements View.OnClickListener {
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_display);
22 |
23 | ImageView imageView = (ImageView) findViewById(R.id.imageView);
24 | findViewById(R.id.returnButton).setOnClickListener(this);
25 |
26 | String filepath = getIntent().getExtras().getString("filepath", null);
27 | File file = new File(filepath);
28 | Glide.with(this).load(file).into(imageView);
29 | }
30 |
31 | @Override
32 | public void onClick(View v) {
33 | startActivity(new Intent(this, MainActivity.class));
34 | finish();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/ezcam/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | bintrayRepo = 'maven'
5 | bintrayName = 'ezcam'
6 |
7 | publishedGroupId = 'me.aflak.libraries'
8 | libraryName = 'EZCam'
9 | artifact = 'ezcam'
10 |
11 | libraryDescription = 'Simple library that allows you to display preview and take pictures easily with callbacks!'
12 |
13 | siteUrl = 'https://github.com/omaflak/Android-Camera2-Library'
14 | gitUrl = 'https://github.com/omaflak/Android-Camera2-Library.git'
15 |
16 | libraryVersion = '2.3.4'
17 |
18 | developerId = 'omaflak'
19 | developerName = 'Omar Aflak'
20 | developerEmail = 'aflakomar@hotmail.fr'
21 |
22 | licenseName = 'The Apache Software License, Version 2.0'
23 | licenseUrl = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
24 | allLicenses = ["Apache-2.0"]
25 | }
26 |
27 | android {
28 | compileSdkVersion 28
29 |
30 | defaultConfig {
31 | minSdkVersion 21
32 | targetSdkVersion 28
33 | versionCode 8
34 | versionName "2.3.4"
35 | }
36 | buildTypes {
37 | release {
38 | minifyEnabled false
39 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
40 | }
41 | }
42 | }
43 |
44 | dependencies {
45 | implementation fileTree(dir: 'libs', include: ['*.jar'])
46 | testImplementation 'junit:junit:4.12'
47 | implementation 'com.android.support:appcompat-v7:28.0.0'
48 | }
49 |
50 | apply from: 'https://raw.githubusercontent.com/omaflak/Bintray/master/install.gradle'
51 | apply from: 'https://raw.githubusercontent.com/omaflak/Bintray/master/bintray.gradle'
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # EZCam [  ](https://bintray.com/omaflak/maven/ezcam/_latestVersion)
2 |
3 | EZCam is an Android library that simplifies the use of Camera 2 API for a basic usage.
4 |
5 | # Dependencie
6 |
7 | Add the following line in your gradle dependencies :
8 |
9 | ```
10 | compile 'me.aflak.libraries:ezcam:X.X'
11 | ```
12 |
13 | Or if you use Maven :
14 |
15 | ```
16 |
17 | me.aflak.libraries
18 | ezcam
19 | X.X
20 | pom
21 |
22 | ```
23 |
24 | # TextureView
25 |
26 | **Important : The TextureView must be in a FrameLayout.**
27 |
28 | ```xml
29 |
32 |
36 |
37 | ```
38 |
39 | # Initialize
40 |
41 | ```java
42 | EZCam cam = new EZCam(Context);
43 | String id = cam.getCamerasList().get(CameraCharacteristics.LENS_FACING_BACK); // should check if LENS_FACING_BACK exist before calling get()
44 | cam.selectCamera(id);
45 | ```
46 |
47 | # Callback
48 |
49 | ```java
50 | cam.setCameraCallback(new EZCamCallback() {
51 | @Override
52 | public void onCameraReady() {
53 | // triggered after cam.open(...)
54 | // you can set capture settings for example:
55 | cam.setCaptureSetting(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY);
56 | cam.setCaptureSetting(CaptureRequest.CONTROL_EFFECT_MODE, CameraMetadata.CONTROL_EFFECT_MODE_NEGATIVE);
57 |
58 | // then start the preview
59 | cam.startPreview();
60 | }
61 |
62 | @Override
63 | public void onPicture(Image image) {
64 | File file = new File(getFilesDir(), "image.jpg"); // internal storage
65 | File file = new File(getExternalFilesDir(null), "image.jpg") // external storage, need permissions
66 | EZCam.saveImage(image, file);
67 | }
68 |
69 | @Override
70 | public void onError(String message) {
71 | // all errors will be passed through this methods
72 | }
73 |
74 | @Override
75 | public void onCameraDisconnected() {
76 | // camera disconnected
77 | }
78 | });
79 | ```
80 |
81 | # Open Camera
82 |
83 | ```java
84 | cam.open(CameraDevice.TEMPLATE_PREVIEW, textureView); // needs Manifest.permission.CAMERA
85 | ```
86 |
87 | # Take picture
88 |
89 | ```java
90 | cam.takePicture();
91 | ```
92 |
93 | # Close camera
94 |
95 | ```java
96 | @Override
97 | protected void onDestroy() {
98 | cam.close();
99 | super.onDestroy();
100 | }
101 | ```
102 |
103 | # TODO
104 |
105 | - Recording videos
106 | - Apply custom filters
107 |
108 | See [MainActivity.java](https://github.com/omaflak/Android-Camera2-Library/blob/master/app/src/main/java/me/aflak/libraries/MainActivity.java)
109 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 | 1.8
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/app/src/main/java/me/aflak/libraries/MainActivity.java:
--------------------------------------------------------------------------------
1 | package me.aflak.libraries;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Intent;
6 | import android.hardware.camera2.CameraCharacteristics;
7 | import android.hardware.camera2.CameraDevice;
8 | import android.hardware.camera2.CameraMetadata;
9 | import android.hardware.camera2.CaptureRequest;
10 | import android.media.Image;
11 | import android.os.Bundle;
12 | import android.util.Log;
13 | import android.view.TextureView;
14 | import android.view.View;
15 |
16 | import com.karumi.dexter.Dexter;
17 | import com.karumi.dexter.PermissionToken;
18 | import com.karumi.dexter.listener.PermissionDeniedResponse;
19 | import com.karumi.dexter.listener.PermissionGrantedResponse;
20 | import com.karumi.dexter.listener.PermissionRequest;
21 | import com.karumi.dexter.listener.single.PermissionListener;
22 |
23 | import java.io.File;
24 | import java.io.IOException;
25 | import java.text.SimpleDateFormat;
26 | import java.util.Date;
27 | import java.util.Locale;
28 |
29 | import me.aflak.ezcam.EZCam;
30 | import me.aflak.ezcam.EZCamCallback;
31 |
32 | public class MainActivity extends Activity implements EZCamCallback, View.OnLongClickListener{
33 | private TextureView textureView;
34 |
35 | private EZCam cam;
36 | private SimpleDateFormat dateFormat;
37 |
38 | private final String TAG = "CAM";
39 |
40 | @Override
41 | protected void onCreate(Bundle savedInstanceState) {
42 | super.onCreate(savedInstanceState);
43 | setContentView(R.layout.activity_main);
44 |
45 | textureView = (TextureView) findViewById(R.id.textureView);
46 | dateFormat = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.getDefault());
47 |
48 | cam = new EZCam(this);
49 | cam.setCameraCallback(this);
50 |
51 | String id = cam.getCamerasList().get(CameraCharacteristics.LENS_FACING_BACK);
52 | cam.selectCamera(id);
53 |
54 | Dexter.withActivity(MainActivity.this).withPermission(Manifest.permission.CAMERA).withListener(new PermissionListener() {
55 | @Override
56 | public void onPermissionGranted(PermissionGrantedResponse response) {
57 | cam.open(CameraDevice.TEMPLATE_PREVIEW, textureView);
58 | }
59 |
60 | @Override
61 | public void onPermissionDenied(PermissionDeniedResponse response) {
62 | Log.e(TAG, "permission denied");
63 | }
64 |
65 | @Override
66 | public void onPermissionRationaleShouldBeShown(PermissionRequest permission, PermissionToken token) {
67 | token.continuePermissionRequest();
68 | }
69 | }).check();
70 | }
71 |
72 | @Override
73 | public boolean onLongClick(View v) {
74 | cam.takePicture();
75 | return false;
76 | }
77 |
78 | @Override
79 | public void onCameraReady() {
80 | cam.setCaptureSetting(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE, CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY);
81 | cam.startPreview();
82 |
83 | textureView.setOnLongClickListener(this);
84 | }
85 |
86 | @Override
87 | public void onPicture(Image image) {
88 | cam.stopPreview();
89 | try {
90 | String filename = "image_"+dateFormat.format(new Date())+".jpg";
91 | File file = new File(getFilesDir(), filename);
92 | EZCam.saveImage(image, file);
93 |
94 | Intent intent = new Intent(this, DisplayActivity.class);
95 | intent.putExtra("filepath", file.getAbsolutePath());
96 | startActivity(intent);
97 | finish();
98 | } catch (IOException e) {
99 | Log.e(TAG, e.getMessage());
100 | }
101 | }
102 |
103 | @Override
104 | public void onCameraDisconnected() {
105 | Log.e(TAG, "Camera disconnected");
106 | }
107 |
108 | @Override
109 | public void onError(String message) {
110 | Log.e(TAG, message);
111 | }
112 |
113 | @Override
114 | protected void onDestroy() {
115 | cam.close();
116 | super.onDestroy();
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/ezcam/src/main/java/me/aflak/ezcam/EZCam.java:
--------------------------------------------------------------------------------
1 | package me.aflak.ezcam;
2 |
3 | import android.Manifest;
4 | import android.app.Activity;
5 | import android.content.Context;
6 | import android.content.pm.PackageManager;
7 | import android.graphics.ImageFormat;
8 | import android.graphics.Matrix;
9 | import android.graphics.RectF;
10 | import android.graphics.SurfaceTexture;
11 | import android.hardware.camera2.CameraAccessException;
12 | import android.hardware.camera2.CameraCaptureSession;
13 | import android.hardware.camera2.CameraCharacteristics;
14 | import android.hardware.camera2.CameraDevice;
15 | import android.hardware.camera2.CameraManager;
16 | import android.hardware.camera2.CaptureRequest;
17 | import android.hardware.camera2.params.StreamConfigurationMap;
18 | import android.media.Image;
19 | import android.media.ImageReader;
20 | import android.os.Build;
21 | import android.os.Handler;
22 | import android.os.HandlerThread;
23 | import android.support.annotation.NonNull;
24 | import android.support.v4.app.ActivityCompat;
25 | import android.util.Size;
26 | import android.util.SparseArray;
27 | import android.view.Gravity;
28 | import android.view.Surface;
29 | import android.view.TextureView;
30 | import android.widget.FrameLayout;
31 |
32 | import java.io.File;
33 | import java.io.FileOutputStream;
34 | import java.io.IOException;
35 | import java.nio.ByteBuffer;
36 | import java.util.Arrays;
37 | import java.util.Collections;
38 |
39 | /**
40 | * Class that simplifies the use of Camera 2 api
41 | *
42 | * @author Omar Aflak
43 | * @since 23/02/2017
44 | */
45 |
46 | public class EZCam {
47 | private Context context;
48 | private EZCamCallback cameraCallback;
49 |
50 | private SparseArray camerasList;
51 | private String currentCamera;
52 | private Size previewSize;
53 |
54 | private CameraManager cameraManager;
55 | private CameraDevice cameraDevice;
56 | private CameraCaptureSession cameraCaptureSession;
57 | private CameraCharacteristics cameraCharacteristics;
58 | private CaptureRequest.Builder captureRequestBuilder;
59 | private CaptureRequest.Builder captureRequestBuilderImageReader;
60 | private ImageReader imageReader;
61 |
62 | private HandlerThread backgroundThread;
63 | private Handler backgroundHandler;
64 |
65 | public EZCam(Context context) {
66 | this.context = context;
67 | this.cameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
68 | }
69 |
70 | /**
71 | * Set callback to receive camera states
72 | * @param cameraCallback callback
73 | */
74 | public void setCameraCallback(EZCamCallback cameraCallback) {
75 | this.cameraCallback = cameraCallback;
76 | }
77 |
78 | /**
79 | * Get available cameras
80 | * @return SparseArray of available cameras ids
81 | */
82 | public SparseArray getCamerasList(){
83 | camerasList = new SparseArray<>();
84 | try {
85 | String[] camerasAvailable = cameraManager.getCameraIdList();
86 | CameraCharacteristics cam;
87 | Integer characteristic;
88 | for (String id : camerasAvailable){
89 | cam = cameraManager.getCameraCharacteristics(id);
90 | characteristic = cam.get(CameraCharacteristics.LENS_FACING);
91 | if (characteristic!=null){
92 | switch (characteristic){
93 | case CameraCharacteristics.LENS_FACING_FRONT:
94 | camerasList.put(CameraCharacteristics.LENS_FACING_FRONT, id);
95 | break;
96 |
97 | case CameraCharacteristics.LENS_FACING_BACK:
98 | camerasList.put(CameraCharacteristics.LENS_FACING_BACK, id);
99 | break;
100 |
101 | case CameraCharacteristics.LENS_FACING_EXTERNAL:
102 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
103 | camerasList.put(CameraCharacteristics.LENS_FACING_EXTERNAL, id);
104 | }
105 | break;
106 | }
107 | }
108 | }
109 | return camerasList;
110 | } catch (CameraAccessException e) {
111 | notifyError(e.getMessage());
112 | return null;
113 | }
114 | }
115 |
116 | /**
117 | * Select the camera you want to open : front, back, external(s)
118 | * @param id Id of the camera which can be retrieved with getCamerasList().get(CameraCharacteristics.LENS_FACING_BACK)
119 | */
120 | public void selectCamera(String id) {
121 | if(camerasList == null){
122 | getCamerasList();
123 | }
124 |
125 | currentCamera = camerasList.indexOfValue(id)<0?null:id;
126 | if(currentCamera == null) {
127 | notifyError("Camera id not found.");
128 | return;
129 | }
130 |
131 | try {
132 | cameraCharacteristics = cameraManager.getCameraCharacteristics(currentCamera);
133 | StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
134 | if(map != null) {
135 | previewSize = Collections.max(Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new CompareSizesByArea());
136 | imageReader = ImageReader.newInstance(previewSize.getWidth(), previewSize.getHeight(), ImageFormat.JPEG, 1);
137 | imageReader.setOnImageAvailableListener(onImageAvailable, backgroundHandler);
138 | }
139 | else{
140 | notifyError("Could not get configuration map.");
141 | }
142 | } catch (CameraAccessException e) {
143 | notifyError(e.getMessage());
144 | }
145 | }
146 |
147 | /**
148 | * Open camera to prepare preview
149 | * @param templateType capture mode e.g. CameraDevice.TEMPLATE_PREVIEW
150 | * @param textureView Surface where preview should be displayed
151 | */
152 | public void open(final int templateType, final TextureView textureView) {
153 | if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
154 | notifyError("You don't have the required permissions.");
155 | return;
156 | }
157 |
158 | startBackgroundThread();
159 |
160 | try {
161 | cameraManager.openCamera(currentCamera, new CameraDevice.StateCallback() {
162 | @Override
163 | public void onOpened(@NonNull CameraDevice camera) {
164 | cameraDevice = camera;
165 | setupPreview(templateType, textureView);
166 | }
167 |
168 | @Override
169 | public void onDisconnected(@NonNull CameraDevice camera) {
170 | if(cameraCallback != null){
171 | cameraCallback.onError("Camera device is no longer available for use.");
172 | cameraCallback.onCameraDisconnected();
173 | }
174 | }
175 |
176 | @Override
177 | public void onError(@NonNull CameraDevice camera, int error) {
178 | switch (error){
179 | case CameraDevice.StateCallback.ERROR_CAMERA_DEVICE:
180 | notifyError("Camera device has encountered a fatal error.");
181 | break;
182 | case CameraDevice.StateCallback.ERROR_CAMERA_DISABLED:
183 | notifyError("Camera device could not be opened due to a device policy.");
184 | break;
185 | case CameraDevice.StateCallback.ERROR_CAMERA_IN_USE:
186 | notifyError("Camera device is in use already.");
187 | break;
188 | case CameraDevice.StateCallback.ERROR_CAMERA_SERVICE:
189 | notifyError("Camera service has encountered a fatal error.");
190 | break;
191 | case CameraDevice.StateCallback.ERROR_MAX_CAMERAS_IN_USE:
192 | notifyError("Camera device could not be opened because there are too many other open camera devices.");
193 | break;
194 | }
195 | }
196 | }, backgroundHandler);
197 | } catch (CameraAccessException e) {
198 | notifyError(e.getMessage());
199 | }
200 | }
201 |
202 | private void setupPreview_(int templateType, TextureView textureView){
203 | Surface surface = new Surface(textureView.getSurfaceTexture());
204 |
205 | try {
206 | captureRequestBuilder = cameraDevice.createCaptureRequest(templateType);
207 | captureRequestBuilder.addTarget(surface);
208 |
209 | captureRequestBuilderImageReader = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
210 | captureRequestBuilderImageReader.addTarget(imageReader.getSurface());
211 |
212 | cameraDevice.createCaptureSession(Arrays.asList(surface, imageReader.getSurface()), new CameraCaptureSession.StateCallback() {
213 | @Override
214 | public void onConfigured(@NonNull CameraCaptureSession session) {
215 | cameraCaptureSession = session;
216 | if(cameraCallback != null){
217 | cameraCallback.onCameraReady();
218 | }
219 | }
220 |
221 | @Override
222 | public void onConfigureFailed(@NonNull CameraCaptureSession session) {
223 | notifyError("Could not configure capture session.");
224 | }
225 | }, backgroundHandler);
226 | } catch (CameraAccessException e) {
227 | notifyError(e.getMessage());
228 | }
229 | }
230 |
231 | private void setupPreview(final int templateType, final TextureView outputSurface){
232 | if(outputSurface.isAvailable()){
233 | setupPreview_(templateType, outputSurface);
234 | }
235 | else{
236 | outputSurface.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
237 | @Override
238 | public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
239 | setAspectRatioTextureView(outputSurface, width, height);
240 | setupPreview_(templateType, outputSurface);
241 | }
242 |
243 | public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {}
244 | public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {return false;}
245 | public void onSurfaceTextureUpdated(SurfaceTexture surface) {}
246 | });
247 | }
248 | }
249 |
250 | /**
251 | * Set CaptureRequest parameters for preview e.g. flash, auto-focus, macro mode, etc.
252 | * @param key e.g. CaptureRequest.CONTROL_EFFECT_MODE
253 | * @param value e.g. CameraMetadata.CONTROL_EFFECT_MODE_NEGATIVE
254 | */
255 | public void setCaptureSetting(CaptureRequest.Key key, T value){
256 | if(captureRequestBuilder!=null && captureRequestBuilderImageReader!=null) {
257 | captureRequestBuilder.set(key, value);
258 | captureRequestBuilderImageReader.set(key, value);
259 | }
260 | }
261 |
262 | /**
263 | * Get characteristic of selected camera e.g. available effects, scene modes, etc.
264 | * @param key e.g. CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS
265 | */
266 | public T getCharacteristic(CameraCharacteristics.Key key){
267 | if(cameraCharacteristics!=null) {
268 | return cameraCharacteristics.get(key);
269 | }
270 | return null;
271 | }
272 |
273 | private void setAspectRatioTextureView(TextureView textureView, int surfaceWidth, int surfaceHeight)
274 | {
275 | int rotation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation();
276 | int newWidth = surfaceWidth, newHeight = surfaceHeight;
277 |
278 | switch (rotation) {
279 | case Surface.ROTATION_0:
280 | newWidth = surfaceWidth;
281 | newHeight = (surfaceWidth * previewSize.getWidth() / previewSize.getHeight());
282 | break;
283 |
284 | case Surface.ROTATION_180:
285 | newWidth = surfaceWidth;
286 | newHeight = (surfaceWidth * previewSize.getWidth() / previewSize.getHeight());
287 | break;
288 |
289 | case Surface.ROTATION_90:
290 | newWidth = surfaceHeight;
291 | newHeight = (surfaceHeight * previewSize.getWidth() / previewSize.getHeight());
292 | break;
293 |
294 | case Surface.ROTATION_270:
295 | newWidth = surfaceHeight;
296 | newHeight = (surfaceHeight * previewSize.getWidth() / previewSize.getHeight());
297 | break;
298 | }
299 |
300 | textureView.setLayoutParams(new FrameLayout.LayoutParams(newWidth, newHeight, Gravity.CENTER));
301 | rotatePreview(textureView, rotation, newWidth, newHeight);
302 | }
303 |
304 | private void rotatePreview(TextureView mTextureView, int rotation, int viewWidth, int viewHeight) {
305 | Matrix matrix = new Matrix();
306 | RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
307 | float centerX = viewRect.centerX();
308 | float centerY = viewRect.centerY();
309 | if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
310 | matrix.postRotate(90 * (rotation - 2), centerX, centerY);
311 | }
312 | else if (Surface.ROTATION_180 == rotation) {
313 | matrix.postRotate(180, centerX, centerY);
314 | }
315 | mTextureView.setTransform(matrix);
316 | }
317 |
318 | /**
319 | * start the preview, capture request is built at each call here
320 | */
321 | public void startPreview(){
322 | try {
323 | cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, backgroundHandler);
324 | } catch (CameraAccessException e) {
325 | notifyError(e.getMessage());
326 | }
327 | }
328 |
329 | /**
330 | * stop the preview
331 | */
332 | public void stopPreview(){
333 | try {
334 | cameraCaptureSession.stopRepeating();
335 | } catch (CameraAccessException e) {
336 | notifyError(e.getMessage());
337 | }
338 | }
339 |
340 | /**
341 | * shortcut to call stopPreview() then startPreview()
342 | */
343 | public void restartPreview(){
344 | stopPreview();
345 | startPreview();
346 | }
347 |
348 | /**
349 | * close the camera definitively
350 | */
351 | public void close(){
352 | cameraDevice.close();
353 | stopBackgroundThread();
354 | }
355 |
356 | /**
357 | * take a picture
358 | */
359 | public void takePicture(){
360 | captureRequestBuilderImageReader.set(CaptureRequest.JPEG_ORIENTATION, cameraCharacteristics.get(CameraCharacteristics.SENSOR_ORIENTATION));
361 | try {
362 | cameraCaptureSession.capture(captureRequestBuilderImageReader.build(), null, backgroundHandler);
363 | } catch (CameraAccessException e) {
364 | notifyError(e.getMessage());
365 | }
366 | }
367 |
368 | private ImageReader.OnImageAvailableListener onImageAvailable = new ImageReader.OnImageAvailableListener() {
369 | @Override
370 | public void onImageAvailable(ImageReader reader) {
371 | if(cameraCallback != null){
372 | cameraCallback.onPicture(imageReader.acquireLatestImage());
373 | }
374 | }
375 | };
376 |
377 | private void notifyError(String message) {
378 | if (cameraCallback != null) {
379 | cameraCallback.onError(message);
380 | }
381 | }
382 |
383 | /**
384 | * Save image to storage
385 | * @param image Image object got from onPicture() callback of EZCamCallback
386 | * @param file File where image is going to be written
387 | * @return File object pointing to the file uri, null if file already exist
388 | */
389 | public static File saveImage(Image image, File file) throws IOException {
390 | if(file.exists()) {
391 | image.close();
392 | return null;
393 | }
394 | ByteBuffer buffer = image.getPlanes()[0].getBuffer();
395 | byte[] bytes = new byte[buffer.remaining()];
396 | buffer.get(bytes);
397 | FileOutputStream output = new FileOutputStream(file);
398 | output.write(bytes);
399 | image.close();
400 | output.close();
401 | return file;
402 | }
403 |
404 | private void startBackgroundThread() {
405 | backgroundThread = new HandlerThread("EZCam");
406 | backgroundThread.start();
407 | backgroundHandler = new Handler(backgroundThread.getLooper());
408 | }
409 |
410 | private void stopBackgroundThread() {
411 | backgroundThread.quitSafely();
412 | try {
413 | backgroundThread.join();
414 | backgroundThread = null;
415 | backgroundHandler = null;
416 | } catch (InterruptedException e) {
417 | notifyError(e.getMessage());
418 | }
419 | }
420 | }
--------------------------------------------------------------------------------