diff --git a/app/build.gradle b/app/build.gradle
index 00628cd..f2ef0ad 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,11 +1,13 @@
apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-android'
android {
compileSdkVersion COMPILE_SDK_VERSION as int
buildToolsVersion BUILD_TOOLS_VERSION as String
defaultConfig {
applicationId "co.fitcom.videorecorder"
- minSdkVersion 17
+ minSdkVersion 21
targetSdkVersion COMPILE_SDK_VERSION as int
versionCode 1
versionName "1.0"
@@ -17,6 +19,10 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
buildToolsVersion '29.0.2'
}
@@ -28,4 +34,9 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation project(':fancycamera')
+ implementation "androidx.core:core-ktx:1.1.0"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
+repositories {
+ mavenCentral()
}
diff --git a/app/src/androidTest/java/co/fitcom/videorecorder/ExampleInstrumentedTest.java b/app/src/androidTest/java/co/fitcom/videorecorder/ExampleInstrumentedTest.java
deleted file mode 100644
index 575bc1e..0000000
--- a/app/src/androidTest/java/co/fitcom/videorecorder/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package co.fitcom.videorecorder;
-
-import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented 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("co.fitcom.videorecorder", appContext.getPackageName());
- }
-}
diff --git a/app/src/androidTest/java/co/fitcom/videorecorder/ExampleInstrumentedTest.kt b/app/src/androidTest/java/co/fitcom/videorecorder/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..0cbe873
--- /dev/null
+++ b/app/src/androidTest/java/co/fitcom/videorecorder/ExampleInstrumentedTest.kt
@@ -0,0 +1,27 @@
+package co.fitcom.videorecorder
+
+import android.content.Context
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ @Throws(Exception::class)
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getTargetContext()
+
+ assertEquals("co.fitcom.videorecorder", appContext.packageName)
+ }
+}
diff --git a/app/src/main/java/co/fitcom/videorecorder/Home.java b/app/src/main/java/co/fitcom/videorecorder/Home.java
deleted file mode 100644
index 79ae73f..0000000
--- a/app/src/main/java/co/fitcom/videorecorder/Home.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Created By Osei Fortune on 4/19/18 10:38 AM
- * Copyright (c) 2018
- * Last modified 4/19/18 10:38 AM
- *
- */
-
-package co.fitcom.videorecorder;
-
-import androidx.appcompat.app.AppCompatActivity;
-import android.os.Bundle;
-
-public class Home extends AppCompatActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_home);
- }
-}
diff --git a/app/src/main/java/co/fitcom/videorecorder/Home.kt b/app/src/main/java/co/fitcom/videorecorder/Home.kt
new file mode 100644
index 0000000..9b5b9ca
--- /dev/null
+++ b/app/src/main/java/co/fitcom/videorecorder/Home.kt
@@ -0,0 +1,19 @@
+/*
+ * Created By Osei Fortune on 4/19/18 10:38 AM
+ * Copyright (c) 2018
+ * Last modified 4/19/18 10:38 AM
+ *
+ */
+
+package co.fitcom.videorecorder
+
+import androidx.appcompat.app.AppCompatActivity
+import android.os.Bundle
+
+class Home : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_home)
+ }
+}
diff --git a/app/src/main/java/co/fitcom/videorecorder/MainActivity.java b/app/src/main/java/co/fitcom/videorecorder/MainActivity.java
deleted file mode 100644
index 5606320..0000000
--- a/app/src/main/java/co/fitcom/videorecorder/MainActivity.java
+++ /dev/null
@@ -1,172 +0,0 @@
-package co.fitcom.videorecorder;
-
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import android.util.Log;
-import android.view.View;
-import android.widget.TextView;
-import android.widget.VideoView;
-
-import java.util.Timer;
-import java.util.TimerTask;
-
-import co.fitcom.fancycamera.CameraEventListenerUI;
-import co.fitcom.fancycamera.EventType;
-import co.fitcom.fancycamera.FancyCamera;
-import co.fitcom.fancycamera.PhotoEvent;
-import co.fitcom.fancycamera.VideoEvent;
-
-public class MainActivity extends AppCompatActivity {
- FancyCamera cameraView;
- VideoView videoPlayer;
- TextView durationView;
- Timer timer;
- TimerTask timerTask;
- Timer levelsTask;
- double level = 0;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- videoPlayer = findViewById(R.id.videoPlayer);
- durationView = findViewById(R.id.durationView);
- cameraView = findViewById(R.id.cameraView);
- cameraView.setCameraPosition(FancyCamera.CameraPosition.BACK);
- cameraView.setQuality(FancyCamera.Quality.HIGHEST.getValue());
- cameraView.setListener(new CameraEventListenerUI() {
- @Override
- public void onCameraOpenUI() {
- Log.d("co.fitcom.test", "Camera Opened");
- }
-
- @Override
- public void onCameraCloseUI() {
- Log.d("co.fitcom.test", "Camera Close");
- }
-
- @Override
- public void onPhotoEventUI(PhotoEvent event) {
-
- }
-
- @Override
- public void onVideoEventUI(VideoEvent event) {
- if (event.getType() == EventType.INFO && event.getMessage().equals(VideoEvent.EventInfo.RECORDING_FINISHED.toString())) {
- timerTask.cancel();
- timer.cancel();
- videoPlayer.setVideoURI(Uri.fromFile(event.getFile()));
- videoPlayer.start();
- } else if (event.getType() == EventType.INFO && event.getMessage().equals(VideoEvent.EventInfo.RECORDING_STARTED.toString())) {
- System.out.println("Recording Started");
- timer = new Timer();
- timerTask = new TimerTask() {
- @Override
- public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- durationView.setText(String.valueOf(cameraView.getDuration()));
- }
- });
- }
- };
- timer.schedule(timerTask, 0, 1000);
-
- } else {
- System.out.println(event.getMessage());
- }
- }
-
- });
- cameraView.setSaveToGallery(true);
- }
-
- public void startRecording(View view) {
- cameraView.setQuality(FancyCamera.Quality.MAX_720P.getValue());
- cameraView.startRecording();
- }
-
- public void stopRecording(View view) {
- cameraView.stopRecording();
- }
-
- public void toggleCamera(View view) {
- cameraView.toggleCamera();
- }
-
-
- public void goToVideo(View view) {
- Intent i = new Intent(this, MainActivity.class);
- startActivity(i);
- }
-
- public void goToPhoto(View view) {
- Intent i = new Intent(this, Photo.class);
- startActivity(i);
- }
-
- public void toggleFlash(View view) {
- cameraView.toggleFlash();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- cameraView.release();
- if (levelsTask != null) {
- levelsTask.cancel();
- }
- if (timerTask != null) {
- timerTask.cancel();
- }
- timerTask = null;
- levelsTask = null;
- }
-
- void start() {
- if (cameraView.isAudioLevelsEnabled()) {
- if (levelsTask == null) {
- levelsTask = new Timer();
- levelsTask.scheduleAtFixedRate(new TimerTask() {
- @Override
- public void run() {
- runOnUiThread(new Runnable() {
- @Override
- public void run() {
- level = Math.pow(10, (0.02 * cameraView.getDB()));
- Log.d("co.test", "Audio Levels" + level);
- }
- });
- }
- }, 0, 1000);
- }
- }
- cameraView.start();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- boolean b = cameraView.hasPermission();
- Log.d("hasPermission", Boolean.toString(b));
- if (cameraView.hasPermission()) {
- start();
- } else {
- cameraView.requestPermission();
- }
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- if (cameraView.hasPermission()) {
- start();
- }
- }
-
-}
diff --git a/app/src/main/java/co/fitcom/videorecorder/MainActivity.kt b/app/src/main/java/co/fitcom/videorecorder/MainActivity.kt
new file mode 100644
index 0000000..767f743
--- /dev/null
+++ b/app/src/main/java/co/fitcom/videorecorder/MainActivity.kt
@@ -0,0 +1,134 @@
+package co.fitcom.videorecorder
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+
+import androidx.appcompat.app.AppCompatActivity
+
+import android.util.Log
+import android.view.View
+import android.widget.TextView
+import android.widget.VideoView
+
+import java.util.Timer
+import java.util.TimerTask
+
+import co.fitcom.fancycamera.CameraEventListenerUI
+import co.fitcom.fancycamera.EventType
+import co.fitcom.fancycamera.FancyCamera
+import co.fitcom.fancycamera.PhotoEvent
+import co.fitcom.fancycamera.VideoEvent
+
+class MainActivity : AppCompatActivity() {
+ internal lateinit var cameraView: FancyCamera
+ internal lateinit var videoPlayer: VideoView
+ internal lateinit var durationView: TextView
+ internal lateinit var timer: Timer
+ internal var timerTask: TimerTask? = null
+ internal var levelsTask: Timer? = null
+ internal var level = 0.0
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ videoPlayer = findViewById(R.id.videoPlayer)
+ durationView = findViewById(R.id.durationView)
+ cameraView = findViewById(R.id.cameraView)
+ cameraView.setCameraPosition(FancyCamera.CameraPosition.BACK)
+ cameraView.setQuality(FancyCamera.Quality.HIGHEST.value)
+ cameraView.setListener(object : CameraEventListenerUI() {
+ override fun onCameraOpenUI() {
+ Log.d("co.fitcom.test", "Camera Opened")
+ }
+
+ override fun onCameraCloseUI() {
+ Log.d("co.fitcom.test", "Camera Close")
+ }
+
+ override fun onPhotoEventUI(event: PhotoEvent) {
+
+ }
+
+ override fun onVideoEventUI(event: VideoEvent) {
+ if (event.type === EventType.INFO && event.message == VideoEvent.EventInfo.RECORDING_FINISHED.toString()) {
+ timerTask!!.cancel()
+ timer.cancel()
+ videoPlayer.setVideoURI(Uri.fromFile(event.file))
+ videoPlayer.start()
+ } else if (event.type === EventType.INFO && event.message == VideoEvent.EventInfo.RECORDING_STARTED.toString()) {
+ println("Recording Started")
+ timer = Timer()
+ timerTask = object : TimerTask() {
+ override fun run() {
+ runOnUiThread { durationView.text = cameraView.duration.toString() }
+ }
+ }
+ timer.schedule(timerTask, 0, 1000)
+
+ } else {
+ println(event.message)
+ }
+ }
+
+ })
+ cameraView.saveToGallery = true
+ }
+
+ fun startRecording(view: View) {
+ cameraView.setQuality(FancyCamera.Quality.MAX_720P.value)
+ cameraView.startRecording()
+ }
+
+ fun stopRecording(view: View) {
+ cameraView.stopRecording()
+ }
+
+ fun toggleCamera(view: View) {
+ cameraView.toggleCamera()
+ }
+
+
+ fun goToVideo(view: View) {
+ val i = Intent(this, MainActivity::class.java)
+ startActivity(i)
+ }
+
+ fun goToPhoto(view: View) {
+ val i = Intent(this, Photo::class.java)
+ startActivity(i)
+ }
+
+ fun toggleFlash(view: View) {
+ cameraView.toggleFlash()
+ }
+
+ override fun onPause() {
+ super.onPause()
+ if (levelsTask != null) {
+ levelsTask!!.cancel()
+ }
+ if (timerTask != null) {
+ timerTask!!.cancel()
+ }
+ timerTask = null
+ levelsTask = null
+ }
+
+ internal fun start() {
+ if (cameraView.isAudioLevelsEnabled) {
+ if (levelsTask == null) {
+ levelsTask = Timer()
+ levelsTask!!.scheduleAtFixedRate(object : TimerTask() {
+ override fun run() {
+ runOnUiThread {
+ level = Math.pow(10.0, 0.02 * cameraView.db)
+ Log.d("co.test", "Audio Levels$level")
+ }
+ }
+ }, 0, 1000)
+ }
+ }
+ }
+
+}
diff --git a/app/src/main/java/co/fitcom/videorecorder/Photo.java b/app/src/main/java/co/fitcom/videorecorder/Photo.java
deleted file mode 100644
index b0416f5..0000000
--- a/app/src/main/java/co/fitcom/videorecorder/Photo.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package co.fitcom.videorecorder;
-
-import android.Manifest;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-
-import androidx.appcompat.app.AppCompatActivity;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.ImageView;
-
-import co.fitcom.fancycamera.CameraEventListenerUI;
-import co.fitcom.fancycamera.EventType;
-import co.fitcom.fancycamera.FancyCamera;
-import co.fitcom.fancycamera.PhotoEvent;
-import co.fitcom.fancycamera.VideoEvent;
-
-public class Photo extends AppCompatActivity {
- FancyCamera cameraView;
- ImageView imageView;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_photo);
-
- imageView = findViewById(R.id.imageView);
- cameraView = findViewById(R.id.PhotoView);
- cameraView.setQuality(FancyCamera.Quality.HIGHEST.getValue());
- cameraView.setListener(new CameraEventListenerUI() {
- @Override
- public void onCameraOpenUI() {
-
- }
-
- @Override
- public void onCameraCloseUI() {
-
- }
-
- @Override
- public void onPhotoEventUI(PhotoEvent event) {
- if (event.getType() == EventType.INFO && event.getMessage().equals(PhotoEvent.EventInfo.PHOTO_TAKEN.toString())) {
- Bitmap myBitmap = BitmapFactory.decodeFile(event.getFile().getAbsolutePath());
- imageView.setImageBitmap(myBitmap);
- } else {
- System.out.println(event.getMessage());
- }
- }
-
- @Override
- public void onVideoEventUI(VideoEvent event) {
-
- }
-
- });
- cameraView.setSaveToGallery(true);
- }
-
- public void takePhoto(View view) {
- if(cameraView.hasStoragePermission()){
- cameraView.takePhoto();
- }else {
- cameraView.requestStoragePermission();
- }
- }
-
- public void toggleFlash(View view) {
- cameraView.toggleFlash();
- }
-
- public void toggleCamera(View view) {
- cameraView.toggleCamera();
- }
-
- public void goToHome(View view) {
- Intent i = new Intent(this, Home.class);
- startActivity(i);
- }
-
-
- @Override
- protected void onPause() {
- super.onPause();
- cameraView.release();
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- cameraView.start();
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
- super.onRequestPermissionsResult(requestCode, permissions, grantResults);
- int count = 0;
- for (int grant : grantResults) {
- if (permissions[count].equals(Manifest.permission.WRITE_EXTERNAL_STORAGE) && grant == PackageManager.PERMISSION_GRANTED) {
- cameraView.takePhoto();
- break;
- }
- count++;
- }
- if (cameraView.hasPermission()) {
- cameraView.start();
- }
- }
-
-}
diff --git a/app/src/main/java/co/fitcom/videorecorder/Photo.kt b/app/src/main/java/co/fitcom/videorecorder/Photo.kt
new file mode 100644
index 0000000..4210079
--- /dev/null
+++ b/app/src/main/java/co/fitcom/videorecorder/Photo.kt
@@ -0,0 +1,96 @@
+package co.fitcom.videorecorder
+
+import android.Manifest
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.net.Uri
+
+import androidx.appcompat.app.AppCompatActivity
+
+import android.os.Bundle
+import android.view.View
+import android.widget.ImageView
+
+import co.fitcom.fancycamera.CameraEventListenerUI
+import co.fitcom.fancycamera.EventType
+import co.fitcom.fancycamera.FancyCamera
+import co.fitcom.fancycamera.PhotoEvent
+import co.fitcom.fancycamera.VideoEvent
+
+class Photo : AppCompatActivity() {
+ internal lateinit var cameraView: FancyCamera
+ internal lateinit var imageView: ImageView
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_photo)
+
+ imageView = findViewById(R.id.imageView)
+ cameraView = findViewById(R.id.PhotoView)
+ cameraView.setQuality(FancyCamera.Quality.HIGHEST.value)
+ cameraView.setListener(object : CameraEventListenerUI() {
+ override fun onCameraOpenUI() {
+
+ }
+
+ override fun onCameraCloseUI() {
+
+ }
+
+ override fun onPhotoEventUI(event: PhotoEvent) {
+ if (event.type === EventType.INFO && event.message == PhotoEvent.EventInfo.PHOTO_TAKEN.toString()) {
+ imageView.setImageURI(Uri.fromFile(event.file!!))
+ } else {
+ println(event.message)
+ }
+ }
+
+ override fun onVideoEventUI(event: VideoEvent) {
+
+ }
+
+ })
+ cameraView.saveToGallery = true
+ }
+
+ fun takePhoto(view: View) {
+ if (cameraView.hasStoragePermission()) {
+ cameraView.takePhoto()
+ } else {
+ cameraView.requestStoragePermission()
+ }
+ }
+
+ fun toggleFlash(view: View) {
+ cameraView.toggleFlash()
+ }
+
+ fun toggleCamera(view: View) {
+ cameraView.toggleCamera()
+ }
+
+ fun goToHome(view: View) {
+ val i = Intent(this, Home::class.java)
+ startActivity(i)
+ }
+
+ override fun onPause() {
+ super.onPause()
+ }
+
+
+ override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
+ super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+ var count = 0
+ for (grant in grantResults) {
+ if (permissions[count] == Manifest.permission.WRITE_EXTERNAL_STORAGE && grant == PackageManager.PERMISSION_GRANTED) {
+ cameraView.takePhoto()
+ break
+ }
+ count++
+ }
+ }
+
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 76bd48e..c96d8ac 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -119,8 +119,8 @@
Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/app/src/test/java/co/fitcom/videorecorder/ExampleUnitTest.kt b/app/src/test/java/co/fitcom/videorecorder/ExampleUnitTest.kt
new file mode 100644
index 0000000..9d50438
--- /dev/null
+++ b/app/src/test/java/co/fitcom/videorecorder/ExampleUnitTest.kt
@@ -0,0 +1,18 @@
+package co.fitcom.videorecorder
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+class ExampleUnitTest {
+ @Test
+ @Throws(Exception::class)
+ fun addition_isCorrect() {
+ assertEquals(4, (2 + 2).toLong())
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 577ec19..5d2b31b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,13 +1,14 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
-
+ ext.kotlin_version = '1.3.60'
+
repositories {
google()
jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.4.2'
+ classpath 'com.android.tools.build:gradle:3.6.0-beta04'
classpath 'com.novoda:bintray-release:0.9.1'
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' // Add this line for jitpack
@@ -20,6 +21,7 @@ buildscript {
classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.0'
}
}
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
diff --git a/fancycamera/build.gradle b/fancycamera/build.gradle
index f3f7da8..964fd0b 100644
--- a/fancycamera/build.gradle
+++ b/fancycamera/build.gradle
@@ -1,13 +1,15 @@
apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-android'
apply plugin: 'com.novoda.bintray-release'
apply plugin: 'com.github.dcendents.android-maven' // for jitpack
-group='co.fitcom.fancycamera'
+group = 'co.fitcom.fancycamera'
ext {
PUBLISH_GROUP_ID = 'co.fitcom'
PUBLISH_ARTIFACT_ID = 'fancycamera'
- PUBLISH_VERSION = '1.2.2'
+ PUBLISH_VERSION = '2.0.0-alpha0'
}
android {
@@ -15,7 +17,7 @@ android {
buildToolsVersion BUILD_TOOLS_VERSION as String
defaultConfig {
- minSdkVersion 17
+ minSdkVersion 21
versionCode 1
versionName "1.0"
@@ -31,14 +33,27 @@ android {
}
buildToolsVersion '29.0.2'
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation "androidx.appcompat:appcompat:1.1.0"
+ implementation "androidx.appcompat:appcompat:1.0.0"
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ implementation "androidx.camera:camera-core:$camerax_version"
+ implementation "androidx.camera:camera-camera2:$camerax_version"
+ implementation "androidx.camera:camera-view:$camerax_view_version"
+ implementation "androidx.camera:camera-extensions:$camerax_ext_version"
+ implementation "androidx.core:core-ktx:1.1.0"
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "androidx.exifinterface:exifinterface:1.1.0"
}
@@ -46,7 +61,10 @@ publish {
userOrg = 'triniwiz'
groupId = 'co.fitcom'
artifactId = 'fancycamera'
- publishVersion = '1.2.2'
+ publishVersion = '2.0.0-alpha0'
desc = ''
website = ''
}
+repositories {
+ mavenCentral()
+}
diff --git a/fancycamera/src/androidTest/java/co/fitcom/fancycamera/ExampleInstrumentedTest.java b/fancycamera/src/androidTest/java/co/fitcom/fancycamera/ExampleInstrumentedTest.java
deleted file mode 100644
index 7b5da4f..0000000
--- a/fancycamera/src/androidTest/java/co/fitcom/fancycamera/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package co.fitcom.fancycamera;
-
-import android.content.Context;
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import static org.junit.Assert.*;
-
-/**
- * Instrumented 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("co.fitcom.fancycamera.test", appContext.getPackageName());
- }
-}
diff --git a/fancycamera/src/androidTest/java/co/fitcom/fancycamera/ExampleInstrumentedTest.kt b/fancycamera/src/androidTest/java/co/fitcom/fancycamera/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000..d9cf175
--- /dev/null
+++ b/fancycamera/src/androidTest/java/co/fitcom/fancycamera/ExampleInstrumentedTest.kt
@@ -0,0 +1,27 @@
+package co.fitcom.fancycamera
+
+import android.content.Context
+import androidx.test.InstrumentationRegistry
+import androidx.test.runner.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ @Throws(Exception::class)
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getTargetContext()
+
+ assertEquals("co.fitcom.fancycamera.test", appContext.packageName)
+ }
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/AutoFitPreviewBuilder.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/AutoFitPreviewBuilder.kt
new file mode 100644
index 0000000..1c251c5
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/AutoFitPreviewBuilder.kt
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * 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
+ *
+ * https://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 co.fitcom.fancycamera
+
+
+import android.content.Context
+import android.graphics.Matrix
+import android.hardware.display.DisplayManager
+import android.util.Log
+import android.util.Size
+import android.view.Display
+import android.view.Surface
+import android.view.TextureView
+import android.view.View
+import android.view.ViewGroup
+import androidx.camera.core.Preview
+import androidx.camera.core.PreviewConfig
+import java.lang.IllegalArgumentException
+import java.lang.ref.WeakReference
+import kotlin.math.roundToInt
+
+/**
+ * Builder for [Preview] that takes in a [WeakReference] of the view finder and [PreviewConfig],
+ * then instantiates a [Preview] which automatically resizes and rotates reacting to config changes.
+ */
+class AutoFitPreviewBuilder private constructor(
+ config: PreviewConfig, viewFinderRef: WeakReference, listener: CameraEventListener?) {
+
+ /** Public instance of preview use-case which can be used by consumers of this adapter */
+ var useCase: Preview
+
+ /** Internal variable used to keep track of the use case's output rotation */
+ private var bufferRotation: Int = 0
+
+ /** Internal variable used to keep track of the view's rotation */
+ private var viewFinderRotation: Int? = null
+
+ /** Internal variable used to keep track of the use-case's output dimension */
+ private var bufferDimens: Size = Size(0, 0)
+
+ /** Internal variable used to keep track of the view's dimension */
+ private var viewFinderDimens: Size = Size(0, 0)
+
+ /** Internal variable used to keep track of the view's display */
+ private var viewFinderDisplay: Int = -1
+
+ /** Internal reference of the [DisplayManager] */
+ private lateinit var displayManager: DisplayManager
+
+
+ /**
+ * We need a display listener for orientation changes that do not trigger a configuration
+ * change, for example if we choose to override config change in manifest or for 180-degree
+ * orientation changes.
+ */
+ private val displayListener = object : DisplayManager.DisplayListener {
+ override fun onDisplayAdded(displayId: Int) = Unit
+ override fun onDisplayRemoved(displayId: Int) = Unit
+ override fun onDisplayChanged(displayId: Int) {
+ val viewFinder = viewFinderRef.get() ?: return
+ if (displayId == viewFinderDisplay) {
+ val display = displayManager.getDisplay(displayId)
+ val rotation = getDisplaySurfaceRotation(display)
+ updateTransform(viewFinder, rotation, bufferDimens, viewFinderDimens)
+ }
+ }
+ }
+
+ init {
+ // Make sure that the view finder reference is valid
+ val viewFinder = viewFinderRef.get() ?:
+ throw IllegalArgumentException("Invalid reference to view finder used")
+
+ // Initialize the display and rotation from texture view information
+ viewFinderDisplay = viewFinder.display.displayId
+ viewFinderRotation = getDisplaySurfaceRotation(viewFinder.display) ?: 0
+
+ // Initialize public use-case with the given config
+ useCase = Preview(config)
+
+ // Every time the view finder is updated, recompute layout
+ useCase.setOnPreviewOutputUpdateListener{
+ val viewFinder =
+ viewFinderRef.get() ?: return@setOnPreviewOutputUpdateListener
+ Log.d(TAG, "Preview output changed. " +
+ "Size: ${it.textureSize}. Rotation: ${it.rotationDegrees}")
+
+ // To update the SurfaceTexture, we have to remove it and re-add it
+ val parent = viewFinder.parent as ViewGroup
+ parent.removeView(viewFinder)
+ parent.addView(viewFinder, 0)
+
+ // Update internal texture
+ viewFinder.surfaceTexture = it.surfaceTexture
+
+ // Apply relevant transformations
+ bufferRotation = it.rotationDegrees
+ val rotation = getDisplaySurfaceRotation(viewFinder.display)
+ updateTransform(viewFinder, rotation, it.textureSize, viewFinderDimens)
+ listener?.onCameraOpen()
+ }
+
+ // Every time the provided texture view changes, recompute layout
+ viewFinder.addOnLayoutChangeListener { view, left, top, right, bottom, _, _, _, _ ->
+ val viewFinder = view as TextureView
+ val newViewFinderDimens = Size(right - left, bottom - top)
+ Log.d(TAG, "View finder layout changed. Size: $newViewFinderDimens")
+ val rotation = getDisplaySurfaceRotation(viewFinder.display)
+ updateTransform(viewFinder, rotation, bufferDimens, newViewFinderDimens)
+ }
+
+ // Every time the orientation of device changes, recompute layout
+ // NOTE: This is unnecessary if we listen to display orientation changes in the camera
+ // fragment and call [Preview.setTargetRotation()] (like we do in this sample), which will
+ // trigger [Preview.OnPreviewOutputUpdateListener] with a new
+ // [PreviewOutput.rotationDegrees]. CameraX Preview use case will not rotate the frames for
+ // us, it will just tell us about the buffer rotation with respect to sensor orientation.
+ // In this sample, we ignore the buffer rotation and instead look at the view finder's
+ // rotation every time [updateTransform] is called, which gets triggered by
+ // [CameraFragment] display listener -- but the approach taken in this sample is not the
+ // only valid one.
+ displayManager = viewFinder.context
+ .getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+ displayManager.registerDisplayListener(displayListener, null)
+
+ // Remove the display listeners when the view is detached to avoid holding a reference to
+ // it outside of the Fragment that owns the view.
+ // NOTE: Even though using a weak reference should take care of this, we still try to avoid
+ // unnecessary calls to the listener this way.
+ viewFinder.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
+ override fun onViewAttachedToWindow(view: View?) =
+ displayManager.registerDisplayListener(displayListener, null)
+ override fun onViewDetachedFromWindow(view: View?) =
+ displayManager.unregisterDisplayListener(displayListener)
+ })
+ }
+
+ /** Helper function that fits a camera preview into the given [TextureView] */
+ private fun updateTransform(textureView: TextureView?, rotation: Int?, newBufferDimens: Size,
+ newViewFinderDimens: Size) {
+ // This should not happen anyway, but now the linter knows
+ textureView ?: return
+
+ if (rotation == viewFinderRotation &&
+ newBufferDimens == bufferDimens &&
+ newViewFinderDimens == viewFinderDimens) {
+ // Nothing has changed, no need to transform output again
+ return
+ }
+
+ if (rotation == null) {
+ // Invalid rotation - wait for valid inputs before setting matrix
+ return
+ } else {
+ // Update internal field with new inputs
+ viewFinderRotation = rotation
+ }
+
+ if (newBufferDimens.width == 0 || newBufferDimens.height == 0) {
+ // Invalid buffer dimens - wait for valid inputs before setting matrix
+ return
+ } else {
+ // Update internal field with new inputs
+ bufferDimens = newBufferDimens
+ }
+
+ if (newViewFinderDimens.width == 0 || newViewFinderDimens.height == 0) {
+ // Invalid view finder dimens - wait for valid inputs before setting matrix
+ return
+ } else {
+ // Update internal field with new inputs
+ viewFinderDimens = newViewFinderDimens
+ }
+
+ val matrix = Matrix()
+ Log.d(TAG, "Applying output transformation.\n" +
+ "View finder size: $viewFinderDimens.\n" +
+ "Preview output size: $bufferDimens\n" +
+ "View finder rotation: $viewFinderRotation\n" +
+ "Preview output rotation: $bufferRotation")
+
+ // Compute the center of the view finder
+ val centerX = viewFinderDimens.width / 2f
+ val centerY = viewFinderDimens.height / 2f
+
+ // Correct preview output to account for display rotation
+ matrix.postRotate(-viewFinderRotation!!.toFloat(), centerX, centerY)
+
+ // Buffers are rotated relative to the device's 'natural' orientation: swap width and height
+ val bufferRatio = bufferDimens.height / bufferDimens.width.toFloat()
+
+ val scaledWidth: Int
+ val scaledHeight: Int
+ // Match longest sides together -- i.e. apply center-crop transformation
+ if (viewFinderDimens.width > viewFinderDimens.height) {
+ scaledHeight = viewFinderDimens.width
+ scaledWidth = (viewFinderDimens.width * bufferRatio).roundToInt()
+ } else {
+ scaledHeight = viewFinderDimens.height
+ scaledWidth = (viewFinderDimens.height * bufferRatio).roundToInt()
+ }
+
+ // Compute the relative scale value
+ val xScale = scaledWidth / viewFinderDimens.width.toFloat()
+ val yScale = scaledHeight / viewFinderDimens.height.toFloat()
+
+ // Scale input buffers to fill the view finder
+ matrix.preScale(xScale, yScale, centerX, centerY)
+
+ // Finally, apply transformations to our TextureView
+ textureView.setTransform(matrix)
+
+
+ }
+
+ companion object {
+ private val TAG = AutoFitPreviewBuilder::class.java.simpleName
+
+ /** Helper function that gets the rotation of a [Display] in degrees */
+ fun getDisplaySurfaceRotation(display: Display?) = when(display?.rotation) {
+ Surface.ROTATION_0 -> 0
+ Surface.ROTATION_90 -> 90
+ Surface.ROTATION_180 -> 180
+ Surface.ROTATION_270 -> 270
+ else -> null
+ }
+
+ /**
+ * Main entrypoint for users of this class: instantiates the adapter and returns an instance
+ * of [Preview] which automatically adjusts in size and rotation to compensate for
+ * config changes.
+ */
+ fun build(config: PreviewConfig, viewFinder: TextureView, listener: CameraEventListener?) =
+ AutoFitPreviewBuilder(config, WeakReference(viewFinder), listener)
+ }
+}
\ No newline at end of file
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/Camera1.java b/fancycamera/src/main/java/co/fitcom/fancycamera/Camera1.java
deleted file mode 100644
index ff8c4b6..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/Camera1.java
+++ /dev/null
@@ -1,857 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:42 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 7:39 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.media.CamcorderProfile;
-import android.media.MediaRecorder;
-import android.net.Uri;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.view.Surface;
-import android.view.TextureView;
-
-import androidx.annotation.Nullable;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.Timer;
-import java.util.TimerTask;
-
-
-public class Camera1 extends CameraBase {
- private final Object lock = new Object();
- private Camera mCamera;
- private Context mContext;
- private FancyCamera.CameraPosition mPosition;
- private FancyCamera.CameraOrientation mOrientation;
- private Handler backgroundHandler;
- private HandlerThread backgroundHandlerThread;
- private boolean isRecording;
- private MediaRecorder mMediaRecorder;
- private boolean isStarted;
- private boolean autoStart;
- private CamcorderProfile mProfile;
- private Timer mTimer;
- private TimerTask mTimerTask;
- private int mDuration = 0;
- private boolean isFlashEnabled = false;
- private boolean mAutoFocus = true;
- private boolean disableHEVC = false;
- private int maxVideoBitrate = -1;
- private int maxAudioBitRate = -1;
- private int maxVideoFrameRate = -1;
- private boolean saveToGallery = false;
- private boolean autoSquareCrop = false;
- private boolean isAudioLevelsEnabled = false;
-
- Camera1(Context context, TextureView textureView, @Nullable FancyCamera.CameraPosition position) {
- super(textureView);
- mContext = context;
- if (position == null) {
- mPosition = FancyCamera.CameraPosition.BACK;
- } else {
- mPosition = position;
- }
- startBackgroundThread();
- setTextViewListener(new TextViewListener() {
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
-
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
-
- }
-
- @Override
- public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
-
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
-
- }
- });
- }
-
- @Override
- MediaRecorder getRecorder() {
- return mMediaRecorder;
- }
-
- @Override
- public void setEnableAudioLevels(boolean enable) {
- synchronized (lock) {
- isAudioLevelsEnabled = enable;
- }
- }
-
- @Override
- public boolean isAudioLevelsEnabled() {
- return isAudioLevelsEnabled;
- }
-
- @Override
- boolean getAutoSquareCrop() {
- return autoSquareCrop;
- }
-
- @Override
- public void setAutoSquareCrop(boolean autoSquareCrop) {
- synchronized (lock) {
- this.autoSquareCrop = autoSquareCrop;
- }
- }
-
- @Override
- public boolean getAutoFocus() {
- return mAutoFocus;
- }
-
- @Override
- public void setAutoFocus(boolean focus) {
- synchronized (lock) {
- mAutoFocus = focus;
- }
- }
-
- @Override
- public boolean getSaveToGallery() {
- return saveToGallery;
- }
-
- @Override
- public void setSaveToGallery(boolean saveToGallery) {
- synchronized (lock) {
- this.saveToGallery = saveToGallery;
- }
- }
-
- @Override
- public int getMaxAudioBitRate() {
- return maxAudioBitRate;
- }
-
- @Override
- public int getMaxVideoBitrate() {
- return maxVideoBitrate;
- }
-
- @Override
- public int getMaxVideoFrameRate() {
- return maxVideoFrameRate;
- }
-
- @Override
- public boolean getDisableHEVC() {
- return disableHEVC;
- }
-
- @Override
- public void setDisableHEVC(boolean disableHEVC) {
- synchronized (lock) {
- this.disableHEVC = disableHEVC;
- }
- }
-
- @Override
- public void setMaxAudioBitRate(int maxAudioBitRate) {
- synchronized (lock) {
- this.maxAudioBitRate = maxAudioBitRate;
- }
- }
-
- @Override
- public void setMaxVideoBitrate(int maxVideoBitrate) {
- synchronized (lock) {
- this.maxVideoBitrate = maxVideoBitrate;
- }
- }
-
- @Override
- public void setMaxVideoFrameRate(int maxVideoFrameRate) {
- synchronized (lock) {
- this.maxVideoFrameRate = maxVideoFrameRate;
- }
- }
-
- @Override
- public int getNumberOfCameras() {
- return Camera.getNumberOfCameras();
- }
-
- @Override
- boolean hasCamera() {
- return Camera.getNumberOfCameras() > 0;
- }
-
- @Override
- boolean cameraStarted() {
- return isStarted;
- }
-
- @Override
- boolean cameraRecording() {
- return isRecording;
- }
-
- private void startBackgroundThread() {
- synchronized (lock) {
- backgroundHandlerThread = new HandlerThread(CameraThread);
- backgroundHandlerThread.start();
- backgroundHandler = new Handler(backgroundHandlerThread.getLooper());
- }
- }
-
- private void stopBackgroundThread() {
- synchronized (lock) {
- backgroundHandlerThread.interrupt();
- try {
- backgroundHandlerThread.join();
- backgroundHandlerThread = null;
- backgroundHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- void openCamera(int width, int height) {
- try {
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (lock) {
- mCamera = Camera.open(mPosition.getValue());
- if (listener != null) {
- listener.onCameraOpen();
- }
- updatePreview();
- }
- }
- });
-
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- }
-
- private void setupPreview() {
- if (mCamera == null) return;
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (lock) {
- try {
- mCamera.reconnect();
- mCamera.setPreviewTexture(getHolder().getSurfaceTexture());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- });
- }
-
- @Override
- void start() {
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (lock) {
- if (getHolder().isAvailable()) {
- updatePreview();
- }
- }
- }
- });
- }
-
- @Override
- void stop() {
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (lock) {
- if (mCamera == null) return;
- mCamera.stopPreview();
- try {
- mCamera.setPreviewTexture(null);
- } catch (IOException e) {
- e.printStackTrace();
- }
- mCamera.release();
- mCamera = null;
- isStarted = false;
- if (listener != null) {
- listener.onCameraClose();
- }
- }
- }
- });
- }
-
- @Override
- void startRecording() {
- synchronized (lock) {
- if (isRecording) {
- return;
- }
- Camera.Parameters params = mCamera.getParameters();
- CamcorderProfile profile = getCamcorderProfile(FancyCamera.Quality.values()[getQuality()]);
- List mSupportedPreviewSizes = params.getSupportedPreviewSizes();
- List mSupportedVideoSizes = params.getSupportedVideoSizes();
- Camera.Size optimalSize = getOptimalVideoSize(mSupportedVideoSizes,
- mSupportedPreviewSizes, getHolder().getWidth(), getHolder().getHeight());
-
- profile.videoFrameWidth = optimalSize.width;
- profile.videoFrameHeight = optimalSize.height;
- params.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
- if (getAutoFocus()) {
- List supportedFocusModes = params.getSupportedFocusModes();
- if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
- params.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
- } else if (supportedFocusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO)) {
- params.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO);
- }
- } else {
- List supportedFocusModes = params.getSupportedFocusModes();
- if (supportedFocusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_FIXED)) {
- params.setFocusMode(android.hardware.Camera.Parameters.FOCUS_MODE_FIXED);
- }
- }
-
- setProfile(profile);
- mCamera.setParameters(params);
- if (mMediaRecorder == null) {
- mMediaRecorder = new MediaRecorder();
- } else {
- mMediaRecorder.reset();
- }
- mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
- @Override
- public void onInfo(MediaRecorder mr, int what, int extra) {
- if (listener != null) {
- switch (what) {
- case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_DURATION_REACHED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_APPROACHING.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_REACHED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.NEXT_OUTPUT_FILE_STARTED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.UNKNOWN.toString()));
- break;
- }
- }
- }
- });
-
- mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
- @Override
- public void onError(MediaRecorder mr, int what, int extra) {
- if (listener != null) {
- switch (what) {
- case MediaRecorder.MEDIA_ERROR_SERVER_DIED:
- listener.onVideoEvent(new VideoEvent(EventType.ERROR, null, VideoEvent.EventError.SERVER_DIED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN:
- listener.onVideoEvent(new VideoEvent(EventType.ERROR, null, VideoEvent.EventError.UNKNOWN.toString()));
- break;
- }
- }
- }
-
- });
-
- DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
- Date today = Calendar.getInstance().getTime();
- if (getSaveToGallery()) {
- File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
- if (!cameraDir.exists()) {
- final boolean mkdirs = cameraDir.mkdirs();
- }
- setFile(new File(cameraDir, "VID_" + df.format(today) + ".mp4"));
- } else {
- setFile(new File(mContext.getExternalFilesDir(null), "VID_" + df.format(today) + ".mp4"));
- }
- mCamera.unlock();
- try {
- CamcorderProfile camcorderProfile = getProfile();
- mMediaRecorder.setCamera(mCamera);
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- mMediaRecorder.setVideoSize(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
- mMediaRecorder.setAudioChannels(camcorderProfile.audioChannels);
- int videoBitRate = camcorderProfile.videoBitRate;
- int maxVideoBitrate = camcorderProfile.videoBitRate;
- if (this.maxVideoBitrate > -1) {
- maxVideoBitrate = this.maxVideoBitrate;
- }
- int maxVideoFrameRate = camcorderProfile.videoFrameRate;
- if (this.maxVideoFrameRate > -1) {
- maxVideoFrameRate = this.maxVideoFrameRate;
- }
- int maxAudioBitRate = camcorderProfile.audioBitRate;
- if (this.maxAudioBitRate > -1) {
- maxAudioBitRate = this.maxAudioBitRate;
- }
- mMediaRecorder.setVideoFrameRate(Math.min(camcorderProfile.videoFrameRate, maxVideoFrameRate));
- mMediaRecorder.setVideoEncodingBitRate(Math.min(camcorderProfile.videoBitRate, maxVideoBitrate));
- mMediaRecorder.setAudioEncodingBitRate(Math.min(camcorderProfile.audioBitRate, maxAudioBitRate));
- mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
- mMediaRecorder.setAudioEncoder(camcorderProfile.audioCodec);
- mMediaRecorder.setOutputFile(getFile().getPath());
- mMediaRecorder.prepare();
- mMediaRecorder.start();
- isRecording = true;
- startDurationTimer();
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- if (listener != null) {
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.RECORDING_STARTED.toString()));
- }
- }
- }
-
- @Override
- void takePhoto() {
- synchronized (lock) {
- if (isRecording) {
- return;
- }
- Camera.Parameters params = mCamera.getParameters();
- CamcorderProfile profile = getCamcorderProfile(FancyCamera.Quality.values()[getQuality()]);
- List mSupportedPreviewSizes = params.getSupportedPreviewSizes();
- List mSupportedVideoSizes = params.getSupportedVideoSizes();
- Camera.Size optimalSize = getOptimalVideoSize(mSupportedVideoSizes,
- mSupportedPreviewSizes, getHolder().getWidth(), getHolder().getHeight());
-
- int width = optimalSize.width;
- int height = optimalSize.height;
- if (getAutoSquareCrop()) {
- int offsetWidth;
- int offsetHeight;
- if (width < height) {
- offsetHeight = (height - width) / 2;
- height = width - offsetHeight;
- } else {
- offsetWidth = (width - height) / 2;
- width = height - offsetWidth;
- }
- }
-
- profile.videoFrameWidth = width;
- profile.videoFrameHeight = height;
-
-
- params.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight);
- setProfile(profile);
- mCamera.setParameters(params);
- DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
- Date today = Calendar.getInstance().getTime();
- if (getSaveToGallery()) {
- File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
- if (!cameraDir.exists()) {
- final boolean mkdirs = cameraDir.mkdirs();
- }
- setFile(new File(cameraDir, "PIC_" + df.format(today) + ".jpg"));
- } else {
- setFile(new File(mContext.getExternalFilesDir(null), "PIC_" + df.format(today) + ".jpg"));
- }
- mCamera.takePicture(null, null, new Camera.PictureCallback() {
- @Override
- public void onPictureTaken(byte[] data, Camera camera) {
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream(getFile());
- fos.write(data);
- Uri contentUri = Uri.fromFile(getFile());
- Intent mediaScanIntent = new android.content.Intent(
- "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
- contentUri
- );
- mContext.sendBroadcast(mediaScanIntent);
- if (getListener() != null) {
- PhotoEvent event = new PhotoEvent(EventType.INFO, getFile(), PhotoEvent.EventInfo.PHOTO_TAKEN.toString());
- getListener().onPhotoEvent(event);
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (fos != null) {
- try {
- fos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- });
- }
- }
-
- @Override
- void stopRecording() {
- synchronized (lock) {
- if (!isRecording) {
- return;
- }
- try {
- mMediaRecorder.stop();
- stopDurationTimer();
- mMediaRecorder.reset();
- mMediaRecorder.release();
- mMediaRecorder = null;
- Uri contentUri = Uri.fromFile(getFile());
- Intent mediaScanIntent = new android.content.Intent(
- "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
- contentUri
- );
- mContext.sendBroadcast(mediaScanIntent);
- if (listener != null) {
- listener.onVideoEvent(new VideoEvent(EventType.INFO, getFile(), VideoEvent.EventInfo.RECORDING_FINISHED.toString()));
- }
- } catch (Exception e) {
- e.printStackTrace();
- final boolean delete = getFile().delete();
- stopDurationTimer();
- } finally {
- isRecording = false;
- mCamera.lock();
- }
- }
- }
-
- @Override
- void toggleCamera() {
- synchronized (lock) {
- stop();
- if (mPosition == FancyCamera.CameraPosition.BACK) {
- setCameraPosition(FancyCamera.CameraPosition.FRONT);
- } else {
- setCameraPosition(FancyCamera.CameraPosition.BACK);
- }
- openCamera(getHolder().getWidth(), getHolder().getHeight());
- // updatePreview();
- }
- }
-
- @Override
- void updatePreview() {
- backgroundHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (lock) {
- setupPreview();
- if (mCamera != null) {
- if (isStarted) {
- mCamera.stopPreview();
- isStarted = false;
- }
- updatePreviewSize();
- updateCameraDisplayOrientation((Activity) mContext, mPosition.getValue(), mCamera);
- setupPreview();
- if (!isStarted) {
- mCamera.startPreview();
- isStarted = true;
- }
- isStarted = true;
- }
- }
- }
- });
- }
-
- @Override
- void release() {
- synchronized (lock) {
- if (isRecording) {
- stopRecording();
- }
- stop();
- }
- }
-
- @Override
- void setCameraPosition(FancyCamera.CameraPosition position) {
- synchronized (lock) {
- stop();
-
- if (Camera.getNumberOfCameras() < 2) {
- mPosition = FancyCamera.CameraPosition.BACK;
- } else {
- mPosition = position;
- }
- if (isStarted) {
- start();
- }
- }
- }
-
- @Override
- void setCameraOrientation(FancyCamera.CameraOrientation orientation) {
- synchronized (lock) {
- mOrientation = orientation;
- }
- }
-
-
- @Override
- boolean hasFlash() {
- if (mCamera != null) {
- Camera.Parameters parameters = mCamera.getParameters();
- boolean hasFlash = false;
- for (String mode : parameters.getSupportedFlashModes()) {
- if (mode.equals("on") || mode.equals("auto")) {
- hasFlash = true;
- break;
- }
- }
- return hasFlash;
- }
-
- return false;
- }
-
-
- @Override
- void toggleFlash() {
- synchronized (lock) {
- if (!hasFlash()) {
- return;
- }
- isFlashEnabled = !isFlashEnabled;
- if (mCamera != null) {
- Camera.Parameters parameters = mCamera.getParameters();
- parameters.setFlashMode(isFlashEnabled ? Camera.Parameters.FLASH_MODE_ON : Camera.Parameters.FLASH_MODE_OFF);
- mCamera.setParameters(parameters);
- }
- }
- }
-
- @Override
- void enableFlash() {
- synchronized (lock) {
- if (!hasFlash()) {
- return;
- }
- isFlashEnabled = true;
- if (mCamera != null) {
- Camera.Parameters parameters = mCamera.getParameters();
- parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
- mCamera.setParameters(parameters);
- }
- }
- }
-
- @Override
- void disableFlash() {
- synchronized (lock) {
- if (!hasFlash()) {
- return;
- }
- isFlashEnabled = false;
- if (mCamera != null) {
- Camera.Parameters parameters = mCamera.getParameters();
- parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
- mCamera.setParameters(parameters);
- }
- }
- }
-
- @Override
- boolean flashEnabled() {
- return isFlashEnabled;
- }
-
- private void setProfile(CamcorderProfile profile) {
- synchronized (lock) {
- mProfile = profile;
- }
- }
-
- private CamcorderProfile getProfile() {
- return mProfile;
- }
-
- private void updatePreviewSize() {
- synchronized (lock) {
- Camera.Parameters params = mCamera.getParameters();
- List mSupportedPreviewSizes = params.getSupportedPreviewSizes();
- List mSupportedVideoSizes = params.getSupportedVideoSizes();
- Camera.Size optimalSize = getOptimalVideoSize(mSupportedVideoSizes,
- mSupportedPreviewSizes, getHolder().getWidth(), getHolder().getHeight());
- params.setPreviewSize(optimalSize.width, optimalSize.height);
- mCamera.setParameters(params);
- }
- }
-
- private void updateCameraDisplayOrientation(Activity activity,
- int cameraId, Camera camera) {
- synchronized (lock) {
- Camera.CameraInfo info = new Camera.CameraInfo();
- Camera.getCameraInfo(cameraId, info);
- int rotation = activity.getWindowManager().getDefaultDisplay()
- .getRotation();
- int degrees = 0;
- switch (rotation) {
- case Surface.ROTATION_0:
- degrees = 0;
- break;
- case Surface.ROTATION_90:
- degrees = 90;
- break;
- case Surface.ROTATION_180:
- degrees = 180;
- break;
- case Surface.ROTATION_270:
- degrees = 270;
- break;
- }
-
- int result;
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- result = (info.orientation + degrees) % 360;
- result = (360 - result) % 360;
- } else {
- result = (info.orientation - degrees + 360) % 360;
- }
- camera.setDisplayOrientation(result);
- }
- }
-
- private CamcorderProfile getCamcorderProfile(FancyCamera.Quality quality) {
- CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
- switch (quality) {
- case MAX_480P:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.QVGA);
- }
- break;
- case MAX_720P:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.MAX_480P);
- }
- break;
- case MAX_1080P:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.MAX_720P);
- }
-
- break;
- case MAX_2160P:
- try {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_2160P);
- } catch (Exception e) {
- profile = getCamcorderProfile(FancyCamera.Quality.HIGHEST);
- }
- break;
- case HIGHEST:
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
- break;
- case LOWEST:
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
- break;
- case QVGA:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_QVGA);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.LOWEST);
- }
- break;
-
- }
- return profile;
- }
-
- private Camera.Size getOptimalVideoSize(List supportedVideoSizes,
- List previewSizes, int w, int h) {
- // Use a very small tolerance because we want an exact match.
- final double ASPECT_TOLERANCE = 0.1;
- double targetRatio = (double) w / h;
-
- // Supported video sizes list might be null, it means that we are allowed to use the preview
- // sizes
- List videoSizes;
- if (supportedVideoSizes != null) {
- videoSizes = supportedVideoSizes;
- } else {
- videoSizes = previewSizes;
- }
- Camera.Size optimalSize = null;
-
- // Start with max value and refine as we iterate over available video sizes. This is the
- // minimum difference between view and camera height.
- double minDiff = Double.MAX_VALUE;
-
- // Target view height
- int targetHeight = h;
-
- // Try to find a video size that matches aspect ratio and the target view size.
- // Iterate over all available sizes and pick the largest size that can fit in the view and
- // still maintain the aspect ratio.
- for (Camera.Size size : videoSizes) {
- double ratio = (double) size.width / size.height;
- if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
- continue;
- if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
- optimalSize = size;
- minDiff = Math.abs(size.height - targetHeight);
- }
- }
-
- // Cannot find video size that matches the aspect ratio, ignore the requirement
- if (optimalSize == null) {
- minDiff = Double.MAX_VALUE;
- for (Camera.Size size : videoSizes) {
- if (Math.abs(size.height - targetHeight) < minDiff && previewSizes.contains(size)) {
- optimalSize = size;
- minDiff = Math.abs(size.height - targetHeight);
- }
- }
- }
- return optimalSize;
- }
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/Camera1.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/Camera1.kt
new file mode 100644
index 0000000..68b0831
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/Camera1.kt
@@ -0,0 +1,698 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:42 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 7:39 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.graphics.SurfaceTexture
+import android.hardware.Camera
+import android.media.CamcorderProfile
+import android.media.MediaRecorder
+import android.net.Uri
+import android.os.Environment
+import android.os.Handler
+import android.os.HandlerThread
+import android.view.Surface
+import android.view.TextureView
+
+import java.io.File
+import java.io.FileNotFoundException
+import java.io.FileOutputStream
+import java.io.IOException
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.Calendar
+import java.util.Date
+import java.util.Locale
+import java.util.Timer
+import java.util.TimerTask
+
+
+class Camera1 internal constructor(private val mContext: Context, textureView: TextureView, position: FancyCamera.CameraPosition?) : CameraBase(textureView) {
+ override var didPauseForPermission: Boolean = false
+ override var quality: Int = 0
+ private val lock = Any()
+ private var mCamera: Camera? = null
+ private var mPosition: FancyCamera.CameraPosition? = null
+ private var mOrientation: FancyCamera.CameraOrientation? = null
+ private var backgroundHandler: Handler? = null
+ private var backgroundHandlerThread: HandlerThread? = null
+ private var isRecording: Boolean = false
+ internal override var recorder: MediaRecorder? = null
+ private set
+ private var isStarted: Boolean = false
+ private val autoStart: Boolean = false
+ private var profile: CamcorderProfile? = null
+ set(profile) {
+ synchronized(lock) {
+ field = profile
+ }
+ }
+ private val mTimer: Timer? = null
+ private val mTimerTask: TimerTask? = null
+ private val mDuration = 0
+ private var isFlashEnabled = false
+ override var autoFocus = true
+ set(focus) {
+ synchronized(lock) {
+ field = focus
+ }
+ }
+ override var disableHEVC = false
+ set(disableHEVC) {
+ synchronized(lock) {
+ field = disableHEVC
+ }
+ }
+ override var maxVideoBitrate = -1
+ set(maxVideoBitrate) {
+ synchronized(lock) {
+ field = maxVideoBitrate
+ }
+ }
+ override var maxAudioBitRate = -1
+ set(maxAudioBitRate) {
+ synchronized(lock) {
+ field = maxAudioBitRate
+ }
+ }
+ override var maxVideoFrameRate = -1
+ set(maxVideoFrameRate) {
+ synchronized(lock) {
+ field = maxVideoFrameRate
+ }
+ }
+ override var saveToGallery = false
+ set(saveToGallery) {
+ synchronized(lock) {
+ field = saveToGallery
+ }
+ }
+ internal override var autoSquareCrop = false
+ set(autoSquareCrop) {
+ synchronized(lock) {
+ field = autoSquareCrop
+ }
+ }
+ override var isAudioLevelsEnabled = false
+ private set
+
+ override val numberOfCameras: Int
+ get() = Camera.getNumberOfCameras()
+
+ init {
+ if (position == null) {
+ mPosition = FancyCamera.CameraPosition.BACK
+ } else {
+ mPosition = position
+ }
+ startBackgroundThread()
+ textViewListener = object : TextViewListener {
+ override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
+
+ }
+
+ override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
+
+ }
+
+ override fun onSurfaceTextureDestroyed(surface: SurfaceTexture) {
+
+ }
+
+ override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
+
+ }
+ }
+ }
+
+ override fun setEnableAudioLevels(enable: Boolean) {
+ synchronized(lock) {
+ isAudioLevelsEnabled = enable
+ }
+ }
+
+ internal override fun hasCamera(): Boolean {
+ return Camera.getNumberOfCameras() > 0
+ }
+
+ internal override fun cameraStarted(): Boolean {
+ return isStarted
+ }
+
+ internal override fun cameraRecording(): Boolean {
+ return isRecording
+ }
+
+ private fun startBackgroundThread() {
+ synchronized(lock) {
+ backgroundHandlerThread = HandlerThread(CameraBase.CameraThread)
+ backgroundHandlerThread!!.start()
+ backgroundHandler = Handler(backgroundHandlerThread!!.looper)
+ }
+ }
+
+ private fun stopBackgroundThread() {
+ synchronized(lock) {
+ backgroundHandlerThread!!.interrupt()
+ try {
+ backgroundHandlerThread!!.join()
+ backgroundHandlerThread = null
+ backgroundHandler = null
+ } catch (e: InterruptedException) {
+ e.printStackTrace()
+ }
+
+ }
+ }
+
+ internal override fun openCamera(width: Int, height: Int) {
+ try {
+ backgroundHandler!!.post {
+ synchronized(lock) {
+ mCamera = Camera.open(mPosition!!.value)
+ listener?.onCameraOpen()
+ updatePreview()
+ }
+ }
+
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ }
+
+ private fun setupPreview() {
+ if (mCamera == null) return
+ backgroundHandler!!.post {
+ synchronized(lock) {
+ try {
+ mCamera!!.reconnect()
+ mCamera!!.setPreviewTexture(holder.surfaceTexture)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ }
+ }
+ }
+
+ internal override fun start() {
+ backgroundHandler!!.post {
+ synchronized(lock) {
+ if (holder.isAvailable) {
+ updatePreview()
+ }
+ }
+ }
+ }
+
+ internal override fun stop() {
+ backgroundHandler!!.post(Runnable {
+ synchronized(lock) {
+ if (mCamera == null) return@Runnable
+ mCamera!!.stopPreview()
+ try {
+ mCamera!!.setPreviewTexture(null)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ mCamera!!.release()
+ mCamera = null
+ isStarted = false
+ listener?.onCameraClose()
+ }
+ })
+ }
+
+ internal override fun startRecording() {
+ synchronized(lock) {
+ if (isRecording) {
+ return
+ }
+ val params = mCamera!!.parameters
+ var profile = getCamcorderProfile(FancyCamera.Quality.values()[quality])
+ val mSupportedPreviewSizes = params.supportedPreviewSizes
+ val mSupportedVideoSizes = params.supportedVideoSizes
+ val optimalSize = getOptimalVideoSize(mSupportedVideoSizes,
+ mSupportedPreviewSizes, holder.width, holder.height)
+
+ profile.videoFrameWidth = optimalSize!!.width
+ profile.videoFrameHeight = optimalSize.height
+ params.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight)
+ if (autoFocus) {
+ val supportedFocusModes = params.supportedFocusModes
+ if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
+ params.focusMode = android.hardware.Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE
+ } else if (supportedFocusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_AUTO)) {
+ params.focusMode = android.hardware.Camera.Parameters.FOCUS_MODE_AUTO
+ }
+ } else {
+ val supportedFocusModes = params.supportedFocusModes
+ if (supportedFocusModes.contains(android.hardware.Camera.Parameters.FOCUS_MODE_FIXED)) {
+ params.focusMode = android.hardware.Camera.Parameters.FOCUS_MODE_FIXED
+ }
+ }
+
+ profile = profile
+ mCamera!!.parameters = params
+ if (recorder == null) {
+ recorder = MediaRecorder()
+ } else {
+ recorder!!.reset()
+ }
+ recorder!!.setOnInfoListener { mr, what, extra ->
+ if (listener != null) {
+ when (what) {
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_DURATION_REACHED.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_APPROACHING.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_REACHED.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.NEXT_OUTPUT_FILE_STARTED.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.UNKNOWN.toString()))
+ }
+ }
+ }
+
+ recorder!!.setOnErrorListener { mr, what, extra ->
+ if (listener != null) {
+ when (what) {
+ MediaRecorder.MEDIA_ERROR_SERVER_DIED -> listener?.onVideoEvent(VideoEvent(EventType.ERROR, null, VideoEvent.EventError.SERVER_DIED.toString()))
+ MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN -> listener?.onVideoEvent(VideoEvent(EventType.ERROR, null, VideoEvent.EventError.UNKNOWN.toString()))
+ }
+ }
+ }
+
+ val df = SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
+ val today = Calendar.getInstance().time
+ if (saveToGallery) {
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera")
+ if (!cameraDir.exists()) {
+ val mkdirs = cameraDir.mkdirs()
+ }
+ file = File(cameraDir, "VID_" + df.format(today) + ".mp4")
+ } else {
+ file = File(mContext.getExternalFilesDir(null), "VID_" + df.format(today) + ".mp4")
+ }
+ mCamera!!.unlock()
+ try {
+ val camcorderProfile = profile
+ recorder!!.setCamera(mCamera)
+ recorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
+ recorder!!.setVideoSource(MediaRecorder.VideoSource.CAMERA)
+ recorder!!.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
+ recorder!!.setVideoSize(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight)
+ recorder!!.setAudioChannels(camcorderProfile.audioChannels)
+ val videoBitRate = camcorderProfile.videoBitRate
+ var maxVideoBitrate = camcorderProfile.videoBitRate
+ if (this.maxVideoBitrate > -1) {
+ maxVideoBitrate = this.maxVideoBitrate
+ }
+ var maxVideoFrameRate = camcorderProfile.videoFrameRate
+ if (this.maxVideoFrameRate > -1) {
+ maxVideoFrameRate = this.maxVideoFrameRate
+ }
+ var maxAudioBitRate = camcorderProfile.audioBitRate
+ if (this.maxAudioBitRate > -1) {
+ maxAudioBitRate = this.maxAudioBitRate
+ }
+ recorder!!.setVideoFrameRate(Math.min(camcorderProfile.videoFrameRate, maxVideoFrameRate))
+ recorder!!.setVideoEncodingBitRate(Math.min(camcorderProfile.videoBitRate, maxVideoBitrate))
+ recorder!!.setAudioEncodingBitRate(Math.min(camcorderProfile.audioBitRate, maxAudioBitRate))
+ recorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
+ recorder!!.setAudioEncoder(camcorderProfile.audioCodec)
+ recorder!!.setOutputFile(file!!.path)
+ recorder!!.prepare()
+ recorder!!.start()
+ isRecording = true
+ startDurationTimer()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ if (listener != null) {
+ listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.RECORDING_STARTED.toString()))
+ }
+ }
+ }
+
+ internal override fun takePhoto() {
+ synchronized(lock) {
+ if (isRecording) {
+ return
+ }
+ val params = mCamera!!.parameters
+ var profile = getCamcorderProfile(FancyCamera.Quality.values()[quality])
+ val mSupportedPreviewSizes = params.supportedPreviewSizes
+ val mSupportedVideoSizes = params.supportedVideoSizes
+ val optimalSize = getOptimalVideoSize(mSupportedVideoSizes,
+ mSupportedPreviewSizes, holder.width, holder.height)
+
+ var width = optimalSize!!.width
+ var height = optimalSize.height
+ if (autoSquareCrop) {
+ val offsetWidth: Int
+ val offsetHeight: Int
+ if (width < height) {
+ offsetHeight = (height - width) / 2
+ height = width - offsetHeight
+ } else {
+ offsetWidth = (width - height) / 2
+ width = height - offsetWidth
+ }
+ }
+
+ profile.videoFrameWidth = width
+ profile.videoFrameHeight = height
+
+
+ params.setPreviewSize(profile.videoFrameWidth, profile.videoFrameHeight)
+ profile = profile
+ mCamera!!.parameters = params
+ val df = SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
+ val today = Calendar.getInstance().time
+ if (saveToGallery) {
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera")
+ if (!cameraDir.exists()) {
+ val mkdirs = cameraDir.mkdirs()
+ }
+ file = File(cameraDir, "PIC_" + df.format(today) + ".jpg")
+ } else {
+ file = File(mContext.getExternalFilesDir(null), "PIC_" + df.format(today) + ".jpg")
+ }
+ mCamera!!.takePicture(null, null, Camera.PictureCallback { data, camera ->
+ var fos: FileOutputStream? = null
+ try {
+ fos = FileOutputStream(file!!)
+ fos.write(data)
+ val contentUri = Uri.fromFile(file)
+ val mediaScanIntent = android.content.Intent(
+ "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
+ contentUri
+ )
+ mContext.sendBroadcast(mediaScanIntent)
+ val event = PhotoEvent(EventType.INFO, file, PhotoEvent.EventInfo.PHOTO_TAKEN.toString())
+ listener?.onPhotoEvent(event)
+ } catch (e: FileNotFoundException) {
+ e.printStackTrace()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close()
+ } catch (e: IOException) {
+ e.printStackTrace()
+ }
+
+ }
+ }
+ })
+ }
+ }
+
+ internal override fun stopRecording() {
+ synchronized(lock) {
+ if (!isRecording) {
+ return
+ }
+ try {
+ recorder!!.stop()
+ stopDurationTimer()
+ recorder!!.reset()
+ recorder!!.release()
+ recorder = null
+ val contentUri = Uri.fromFile(file)
+ val mediaScanIntent = android.content.Intent(
+ "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
+ contentUri
+ )
+ mContext.sendBroadcast(mediaScanIntent)
+ listener?.onVideoEvent(VideoEvent(EventType.INFO, file, VideoEvent.EventInfo.RECORDING_FINISHED.toString()))
+ } catch (e: Exception) {
+ e.printStackTrace()
+ val delete = file!!.delete()
+ stopDurationTimer()
+ } finally {
+ isRecording = false
+ mCamera!!.lock()
+ }
+ }
+ }
+
+ internal override fun toggleCamera() {
+ synchronized(lock) {
+ stop()
+ if (mPosition == FancyCamera.CameraPosition.BACK) {
+ setCameraPosition(FancyCamera.CameraPosition.FRONT)
+ } else {
+ setCameraPosition(FancyCamera.CameraPosition.BACK)
+ }
+ openCamera(holder.width, holder.height)
+ // updatePreview();
+ }
+ }
+
+ internal override fun updatePreview() {
+ backgroundHandler!!.post {
+ synchronized(lock) {
+ setupPreview()
+ if (mCamera != null) {
+ if (isStarted) {
+ mCamera!!.stopPreview()
+ isStarted = false
+ }
+ updatePreviewSize()
+ updateCameraDisplayOrientation(mContext as Activity, mPosition!!.value, mCamera)
+ setupPreview()
+ if (!isStarted) {
+ mCamera!!.startPreview()
+ isStarted = true
+ }
+ isStarted = true
+ }
+ }
+ }
+ }
+
+ internal override fun release() {
+ synchronized(lock) {
+ if (isRecording) {
+ stopRecording()
+ }
+ stop()
+ }
+ }
+
+ internal override fun setCameraPosition(position: FancyCamera.CameraPosition) {
+ synchronized(lock) {
+ stop()
+
+ if (Camera.getNumberOfCameras() < 2) {
+ mPosition = FancyCamera.CameraPosition.BACK
+ } else {
+ mPosition = position
+ }
+ if (isStarted) {
+ start()
+ }
+ }
+ }
+
+ internal override fun setCameraOrientation(orientation: FancyCamera.CameraOrientation) {
+ synchronized(lock) {
+ mOrientation = orientation
+ }
+ }
+
+
+ internal override fun hasFlash(): Boolean {
+ if (mCamera != null) {
+ val parameters = mCamera!!.parameters
+ var hasFlash = false
+ for (mode in parameters.supportedFlashModes) {
+ if (mode == "on" || mode == "auto") {
+ hasFlash = true
+ break
+ }
+ }
+ return hasFlash
+ }
+
+ return false
+ }
+
+
+ internal override fun toggleFlash() {
+ synchronized(lock) {
+ if (!hasFlash()) {
+ return
+ }
+ isFlashEnabled = !isFlashEnabled
+ if (mCamera != null) {
+ val parameters = mCamera!!.parameters
+ parameters.flashMode = if (isFlashEnabled) Camera.Parameters.FLASH_MODE_ON else Camera.Parameters.FLASH_MODE_OFF
+ mCamera!!.parameters = parameters
+ }
+ }
+ }
+
+ internal override fun enableFlash() {
+ synchronized(lock) {
+ if (!hasFlash()) {
+ return
+ }
+ isFlashEnabled = true
+ if (mCamera != null) {
+ val parameters = mCamera!!.parameters
+ parameters.flashMode = Camera.Parameters.FLASH_MODE_ON
+ mCamera!!.parameters = parameters
+ }
+ }
+ }
+
+ internal override fun disableFlash() {
+ synchronized(lock) {
+ if (!hasFlash()) {
+ return
+ }
+ isFlashEnabled = false
+ if (mCamera != null) {
+ val parameters = mCamera!!.parameters
+ parameters.flashMode = Camera.Parameters.FLASH_MODE_OFF
+ mCamera!!.parameters = parameters
+ }
+ }
+ }
+
+ internal override fun flashEnabled(): Boolean {
+ return isFlashEnabled
+ }
+
+ private fun updatePreviewSize() {
+ synchronized(lock) {
+ val params = mCamera!!.parameters
+ val mSupportedPreviewSizes = params.supportedPreviewSizes
+ val mSupportedVideoSizes = params.supportedVideoSizes
+ val optimalSize = getOptimalVideoSize(mSupportedVideoSizes,
+ mSupportedPreviewSizes, holder.width, holder.height)
+ params.setPreviewSize(optimalSize!!.width, optimalSize.height)
+ mCamera!!.parameters = params
+ }
+ }
+
+ private fun updateCameraDisplayOrientation(activity: Activity,
+ cameraId: Int, camera: Camera?) {
+ synchronized(lock) {
+ val info = Camera.CameraInfo()
+ Camera.getCameraInfo(cameraId, info)
+ val rotation = activity.windowManager.defaultDisplay
+ .rotation
+ var degrees = 0
+ when (rotation) {
+ Surface.ROTATION_0 -> degrees = 0
+ Surface.ROTATION_90 -> degrees = 90
+ Surface.ROTATION_180 -> degrees = 180
+ Surface.ROTATION_270 -> degrees = 270
+ }
+
+ var result: Int
+ if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
+ result = (info.orientation + degrees) % 360
+ result = (360 - result) % 360
+ } else {
+ result = (info.orientation - degrees + 360) % 360
+ }
+ camera!!.setDisplayOrientation(result)
+ }
+ }
+
+ private fun getCamcorderProfile(quality: FancyCamera.Quality): CamcorderProfile {
+ var profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW)
+ when (quality) {
+ FancyCamera.Quality.MAX_480P -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.QVGA)
+ }
+ FancyCamera.Quality.MAX_720P -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.MAX_480P)
+ }
+ FancyCamera.Quality.MAX_1080P -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.MAX_720P)
+ }
+ FancyCamera.Quality.MAX_2160P -> try {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_2160P)
+ } catch (e: Exception) {
+ profile = getCamcorderProfile(FancyCamera.Quality.HIGHEST)
+ }
+
+ FancyCamera.Quality.HIGHEST -> profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)
+ FancyCamera.Quality.LOWEST -> profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW)
+ FancyCamera.Quality.QVGA -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_QVGA)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.LOWEST)
+ }
+ }
+ return profile
+ }
+
+ private fun getOptimalVideoSize(supportedVideoSizes: List?,
+ previewSizes: List, w: Int, h: Int): Camera.Size? {
+ // Use a very small tolerance because we want an exact match.
+ val ASPECT_TOLERANCE = 0.1
+ val targetRatio = w.toDouble() / h
+
+ // Supported video sizes list might be null, it means that we are allowed to use the preview
+ // sizes
+ val videoSizes: List
+ if (supportedVideoSizes != null) {
+ videoSizes = supportedVideoSizes
+ } else {
+ videoSizes = previewSizes
+ }
+ var optimalSize: Camera.Size? = null
+
+ // Start with max value and refine as we iterate over available video sizes. This is the
+ // minimum difference between view and camera height.
+ var minDiff = java.lang.Double.MAX_VALUE
+
+ // Target view height
+
+ // Try to find a video size that matches aspect ratio and the target view size.
+ // Iterate over all available sizes and pick the largest size that can fit in the view and
+ // still maintain the aspect ratio.
+ for (size in videoSizes) {
+ val ratio = size.width.toDouble() / size.height
+ if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
+ continue
+ if (Math.abs(size.height - h) < minDiff && previewSizes.contains(size)) {
+ optimalSize = size
+ minDiff = Math.abs(size.height - h).toDouble()
+ }
+ }
+
+ // Cannot find video size that matches the aspect ratio, ignore the requirement
+ if (optimalSize == null) {
+ minDiff = java.lang.Double.MAX_VALUE
+ for (size in videoSizes) {
+ if (Math.abs(size.height - h) < minDiff && previewSizes.contains(size)) {
+ optimalSize = size
+ minDiff = Math.abs(size.height - h).toDouble()
+ }
+ }
+ }
+ return optimalSize
+ }
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/Camera2.java b/fancycamera/src/main/java/co/fitcom/fancycamera/Camera2.java
deleted file mode 100644
index 3843dbb..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/Camera2.java
+++ /dev/null
@@ -1,1372 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:42 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 8:42 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import android.Manifest;
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.res.Configuration;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.ImageFormat;
-import android.graphics.Matrix;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.graphics.RectF;
-import android.graphics.SurfaceTexture;
-import android.graphics.YuvImage;
-import android.hardware.camera2.CameraAccessException;
-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.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.TotalCaptureResult;
-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.Build;
-import android.os.Environment;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.util.Log;
-import android.util.Size;
-import android.util.SparseIntArray;
-import android.view.Display;
-import android.view.Surface;
-import android.view.TextureView;
-import android.view.WindowManager;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.core.app.ActivityCompat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.lang.reflect.Field;
-import java.nio.ByteBuffer;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-
-@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
-class Camera2 extends CameraBase {
- private static final Object lock = new Object();
- private static final String TAG = "Camera2.Fancy";
- private CameraManager mManager;
- MediaRecorder mMediaRecorder;
- private FancyCamera.CameraPosition mPosition;
- private FancyCamera.CameraOrientation mOrientation;
- private Context mContext;
- private Handler backgroundHandler;
- private HandlerThread backgroundHandlerThread;
- private CameraCharacteristics characteristics;
- private Size previewSize;
- private Size videoSize;
- private CaptureRequest.Builder mPreviewBuilder;
- private CameraDevice mCameraDevice;
- private CameraCaptureSession mPreviewSession;
- private boolean isRecording = false;
- private boolean isStarted = false;
- private boolean isFlashEnabled = false;
- private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
- private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
- private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
- private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
- private Integer mSensorOrientation;
- private String cameraIdToOpen = "0";
- private boolean mAutoFocus = true;
- private boolean disableHEVC = false;
- private int maxVideoBitrate = -1;
- private int maxAudioBitRate = -1;
- private int maxVideoFrameRate = -1;
- private boolean saveToGallery = false;
- private boolean autoSquareCrop = false;
- private boolean isAudioLevelsEnabled = false;
- private ImageReader reader;
-
- private ImageReader.OnImageAvailableListener readOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
- @Override
- public void onImageAvailable(ImageReader imageReader) {
- Image image = imageReader.acquireLatestImage();
- try {
- Bitmap bitmap = imageToBitmap(image);
- save(bitmap);
- } catch (Throwable t) {
- // IOException and java.nio.BufferOverflowException are known possibilities,
- // but likely OutOfMemoryError too
- // Buffers and Bitmaps are crash prone
- t.printStackTrace();
- } finally {
- reader = null;
- Uri contentUri = Uri.fromFile(getFile());
- Intent mediaScanIntent = new android.content.Intent(
- "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
- contentUri
- );
- mContext.sendBroadcast(mediaScanIntent);
- if (getListener() != null) {
- PhotoEvent event = new PhotoEvent(EventType.INFO, getFile(), PhotoEvent.EventInfo.PHOTO_TAKEN.toString());
- getListener().onPhotoEvent(event);
- } else {
- Log.w(TAG, "No listener found");
- }
- }
- }
-
- private Bitmap imageToBitmap(Image image) {
- // NV21 is a plane of 8 bit Y values followed by interleaved Cb Cr
- ByteBuffer ib = ByteBuffer.allocate(image.getHeight() * image.getWidth() * 2);
-
- ByteBuffer y = image.getPlanes()[0].getBuffer();
- ByteBuffer cr = image.getPlanes()[1].getBuffer();
- ByteBuffer cb = image.getPlanes()[2].getBuffer();
- ib.put(y);
- ib.put(cb);
- ib.put(cr);
-
- YuvImage yuvImage = new YuvImage(ib.array(),
- ImageFormat.NV21, image.getWidth(), image.getHeight(), null);
-
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- yuvImage.compressToJpeg(new Rect(0, 0,
- image.getWidth(), image.getHeight()), 50, out);
- byte[] imageBytes = out.toByteArray();
- return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.length);
- }
-
- private void save(Bitmap bm) throws IOException {
- int originalWidth = bm.getWidth();
- int originalHeight = bm.getHeight();
- int offsetWidth = 0;
- int offsetHeight = 0;
- if (getAutoSquareCrop()) {
- if (originalWidth < originalHeight) {
- offsetHeight = (originalHeight - originalWidth) / 2;
- originalHeight = originalWidth;
- } else {
- offsetWidth = (originalWidth - originalHeight) / 2;
- originalWidth = originalHeight;
- }
- }
-
- // this flips the front camera image to not be 'mirrored effect' for selfies
- // does not flip if using the back camera
- Matrix matrix = new Matrix();
- if (mPosition == FancyCamera.CameraPosition.FRONT) {
- float[] mirrorY = {-1, 0, 0, 0, 1, 0, 0, 0, 1};
- Matrix matrixMirrorY = new Matrix();
- matrixMirrorY.setValues(mirrorY);
- matrix.postConcat(matrixMirrorY);
- matrix.postRotate(90);
- } else {
- matrix.postRotate(mSensorOrientation);
- }
-
- Bitmap rotated = Bitmap.createBitmap(bm, offsetWidth, offsetHeight, originalWidth, originalHeight, matrix, false);
-
- File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
- if (!cameraDir.exists()) {
- final boolean mkdirs = cameraDir.mkdirs();
- }
- try (OutputStream outputStream = new FileOutputStream(getFile())) {
- rotated.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
- }
- bm.recycle();
- rotated.recycle();
- }
- };
-
- Camera2(Context context, TextureView textureView, @Nullable FancyCamera.CameraPosition position, @Nullable FancyCamera.CameraOrientation orientation) {
- super(textureView);
-
- if (position == null) {
- mPosition = FancyCamera.CameraPosition.BACK;
- } else {
- mPosition = position;
- }
-
- if (orientation == null) {
- mOrientation = FancyCamera.CameraOrientation.UNKNOWN;
- } else {
- mOrientation = orientation;
- }
-
- mContext = context;
- startBackgroundThread();
-
- setTextViewListener(new TextViewListener() {
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
-
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if (getHolder() != null) {
- Handler mainHandler = new Handler(mContext.getMainLooper());
- mainHandler.post(new Runnable() {
- @Override
- public void run() {
- configureTransform(getHolder().getWidth(), getHolder().getHeight());
- }
- });
- }
- }
-
- @Override
- public void onSurfaceTextureDestroyed(SurfaceTexture surface) {
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- }
- });
-
- }
-
- @Override
- public void setEnableAudioLevels(boolean enable) {
- synchronized (lock) {
- isAudioLevelsEnabled = enable;
- }
- }
-
- @Override
- public boolean isAudioLevelsEnabled() {
- return isAudioLevelsEnabled;
- }
-
- @Override
- boolean getAutoSquareCrop() {
- return autoSquareCrop;
- }
-
- @Override
- public void setAutoSquareCrop(boolean autoSquareCrop) {
- synchronized (lock) {
- this.autoSquareCrop = autoSquareCrop;
- }
- }
-
- @Override
- public boolean getAutoFocus() {
- return mAutoFocus;
- }
-
- @Override
- public void setAutoFocus(boolean focus) {
- synchronized (lock) {
- mAutoFocus = focus;
- }
- }
-
- @Override
- public boolean getSaveToGallery() {
- return saveToGallery;
- }
-
- @Override
- public void setSaveToGallery(boolean saveToGallery) {
- synchronized (lock) {
- this.saveToGallery = saveToGallery;
- }
- }
-
- @Override
- public int getMaxAudioBitRate() {
- return maxAudioBitRate;
- }
-
- @Override
- public int getMaxVideoBitrate() {
- return maxVideoBitrate;
- }
-
- @Override
- public int getMaxVideoFrameRate() {
- return maxVideoFrameRate;
- }
-
- @Override
- public boolean getDisableHEVC() {
- return disableHEVC;
- }
-
- @Override
- public void setDisableHEVC(boolean disableHEVC) {
- synchronized (lock) {
- this.disableHEVC = disableHEVC;
- }
- }
-
- @Override
- public void setMaxAudioBitRate(int maxAudioBitRate) {
- synchronized (lock) {
- this.maxAudioBitRate = maxAudioBitRate;
- }
- }
-
- @Override
- public void setMaxVideoBitrate(int maxVideoBitrate) {
- synchronized (lock) {
- this.maxVideoBitrate = maxVideoBitrate;
- }
- }
-
- @Override
- public void setMaxVideoFrameRate(int maxVideoFrameRate) {
- synchronized (lock) {
- this.maxVideoFrameRate = maxVideoFrameRate;
- }
- }
-
- @Override
- public int getNumberOfCameras() {
- if (mManager != null) {
- try {
- return mManager.getCameraIdList().length;
- } catch (CameraAccessException e) {
- e.printStackTrace();
- return 0;
- }
- }
- return 0;
- }
-
- private void startBackgroundThread() {
- synchronized (lock) {
- backgroundHandlerThread = new HandlerThread(CameraThread);
- backgroundHandlerThread.start();
- backgroundHandler = new Handler(backgroundHandlerThread.getLooper());
- }
- }
-
-
- private void stopBackgroundThread() {
- synchronized (lock) {
- backgroundHandlerThread.interrupt();
- try {
- backgroundHandlerThread.join();
- backgroundHandlerThread = null;
- backgroundHandler = null;
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-
-
- @Override
- boolean hasCamera() {
- if (mManager == null) return false;
- try {
- return mManager.getCameraIdList().length > 0;
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
- return false;
- }
-
- @Override
- boolean cameraStarted() {
- return isStarted;
- }
-
- @Override
- boolean cameraRecording() {
- return isRecording;
- }
-
-
- static {
- DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
- DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
- DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
- DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
- }
-
- static {
- INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
- INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
- INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
- INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
- }
-
-
- @Override
- void openCamera(int width, int height) {
- synchronized (lock) {
- if (mManager == null) {
- mManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
- }
- if (mManager == null) {
- // emit error
- return;
- }
- try {
- String[] cameraList = mManager.getCameraIdList();
- int cameraOrientation;
- StreamConfigurationMap map = null;
- for (String cameraId : cameraList) {
- if (mPosition == FancyCamera.CameraPosition.FRONT) {
- characteristics = mManager.getCameraCharacteristics(cameraId);
- cameraOrientation = characteristics.get(CameraCharacteristics.LENS_FACING);
- if (cameraOrientation == CameraCharacteristics.LENS_FACING_FRONT) {
- cameraIdToOpen = cameraId;
- map = characteristics
- .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- break;
- }
-
- } else {
- characteristics = mManager.getCameraCharacteristics(cameraId);
- cameraOrientation = characteristics.get(CameraCharacteristics.LENS_FACING);
- if (cameraOrientation == CameraCharacteristics.LENS_FACING_BACK) {
- cameraIdToOpen = cameraId;
- map = characteristics
- .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
- break;
- }
- }
- }
-
- if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
-
- if (map == null) {
- throw new RuntimeException("Cannot get available preview/video sizes");
- }
-
-
- previewSize = getPreviewSize(map.getOutputSizes(SurfaceTexture.class));
- videoSize = getPreviewSize(map.getOutputSizes(MediaRecorder.class));
-
- mMediaRecorder = new MediaRecorder();
- mManager.openCamera(cameraIdToOpen, new CameraDevice.StateCallback() {
- @Override
- public void onOpened(@NonNull CameraDevice camera) {
- synchronized (lock) {
- mCameraDevice = camera;
- isStarted = true;
- if (getHolder() != null) {
- Handler mainHandler = new Handler(mContext.getMainLooper());
- mainHandler.post(new Runnable() {
- @Override
- public void run() {
- configureTransform(getHolder().getWidth(), getHolder().getHeight());
- }
- });
- }
- startPreview();
- if (listener != null) {
- listener.onCameraOpen();
- }
- }
-
- }
-
- @Override
- public void onDisconnected(@NonNull CameraDevice camera) {
- synchronized (lock) {
- camera.close();
- isStarted = false;
- mCameraDevice = null;
- if (listener != null) {
- listener.onCameraClose();
- }
- }
- }
-
- @Override
- public void onError(@NonNull CameraDevice camera, int error) {
- synchronized (lock) {
- camera.close();
- mCameraDevice = null;
- isStarted = false;
- if (listener != null) {
- listener.onCameraClose();
- }
- }
- }
- }, backgroundHandler);
-
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
-
- }
-
-
- @Override
- MediaRecorder getRecorder() {
- return mMediaRecorder;
- }
-
- private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
- builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
- }
-
- private static int getIntFieldIfExists(Class> klass, String fieldName,
- Class> obj, int defaultVal) {
- try {
- Field f = klass.getDeclaredField(fieldName);
- return f.getInt(obj);
- } catch (Exception e) {
- return defaultVal;
- }
- }
-
- private static boolean isEmulator() {
- return Build.FINGERPRINT.startsWith("generic")
- || Build.FINGERPRINT.startsWith("unknown")
- || Build.MODEL.contains("google_sdk")
- || Build.MODEL.contains("Emulator")
- || Build.MODEL.contains("Android SDK built for x86")
- || Build.MANUFACTURER.contains("Genymotion")
- || (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
- || "google_sdk".equals(Build.PRODUCT);
- }
-
- @SuppressLint("InlinedApi")
- private void setUpMediaRecorder() throws IOException {
- final Activity activity = (Activity) mContext;
- if (null == activity) {
- return;
- }
-
- synchronized (lock) {
- mMediaRecorder = new MediaRecorder();
- mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
- DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
- Date today = Calendar.getInstance().getTime();
- if (getSaveToGallery() && hasStoragePermission()) {
- File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
- if (!cameraDir.exists()) {
- final boolean mkdirs = cameraDir.mkdirs();
- }
- setFile(new File(cameraDir, "VID_" + df.format(today) + ".mp4"));
- } else {
- setFile(new File(mContext.getExternalFilesDir(null), "VID_" + df.format(today) + ".mp4"));
- }
- CamcorderProfile profile = getCamcorderProfile(FancyCamera.Quality.values()[quality]);
- mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
- mMediaRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight());
- mMediaRecorder.setAudioChannels(profile.audioChannels);
-
- if (mOrientation == null || mOrientation == FancyCamera.CameraOrientation.UNKNOWN) {
- int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
-
- int orientation = activity.getResources().getConfiguration().orientation;
-
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- if (mSensorOrientation != null) {
- switch (mSensorOrientation) {
- case SENSOR_ORIENTATION_DEFAULT_DEGREES:
- mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
- break;
- case SENSOR_ORIENTATION_INVERSE_DEGREES:
- mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
- break;
- }
- }
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE && Surface.ROTATION_90 == rotation) {
- mMediaRecorder.setOrientationHint(0);
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE && Surface.ROTATION_270 == rotation) {
- mMediaRecorder.setOrientationHint(0);
- }
- } else {
- switch (mOrientation) {
- case PORTRAIT_UPSIDE_DOWN:
- mMediaRecorder.setOrientationHint(270);
- break;
- case LANDSCAPE_LEFT:
- mMediaRecorder.setOrientationHint(0);
- break;
- case LANDSCAPE_RIGHT:
- mMediaRecorder.setOrientationHint(180);
- break;
- default:
- mMediaRecorder.setOrientationHint(90);
- break;
- }
- }
-
- boolean isHEVCSupported = !disableHEVC && android.os.Build.VERSION.SDK_INT >= 24;
-
- // Use half bit rate for HEVC
- int videoBitRate = isHEVCSupported ? profile.videoBitRate / 2 : profile.videoBitRate;
- int maxVideoBitrate = profile.videoBitRate;
- if (this.maxVideoBitrate > -1) {
- maxVideoBitrate = this.maxVideoBitrate;
- }
- int maxVideoFrameRate = profile.videoFrameRate;
- if (this.maxVideoFrameRate > -1) {
- maxVideoFrameRate = this.maxVideoFrameRate;
- }
- int maxAudioBitRate = profile.audioBitRate;
- if (this.maxAudioBitRate > -1) {
- maxAudioBitRate = this.maxAudioBitRate;
- }
-
- mMediaRecorder.setVideoFrameRate(Math.min(profile.videoFrameRate, maxVideoFrameRate));
- mMediaRecorder.setVideoEncodingBitRate(Math.min(videoBitRate, maxVideoBitrate));
- mMediaRecorder.setAudioEncodingBitRate(Math.min(profile.audioBitRate, maxAudioBitRate));
-
- if (isHEVCSupported) {
- int h265 = Camera2.getIntFieldIfExists(MediaRecorder.VideoEncoder.class,
- "HEVC", null, MediaRecorder.VideoEncoder.DEFAULT);
- if (h265 == MediaRecorder.VideoEncoder.DEFAULT) {
- h265 = Camera2.getIntFieldIfExists(MediaRecorder.VideoEncoder.class,
- "H265", null, MediaRecorder.VideoEncoder.DEFAULT);
- }
- // Emulator seems to dislike H264/HEVC
- if (isEmulator()) {
- mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
- } else {
- mMediaRecorder.setVideoEncoder(h265);
- }
- } else {
- mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
- }
- mMediaRecorder.setAudioEncoder(profile.audioCodec);
- mMediaRecorder.setOutputFile(getFile().getPath());
- mMediaRecorder.setOnInfoListener(new MediaRecorder.OnInfoListener() {
- @Override
- public void onInfo(MediaRecorder mr, int what, int extra) {
- if (listener != null) {
- switch (what) {
- case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_DURATION_REACHED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_APPROACHING.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_REACHED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.NEXT_OUTPUT_FILE_STARTED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN:
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.UNKNOWN.toString()));
- break;
- }
- }
- }
- });
-
- mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
- @Override
- public void onError(MediaRecorder mr, int what, int extra) {
- if (listener != null) {
- switch (what) {
- case MediaRecorder.MEDIA_ERROR_SERVER_DIED:
- listener.onVideoEvent(new VideoEvent(EventType.ERROR, null, VideoEvent.EventError.SERVER_DIED.toString()));
- break;
- case MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN:
- listener.onVideoEvent(new VideoEvent(EventType.ERROR, null, VideoEvent.EventError.UNKNOWN.toString()));
- break;
- }
- }
- }
-
- });
-
- mMediaRecorder.prepare();
- }
- }
-
-
- private void updateAutoFocus(boolean isVideo) {
- if (mAutoFocus) {
- int[] modes = characteristics.get(
- CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
- // Auto focus is not supported
- if (modes == null || modes.length == 0 ||
- (modes.length == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF)) {
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_OFF);
- } else {
- boolean hasVideoSupport = false;
- boolean hasPhotoSupport = false;
- boolean hasAutoSupport = false;
- for (int mode : modes) {
- if (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO) {
- hasVideoSupport = true;
- }
- if (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
- hasPhotoSupport = true;
- }
- if (mode == CaptureRequest.CONTROL_AF_MODE_AUTO) {
- hasAutoSupport = true;
- }
- }
- if (isVideo) {
- if (hasVideoSupport) {
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO);
- } else if (hasAutoSupport) {
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_AUTO);
- }
- } else {
- if (hasPhotoSupport) {
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
- } else if (hasAutoSupport) {
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_AUTO);
- }
-
- }
- }
- } else {
- mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE,
- CaptureRequest.CONTROL_AF_MODE_OFF);
- }
- }
-
- private Size getPreviewSize(Size[] sizes) {
- return getSupportedSize(sizes, quality);
- }
-
- private Size getSupportedSize(Size[] sizes, int quality) {
- WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Point deviceSize = new Point();
- display.getSize(deviceSize);
- int width = deviceSize.x;
- int height = deviceSize.y;
-
- Size optimalSize = null;
- int count = 0;
- for (Size size : sizes) {
- count++;
- if (quality == FancyCamera.Quality.LOWEST.getValue()) {
- return sizes[sizes.length - 1];
- } else if (quality == FancyCamera.Quality.HIGHEST.getValue()) {
- if (size.getHeight() <= height && size.getWidth() <= width) {
- optimalSize = size;
- break;
- } else {
- if (count == sizes.length - 1) {
- optimalSize = sizes[sizes.length - 1];
- break;
- }
- }
- } else if (quality == FancyCamera.Quality.MAX_480P.getValue()) {
- if (size.getHeight() == 480 && size.getWidth() <= width) {
- optimalSize = size;
- break;
- } else {
- if (count == sizes.length - 1) {
- optimalSize = getSupportedSize(sizes, FancyCamera.Quality.QVGA.getValue());
- break;
- }
- }
- } else if (quality == FancyCamera.Quality.MAX_720P.getValue()) {
- if (size.getHeight() == 720 && size.getWidth() <= width) {
- optimalSize = size;
- break;
- } else {
- if (count == sizes.length - 1) {
- optimalSize = getSupportedSize(sizes, FancyCamera.Quality.MAX_480P.getValue());
- break;
- }
- }
- } else if (quality == FancyCamera.Quality.MAX_1080P.getValue()) {
- if (size.getHeight() == 1080 && size.getWidth() <= width) {
- optimalSize = size;
- break;
- } else {
- if (count == sizes.length - 1) {
- optimalSize = getSupportedSize(sizes, FancyCamera.Quality.MAX_720P.getValue());
- break;
- }
- }
- } else if (quality == FancyCamera.Quality.MAX_2160P.getValue()) {
- if (size.getHeight() == 2160 && size.getWidth() <= width) {
- optimalSize = size;
- break;
- } else {
- if (count == sizes.length - 1) {
- optimalSize = getSupportedSize(sizes, FancyCamera.Quality.MAX_1080P.getValue());
- break;
- }
- }
- } else if (quality == FancyCamera.Quality.QVGA.getValue()) {
- if (size.getHeight() == 240 && size.getWidth() <= width) {
- optimalSize = size;
- break;
- } else {
- if (count == sizes.length - 1) {
- optimalSize = sizes[sizes.length - 1];
- break;
- }
- }
- }
- }
- return optimalSize;
- }
-
-
- private void startPreview() {
- synchronized (lock) {
- if (mCameraDevice == null || !getHolder().isAvailable()) {
- return;
- }
- SurfaceTexture texture = getHolder().getSurfaceTexture();
- assert texture != null;
- texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
- try {
- if (mCameraDevice == null) return;
- mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
- Surface previewSurface = new Surface(texture);
- mPreviewBuilder.addTarget(previewSurface);
- updateAutoFocus(true);
- // tempOffFlash(mPreviewBuilder);
- mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface),
- new CameraCaptureSession.StateCallback() {
- @Override
- public void onConfigured(@NonNull CameraCaptureSession session) {
- synchronized (lock) {
- mPreviewSession = session;
- updatePreview();
- }
- }
-
- @Override
- public void onConfigureFailed(@NonNull CameraCaptureSession session) {
- Log.e(TAG, "configure fail " + session.toString());
- }
- }, backgroundHandler);
- } catch (CameraAccessException e) {
- Log.e(TAG, "CameraAccessException " + e.toString());
- e.printStackTrace();
- }
- }
- }
-
- private void closePreviewSession() {
- synchronized (lock) {
- if (mPreviewSession != null) {
- mPreviewSession.close();
- mPreviewSession = null;
- }
-
- if (null != mMediaRecorder) {
- mMediaRecorder.reset();
- mMediaRecorder.release();
- mMediaRecorder = null;
- }
- }
- }
-
- @Override
- void start() {
- synchronized (lock) {
- if (isStarted && mCameraDevice != null) {
- return;
- }
- stop();
- openCamera(getHolder().getWidth(), getHolder().getHeight());
- }
- }
-
-
- @Override
- void stop() {
- synchronized (lock) {
- if (!isStarted && mCameraDevice == null) {
- return;
- }
- if (mCameraDevice != null) {
- closePreviewSession();
- isStarted = false;
- if (mCameraDevice == null) {
- return;
- }
- mCameraDevice.close();
- mCameraDevice = null;
- }
- }
- }
-
-
- @Override
- void startRecording() {
- synchronized (lock) {
- if (null == mCameraDevice || !getHolder().isAvailable() || null == previewSize || isRecording) {
- return;
- }
- closePreviewSession();
- try {
- setUpMediaRecorder();
- } catch (IOException e) {
- e.printStackTrace();
- return;
- }
- SurfaceTexture texture = getHolder().getSurfaceTexture();
- assert texture != null;
- texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
- try {
- mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- return;
- }
- updateAutoFocus(true);
- setupFlash(mPreviewBuilder);
- List surfaces = new ArrayList<>();
- Surface previewSurface = new Surface(texture);
- surfaces.add(previewSurface);
- mPreviewBuilder.addTarget(previewSurface);
- Surface recorderSurface = mMediaRecorder.getSurface();
- surfaces.add(recorderSurface);
- mPreviewBuilder.addTarget(recorderSurface);
- try {
- mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
- @Override
- public void onConfigured(@NonNull final CameraCaptureSession cameraCaptureSession) {
- synchronized (lock) {
- mPreviewSession = cameraCaptureSession;
- updatePreview();
- Handler mainHandler = new Handler(mContext.getMainLooper());
- mainHandler.post(new Runnable() {
- @Override
- public void run() {
- synchronized (lock) {
- try {
- mMediaRecorder.start();
- } catch (IllegalStateException e) {
- try {
- setUpMediaRecorder();
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- }
- isRecording = true;
- startDurationTimer();
- if (listener != null) {
- listener.onVideoEvent(new VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.RECORDING_STARTED.toString()));
- }
- }
- }
- });
- }
- }
-
- @Override
- public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
- Log.e(TAG, "onConfigureFailed");
- }
- }, backgroundHandler);
- } catch (CameraAccessException e) {
- Log.e(TAG, e.toString());
- e.printStackTrace();
- }
- }
- }
-
- private boolean isCapturingPhoto;
-
- @Override
- void takePhoto() {
- synchronized (lock) {
- if (null == mCameraDevice || !getHolder().isAvailable() || null == previewSize || isRecording || isCapturingPhoto) {
- return;
- }
-
- try {
- isCapturingPhoto = true;
- Size[] jpegSizes = null;
-
- int width = 640;
- int height = 480;
- if (characteristics != null) {
- jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
- }
-
- if (jpegSizes != null && jpegSizes.length > 0) {
- Size size = jpegSizes[0];
- width = size.getHeight();
- height = size.getHeight();
- }
-
- reader = ImageReader.newInstance(width, height, ImageFormat.YUV_420_888, 2);
-
- SurfaceTexture texture = getHolder().getSurfaceTexture();
- assert texture != null;
- texture.setDefaultBufferSize(width, height);
-
- List surfaces = new ArrayList<>();
- surfaces.add(reader.getSurface());
- Surface previewSurface = new Surface(texture);
- surfaces.add(previewSurface);
-
- final CaptureRequest.Builder photoPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
- setupFlash(photoPreviewBuilder);
- photoPreviewBuilder.addTarget(reader.getSurface());
- photoPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
-
-
- DateFormat df = new SimpleDateFormat("yyyyMMddHHmmss", Locale.US);
- Date today = Calendar.getInstance().getTime();
- if (getSaveToGallery() && hasStoragePermission()) {
- File cameraDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
- if (!cameraDir.exists()) {
- final boolean mkdirs = cameraDir.mkdirs();
- }
- setFile(new File(cameraDir, "PIC_" + df.format(today) + ".jpg"));
- } else {
- setFile(new File(mContext.getExternalFilesDir(null), "PIC_" + df.format(today) + ".jpg"));
- }
-
- reader.setOnImageAvailableListener(readOnImageAvailableListener, backgroundHandler);
-
- final CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
- @Override
- public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
- super.onCaptureCompleted(session, request, result);
- synchronized (lock) {
- stop();
- start(); //TODO allow user to choose
- isCapturingPhoto = false;
- }
- }
-
- @Override
- public void onCaptureFailed(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull CaptureFailure failure) {
- super.onCaptureFailed(session, request, failure);
- Log.e(TAG, "onCaptureFailed " + session + " " + request);
- }
- };
-
- mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
- @Override
- public void onConfigured(@NonNull CameraCaptureSession session) {
- synchronized (lock) {
- if (isStarted) {
- try {
- session.capture(photoPreviewBuilder.build(), captureCallback, backgroundHandler);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- isCapturingPhoto = false;
- }
- }
- }
- }
-
- @Override
- public void onConfigureFailed(@NonNull CameraCaptureSession session) {
- synchronized (lock) {
- isCapturingPhoto = false;
- }
- }
- }, backgroundHandler);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- isCapturingPhoto = false;
- }
- }
-
- }
-
- private void stopRecord() {
- synchronized (lock) {
- if (!isRecording) {
- return;
- }
-
- try {
- if (mPreviewSession != null) {
- mPreviewSession.stopRepeating();
- mPreviewSession.abortCaptures();
- }
- } catch (CameraAccessException e) {
- e.printStackTrace();
- }
-
-
- try {
- if (mMediaRecorder != null) {
- mMediaRecorder.stop();
- }
- } catch (RuntimeException e) {
- //handle the exception
- }
-
- isStarted = false;
- isRecording = false;
- stopDurationTimer();
-
- if (mMediaRecorder != null) {
- mMediaRecorder.reset();
- mMediaRecorder.release();
- }
-
- mMediaRecorder = null;
-
- if (getFile() != null) {
- Uri contentUri = Uri.fromFile(getFile());
- Intent mediaScanIntent = new android.content.Intent(
- "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
- contentUri
- );
- mContext.sendBroadcast(mediaScanIntent);
- }
-
- if (listener != null) {
- listener.onVideoEvent(new VideoEvent(EventType.INFO, getFile(), VideoEvent.EventInfo.RECORDING_FINISHED.toString()));
- }
- setFile(null);
- }
- }
-
- @Override
- void stopRecording() {
- synchronized (lock) {
- stopRecord();
- start();
- }
- }
-
-
- private void configureTransform(int viewWidth, int viewHeight) {
- synchronized (lock) {
- Activity activity = (Activity) mContext;
- if (null == getHolder() || null == previewSize || null == activity) {
- return;
- }
- int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
- Matrix matrix = new Matrix();
- RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
- RectF bufferRect = new RectF(0, 0, previewSize.getHeight(), previewSize.getWidth());
- float centerX = viewRect.centerX();
- float centerY = viewRect.centerY();
-
- bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
- matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
- float scale = Math.max(
- (float) viewHeight / previewSize.getHeight(),
- (float) viewWidth / previewSize.getWidth());
- matrix.postScale(scale, scale, centerX, centerY);
-
- if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
- matrix.postRotate(90 * (rotation - 2), centerX, centerY);
- } else if (Surface.ROTATION_180 == rotation) {
- matrix.postRotate(180, centerX, centerY);
- } else {
- matrix.postRotate(0, centerX, centerY);
- }
- getHolder().setTransform(matrix);
- }
- }
-
-
- @Override
- void toggleCamera() {
- synchronized (lock) {
- if (isRecording) {
- stopRecord();
- }
- stop();
- if (mPosition == FancyCamera.CameraPosition.BACK) {
- mPosition = FancyCamera.CameraPosition.FRONT;
- } else {
- mPosition = FancyCamera.CameraPosition.BACK;
- }
- start();
-
- }
- }
-
-
- private void tempOffFlash(CaptureRequest.Builder builder) {
- builder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
- }
-
- private void setupFlash(CaptureRequest.Builder builder) {
- builder.set(CaptureRequest.FLASH_MODE, isFlashEnabled ? CaptureRequest.FLASH_MODE_TORCH : CaptureRequest.FLASH_MODE_OFF);
- }
-
- @Override
- void updatePreview() {
- synchronized (lock) {
- if (null == mCameraDevice || null == mPreviewSession) {
- return;
- }
- setUpCaptureRequestBuilder(mPreviewBuilder);
- try {
- mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), null, backgroundHandler);
- } catch (CameraAccessException e) {
- e.printStackTrace();
- } catch (IllegalStateException e) {
- e.printStackTrace();
- }
- }
- }
-
- @Override
- void release() {
- synchronized (lock) {
- if (isRecording) {
- stopRecord();
- }
- stop();
- }
- }
-
-
- @Override
- void setCameraPosition(FancyCamera.CameraPosition position) {
- synchronized (lock) {
- if (position != mPosition) {
- mPosition = position;
- if (null == mCameraDevice) {
- return;
- }
- stop();
- start();
- }
- }
- }
-
- @Override
- void setCameraOrientation(FancyCamera.CameraOrientation orientation) {
- synchronized (lock) {
- mOrientation = orientation;
- }
- }
-
- @Override
- boolean hasFlash() {
- if (characteristics != null) {
- Boolean info = characteristics.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
- if (info != null) {
- return info;
- }
- }
- return false;
- }
-
- @Override
- void toggleFlash() {
- if (!hasFlash()) {
- return;
- }
- isFlashEnabled = !isFlashEnabled;
- if (mCameraDevice != null) {
- start();
- }
- }
-
- @Override
- void enableFlash() {
- if (!hasFlash()) {
- return;
- }
- isFlashEnabled = true;
- if (mCameraDevice != null) {
- start();
- }
- }
-
- @Override
- void disableFlash() {
- if (!hasFlash()) {
- return;
- }
- isFlashEnabled = false;
- if (mCameraDevice != null) {
- start();
- }
-
- }
-
- @Override
- boolean flashEnabled() {
- return isFlashEnabled;
- }
-
- private CamcorderProfile getCamcorderProfile(FancyCamera.Quality quality) {
- CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
- switch (quality) {
- case MAX_480P:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.QVGA);
- }
- break;
- case MAX_720P:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.MAX_480P);
- }
- break;
- case MAX_1080P:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.MAX_720P);
- }
-
- break;
- case MAX_2160P:
- try {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_2160P);
- } catch (Exception e) {
- profile = getCamcorderProfile(FancyCamera.Quality.HIGHEST);
- }
- break;
- case HIGHEST:
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
- break;
- case LOWEST:
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW);
- break;
- case QVGA:
- if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA)) {
- profile = CamcorderProfile.get(CamcorderProfile.QUALITY_QVGA);
- } else {
- profile = getCamcorderProfile(FancyCamera.Quality.LOWEST);
- }
- break;
-
- }
- return profile;
- }
-
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/Camera2.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/Camera2.kt
new file mode 100644
index 0000000..f868a28
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/Camera2.kt
@@ -0,0 +1,1158 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:42 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 8:42 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import android.Manifest
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.app.Application
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.content.res.Configuration
+import android.graphics.*
+import android.hardware.camera2.CameraAccessException
+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.CaptureFailure
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.TotalCaptureResult
+import android.hardware.camera2.params.StreamConfigurationMap
+import android.hardware.display.DisplayManager
+import android.media.*
+import android.net.Uri
+import android.os.Build
+import android.os.Environment
+import android.os.Handler
+import android.os.HandlerThread
+import android.util.*
+import androidx.camera.view.CameraView
+import androidx.core.app.ActivityCompat
+
+import java.lang.reflect.Field
+import java.nio.ByteBuffer
+import java.text.DateFormat
+import java.text.SimpleDateFormat
+import java.util.ArrayList
+import java.util.Calendar
+import java.util.Collections
+import java.util.Date
+import java.util.Locale
+import android.view.*
+import androidx.camera.core.*
+import androidx.camera.core.impl.utils.executor.CameraXExecutors
+import androidx.camera.view.TextureViewMeteringPointFactory
+import androidx.exifinterface.media.ExifInterface
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.OnLifecycleEvent
+import com.google.common.util.concurrent.ListenableFuture
+import java.io.*
+import java.lang.ref.WeakReference
+import java.nio.Buffer
+import java.nio.file.FileSystem
+import java.nio.file.Files
+import java.nio.file.Path
+import java.text.ParseException
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+import kotlin.math.max
+import kotlin.math.min
+
+@androidx.annotation.RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+@SuppressLint("RestrictedApi")
+internal class Camera2(private val mContext: Context, private val textureView: TextureView, private val position: FancyCamera.CameraPosition?, private val orientation: FancyCamera.CameraOrientation?) : CameraBase(textureView) {
+ override fun updatePreview() {
+
+ }
+
+ private var mManager: CameraManager? = null
+ internal override var recorder: MediaRecorder? = null
+ private var mPosition: FancyCamera.CameraPosition? = null
+ private var mOrientation: FancyCamera.CameraOrientation? = null
+ private var backgroundHandler: Handler? = null
+ private var backgroundHandlerThread: HandlerThread? = null
+ private var characteristics: CameraCharacteristics? = null
+ private var previewSize: Size? = null
+ private var videoSize: Size? = null
+ private var mPreviewBuilder: CaptureRequest.Builder? = null
+ private var mCameraDevice: CameraDevice? = null
+ private var mPreviewSession: CameraCaptureSession? = null
+ private var isRecording = false
+ private var isStarted = false
+ private var isFlashEnabled = false
+ private var mSensorOrientation: Int? = null
+ private var cameraIdToOpen = "0"
+ override var autoFocus = true
+ set(focus) {
+ synchronized(lock) {
+ field = focus
+ }
+ }
+ override var disableHEVC = false
+ override var maxVideoBitrate = -1
+ override var maxAudioBitRate = -1
+ override var maxVideoFrameRate = -1
+ override var saveToGallery = false
+ override var autoSquareCrop = false
+ override var isAudioLevelsEnabled = false
+ private set
+ private var reader: ImageReader? = null
+
+ private val readOnImageAvailableListener = object : ImageReader.OnImageAvailableListener {
+ override fun onImageAvailable(imageReader: ImageReader) {
+ val image = imageReader.acquireLatestImage()
+ val bitmap = imageToBitmap(image)
+ try {
+ save(bitmap)
+ } catch (e: IOException) {
+ e.printStackTrace()
+ } finally {
+ reader = null
+ val contentUri = Uri.fromFile(file)
+ val mediaScanIntent = android.content.Intent(
+ "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
+ contentUri
+ )
+ mContext.sendBroadcast(mediaScanIntent)
+ if (listener != null) {
+ val event = PhotoEvent(EventType.INFO, file, PhotoEvent.EventInfo.PHOTO_TAKEN.toString())
+ listener?.onPhotoEvent(event)
+ } else {
+ Log.w(TAG, "No listener found")
+ }
+ }
+ }
+
+ private fun imageToBitmap(image: Image): Bitmap {
+ // NV21 is a plane of 8 bit Y values followed by interleaved Cb Cr
+ val ib = ByteBuffer.allocate(image.height * image.width * 2)
+
+ val y = image.planes[0].buffer
+ val cr = image.planes[1].buffer
+ val cb = image.planes[2].buffer
+ ib.put(y)
+ ib.put(cb)
+ ib.put(cr)
+
+ val yuvImage = YuvImage(ib.array(),
+ ImageFormat.NV21, image.width, image.height, null)
+
+ val out = ByteArrayOutputStream()
+ yuvImage.compressToJpeg(Rect(0, 0,
+ image.width, image.height), 50, out)
+ val imageBytes = out.toByteArray()
+ return BitmapFactory.decodeByteArray(imageBytes, 0, imageBytes.size)
+ }
+
+ @Throws(IOException::class)
+ private fun save(bm: Bitmap) {
+ var originalWidth = bm.width
+ var originalHeight = bm.height
+ var offsetWidth = 0
+ var offsetHeight = 0
+ if (autoSquareCrop) {
+ if (originalWidth < originalHeight) {
+ offsetHeight = (originalHeight - originalWidth) / 2
+ originalHeight = originalWidth
+ } else {
+ offsetWidth = (originalWidth - originalHeight) / 2
+ originalWidth = originalHeight
+ }
+ }
+
+ // this flips the front camera image to not be 'mirrored effect' for selfies
+ // does not flip if using the back camera
+ val matrix = Matrix()
+ if (mPosition == FancyCamera.CameraPosition.FRONT) {
+ val mirrorY = floatArrayOf(-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f)
+ val matrixMirrorY = Matrix()
+ matrixMirrorY.setValues(mirrorY)
+ matrix.postConcat(matrixMirrorY)
+ matrix.postRotate(90f)
+ } else {
+ matrix.postRotate(mSensorOrientation!!.toFloat())
+ }
+
+ val rotated = Bitmap.createBitmap(bm, offsetWidth, offsetHeight, originalWidth, originalHeight, matrix, false)
+
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera")
+ if (!cameraDir.exists()) {
+ val mkdirs = cameraDir.mkdirs()
+ }
+ FileOutputStream(file!!).use { outputStream -> rotated.compress(Bitmap.CompressFormat.JPEG, 100, outputStream) }
+ bm.recycle()
+ rotated.recycle()
+ }
+ }
+
+ override val numberOfCameras: Int
+ get() {
+ return CameraX.getCameraFactory().availableCameraIds.size
+ }
+
+ private var isCapturingPhoto: Boolean = false
+ private lateinit var previewConfig: PreviewConfig
+ private lateinit var videoCaptureConfig: VideoCaptureConfig
+ private lateinit var imageCaptureConfig: ImageCaptureConfig
+ private var preview: Preview? = null
+ private var imageCapture: ImageCapture? = null
+ private var videoCapture: VideoCapture? = null
+ private lateinit var displayManager: DisplayManager
+
+ private var textureViewDisplay: Int = -1
+
+
+ private val displayListener = object : DisplayManager.DisplayListener {
+ override fun onDisplayAdded(displayId: Int) = Unit
+ override fun onDisplayRemoved(displayId: Int) = Unit
+ override fun onDisplayChanged(displayId: Int) {
+ if (displayId == textureViewDisplay) {
+ val display = displayManager.getDisplay(displayId)
+ preview?.setTargetRotation(display.rotation)
+ imageCapture?.setTargetRotation(display.rotation)
+ videoCapture?.setTargetRotation(display.rotation)
+ }
+ }
+ }
+
+
+ private var mQuality = 0
+ override var quality: Int
+ set(value) {
+ mQuality = value
+ refreshCamera()
+ }
+ get() {
+ return mQuality
+ }
+
+ override var didPauseForPermission = false
+ var didCreateBefore = false
+
+ init {
+ mManager = mContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+
+ displayManager = mContext
+ .getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
+
+
+ (mContext as LifecycleOwner).lifecycle.addObserver(object : LifecycleObserver {
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
+ fun onCreate() {
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
+ fun onPause() {
+ displayManager.unregisterDisplayListener(displayListener)
+ didCreateBefore = true
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
+ fun onStopped() {
+ }
+
+ @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
+ fun onResume() {
+ displayManager.registerDisplayListener(displayListener, null)
+ if (didPauseForPermission) {
+ refreshCamera()
+ didPauseForPermission = false
+ }
+
+ if (!didPauseForPermission && didCreateBefore) {
+ // refreshCamera()
+ }
+ }
+ })
+
+ val lensFacing = when (position) {
+ FancyCamera.CameraPosition.FRONT -> {
+ mPosition = FancyCamera.CameraPosition.FRONT
+ CameraX.LensFacing.FRONT
+ }
+ FancyCamera.CameraPosition.BACK -> {
+ mPosition = FancyCamera.CameraPosition.BACK
+ CameraX.LensFacing.BACK
+ }
+ else -> {
+ mPosition = FancyCamera.CameraPosition.BACK
+ CameraX.LensFacing.BACK
+ }
+ }
+
+
+ previewConfig = PreviewConfig.Builder()
+ .apply {
+ setBackgroundExecutor(executorService)
+ setLensFacing(lensFacing)
+ }.build()
+ videoCaptureConfig = VideoCaptureConfig.Builder()
+ .apply {
+ setBackgroundExecutor(executorService)
+ setLensFacing(lensFacing)
+ }.build()
+ imageCaptureConfig = ImageCaptureConfig.Builder()
+ .apply {
+ setBackgroundExecutor(executorService)
+ setLensFacing(lensFacing)
+ }.build()
+
+ textureView.post {
+ textureViewDisplay = textureView.display.displayId
+ refreshCamera()
+ }
+ }
+
+ private fun currentLens(): CameraX.LensFacing {
+ return when (mPosition) {
+ FancyCamera.CameraPosition.FRONT -> CameraX.LensFacing.FRONT
+ FancyCamera.CameraPosition.BACK -> CameraX.LensFacing.BACK
+ else -> CameraX.LensFacing.BACK
+ }
+ }
+
+ private fun rotationToSurfaceRotation(rotation: Int): Int {
+ return when (rotation) {
+ 270 -> Surface.ROTATION_270
+ 180 -> Surface.ROTATION_180
+ 90 -> Surface.ROTATION_90
+ else -> Surface.ROTATION_0
+ }
+ }
+
+ private var didOpen = false
+
+ private lateinit var autoFitPreview: AutoFitPreviewBuilder
+
+ @SuppressLint("ClickableViewAccessibility")
+ private fun refreshCamera() {
+ CameraX.unbindAll()
+ if (!textureView.isAvailable || !hasPermission()) {
+ return
+ }
+
+ try {
+ val cameraOrientation = when (orientation) {
+ FancyCamera.CameraOrientation.PORTRAIT_UPSIDE_DOWN -> Surface.ROTATION_270
+ FancyCamera.CameraOrientation.PORTRAIT -> Surface.ROTATION_90
+ FancyCamera.CameraOrientation.LANDSCAPE_LEFT -> Surface.ROTATION_0
+ FancyCamera.CameraOrientation.LANDSCAPE_RIGHT -> Surface.ROTATION_180
+ else -> textureView.display.rotation
+ }
+
+
+ val currentLens = currentLens()
+
+
+ val config = PreviewConfig.Builder.fromConfig(previewConfig)
+ .apply {
+ setTargetRotation(cameraOrientation)
+ setLensFacing(currentLens)
+ }
+ val imageConfig = ImageCaptureConfig.Builder.fromConfig(imageCaptureConfig)
+ .apply {
+ setTargetRotation(cameraOrientation)
+ setLensFacing(currentLens)
+ setCaptureMode(ImageCapture.CaptureMode.MIN_LATENCY)
+ }
+
+ val profile = getCamcorderProfile(FancyCamera.Quality.values()[quality])
+
+ config.setTargetResolution(Size(profile.videoFrameWidth, profile.videoFrameHeight))
+
+ videoCaptureConfig = VideoCaptureConfig.Builder.fromConfig(videoCaptureConfig)
+ .apply {
+ setTargetRotation(cameraOrientation)
+ setLensFacing(currentLens)
+ setTargetResolution(Size(profile.videoFrameWidth, profile.videoFrameHeight))
+ setMaxResolution(Size(profile.videoFrameWidth, profile.videoFrameHeight))
+
+ var _maxVideoBitrate = profile.videoBitRate
+ if (maxVideoBitrate > -1) {
+ _maxVideoBitrate = maxVideoBitrate
+ }
+ var _maxVideoFrameRate = profile.videoFrameRate
+ if (maxVideoFrameRate > -1) {
+ _maxVideoFrameRate = maxVideoFrameRate
+ }
+ var _maxAudioBitRate = profile.audioBitRate
+ if (maxAudioBitRate > -1) {
+ _maxAudioBitRate = maxAudioBitRate
+ }
+
+ setAudioBitRate(min(profile.audioBitRate, _maxAudioBitRate))
+ setBitRate(min(profile.videoBitRate, _maxVideoBitrate))
+ setVideoFrameRate(min(profile.videoFrameRate, _maxVideoFrameRate))
+ }.build()
+
+ previewConfig = config.build()
+ autoFitPreview = AutoFitPreviewBuilder.build(previewConfig, textureView, listener) //Preview(previewConfig)
+ preview = autoFitPreview.useCase
+ val cameraControl = CameraX.getCameraControl(currentLens)
+
+ textureView.setOnTouchListener { _, event ->
+ if (event.action != MotionEvent.ACTION_UP) {
+ return@setOnTouchListener false
+ }
+
+ val factory = TextureViewMeteringPointFactory(textureView)
+ val point = factory.createPoint(event.x, event.y)
+ val action = FocusMeteringAction.Builder.from(point).build()
+ cameraControl.startFocusAndMetering(action)
+ return@setOnTouchListener true
+ }
+ videoCapture = VideoCapture(videoCaptureConfig)
+ imageCaptureConfig = imageConfig.build()
+ imageCapture = ImageCapture(imageCaptureConfig)
+
+ CameraX.bindToLifecycle(mContext as LifecycleOwner, preview!!)
+ } catch (e: IllegalStateException) {
+ CameraX.unbindAll()
+ e.printStackTrace()
+ }
+
+ }
+
+ override fun setEnableAudioLevels(enable: Boolean) {
+ isAudioLevelsEnabled = enable
+ }
+
+ override fun hasCamera(): Boolean {
+ if (mManager == null) return false
+ try {
+ return mManager!!.cameraIdList.isNotEmpty()
+ } catch (e: CameraAccessException) {
+ e.printStackTrace()
+ }
+ return false
+ }
+
+ override fun cameraStarted(): Boolean {
+ return isStarted
+ }
+
+ override fun cameraRecording(): Boolean {
+ return isRecording
+ }
+
+
+ override fun openCamera(width: Int, height: Int) {
+
+ }
+
+
+ @SuppressLint("InlinedApi")
+ @Throws(IOException::class)
+ private fun setUpMediaRecorder() {
+ val activity = mContext as Activity ?: return
+
+ synchronized(lock) {
+ recorder = MediaRecorder()
+ recorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
+ recorder!!.setVideoSource(MediaRecorder.VideoSource.SURFACE)
+ val df = SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
+ val today = Calendar.getInstance().time
+ if (saveToGallery && hasStoragePermission()) {
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera")
+ if (!cameraDir.exists()) {
+ val mkdirs = cameraDir.mkdirs()
+ }
+ file = File(cameraDir, "VID_" + df.format(today) + ".mp4")
+ } else {
+ file = File(mContext.getExternalFilesDir(null), "VID_" + df.format(today) + ".mp4")
+ }
+ val profile = getCamcorderProfile(FancyCamera.Quality.values()[quality])
+ recorder!!.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4)
+ recorder!!.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight)
+ recorder!!.setAudioChannels(profile.audioChannels)
+
+ if (mOrientation == null || mOrientation == FancyCamera.CameraOrientation.UNKNOWN) {
+ val rotation = activity.windowManager.defaultDisplay.rotation
+
+ val orientation = activity.resources.configuration.orientation
+
+ if (orientation == Configuration.ORIENTATION_PORTRAIT) {
+ if (mSensorOrientation != null) {
+ when (mSensorOrientation) {
+ SENSOR_ORIENTATION_DEFAULT_DEGREES -> recorder!!.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation))
+ SENSOR_ORIENTATION_INVERSE_DEGREES -> recorder!!.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation))
+ }
+ }
+ } else if (orientation == Configuration.ORIENTATION_LANDSCAPE && Surface.ROTATION_90 == rotation) {
+ recorder!!.setOrientationHint(0)
+ } else if (orientation == Configuration.ORIENTATION_LANDSCAPE && Surface.ROTATION_270 == rotation) {
+ recorder!!.setOrientationHint(0)
+ }
+ } else {
+ when (mOrientation) {
+ FancyCamera.CameraOrientation.PORTRAIT_UPSIDE_DOWN -> recorder!!.setOrientationHint(270)
+ FancyCamera.CameraOrientation.LANDSCAPE_LEFT -> recorder!!.setOrientationHint(0)
+ FancyCamera.CameraOrientation.LANDSCAPE_RIGHT -> recorder!!.setOrientationHint(180)
+ else -> recorder!!.setOrientationHint(90)
+ }
+ }
+
+ val isHEVCSupported = !this.disableHEVC && android.os.Build.VERSION.SDK_INT >= 24
+
+ // Use half bit rate for HEVC
+ val videoBitRate = if (isHEVCSupported) profile.videoBitRate / 2 else profile.videoBitRate
+ var maxVideoBitrate = profile.videoBitRate
+ if (this.maxVideoBitrate > -1) {
+ maxVideoBitrate = this.maxVideoBitrate
+ }
+ var maxVideoFrameRate = profile.videoFrameRate
+ if (this.maxVideoFrameRate > -1) {
+ maxVideoFrameRate = this.maxVideoFrameRate
+ }
+ var maxAudioBitRate = profile.audioBitRate
+ if (this.maxAudioBitRate > -1) {
+ maxAudioBitRate = this.maxAudioBitRate
+ }
+
+ recorder!!.setVideoFrameRate(Math.min(profile.videoFrameRate, maxVideoFrameRate))
+ recorder!!.setVideoEncodingBitRate(Math.min(videoBitRate, maxVideoBitrate))
+ recorder!!.setAudioEncodingBitRate(Math.min(profile.audioBitRate, maxAudioBitRate))
+
+ if (isHEVCSupported) {
+ var h265 = Camera2.getIntFieldIfExists(MediaRecorder.VideoEncoder::class.java,
+ "HEVC", null, MediaRecorder.VideoEncoder.DEFAULT)
+ if (h265 == MediaRecorder.VideoEncoder.DEFAULT) {
+ h265 = Camera2.getIntFieldIfExists(MediaRecorder.VideoEncoder::class.java,
+ "H265", null, MediaRecorder.VideoEncoder.DEFAULT)
+ }
+ // Emulator seems to dislike H264/HEVC
+ if (isEmulator) {
+ recorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
+ } else {
+ recorder!!.setVideoEncoder(h265)
+ }
+ } else {
+ recorder!!.setVideoEncoder(MediaRecorder.VideoEncoder.H264)
+ }
+ recorder!!.setAudioEncoder(profile.audioCodec)
+ recorder!!.setOutputFile(file!!.path)
+ recorder!!.setOnInfoListener { mr, what, extra ->
+ if (listener != null) {
+ when (what) {
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_DURATION_REACHED.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_APPROACHING -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_APPROACHING.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.MAX_FILESIZE_REACHED.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_NEXT_OUTPUT_FILE_STARTED -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.NEXT_OUTPUT_FILE_STARTED.toString()))
+ MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN -> listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.UNKNOWN.toString()))
+ }
+ }
+ }
+
+ recorder!!.setOnErrorListener { mr, what, extra ->
+ if (listener != null) {
+ when (what) {
+ MediaRecorder.MEDIA_ERROR_SERVER_DIED -> listener?.onVideoEvent(VideoEvent(EventType.ERROR, null, VideoEvent.EventError.SERVER_DIED.toString()))
+ MediaRecorder.MEDIA_RECORDER_ERROR_UNKNOWN -> listener?.onVideoEvent(VideoEvent(EventType.ERROR, null, VideoEvent.EventError.UNKNOWN.toString()))
+ }
+ }
+ }
+
+ recorder!!.prepare()
+ }
+ }
+
+
+ private fun updateAutoFocus(isVideo: Boolean) {
+ if (autoFocus) {
+ val modes = characteristics!!.get(
+ CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES)
+ // Auto focus is not supported
+ if (modes == null || modes.size == 0 ||
+ modes.size == 1 && modes[0] == CameraCharacteristics.CONTROL_AF_MODE_OFF) {
+ mPreviewBuilder!!.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_OFF)
+ } else {
+ var hasVideoSupport = false
+ var hasPhotoSupport = false
+ var hasAutoSupport = false
+ for (mode in modes) {
+ if (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO) {
+ hasVideoSupport = true
+ }
+ if (mode == CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+ hasPhotoSupport = true
+ }
+ if (mode == CaptureRequest.CONTROL_AF_MODE_AUTO) {
+ hasAutoSupport = true
+ }
+ }
+ if (isVideo) {
+ if (hasVideoSupport) {
+ mPreviewBuilder!!.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_VIDEO)
+ } else if (hasAutoSupport) {
+ mPreviewBuilder!!.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_AUTO)
+ }
+ } else {
+ if (hasPhotoSupport) {
+ mPreviewBuilder!!.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
+ } else if (hasAutoSupport) {
+ mPreviewBuilder!!.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_AUTO)
+ }
+
+ }
+ }
+ } else {
+ mPreviewBuilder!!.set(CaptureRequest.CONTROL_AF_MODE,
+ CaptureRequest.CONTROL_AF_MODE_OFF)
+ }
+ }
+
+ private fun getPreviewSize(sizes: Array): Size? {
+ return getSupportedSize(sizes, quality)
+ }
+
+ private fun getSupportedSize(sizes: Array, quality: Int): Size? {
+ val wm = mContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager
+ val display = wm.defaultDisplay
+ val deviceSize = Point()
+ display.getSize(deviceSize)
+ val width = deviceSize.x
+ val height = deviceSize.y
+
+ var optimalSize: Size? = null
+ var count = 0
+ for (size in sizes) {
+ count++
+ if (quality == FancyCamera.Quality.LOWEST.value) {
+ return sizes[sizes.size - 1]
+ } else if (quality == FancyCamera.Quality.HIGHEST.value) {
+ if (size.height <= height && size.width <= width) {
+ optimalSize = size
+ break
+ } else {
+ if (count == sizes.size - 1) {
+ optimalSize = sizes[sizes.size - 1]
+ break
+ }
+ }
+ } else if (quality == FancyCamera.Quality.MAX_480P.value) {
+ if (size.height == 480 && size.width <= width) {
+ optimalSize = size
+ break
+ } else {
+ if (count == sizes.size - 1) {
+ optimalSize = getSupportedSize(sizes, FancyCamera.Quality.QVGA.value)
+ break
+ }
+ }
+ } else if (quality == FancyCamera.Quality.MAX_720P.value) {
+ if (size.height == 720 && size.width <= width) {
+ optimalSize = size
+ break
+ } else {
+ if (count == sizes.size - 1) {
+ optimalSize = getSupportedSize(sizes, FancyCamera.Quality.MAX_480P.value)
+ break
+ }
+ }
+ } else if (quality == FancyCamera.Quality.MAX_1080P.value) {
+ if (size.height == 1080 && size.width <= width) {
+ optimalSize = size
+ break
+ } else {
+ if (count == sizes.size - 1) {
+ optimalSize = getSupportedSize(sizes, FancyCamera.Quality.MAX_720P.value)
+ break
+ }
+ }
+ } else if (quality == FancyCamera.Quality.MAX_2160P.value) {
+ if (size.height == 2160 && size.width <= width) {
+ optimalSize = size
+ break
+ } else {
+ if (count == sizes.size - 1) {
+ optimalSize = getSupportedSize(sizes, FancyCamera.Quality.MAX_1080P.value)
+ break
+ }
+ }
+ } else if (quality == FancyCamera.Quality.QVGA.value) {
+ if (size.height == 240 && size.width <= width) {
+ optimalSize = size
+ break
+ } else {
+ if (count == sizes.size - 1) {
+ optimalSize = sizes[sizes.size - 1]
+ break
+ }
+ }
+ }
+ }
+ return optimalSize
+ }
+
+
+ private fun startPreview() {
+
+ }
+
+ private fun closePreviewSession() {
+
+ }
+
+ override fun start() {
+ refreshCamera()
+ }
+
+
+ override fun stop() {
+ CameraX.unbindAll()
+ }
+
+
+ override fun startRecording() {
+ CameraX.unbind(imageCapture!!)
+ if (!CameraX.isBound(videoCapture)) {
+ try {
+ CameraX.bindToLifecycle(mContext as LifecycleOwner, videoCapture!!)
+ } catch (e: IllegalStateException) {
+ // Try refreshing camera
+ e.printStackTrace()
+ refreshCamera()
+ try {
+ CameraX.bindToLifecycle(mContext as LifecycleOwner, videoCapture!!)
+ } catch (e: IllegalStateException) {
+ e.printStackTrace()
+ return
+ }
+ }
+ }
+
+ val df = SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
+ val today = Calendar.getInstance().time
+ if (saveToGallery && hasStoragePermission()) {
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera")
+ if (!cameraDir.exists()) {
+ cameraDir.mkdirs()
+ }
+ file = File(cameraDir, "VID_" + df.format(today) + ".mp4")
+ } else {
+ file = File(mContext.getExternalFilesDir(null), "VID_" + df.format(today) + ".mp4")
+ }
+
+ videoCapture?.startRecording(file!!, executorService, object : VideoCapture.OnVideoSavedListener {
+ override fun onVideoSaved(videoFile: File) {
+ isStarted = false
+ isRecording = false
+ stopDurationTimer()
+
+ if (file != null && saveToGallery && hasStoragePermission()) {
+ val contentUri = Uri.fromFile(file)
+ val mediaScanIntent = Intent(
+ "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
+ contentUri
+ )
+ mContext.sendBroadcast(mediaScanIntent)
+ }
+
+ if (listener != null) {
+ listener?.onVideoEvent(VideoEvent(EventType.INFO, videoFile, VideoEvent.EventInfo.RECORDING_FINISHED.toString()))
+ }
+ file = null
+ }
+
+ override fun onError(videoCaptureError: VideoCapture.VideoCaptureError, message: String, cause: Throwable?) {
+ isStarted = false
+ isRecording = false
+ stopDurationTimer()
+ file = null
+ listener?.onVideoEvent(VideoEvent(EventType.ERROR, null, VideoEvent.EventError.UNKNOWN.toString()))
+ }
+ })
+ isRecording = true
+ startDurationTimer()
+ listener?.onVideoEvent(VideoEvent(EventType.INFO, null, VideoEvent.EventInfo.RECORDING_STARTED.toString()))
+ }
+
+
+ fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int {
+ // Raw height and width of image
+ val (height: Int, width: Int) = options.run { outHeight to outWidth }
+ var inSampleSize = 1
+
+ if (height > reqHeight || width > reqWidth) {
+
+ val halfHeight: Int = height / 2
+ val halfWidth: Int = width / 2
+
+ // Calculate the largest inSampleSize value that is a power of 2 and keeps both
+ // height and width larger than the requested height and width.
+ while (halfHeight / inSampleSize >= reqHeight && halfWidth / inSampleSize >= reqWidth) {
+ inSampleSize *= 2
+ }
+ }
+
+ return inSampleSize
+ }
+
+ override fun takePhoto() {
+ CameraX.unbind(videoCapture!!)
+
+ if (!CameraX.isBound(imageCapture!!)) {
+ try {
+ CameraX.bindToLifecycle(mContext as LifecycleOwner, imageCapture!!)
+ } catch (e: IllegalStateException) {
+ // Try refreshing
+ e.printStackTrace()
+ refreshCamera()
+ try {
+ CameraX.bindToLifecycle(mContext as LifecycleOwner, videoCapture!!)
+ } catch (e: IllegalStateException) {
+ e.printStackTrace()
+ return
+ }
+ }
+ }
+
+
+ val df = SimpleDateFormat("yyyyMMddHHmmss", Locale.US)
+ val today = Calendar.getInstance().time
+ if (saveToGallery && hasStoragePermission()) {
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera")
+ if (!cameraDir.exists()) {
+ cameraDir.mkdirs()
+ }
+ file = File(cameraDir, "PIC_" + df.format(today) + ".jpg")
+ } else {
+ file = File(mContext.getExternalFilesDir(null), "PIC_" + df.format(today) + ".jpg")
+ }
+
+ val meta = ImageCapture.Metadata().apply {
+ isReversedHorizontal = imageCaptureConfig.lensFacing == CameraX.LensFacing.FRONT
+ }
+
+ if (autoSquareCrop) {
+ imageCapture?.takePicture(executorService, object : ImageCapture.OnImageCapturedListener() {
+ override fun onCaptureSuccess(image: ImageProxy, rotationDegrees: Int) {
+ CameraXExecutors.ioExecutor().execute {
+ var isError = false
+ try {
+ val buffer = image.planes.first().buffer
+ val bytes = ByteArray(buffer!!.remaining())
+ buffer.get(bytes)
+
+ val bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
+ val matrix = Matrix()
+
+ matrix.postRotate(rotationDegrees.toFloat())
+
+ if (imageCaptureConfig.lensFacing == CameraX.LensFacing.FRONT) {
+ matrix.postScale(-1f, 1f);
+ }
+
+ var originalWidth = bm.width
+ var originalHeight = bm.height
+ var offsetWidth = 0
+ var offsetHeight = 0
+ if (autoSquareCrop) {
+ if (originalWidth < originalHeight) {
+ offsetHeight = (originalHeight - originalWidth) / 2;
+ originalHeight = originalWidth;
+ } else {
+ offsetWidth = (originalWidth - originalHeight) / 2;
+ originalWidth = originalHeight;
+ }
+ }
+ val rotated = Bitmap.createBitmap(bm, offsetWidth, offsetHeight, originalWidth, originalHeight, matrix, false);
+
+ val cameraDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), "Camera");
+ if (!cameraDir.exists()) {
+ cameraDir.mkdirs();
+ }
+ val outputStream = FileOutputStream(file!!, false)
+ rotated.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)
+
+
+ /*
+ val exif = ExifInterface(file!!.absolutePath)
+
+ val now = System.currentTimeMillis()
+ val datetime = convertToExifDateTime(now)
+
+ exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, datetime)
+ exif.setAttribute(ExifInterface.TAG_DATETIME_DIGITIZED, datetime)
+
+ try {
+ val subsec = java.lang.Long.toString(now - convertFromExifDateTime(datetime).getTime())
+ exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, subsec)
+ exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_DIGITIZED, subsec)
+ } catch (e: ParseException) {
+ }
+
+ exif.rotate(rotationDegrees)
+ if (meta.isReversedHorizontal) {
+ exif.flipHorizontally()
+ }
+ if (meta.isReversedVertical) {
+ exif.flipVertically()
+ }
+ if (meta.location != null) {
+
+ exif.setGpsInfo(meta.location!!)
+ }
+ exif.saveAttributes()
+
+ */
+
+ bm.recycle()
+ rotated.recycle()
+ } catch (e: Exception) {
+ isError = true
+ val event = PhotoEvent(EventType.ERROR, null, PhotoEvent.EventError.UNKNOWN.toString())
+ listener?.onPhotoEvent(event)
+ } finally {
+ try {
+ image.close()
+ } catch (e: Exception) {
+
+ }
+ if (!isError) {
+ if (saveToGallery && hasStoragePermission()) {
+ val contentUri = Uri.fromFile(file)
+ val mediaScanIntent = Intent(
+ "android.intent.action.MEDIA_SCANNER_SCAN_FILE",
+ contentUri
+ )
+ mContext.sendBroadcast(mediaScanIntent)
+ }
+ val event = PhotoEvent(EventType.INFO, file, PhotoEvent.EventInfo.PHOTO_TAKEN.toString())
+ listener?.onPhotoEvent(event)
+ }
+ }
+ }
+
+ }
+
+ override fun onError(imageCaptureError: ImageCapture.ImageCaptureError, message: String, cause: Throwable?) {
+ val event = PhotoEvent(EventType.ERROR, null, PhotoEvent.EventError.UNKNOWN.toString())
+ listener?.onPhotoEvent(event)
+ }
+ })
+ } else {
+ imageCapture?.takePicture(file!!, meta, executorService, object : ImageCapture.OnImageSavedListener {
+ override fun onImageSaved(file: File) {
+ val event = PhotoEvent(EventType.INFO, file, PhotoEvent.EventInfo.PHOTO_TAKEN.toString())
+ listener?.onPhotoEvent(event)
+ }
+
+ override fun onError(imageCaptureError: ImageCapture.ImageCaptureError, message: String, cause: Throwable?) {
+ val event = PhotoEvent(EventType.ERROR, null, PhotoEvent.EventError.UNKNOWN.toString())
+ listener?.onPhotoEvent(event)
+ }
+ })
+ }
+ }
+
+
+ private val DATE_FORMAT = object : ThreadLocal() {
+ public override fun initialValue(): SimpleDateFormat {
+ return SimpleDateFormat("yyyy:MM:dd", Locale.US)
+ }
+ }
+ private val TIME_FORMAT = object : ThreadLocal() {
+ public override fun initialValue(): SimpleDateFormat {
+ return SimpleDateFormat("HH:mm:ss", Locale.US)
+ }
+ }
+ private val DATETIME_FORMAT = object : ThreadLocal() {
+ public override fun initialValue(): SimpleDateFormat {
+ return SimpleDateFormat("yyyy:MM:dd HH:mm:ss", Locale.US)
+ }
+ }
+
+ private fun convertToExifDateTime(timestamp: Long): String {
+ return DATETIME_FORMAT.get()!!.format(Date(timestamp))
+ }
+
+ @Throws(ParseException::class)
+ private fun convertFromExifDateTime(dateTime: String): Date {
+ return DATETIME_FORMAT.get()!!.parse(dateTime)
+ }
+
+ private fun stopRecord() {
+ videoCapture?.stopRecording()
+ }
+
+ override fun stopRecording() {
+ stopRecord()
+ }
+
+
+ private fun configureTransform(viewWidth: Int, viewHeight: Int) {
+
+ }
+
+
+ override fun toggleCamera() {
+ if (mPosition == FancyCamera.CameraPosition.BACK) {
+ mPosition = FancyCamera.CameraPosition.FRONT
+ } else {
+ mPosition = FancyCamera.CameraPosition.BACK
+ }
+ refreshCamera()
+ }
+
+
+ override fun release() {
+ CameraX.unbindAll()
+ }
+
+
+ override fun setCameraPosition(position: FancyCamera.CameraPosition) {
+ if (position != mPosition) {
+ mPosition = position
+ refreshCamera()
+ }
+ }
+
+ override fun setCameraOrientation(orientation: FancyCamera.CameraOrientation) {
+ mOrientation = orientation
+ refreshCamera()
+ }
+
+ override fun hasFlash(): Boolean {
+ return try {
+ val cameraInfo = CameraX.getCameraInfo(currentLens())
+ cameraInfo.isFlashAvailable.value ?: false
+ } catch (e: Exception) {
+ false
+ }
+ }
+
+ override fun toggleFlash() {
+ if (!hasFlash()) {
+ return
+ }
+ isFlashEnabled = !isFlashEnabled
+
+ imageCapture?.flashMode = when (isFlashEnabled) {
+ true -> FlashMode.ON
+ else -> FlashMode.OFF
+ }
+
+ }
+
+ override fun enableFlash() {
+ if (!hasFlash()) {
+ return
+ }
+ isFlashEnabled = true
+ imageCapture?.flashMode = FlashMode.ON
+ }
+
+ override fun disableFlash() {
+ if (!hasFlash()) {
+ return
+ }
+ isFlashEnabled = false
+ imageCapture?.flashMode = FlashMode.OFF
+
+ }
+
+ override fun flashEnabled(): Boolean {
+ return isFlashEnabled
+ }
+
+ private fun getCamcorderProfile(quality: FancyCamera.Quality): CamcorderProfile {
+ var profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW)
+ when (quality) {
+ FancyCamera.Quality.MAX_480P -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_480P)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_480P)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.QVGA)
+ }
+ FancyCamera.Quality.MAX_720P -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_720P)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.MAX_480P)
+ }
+ FancyCamera.Quality.MAX_1080P -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_1080P)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.MAX_720P)
+ }
+ FancyCamera.Quality.MAX_2160P -> try {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_2160P)
+ } catch (e: Exception) {
+ profile = getCamcorderProfile(FancyCamera.Quality.HIGHEST)
+ }
+
+ FancyCamera.Quality.HIGHEST -> profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH)
+ FancyCamera.Quality.LOWEST -> profile = CamcorderProfile.get(CamcorderProfile.QUALITY_LOW)
+ FancyCamera.Quality.QVGA -> if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_QVGA)) {
+ profile = CamcorderProfile.get(CamcorderProfile.QUALITY_QVGA)
+ } else {
+ profile = getCamcorderProfile(FancyCamera.Quality.LOWEST)
+ }
+ }
+ return profile
+ }
+
+ companion object {
+ private val lock = Any()
+ private val TAG = "Camera2.Fancy"
+ private val SENSOR_ORIENTATION_DEFAULT_DEGREES = 90
+ private val SENSOR_ORIENTATION_INVERSE_DEGREES = 270
+ private val DEFAULT_ORIENTATIONS = SparseIntArray()
+ private val INVERSE_ORIENTATIONS = SparseIntArray()
+
+ @JvmStatic
+ val executorService = Executors.newSingleThreadExecutor()
+
+
+ init {
+ DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90)
+ DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0)
+ DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270)
+ DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180)
+ }
+
+ init {
+ INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270)
+ INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180)
+ INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90)
+ INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0)
+ }
+
+ private fun getIntFieldIfExists(klass: Class<*>, fieldName: String,
+ obj: Class<*>?, defaultVal: Int): Int {
+ try {
+ val f = klass.getDeclaredField(fieldName)
+ return f.getInt(obj)
+ } catch (e: Exception) {
+ return defaultVal
+ }
+
+ }
+
+ private val isEmulator: Boolean
+ get() = (Build.FINGERPRINT.startsWith("generic")
+ || Build.FINGERPRINT.startsWith("unknown")
+ || Build.MODEL.contains("google_sdk")
+ || Build.MODEL.contains("Emulator")
+ || Build.MODEL.contains("Android SDK built for x86")
+ || Build.MANUFACTURER.contains("Genymotion")
+ || Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
+ || "google_sdk" == Build.PRODUCT)
+ }
+
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraBase.java b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraBase.java
deleted file mode 100644
index f3337f0..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraBase.java
+++ /dev/null
@@ -1,185 +0,0 @@
-
-/*
- * Created By Osei Fortune on 2/16/18 8:42 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 8:42 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.media.MediaRecorder;
-import android.os.Build;
-import android.view.TextureView;
-
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
-import java.io.File;
-import java.util.Timer;
-import java.util.TimerTask;
-
-public abstract class CameraBase {
- private Timer mTimer;
- private TimerTask mTimerTask;
- private int mDuration = 0;
- private TextureView holder;
- private File mFile;
- CameraEventListener listener;
- int quality;
-
- CameraBase(TextureView holder) {
- this.holder = holder;
- }
-
- public final static String CameraThread = "CameraThread";
-
- private TextViewListener textViewListener;
-
- public TextureView getHolder() {
- return holder;
- }
-
- void setFile(File file) {
- mFile = file;
- }
-
- File getFile() {
- return mFile;
- }
-
- void setQuality(int quality) {
- this.quality = quality;
- }
-
- int getQuality() {
- return quality;
- }
-
- abstract public void setEnableAudioLevels(boolean enable);
-
- abstract public boolean isAudioLevelsEnabled();
-
- abstract boolean getAutoSquareCrop();
-
- abstract void setAutoSquareCrop(boolean crop);
-
- abstract MediaRecorder getRecorder();
-
- abstract boolean getSaveToGallery();
-
- abstract public void setSaveToGallery(boolean saveToGallery);
-
- abstract public boolean getAutoFocus();
-
- abstract public void setAutoFocus(boolean focus);
-
- abstract int getMaxAudioBitRate();
-
- abstract int getMaxVideoBitrate();
-
- abstract int getMaxVideoFrameRate();
-
- abstract public boolean getDisableHEVC();
-
- abstract public void setDisableHEVC(boolean disableHEVC);
-
- abstract public void setMaxAudioBitRate(int maxAudioBitRate);
-
- abstract public void setMaxVideoBitrate(int maxVideoBitrate);
-
- abstract public void setMaxVideoFrameRate(int maxVideoFrameRate);
-
- abstract public int getNumberOfCameras();
-
- abstract boolean hasCamera();
-
- abstract boolean hasFlash();
-
- abstract boolean cameraStarted();
-
- abstract boolean cameraRecording();
-
- abstract void openCamera(int width, int height);
-
- abstract void start();
-
- abstract void stop();
-
- abstract void startRecording();
-
- abstract void takePhoto();
-
- abstract void stopRecording();
-
- abstract void toggleCamera();
-
- abstract void updatePreview();
-
- abstract void release();
-
- abstract void setCameraPosition(FancyCamera.CameraPosition position);
-
- abstract void setCameraOrientation(FancyCamera.CameraOrientation orientation);
-
- abstract void toggleFlash();
-
- abstract void enableFlash();
-
- abstract void disableFlash();
-
- abstract boolean flashEnabled();
-
- public void setTextViewListener(TextViewListener listener) {
- textViewListener = listener;
- }
-
- public TextViewListener getTextViewListener() {
- return textViewListener;
- }
-
- public CameraEventListener getListener() {
- return listener;
- }
-
- public void setListener(CameraEventListener listener) {
- this.listener = listener;
- }
-
- void startDurationTimer() {
- mTimer = new Timer();
- mTimerTask = new TimerTask() {
- @Override
- public void run() {
- mDuration += 1;
- }
- };
- mTimer.schedule(mTimerTask, 0, 1000);
- }
-
- void stopDurationTimer() {
- mTimerTask.cancel();
- mTimer.cancel();
- mDuration = 0;
- }
-
- int getDuration() {
- return mDuration;
- }
-
- public boolean hasStoragePermission() {
- if (Build.VERSION.SDK_INT < 23) {
- return true;
- }
- return ContextCompat.checkSelfPermission(holder.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == (PackageManager.PERMISSION_GRANTED);
- }
-
- public void requestStoragePermission() {
- ActivityCompat.requestPermissions((Activity) holder.getContext(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 868);
- }
-
-}
\ No newline at end of file
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraBase.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraBase.kt
new file mode 100644
index 0000000..ae5aab1
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraBase.kt
@@ -0,0 +1,142 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:42 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 8:42 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.media.MediaRecorder
+import android.os.Build
+import android.view.TextureView
+
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+
+import java.io.File
+import java.util.Timer
+import java.util.TimerTask
+
+abstract class CameraBase internal constructor(val holder: TextureView) {
+ private var mTimer: Timer? = null
+ private var mTimerTask: TimerTask? = null
+ internal var duration = 0
+ private set
+ internal var file: File? = null
+ var listener: CameraEventListener? = null
+ internal abstract var quality: Int
+
+ private val VIDEO_RECORDER_PERMISSIONS_REQUEST = 868
+ private val VIDEO_RECORDER_PERMISSIONS = arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)
+
+ var textViewListener: TextViewListener? = null
+
+ abstract val isAudioLevelsEnabled: Boolean
+
+ internal abstract var autoSquareCrop: Boolean
+
+ internal abstract val recorder: MediaRecorder?
+
+ internal abstract var saveToGallery: Boolean
+
+ abstract var autoFocus: Boolean
+
+ internal abstract var maxAudioBitRate: Int
+
+ internal abstract var maxVideoBitrate: Int
+
+ internal abstract var maxVideoFrameRate: Int
+ internal abstract var didPauseForPermission: Boolean
+
+ abstract var disableHEVC: Boolean
+
+ abstract val numberOfCameras: Int
+
+ abstract fun setEnableAudioLevels(enable: Boolean)
+
+ internal abstract fun hasCamera(): Boolean
+
+ internal abstract fun hasFlash(): Boolean
+
+ internal abstract fun cameraStarted(): Boolean
+
+ internal abstract fun cameraRecording(): Boolean
+
+ internal abstract fun openCamera(width: Int, height: Int)
+
+ internal abstract fun start()
+
+ internal abstract fun stop()
+
+ internal abstract fun startRecording()
+
+ internal abstract fun takePhoto()
+
+ internal abstract fun stopRecording()
+
+ internal abstract fun toggleCamera()
+
+ internal abstract fun updatePreview()
+
+ internal abstract fun release()
+
+ internal abstract fun setCameraPosition(position: FancyCamera.CameraPosition)
+
+ internal abstract fun setCameraOrientation(orientation: FancyCamera.CameraOrientation)
+
+ internal abstract fun toggleFlash()
+
+ internal abstract fun enableFlash()
+
+ internal abstract fun disableFlash()
+
+ internal abstract fun flashEnabled(): Boolean
+
+ internal fun startDurationTimer() {
+ mTimer = Timer()
+ mTimerTask = object : TimerTask() {
+ override fun run() {
+ duration += 1
+ }
+ }
+ mTimer?.schedule(mTimerTask, 0, 1000)
+ }
+
+ internal fun stopDurationTimer() {
+ mTimerTask?.cancel()
+ mTimer?.cancel()
+ duration = 0
+ }
+
+ fun hasStoragePermission(): Boolean {
+ return if (Build.VERSION.SDK_INT < 23) {
+ true
+ } else ContextCompat.checkSelfPermission(holder.context, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
+ }
+
+ fun requestStoragePermission() {
+ ActivityCompat.requestPermissions(holder.context as Activity, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 868)
+ }
+
+ fun requestPermission() {
+ didPauseForPermission = true
+ ActivityCompat.requestPermissions(holder.context as Activity, VIDEO_RECORDER_PERMISSIONS, VIDEO_RECORDER_PERMISSIONS_REQUEST)
+ }
+
+ fun hasPermission(): Boolean {
+ return if (Build.VERSION.SDK_INT < 23) {
+ true
+ } else ContextCompat.checkSelfPermission(holder.context, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(holder.context, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
+ }
+
+ companion object {
+
+ val CameraThread = "CameraThread"
+ }
+
+}
\ No newline at end of file
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListener.java b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListener.java
deleted file mode 100644
index 5cb534a..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListener.java
+++ /dev/null
@@ -1,15 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:43 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 7:16 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-public interface CameraEventListener {
- void onCameraOpen();
- void onCameraClose();
- void onPhotoEvent(PhotoEvent event);
- void onVideoEvent(VideoEvent event);
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListener.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListener.kt
new file mode 100644
index 0000000..1984406
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListener.kt
@@ -0,0 +1,15 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:43 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 7:16 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+interface CameraEventListener {
+ fun onCameraOpen()
+ fun onCameraClose()
+ fun onPhotoEvent(event: PhotoEvent)
+ fun onVideoEvent(event: VideoEvent)
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListenerUI.java b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListenerUI.java
deleted file mode 100644
index 597a867..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListenerUI.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:43 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 7:51 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.util.Log;
-
-import java.io.File;
-
-
-public abstract class CameraEventListenerUI implements CameraEventListener {
- private Handler handler;
- private static final int WHAT_PHOTO_EVENT = 0x01;
- private static final int WHAT_VIDEO_EVENT = 0x02;
- private static final int WHAT_CAMERA_CLOSE_EVENT = 0x03;
- private static final int WHAT_CAMERA_OPEN_EVENT = 0x04;
- private static final String MESSAGE = "message";
- private static final String TYPE = "type";
- private static final String FILE = "file";
-
- private void ensureHandler() {
- if (handler != null) {
- return;
- }
- synchronized (CameraEventListenerUI.class) {
- if (handler == null) {
- handler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- Bundle eventData = msg.getData();
- if (eventData == null && (msg.what != WHAT_CAMERA_CLOSE_EVENT || msg.what != WHAT_CAMERA_OPEN_EVENT)) {
- return;
- }
- EventType type = (EventType) eventData.getSerializable(TYPE);
- String message = eventData.getString(MESSAGE);
- File file = null;
- switch (msg.what) {
- case WHAT_PHOTO_EVENT:
- if (eventData.getString(FILE) != null) {
- file = new File(eventData.getString(FILE));
- }
- onPhotoEventUI(new PhotoEvent(type, file, message));
- break;
- case WHAT_VIDEO_EVENT:
- if (eventData.getString(FILE) != null) {
- file = new File(eventData.getString(FILE));
- }
- onVideoEventUI(new VideoEvent(type, file, message));
- break;
- case WHAT_CAMERA_CLOSE_EVENT:
- onCameraCloseUI();
- break;
- case WHAT_CAMERA_OPEN_EVENT:
- onCameraOpenUI();
- break;
- }
- }
- };
- }
- }
- }
-
-
- @Override
- public void onPhotoEvent(PhotoEvent event) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- onPhotoEventUI(event);
- return;
- }
- ensureHandler();
- Message message = handler.obtainMessage();
- message.what = WHAT_PHOTO_EVENT;
- Bundle bundle = new Bundle();
- bundle.putString(MESSAGE, event.getMessage());
- bundle.putSerializable(TYPE, event.getType());
- if (event.getFile() != null) {
- bundle.putString(FILE, event.getFile().getPath());
- }
- message.setData(bundle);
- handler.sendMessage(message);
- }
-
- @Override
- public void onVideoEvent(VideoEvent event) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- onVideoEventUI(event);
- return;
- }
- ensureHandler();
- Message message = handler.obtainMessage();
- message.what = WHAT_VIDEO_EVENT;
- Bundle bundle = new Bundle();
- bundle.putString(MESSAGE, event.getMessage());
- bundle.putSerializable(TYPE, event.getType());
- if (event.getFile() != null) {
- bundle.putString(FILE, event.getFile().getPath());
- }
- message.setData(bundle);
- handler.sendMessage(message);
- }
-
- @Override
- public void onCameraClose() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- onCameraCloseUI();
- return;
- }
- ensureHandler();
- Message message = handler.obtainMessage();
- message.what = WHAT_CAMERA_CLOSE_EVENT;
- Bundle bundle = new Bundle();
- message.setData(bundle);
- handler.sendMessage(message);
- }
-
- @Override
- public void onCameraOpen() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- onCameraOpenUI();
- return;
- }
- ensureHandler();
- Message message = handler.obtainMessage();
- message.what = WHAT_CAMERA_OPEN_EVENT;
- Bundle bundle = new Bundle();
- message.setData(bundle);
- handler.sendMessage(message);
- }
-
- public abstract void onCameraOpenUI();
-
- public abstract void onCameraCloseUI();
-
- public abstract void onPhotoEventUI(PhotoEvent event);
-
- public abstract void onVideoEventUI(VideoEvent event);
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListenerUI.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListenerUI.kt
new file mode 100644
index 0000000..a56d551
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/CameraEventListenerUI.kt
@@ -0,0 +1,139 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:43 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 7:51 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.os.Message
+import android.util.Log
+
+import java.io.File
+
+
+abstract class CameraEventListenerUI : CameraEventListener {
+ private var handler: Handler? = null
+
+ private fun ensureHandler() {
+ if (handler != null) {
+ return
+ }
+ synchronized(CameraEventListenerUI::class.java) {
+ if (handler == null) {
+ handler = object : Handler(Looper.getMainLooper()) {
+ override fun handleMessage(msg: Message) {
+ val eventData = msg.data
+ if (eventData == null && (msg.what != WHAT_CAMERA_CLOSE_EVENT || msg.what != WHAT_CAMERA_OPEN_EVENT)) {
+ return
+ }
+ val type = eventData!!.getSerializable(TYPE) as EventType
+ val message = eventData.getString(MESSAGE)
+ var file: File? = null
+ when (msg.what) {
+ WHAT_PHOTO_EVENT -> {
+ if (eventData.getString(FILE) != null) {
+ file = File(eventData.getString(FILE)!!)
+ }
+ onPhotoEventUI(PhotoEvent(type, file, message))
+ }
+ WHAT_VIDEO_EVENT -> {
+ if (eventData.getString(FILE) != null) {
+ file = File(eventData.getString(FILE)!!)
+ }
+ onVideoEventUI(VideoEvent(type, file, message))
+ }
+ WHAT_CAMERA_CLOSE_EVENT -> onCameraCloseUI()
+ WHAT_CAMERA_OPEN_EVENT -> onCameraOpenUI()
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ override fun onPhotoEvent(event: PhotoEvent) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ onPhotoEventUI(event)
+ return
+ }
+ ensureHandler()
+ val message = handler!!.obtainMessage()
+ message.what = WHAT_PHOTO_EVENT
+ val bundle = Bundle()
+ bundle.putString(MESSAGE, event.message)
+ bundle.putSerializable(TYPE, event.type)
+ if (event.file != null) {
+ bundle.putString(FILE, event.file!!.path)
+ }
+ message.data = bundle
+ handler!!.sendMessage(message)
+ }
+
+ override fun onVideoEvent(event: VideoEvent) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ onVideoEventUI(event)
+ return
+ }
+ ensureHandler()
+ val message = handler!!.obtainMessage()
+ message.what = WHAT_VIDEO_EVENT
+ val bundle = Bundle()
+ bundle.putString(MESSAGE, event.message)
+ bundle.putSerializable(TYPE, event.type)
+ if (event.file != null) {
+ bundle.putString(FILE, event.file!!.path)
+ }
+ message.data = bundle
+ handler!!.sendMessage(message)
+ }
+
+ override fun onCameraClose() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ onCameraCloseUI()
+ return
+ }
+ ensureHandler()
+ val message = handler!!.obtainMessage()
+ message.what = WHAT_CAMERA_CLOSE_EVENT
+ val bundle = Bundle()
+ message.data = bundle
+ handler!!.sendMessage(message)
+ }
+
+ override fun onCameraOpen() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ onCameraOpenUI()
+ return
+ }
+ ensureHandler()
+ val message = handler!!.obtainMessage()
+ message.what = WHAT_CAMERA_OPEN_EVENT
+ val bundle = Bundle()
+ message.data = bundle
+ handler!!.sendMessage(message)
+ }
+
+ abstract fun onCameraOpenUI()
+
+ abstract fun onCameraCloseUI()
+
+ abstract fun onPhotoEventUI(event: PhotoEvent)
+
+ abstract fun onVideoEventUI(event: VideoEvent)
+
+ companion object {
+ private val WHAT_PHOTO_EVENT = 0x01
+ private val WHAT_VIDEO_EVENT = 0x02
+ private val WHAT_CAMERA_CLOSE_EVENT = 0x03
+ private val WHAT_CAMERA_OPEN_EVENT = 0x04
+ private val MESSAGE = "message"
+ private val TYPE = "type"
+ private val FILE = "file"
+ }
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/EventType.java b/fancycamera/src/main/java/co/fitcom/fancycamera/EventType.kt
similarity index 71%
rename from fancycamera/src/main/java/co/fitcom/fancycamera/EventType.java
rename to fancycamera/src/main/java/co/fitcom/fancycamera/EventType.kt
index dc41254..159cc7c 100644
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/EventType.java
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/EventType.kt
@@ -5,10 +5,10 @@
*
*/
-package co.fitcom.fancycamera;
+package co.fitcom.fancycamera
-public enum EventType {
+enum class EventType {
ERROR,
INFO
}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/FancyCamera.java b/fancycamera/src/main/java/co/fitcom/fancycamera/FancyCamera.java
deleted file mode 100644
index 3604aba..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/FancyCamera.java
+++ /dev/null
@@ -1,493 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:43 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 7:58 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import android.Manifest;
-import android.app.Activity;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.res.TypedArray;
-import android.graphics.SurfaceTexture;
-import android.media.MediaRecorder;
-import android.os.Build;
-
-import androidx.annotation.Nullable;
-import androidx.core.app.ActivityCompat;
-import androidx.core.content.ContextCompat;
-
-import android.util.AttributeSet;
-import android.view.TextureView;
-import android.view.ViewGroup;
-
-import java.io.File;
-import java.io.IOException;
-
-
-public class FancyCamera extends TextureView implements TextureView.SurfaceTextureListener {
- private boolean mFlashEnabled = false;
- private boolean isStarted = false;
- private int mCameraPosition = 0;
- private int mCameraOrientation = 0;
- private int mQuality = Quality.MAX_480P.getValue();
- private final Object mLock = new Object();
- private CameraEventListener listener;
- private final int VIDEO_RECORDER_PERMISSIONS_REQUEST = 868;
- private String[] VIDEO_RECORDER_PERMISSIONS = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA};
- private boolean isReady = false;
- private CameraBase cameraBase;
- private MediaRecorder recorder;
- private boolean isGettingAudioLvls = false;
- static final private double EMA_FILTER = 0.6;
- private double mEMA = 0.0;
- private CameraEventListener internalListener;
-
- public FancyCamera(Context context) {
- super(context);
- init(context, null);
- }
-
- public FancyCamera(Context context, AttributeSet attrs) {
- super(context, attrs);
- init(context, attrs);
- }
-
- private void initListener() {
- if (isAudioLevelsEnabled()) {
- if (!hasPermission()) {
- return;
- }
- if (recorder != null) deInitListener();
- recorder = new MediaRecorder();
- recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
- recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
- recorder.setOutputFile("/dev/null");
- try {
- recorder.prepare();
- recorder.start();
- isGettingAudioLvls = true;
- mEMA = 0.0;
- } catch (IOException e) {
- // Need this ???
- e.printStackTrace();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- public void deInitListener() {
- if (isAudioLevelsEnabled() && isGettingAudioLvls) {
- try {
- recorder.stop();
- recorder.release();
- recorder = null;
- isGettingAudioLvls = false;
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
-
- private void init(Context context, @Nullable AttributeSet attrs) {
- if (Build.VERSION.SDK_INT >= 21) {
- cameraBase = new Camera2(getContext(), this, CameraPosition.values()[getCameraPosition()], CameraOrientation.values()[getCameraOrientation()]);
- } else {
- cameraBase = new Camera1(getContext(), this, CameraPosition.values()[getCameraPosition()]);
- }
-
- internalListener = new CameraEventListener() {
- @Override
- public void onCameraOpen() {
- initListener();
- if (listener != null) {
- listener.onCameraOpen();
- }
- }
-
- @Override
- public void onCameraClose() {
- deInitListener();
- if (listener != null) {
- listener.onCameraClose();
- }
- }
-
- @Override
- public void onPhotoEvent(PhotoEvent event) {
- if (listener != null) {
- listener.onPhotoEvent(event);
- }
- }
-
- @Override
- public void onVideoEvent(VideoEvent event) {
- if (listener != null) {
- listener.onVideoEvent(event);
- }
- }
- };
-
- cameraBase.setListener(internalListener);
-
- if (attrs != null) {
- TypedArray a = context.obtainStyledAttributes(
- attrs,
- R.styleable.FancyCamera);
-
- try {
- mFlashEnabled = a.getBoolean(R.styleable.FancyCamera_enableFlash, false);
- setSaveToGallery(a.getBoolean(R.styleable.FancyCamera_saveToGallery, false));
- mQuality = a.getInteger(R.styleable.FancyCamera_quality, Quality.MAX_480P.getValue());
- setQuality(mQuality);
- mCameraPosition = a.getInteger(R.styleable.FancyCamera_cameraPosition, 0);
- setCameraPosition(mCameraPosition);
- mCameraOrientation = a.getInteger(R.styleable.FancyCamera_cameraOrientation, 0);
- setCameraOrientation(mCameraOrientation);
- setDisableHEVC(a.getBoolean(R.styleable.FancyCamera_disableHEVC, false));
- setMaxAudioBitRate(a.getInteger(R.styleable.FancyCamera_maxAudioBitRate, -1));
- setMaxVideoBitrate(a.getInteger(R.styleable.FancyCamera_maxVideoBitrate, -1));
- setMaxVideoFrameRate(a.getInteger(R.styleable.FancyCamera_maxVideoFrameRate, -1));
- setEnableAudioLevels(a.getBoolean(R.styleable.FancyCamera_audioLevels, false));
-
- } finally {
- a.recycle();
- }
- }
- this.setSurfaceTextureListener(this);
- }
-
- public int getNumberOfCameras() {
- return cameraBase.getNumberOfCameras();
- }
-
- boolean getAutoSquareCrop() {
- return cameraBase.getAutoSquareCrop();
- }
-
- public void setAutoSquareCrop(boolean autoSquareCrop) {
- cameraBase.setAutoSquareCrop(autoSquareCrop);
- }
-
- public boolean getAutoFocus() {
- return cameraBase.getAutoFocus();
- }
-
- public void setAutoFocus(boolean focus) {
- cameraBase.setAutoFocus(focus);
- }
-
- public boolean getSaveToGallery() {
- return cameraBase.getSaveToGallery();
- }
-
- public void setSaveToGallery(boolean saveToGallery) {
- cameraBase.setSaveToGallery(saveToGallery);
- }
-
- public void setFile(File file) {
- cameraBase.setFile(file);
- }
-
- File getFile() {
- return cameraBase.getFile();
- }
-
- public boolean hasFlash() {
- return cameraBase.hasFlash();
- }
-
- public void toggleFlash() {
- cameraBase.toggleFlash();
- }
-
- public void enableFlash() {
- cameraBase.enableFlash();
- }
-
- public void disableFlash() {
- cameraBase.disableFlash();
- }
-
- public boolean flashEnabled() {
- return cameraBase.flashEnabled();
- }
-
- public boolean cameraStarted() {
- return cameraBase.cameraStarted();
- }
-
- public boolean cameraRecording() {
- return cameraBase.cameraRecording();
- }
-
- public void takePhoto() {
- cameraBase.takePhoto();
- }
-
- public int getCameraPosition() {
- return mCameraPosition;
- }
-
- public int getCameraOrientation() {
- return mCameraOrientation;
- }
-
- public int getDuration() {
- return cameraBase.getDuration();
- }
-
- public void setQuality(int quality) {
- mQuality = Quality.MAX_480P.getValue();
- switch (quality) {
- case 0:
- mQuality = 0;
- break;
- case 1:
- mQuality = 1;
- break;
- case 2:
- mQuality = 2;
- break;
- case 3:
- mQuality = 3;
- break;
- case 4:
- mQuality = 4;
- break;
- case 5:
- mQuality = 5;
- break;
- case 6:
- mQuality = 6;
- break;
-
- }
- cameraBase.setQuality(mQuality);
- }
-
- public void setListener(CameraEventListener listener) {
- this.listener = listener;
- }
-
- public void setCameraPosition(int position) {
- cameraBase.setCameraPosition(CameraPosition.values()[position]);
- }
-
- public void setCameraPosition(FancyCamera.CameraPosition position) {
- cameraBase.setCameraPosition(position);
- }
-
- public void setCameraOrientation(int orientation) {
- cameraBase.setCameraOrientation(CameraOrientation.values()[orientation]);
- }
-
- public void setCameraOrientation(FancyCamera.CameraOrientation orientation) {
- cameraBase.setCameraOrientation(orientation);
- }
-
- public void requestPermission() {
- ActivityCompat.requestPermissions((Activity) getContext(), VIDEO_RECORDER_PERMISSIONS, VIDEO_RECORDER_PERMISSIONS_REQUEST);
- }
-
- public boolean hasPermission() {
- if (Build.VERSION.SDK_INT < 23) {
- return true;
- }
- return ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == (PackageManager.PERMISSION_GRANTED) && ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO) == (PackageManager.PERMISSION_GRANTED);
- }
-
- public boolean hasStoragePermission() {
- return cameraBase.hasStoragePermission();
- }
-
- public void requestStoragePermission() {
- cameraBase.requestStoragePermission();
- }
-
- public void start() {
- cameraBase.start();
- }
-
- public void stopRecording() {
- cameraBase.stopRecording();
- }
-
- public void startRecording() {
- deInitListener();
- cameraBase.startRecording();
- }
-
- public void stop() {
- cameraBase.stop();
- }
-
- public void release() {
- cameraBase.release();
- deInitListener();
- }
-
- @Override
- public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
- if (cameraBase.getTextViewListener() != null) {
- cameraBase.getTextViewListener().onSurfaceTextureAvailable(surface, width, height);
- }
- if (!hasPermission()) {
- requestPermission();
- return;
- }
- cameraBase.openCamera(width, height);
- }
-
- @Override
- public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
- if (cameraBase.getTextViewListener() != null) {
- cameraBase.getTextViewListener().onSurfaceTextureSizeChanged(surface, width, height);
- }
- cameraBase.updatePreview();
- }
-
- @Override
- public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
- if (cameraBase.getTextViewListener() != null) {
- cameraBase.getTextViewListener().onSurfaceTextureDestroyed(surface);
- }
- return true;
- }
-
- @Override
- public void onSurfaceTextureUpdated(SurfaceTexture surface) {
- if (cameraBase.getTextViewListener() != null) {
- cameraBase.getTextViewListener().onSurfaceTextureUpdated(surface);
- }
- }
-
- public enum Quality {
- MAX_480P(0),
- MAX_720P(1),
- MAX_1080P(2),
- MAX_2160P(3),
- HIGHEST(4),
- LOWEST(5),
- QVGA(6);
- private int value;
-
- private Quality(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return value;
- }
- }
-
- public enum CameraOrientation {
- UNKNOWN(0),
- PORTRAIT(1),
- PORTRAIT_UPSIDE_DOWN(2),
- LANDSCAPE_LEFT(3),
- LANDSCAPE_RIGHT(4);
- private int value;
-
- CameraOrientation(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return value;
- }
- }
-
- public enum CameraPosition {
- BACK(0),
- FRONT(1);
- private int value;
-
- CameraPosition(int value) {
- this.value = value;
- }
-
- public int getValue() {
- return value;
- }
- }
-
- public void toggleCamera() {
- cameraBase.toggleCamera();
- }
-
- public void setEnableAudioLevels(boolean enableAudioLevels) {
- cameraBase.setEnableAudioLevels(enableAudioLevels);
- }
-
- public boolean isAudioLevelsEnabled() {
- return cameraBase.isAudioLevelsEnabled();
- }
-
- public double getAmplitude() {
- double amp = 0;
- if (isAudioLevelsEnabled()) {
- if (cameraRecording()) {
- amp = cameraBase.getRecorder() != null ? cameraBase.getRecorder().getMaxAmplitude() : 0;
- return amp;
- }
- try {
- amp = recorder != null ? recorder.getMaxAmplitude() : 0;
- } catch (Exception ignored) {
- amp = 0;
- }
- }
- return amp;
- }
-
- public double getDB() {
- return 20 * Math.log10(getAmplitude() / 32767.0);
- }
-
- public double getAmplitudeEMA() {
- double amp = getAmplitude();
- mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA;
- return mEMA;
- }
-
- public int getMaxAudioBitRate() {
- return cameraBase.getMaxAudioBitRate();
- }
-
-
- public int getMaxVideoBitrate() {
- return cameraBase.getMaxVideoBitrate();
- }
-
-
- public int getMaxVideoFrameRate() {
- return cameraBase.getMaxVideoFrameRate();
- }
-
-
- public boolean getDisableHEVC() {
- return cameraBase.getDisableHEVC();
- }
-
-
- public void setDisableHEVC(boolean disableHEVC) {
- cameraBase.setDisableHEVC(disableHEVC);
- }
-
- public void setMaxAudioBitRate(int maxAudioBitRate) {
- cameraBase.setMaxAudioBitRate(maxAudioBitRate);
- }
-
- public void setMaxVideoBitrate(int maxVideoBitrate) {
- cameraBase.setMaxVideoBitrate(maxVideoBitrate);
- }
-
- public void setMaxVideoFrameRate(int maxVideoFrameRate) {
- cameraBase.setMaxVideoFrameRate(maxVideoFrameRate);
- }
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/FancyCamera.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/FancyCamera.kt
new file mode 100644
index 0000000..6fb86cd
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/FancyCamera.kt
@@ -0,0 +1,413 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:43 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 7:58 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import android.Manifest
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.TypedArray
+import android.graphics.SurfaceTexture
+import android.media.MediaRecorder
+import android.os.Build
+import androidx.core.app.ActivityCompat
+import androidx.core.content.ContextCompat
+
+import android.util.AttributeSet
+import android.view.TextureView
+import android.view.ViewGroup
+
+import java.io.File
+import java.io.IOException
+
+
+class FancyCamera : TextureView, TextureView.SurfaceTextureListener {
+ private var mFlashEnabled = false
+ private val isStarted = false
+ private var mCameraPosition = 0
+ private var mCameraOrientation = 0
+ private var mQuality = Quality.MAX_480P.value
+ private val mLock = Any()
+ private var listener: CameraEventListener? = null
+ private val isReady = false
+ private var cameraBase: CameraBase? = null
+ private var recorder: MediaRecorder? = null
+ private var isGettingAudioLvls = false
+ private var mEMA = 0.0
+ private var internalListener: CameraEventListener? = null
+
+ val numberOfCameras: Int
+ get() = cameraBase!!.numberOfCameras
+
+ var autoSquareCrop: Boolean
+ get() = cameraBase!!.autoSquareCrop
+ set(autoSquareCrop) {
+ cameraBase!!.autoSquareCrop = autoSquareCrop
+ }
+
+ var autoFocus: Boolean
+ get() = cameraBase!!.autoFocus
+ set(focus) {
+ cameraBase!!.autoFocus = focus
+ }
+
+ var saveToGallery: Boolean
+ get() = cameraBase!!.saveToGallery
+ set(saveToGallery) {
+ cameraBase!!.saveToGallery = saveToGallery
+ }
+
+ internal var file: File?
+ get() = cameraBase!!.file
+ set(file) {
+ cameraBase!!.file = file
+ }
+
+ var cameraPosition: Int
+ get() = mCameraPosition
+ set(position) = cameraBase!!.setCameraPosition(CameraPosition.values()[position])
+
+ var cameraOrientation: Int
+ get() = mCameraOrientation
+ set(orientation) = cameraBase!!.setCameraOrientation(CameraOrientation.values()[orientation])
+
+ val duration: Int
+ get() = cameraBase!!.duration
+
+ val isAudioLevelsEnabled: Boolean
+ get() = cameraBase!!.isAudioLevelsEnabled
+
+ val amplitude: Double
+ get() {
+ var amp = 0.0
+ if (isAudioLevelsEnabled) {
+ if (cameraRecording()) {
+ amp = (if (cameraBase!!.recorder != null) cameraBase!!.recorder?.maxAmplitude else 0)!!.toDouble()
+ return amp
+ }
+ try {
+ amp = (if (recorder != null) recorder!!.maxAmplitude else 0).toDouble()
+ } catch (ignored: Exception) {
+ amp = 0.0
+ }
+
+ }
+ return amp
+ }
+
+ val db: Double
+ get() = 20 * Math.log10(amplitude / 32767.0)
+
+ val amplitudeEMA: Double
+ get() {
+ val amp = amplitude
+ mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA
+ return mEMA
+ }
+
+ var maxAudioBitRate: Int
+ get() = cameraBase!!.maxAudioBitRate
+ set(maxAudioBitRate) {
+ cameraBase!!.maxAudioBitRate = maxAudioBitRate
+ }
+
+
+ var maxVideoBitrate: Int
+ get() = cameraBase!!.maxVideoBitrate
+ set(maxVideoBitrate) {
+ cameraBase!!.maxVideoBitrate = maxVideoBitrate
+ }
+
+
+ var maxVideoFrameRate: Int
+ get() = cameraBase!!.maxVideoFrameRate
+ set(maxVideoFrameRate) {
+ cameraBase!!.maxVideoFrameRate = maxVideoFrameRate
+ }
+
+
+ var disableHEVC: Boolean
+ get() = cameraBase!!.disableHEVC
+ set(disableHEVC) {
+ cameraBase!!.disableHEVC = disableHEVC
+ }
+
+ constructor(context: Context) : super(context) {
+ init(context, null)
+ }
+
+ constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
+ init(context, attrs)
+ }
+
+ private fun initListener() {
+ if (isAudioLevelsEnabled) {
+ if (!hasPermission()) {
+ return
+ }
+ if (recorder != null) deInitListener()
+ recorder = MediaRecorder()
+ recorder!!.setAudioSource(MediaRecorder.AudioSource.MIC)
+ recorder!!.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
+ recorder!!.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
+ recorder!!.setOutputFile("/dev/null")
+ try {
+ recorder!!.prepare()
+ recorder!!.start()
+ isGettingAudioLvls = true
+ mEMA = 0.0
+ } catch (e: IOException) {
+ // Need this ???
+ e.printStackTrace()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ }
+ }
+
+ fun deInitListener() {
+ if (isAudioLevelsEnabled && isGettingAudioLvls) {
+ try {
+ recorder!!.stop()
+ recorder!!.release()
+ recorder = null
+ isGettingAudioLvls = false
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ }
+ }
+
+ private fun init(context: Context, attrs: AttributeSet?) {
+ if (Build.VERSION.SDK_INT >= 21) {
+ cameraBase = Camera2(getContext(), this, CameraPosition.values()[cameraPosition], CameraOrientation.values()[cameraOrientation])
+ } else {
+ cameraBase = Camera1(getContext(), this, CameraPosition.values()[cameraPosition])
+ }
+
+ internalListener = object : CameraEventListener {
+ override fun onCameraOpen() {
+ initListener()
+ if (listener != null) {
+ listener!!.onCameraOpen()
+ }
+ }
+
+ override fun onCameraClose() {
+ deInitListener()
+ if (listener != null) {
+ listener!!.onCameraClose()
+ }
+ }
+
+ override fun onPhotoEvent(event: PhotoEvent) {
+ if (listener != null) {
+ listener!!.onPhotoEvent(event)
+ }
+ }
+
+ override fun onVideoEvent(event: VideoEvent) {
+ if (listener != null) {
+ listener!!.onVideoEvent(event)
+ }
+ }
+ }
+
+ cameraBase!!.listener = internalListener
+
+ if (attrs != null) {
+ val a = context.obtainStyledAttributes(
+ attrs,
+ R.styleable.FancyCamera)
+
+ try {
+ mFlashEnabled = a.getBoolean(R.styleable.FancyCamera_enableFlash, false)
+ saveToGallery = a.getBoolean(R.styleable.FancyCamera_saveToGallery, false)
+ mQuality = a.getInteger(R.styleable.FancyCamera_quality, Quality.MAX_480P.value)
+ setQuality(mQuality)
+ mCameraPosition = a.getInteger(R.styleable.FancyCamera_cameraPosition, 0)
+ cameraPosition = mCameraPosition
+ mCameraOrientation = a.getInteger(R.styleable.FancyCamera_cameraOrientation, 0)
+ cameraOrientation = mCameraOrientation
+ disableHEVC = a.getBoolean(R.styleable.FancyCamera_disableHEVC, false)
+ maxAudioBitRate = a.getInteger(R.styleable.FancyCamera_maxAudioBitRate, -1)
+ maxVideoBitrate = a.getInteger(R.styleable.FancyCamera_maxVideoBitrate, -1)
+ maxVideoFrameRate = a.getInteger(R.styleable.FancyCamera_maxVideoFrameRate, -1)
+ setEnableAudioLevels(a.getBoolean(R.styleable.FancyCamera_audioLevels, false))
+
+ } finally {
+ a.recycle()
+ }
+ }
+ this.surfaceTextureListener = this
+ }
+
+ fun hasFlash(): Boolean {
+ return cameraBase!!.hasFlash()
+ }
+
+ fun toggleFlash() {
+ cameraBase!!.toggleFlash()
+ }
+
+ fun enableFlash() {
+ cameraBase!!.enableFlash()
+ }
+
+ fun disableFlash() {
+ cameraBase!!.disableFlash()
+ }
+
+ fun flashEnabled(): Boolean {
+ return cameraBase!!.flashEnabled()
+ }
+
+ fun cameraStarted(): Boolean {
+ return cameraBase!!.cameraStarted()
+ }
+
+ fun cameraRecording(): Boolean {
+ return cameraBase!!.cameraRecording()
+ }
+
+ fun takePhoto() {
+ cameraBase!!.takePhoto()
+ }
+
+ fun setQuality(quality: Int) {
+ mQuality = Quality.MAX_480P.value
+ when (quality) {
+ 0 -> mQuality = 0
+ 1 -> mQuality = 1
+ 2 -> mQuality = 2
+ 3 -> mQuality = 3
+ 4 -> mQuality = 4
+ 5 -> mQuality = 5
+ 6 -> mQuality = 6
+ }
+ cameraBase!!.quality = mQuality
+ }
+
+ fun setListener(listener: CameraEventListener) {
+ this.listener = listener
+ }
+
+ fun setCameraPosition(position: FancyCamera.CameraPosition) {
+ cameraBase!!.setCameraPosition(position)
+ }
+
+ fun setCameraOrientation(orientation: FancyCamera.CameraOrientation) {
+ cameraBase!!.setCameraOrientation(orientation)
+ }
+
+ fun requestPermission() {
+ cameraBase!!.requestPermission()
+ }
+
+ fun hasPermission(): Boolean {
+ return cameraBase!!.hasPermission()
+ }
+
+ fun hasStoragePermission(): Boolean {
+ return cameraBase!!.hasStoragePermission()
+ }
+
+ fun requestStoragePermission() {
+ cameraBase!!.requestStoragePermission()
+ }
+
+ fun start() {
+ cameraBase!!.start()
+ }
+
+ fun stopRecording() {
+ cameraBase!!.stopRecording()
+ }
+
+ fun startRecording() {
+ deInitListener()
+ cameraBase!!.startRecording()
+ }
+
+ fun stop() {
+ cameraBase!!.stop()
+ }
+
+ fun release() {
+ cameraBase!!.release()
+ deInitListener()
+ }
+
+ override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
+ if (cameraBase!!.textViewListener != null) {
+ cameraBase!!.textViewListener!!.onSurfaceTextureAvailable(surface, width, height)
+ }
+ if (!hasPermission()) {
+ requestPermission()
+ return
+ }
+ cameraBase!!.openCamera(width, height)
+ }
+
+ override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
+ if (cameraBase!!.textViewListener != null) {
+ cameraBase!!.textViewListener!!.onSurfaceTextureSizeChanged(surface, width, height)
+ }
+ cameraBase!!.updatePreview()
+ }
+
+ override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
+ if (cameraBase!!.textViewListener != null) {
+ cameraBase!!.textViewListener!!.onSurfaceTextureDestroyed(surface)
+ }
+ return true
+ }
+
+ override fun onSurfaceTextureUpdated(surface: SurfaceTexture) {
+ if (cameraBase!!.textViewListener != null) {
+ cameraBase!!.textViewListener!!.onSurfaceTextureUpdated(surface)
+ }
+ }
+
+ enum class Quality private constructor(val value: Int) {
+ MAX_480P(0),
+ MAX_720P(1),
+ MAX_1080P(2),
+ MAX_2160P(3),
+ HIGHEST(4),
+ LOWEST(5),
+ QVGA(6)
+ }
+
+ enum class CameraOrientation private constructor(val value: Int) {
+ UNKNOWN(0),
+ PORTRAIT(1),
+ PORTRAIT_UPSIDE_DOWN(2),
+ LANDSCAPE_LEFT(3),
+ LANDSCAPE_RIGHT(4)
+ }
+
+ enum class CameraPosition private constructor(val value: Int) {
+ BACK(0),
+ FRONT(1)
+ }
+
+ fun toggleCamera() {
+ cameraBase!!.toggleCamera()
+ }
+
+ fun setEnableAudioLevels(enableAudioLevels: Boolean) {
+ cameraBase!!.setEnableAudioLevels(enableAudioLevels)
+ }
+
+ companion object {
+ private val EMA_FILTER = 0.6
+ }
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/PhotoEvent.java b/fancycamera/src/main/java/co/fitcom/fancycamera/PhotoEvent.java
deleted file mode 100644
index ba83595..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/PhotoEvent.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:43 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 7:39 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import androidx.annotation.Nullable;
-
-import java.io.File;
-
-
-public class PhotoEvent {
- private EventType mType;
- private File mFile;
- private String mMessage;
-
- PhotoEvent(EventType type, @Nullable File file, @Nullable String message) {
- mType = type;
- mFile = file;
- mMessage = message;
- }
-
- public EventType getType() {
- return mType;
- }
-
- public File getFile() {
- return mFile;
- }
-
- public String getMessage() {
- return mMessage;
- }
-
- public enum EventError {
- UNKNOWN {
- @Override
- public String toString() {
- return "Unknown";
- }
- }
- }
-
- public enum EventInfo {
- PHOTO_TAKEN {
- @Override
- public String toString() {
- return "Photo taken";
- }
- },
- UNKNOWN {
- @Override
- public String toString() {
- return "Unknown";
- }
- }
- }
-
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/PhotoEvent.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/PhotoEvent.kt
new file mode 100644
index 0000000..132ce14
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/PhotoEvent.kt
@@ -0,0 +1,36 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:43 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 7:39 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import java.io.File
+
+
+class PhotoEvent internal constructor(val type: EventType, val file: File?, val message: String?) {
+
+ enum class EventError {
+ UNKNOWN {
+ override fun toString(): String {
+ return "Unknown"
+ }
+ }
+ }
+
+ enum class EventInfo {
+ PHOTO_TAKEN {
+ override fun toString(): String {
+ return "Photo taken"
+ }
+ },
+ UNKNOWN {
+ override fun toString(): String {
+ return "Unknown"
+ }
+ }
+ }
+
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/TextViewListener.java b/fancycamera/src/main/java/co/fitcom/fancycamera/TextViewListener.java
deleted file mode 100644
index 42d57d8..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/TextViewListener.java
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- * Created By Osei Fortune on 3/24/18 5:18 PM
- * Copyright (c) 2018
- * Last modified 3/24/18 5:18 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import android.graphics.SurfaceTexture;
-
-/**
- * Created by triniwiz on 3/24/18
- */
-public interface TextViewListener {
- void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height);
- void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height);
- void onSurfaceTextureDestroyed(SurfaceTexture surface);
- void onSurfaceTextureUpdated(SurfaceTexture surface);
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/TextViewListener.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/TextViewListener.kt
new file mode 100644
index 0000000..3ee52b7
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/TextViewListener.kt
@@ -0,0 +1,20 @@
+/*
+ * Created By Osei Fortune on 3/24/18 5:18 PM
+ * Copyright (c) 2018
+ * Last modified 3/24/18 5:18 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import android.graphics.SurfaceTexture
+
+/**
+ * Created by triniwiz on 3/24/18
+ */
+interface TextViewListener {
+ fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int)
+ fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int)
+ fun onSurfaceTextureDestroyed(surface: SurfaceTexture)
+ fun onSurfaceTextureUpdated(surface: SurfaceTexture)
+}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/VideoEvent.java b/fancycamera/src/main/java/co/fitcom/fancycamera/VideoEvent.java
deleted file mode 100644
index 8d77fe2..0000000
--- a/fancycamera/src/main/java/co/fitcom/fancycamera/VideoEvent.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Created By Osei Fortune on 2/16/18 8:44 PM
- * Copyright (c) 2018
- * Last modified 2/16/18 7:39 PM
- *
- */
-
-package co.fitcom.fancycamera;
-
-import androidx.annotation.Nullable;
-
-import java.io.File;
-
-public class VideoEvent {
- private EventType mType;
- private File mFile;
- private String mMessage;
- VideoEvent(EventType type, @Nullable File file, @Nullable String message){
- mType = type;
- mFile = file;
- mMessage = message;
- }
- public EventType getType(){
- return mType;
- }
- public File getFile(){
- return mFile;
- }
-
- public String getMessage(){
- return mMessage;
- }
-
- public enum EventError{
- SERVER_DIED{
- @Override
- public String toString() {
- return "Server died";
- }
- },
- UNKNOWN{
- @Override
- public String toString() {
- return "Unknown";
- }
- }
- }
-
- public enum EventInfo{
- RECORDING_STARTED{
- @Override
- public String toString() {
- return "Recording started";
- }
- },
- RECORDING_FINISHED{
- @Override
- public String toString() {
- return "Recording finished";
- }
- },
- MAX_DURATION_REACHED {
- @Override
- public String toString() {
- return "Max duration reached";
- }
- },
- MAX_FILESIZE_APPROACHING{
- @Override
- public String toString() {
- return "Max filesize approaching";
- }
- },
- MAX_FILESIZE_REACHED{
- @Override
- public String toString() {
- return "Max filesize reached";
- }
- },
- NEXT_OUTPUT_FILE_STARTED{
- @Override
- public String toString() {
- return "Next output file started";
- }
- },
- UNKNOWN{
- @Override
- public String toString() {
- return "Unknown";
- }
- }
- }
-}
diff --git a/fancycamera/src/main/java/co/fitcom/fancycamera/VideoEvent.kt b/fancycamera/src/main/java/co/fitcom/fancycamera/VideoEvent.kt
new file mode 100644
index 0000000..e5e38cc
--- /dev/null
+++ b/fancycamera/src/main/java/co/fitcom/fancycamera/VideoEvent.kt
@@ -0,0 +1,64 @@
+/*
+ * Created By Osei Fortune on 2/16/18 8:44 PM
+ * Copyright (c) 2018
+ * Last modified 2/16/18 7:39 PM
+ *
+ */
+
+package co.fitcom.fancycamera
+
+import java.io.File
+
+class VideoEvent internal constructor(val type: EventType, val file: File?, val message: String?) {
+
+ enum class EventError {
+ SERVER_DIED {
+ override fun toString(): String {
+ return "Server died"
+ }
+ },
+ UNKNOWN {
+ override fun toString(): String {
+ return "Unknown"
+ }
+ }
+ }
+
+ enum class EventInfo {
+ RECORDING_STARTED {
+ override fun toString(): String {
+ return "Recording started"
+ }
+ },
+ RECORDING_FINISHED {
+ override fun toString(): String {
+ return "Recording finished"
+ }
+ },
+ MAX_DURATION_REACHED {
+ override fun toString(): String {
+ return "Max duration reached"
+ }
+ },
+ MAX_FILESIZE_APPROACHING {
+ override fun toString(): String {
+ return "Max filesize approaching"
+ }
+ },
+ MAX_FILESIZE_REACHED {
+ override fun toString(): String {
+ return "Max filesize reached"
+ }
+ },
+ NEXT_OUTPUT_FILE_STARTED {
+ override fun toString(): String {
+ return "Next output file started"
+ }
+ },
+ UNKNOWN {
+ override fun toString(): String {
+ return "Unknown"
+ }
+ }
+ }
+}
diff --git a/fancycamera/src/test/java/co/fitcom/fancycamera/ExampleUnitTest.java b/fancycamera/src/test/java/co/fitcom/fancycamera/ExampleUnitTest.java
deleted file mode 100644
index b84bee9..0000000
--- a/fancycamera/src/test/java/co/fitcom/fancycamera/ExampleUnitTest.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package co.fitcom.fancycamera;
-
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
\ No newline at end of file
diff --git a/fancycamera/src/test/java/co/fitcom/fancycamera/ExampleUnitTest.kt b/fancycamera/src/test/java/co/fitcom/fancycamera/ExampleUnitTest.kt
new file mode 100644
index 0000000..41d6350
--- /dev/null
+++ b/fancycamera/src/test/java/co/fitcom/fancycamera/ExampleUnitTest.kt
@@ -0,0 +1,18 @@
+package co.fitcom.fancycamera
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see [Testing documentation](http://d.android.com/tools/testing)
+ */
+class ExampleUnitTest {
+ @Test
+ @Throws(Exception::class)
+ fun addition_isCorrect() {
+ assertEquals(4, (2 + 2).toLong())
+ }
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
index 40b1fd6..70a40c5 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -18,4 +18,7 @@ org.gradle.jvmargs=-Xmx1536m
BUILD_TOOLS_VERSION=28.0.3
COMPILE_SDK_VERSION=28
android.useAndroidX=true
-android.enableJetifier=true
\ No newline at end of file
+android.enableJetifier=true
+camerax_version = 1.0.0-alpha06
+camerax_view_version = 1.0.0-alpha03
+camerax_ext_version = 1.0.0-alpha03
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 9945ebb..70a9e44 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Aug 08 15:52:29 AST 2019
+#Sun Nov 17 18:15:25 AST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.1-all.zip