Add new changes GeoFencing-GeoTaging and hide Manual Inword Option etc
@@ -0,0 +1 @@
|
||||
/build
|
||||
@@ -0,0 +1,78 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
}
|
||||
|
||||
group = 'io.github.memfis19'
|
||||
version = '0.3.7'
|
||||
|
||||
ext {
|
||||
PUBLISH_GROUP_ID = 'io.github.memfis19'
|
||||
PUBLISH_ARTIFACT_ID = 'annca'
|
||||
PUBLISH_VERSION = '0.3.7'
|
||||
PUBLISH_VERSION_CODE = 11
|
||||
SUPPORT_VERSION = '25.3.1'
|
||||
TARGET_SDK = 35
|
||||
MIN_SDK = 24
|
||||
}
|
||||
|
||||
android {
|
||||
namespace ("io.github.memfis19.annca")
|
||||
compileSdk(35)
|
||||
defaultConfig {
|
||||
minSdkVersion MIN_SDK
|
||||
targetSdkVersion TARGET_SDK
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ✅ Sources JAR
|
||||
tasks.register('generateSourcesJar', Jar) {
|
||||
archiveClassifier.set('sources')
|
||||
from android.sourceSets.main.java.srcDirs
|
||||
}
|
||||
|
||||
// ✅ Javadocs Task
|
||||
tasks.register('generateJavadocs', Javadoc) {
|
||||
failOnError = false
|
||||
source = android.sourceSets.main.java.srcDirs
|
||||
classpath += project.files(android.bootClasspath.join(File.pathSeparator))
|
||||
}
|
||||
|
||||
// ✅ Javadocs JAR
|
||||
tasks.register("generateJavadocsJar", Jar) {
|
||||
archiveClassifier.set("javadoc")
|
||||
from(tasks.named("generateJavadocs"))
|
||||
}
|
||||
|
||||
tasks.named("generateJavadocsJar").configure {
|
||||
dependsOn tasks.named("generateJavadocs")
|
||||
}
|
||||
|
||||
// ✅ Publishing artifacts
|
||||
artifacts {
|
||||
archives(tasks.named("generateSourcesJar"))
|
||||
archives(tasks.named("generateJavadocsJar"))
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
androidTestImplementation 'androidx.annotation:annotation:1.2.0'
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
}
|
||||
|
||||
tasks.register('wrapper', Wrapper) {
|
||||
gradleVersion = '2.4'
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /Users/memfis/Library/Android/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -0,0 +1,11 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="io.github.memfis19.annca">
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:supportsRtl="true">
|
||||
|
||||
</application>
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.github.memfis19.annca;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.RequiresPermission;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.ui.camera.Camera1Activity;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class Annca {
|
||||
|
||||
private final AnncaConfiguration anncaConfiguration;
|
||||
|
||||
/***
|
||||
* Creates Annca instance with default configuration set to photo with medium quality.
|
||||
*
|
||||
* @param activity - fromList which request was invoked
|
||||
* @param requestCode - request code which will return in onActivityForResult
|
||||
*/
|
||||
public Annca(Activity activity, @IntRange(from = 0) int requestCode) {
|
||||
AnncaConfiguration.Builder builder = new AnncaConfiguration.Builder(activity, requestCode);
|
||||
anncaConfiguration = builder.build();
|
||||
}
|
||||
|
||||
public Annca(Fragment fragment, @IntRange(from = 0) int requestCode) {
|
||||
AnncaConfiguration.Builder builder = new AnncaConfiguration.Builder(fragment, requestCode);
|
||||
anncaConfiguration = builder.build();
|
||||
}
|
||||
|
||||
/***
|
||||
* Creates Annca instance with custom camera configuration.
|
||||
*
|
||||
*/
|
||||
public Annca(AnncaConfiguration cameraConfiguration) {
|
||||
this.anncaConfiguration = cameraConfiguration;
|
||||
}
|
||||
|
||||
@RequiresPermission(Manifest.permission.CAMERA)
|
||||
public void launchCamera(String path, boolean showGrid, int cameraFace) {
|
||||
if (anncaConfiguration == null || (anncaConfiguration.getActivity() == null &&
|
||||
anncaConfiguration.getFragment() == null))
|
||||
return;
|
||||
Intent cameraIntent;
|
||||
if (anncaConfiguration.getFragment() != null) {
|
||||
cameraIntent = new Intent(anncaConfiguration.getFragment().getContext(), Camera1Activity.class);
|
||||
} else {
|
||||
cameraIntent = new Intent(anncaConfiguration.getActivity(), Camera1Activity.class);
|
||||
}
|
||||
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.REQUEST_CODE, anncaConfiguration.getRequestCode());
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.CAMERA_FACE, cameraFace);
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.FILE_PATH, path);
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.SHOW_GRID, showGrid);
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.MEDIA_RESULT_BEHAVIOUR, anncaConfiguration.getMediaResultBehaviour());
|
||||
if (anncaConfiguration.getMediaAction() > 0)
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.MEDIA_ACTION, anncaConfiguration.getMediaAction());
|
||||
if (anncaConfiguration.getMediaQuality() > 0)
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.MEDIA_QUALITY, anncaConfiguration.getMediaQuality());
|
||||
if (anncaConfiguration.getVideoDuration() > 0)
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.VIDEO_DURATION, anncaConfiguration.getVideoDuration());
|
||||
if (anncaConfiguration.getVideoFileSize() > 0)
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.VIDEO_FILE_SIZE, anncaConfiguration.getVideoFileSize());
|
||||
if (anncaConfiguration.getMinimumVideoDuration() > 0)
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.MINIMUM_VIDEO_DURATION, anncaConfiguration.getMinimumVideoDuration());
|
||||
cameraIntent.putExtra(AnncaConfiguration.Arguments.FLASH_MODE, anncaConfiguration.getFlashMode());
|
||||
if (anncaConfiguration.getFragment() != null) {
|
||||
anncaConfiguration.getFragment().startActivityForResult(cameraIntent, anncaConfiguration.getRequestCode());
|
||||
} else {
|
||||
anncaConfiguration.getActivity().startActivityForResult(cameraIntent, anncaConfiguration.getRequestCode());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package io.github.memfis19.annca.internal.configuration;
|
||||
|
||||
import android.app.Activity;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public final class AnncaConfiguration {
|
||||
|
||||
public static final int MEDIA_QUALITY_AUTO = 10;
|
||||
public static final int MEDIA_QUALITY_LOWEST = 15;
|
||||
public static final int MEDIA_QUALITY_LOW = 11;
|
||||
public static final int MEDIA_QUALITY_MEDIUM = 12;
|
||||
public static final int MEDIA_QUALITY_HIGH = 13;
|
||||
public static final int MEDIA_QUALITY_HIGHEST = 14;
|
||||
|
||||
public static final int MEDIA_ACTION_VIDEO = 100;
|
||||
public static final int MEDIA_ACTION_PHOTO = 101;
|
||||
public static final int MEDIA_ACTION_UNSPECIFIED = 102;
|
||||
|
||||
public static final int CAMERA_FACE_FRONT = 0x6;
|
||||
public static final int CAMERA_FACE_REAR = 0x7;
|
||||
|
||||
public static final int SENSOR_POSITION_UP = 90;
|
||||
public static final int SENSOR_POSITION_UP_SIDE_DOWN = 270;
|
||||
public static final int SENSOR_POSITION_LEFT = 0;
|
||||
public static final int SENSOR_POSITION_RIGHT = 180;
|
||||
public static final int SENSOR_POSITION_UNSPECIFIED = -1;
|
||||
|
||||
public static final int DISPLAY_ROTATION_0 = 0;
|
||||
public static final int DISPLAY_ROTATION_90 = 90;
|
||||
public static final int DISPLAY_ROTATION_180 = 180;
|
||||
public static final int DISPLAY_ROTATION_270 = 270;
|
||||
|
||||
public static final int ORIENTATION_PORTRAIT = 0x111;
|
||||
public static final int ORIENTATION_LANDSCAPE = 0x222;
|
||||
|
||||
public static final int FLASH_MODE_ON = 1;
|
||||
public static final int FLASH_MODE_OFF = 2;
|
||||
public static final int FLASH_MODE_AUTO = 3;
|
||||
|
||||
public static final int PREVIEW = 1;
|
||||
public static final int CLOSE = 2;
|
||||
public static final int CONTINUE = 3;
|
||||
|
||||
public interface Arguments {
|
||||
String REQUEST_CODE = "io.memfis19.annca.request_code";
|
||||
String MEDIA_ACTION = "io.memfis19.annca.media_action";
|
||||
String MEDIA_QUALITY = "io.memfis19.annca.camera_media_quality";
|
||||
String VIDEO_DURATION = "io.memfis19.annca.video_duration";
|
||||
String MINIMUM_VIDEO_DURATION = "io.memfis19.annca.minimum.video_duration";
|
||||
String VIDEO_FILE_SIZE = "io.memfis19.annca.camera_video_file_size";
|
||||
String FLASH_MODE = "io.memfis19.annca.camera_flash_mode";
|
||||
String FILE_PATH = "io.memfis19.annca.camera_video_file_path";
|
||||
String SHOW_GRID = "showGrid";
|
||||
String CAMERA_FACE = "io.memfis19.annca.camera_face";
|
||||
String MEDIA_RESULT_BEHAVIOUR = "io.memfis19.annca.media_result_behaviour";
|
||||
}
|
||||
|
||||
@IntDef({MEDIA_QUALITY_AUTO, MEDIA_QUALITY_LOWEST, MEDIA_QUALITY_LOW, MEDIA_QUALITY_MEDIUM, MEDIA_QUALITY_HIGH, MEDIA_QUALITY_HIGHEST})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface MediaQuality {
|
||||
}
|
||||
|
||||
@IntDef({PREVIEW, CLOSE, CONTINUE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface MediaResultBehaviour {
|
||||
}
|
||||
|
||||
@IntDef({MEDIA_ACTION_VIDEO, MEDIA_ACTION_PHOTO, MEDIA_ACTION_UNSPECIFIED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface MediaAction {
|
||||
}
|
||||
|
||||
@IntDef({FLASH_MODE_ON, FLASH_MODE_OFF, FLASH_MODE_AUTO})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FlashMode {
|
||||
}
|
||||
|
||||
@IntDef({CAMERA_FACE_FRONT, CAMERA_FACE_REAR})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface CameraFace {
|
||||
}
|
||||
|
||||
@IntDef({SENSOR_POSITION_UP, SENSOR_POSITION_UP_SIDE_DOWN, SENSOR_POSITION_LEFT, SENSOR_POSITION_RIGHT, SENSOR_POSITION_UNSPECIFIED})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface SensorPosition {
|
||||
}
|
||||
|
||||
@IntDef({DISPLAY_ROTATION_0, DISPLAY_ROTATION_90, DISPLAY_ROTATION_180, DISPLAY_ROTATION_270})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DisplayRotation {
|
||||
}
|
||||
|
||||
@IntDef({ORIENTATION_PORTRAIT, ORIENTATION_LANDSCAPE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface DeviceDefaultOrientation {
|
||||
}
|
||||
|
||||
private Activity activity = null;
|
||||
private Fragment fragment = null;
|
||||
|
||||
private int requestCode = -1;
|
||||
|
||||
@MediaAction
|
||||
private int mediaAction = -1;
|
||||
|
||||
@MediaResultBehaviour
|
||||
private int mediaResultBehaviour = PREVIEW;
|
||||
|
||||
@MediaQuality
|
||||
private int mediaQuality = -1;
|
||||
|
||||
@CameraFace
|
||||
private int cameraFace = CAMERA_FACE_REAR;
|
||||
|
||||
private int videoDuration = -1;
|
||||
|
||||
private long videoFileSize = -1;
|
||||
|
||||
private int minimumVideoDuration = -1;
|
||||
|
||||
private String outPutFilePath = "";
|
||||
|
||||
@FlashMode
|
||||
private int flashMode = FLASH_MODE_AUTO;
|
||||
|
||||
private AnncaConfiguration(Activity activity, int requestCode) {
|
||||
this.activity = activity;
|
||||
this.requestCode = requestCode;
|
||||
}
|
||||
|
||||
private AnncaConfiguration(@NonNull Fragment fragment, int requestCode) {
|
||||
this.fragment = fragment;
|
||||
this.requestCode = requestCode;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private AnncaConfiguration anncaConfiguration;
|
||||
|
||||
|
||||
public Builder(@NonNull Activity activity, @IntRange(from = 0) int requestCode) {
|
||||
anncaConfiguration = new AnncaConfiguration(activity, requestCode);
|
||||
}
|
||||
|
||||
public Builder(@NonNull Fragment fragment, @IntRange(from = 0) int requestCode) {
|
||||
anncaConfiguration = new AnncaConfiguration(fragment, requestCode);
|
||||
}
|
||||
|
||||
public Builder setMediaAction(@MediaAction int mediaAction) {
|
||||
anncaConfiguration.mediaAction = mediaAction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setCameraFace(@CameraFace int cameraFace) {
|
||||
anncaConfiguration.cameraFace = cameraFace;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMediaResultBehaviour(@MediaResultBehaviour int mediaResultBehaviour) {
|
||||
anncaConfiguration.mediaResultBehaviour = mediaResultBehaviour;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setMediaQuality(@MediaQuality int mediaQuality) {
|
||||
anncaConfiguration.mediaQuality = mediaQuality;
|
||||
return this;
|
||||
}
|
||||
|
||||
/***
|
||||
* @param videoDurationInMilliseconds - video duration in milliseconds
|
||||
* @return
|
||||
*/
|
||||
public Builder setVideoDuration(@IntRange(from = 1000, to = Integer.MAX_VALUE) int videoDurationInMilliseconds) {
|
||||
anncaConfiguration.videoDuration = videoDurationInMilliseconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
/***
|
||||
* @param minimumVideoDurationInMilliseconds - minimum video duration in milliseconds, used only in video mode
|
||||
* for auto quality.
|
||||
* @return
|
||||
*/
|
||||
public Builder setMinimumVideoDuration(@IntRange(from = 1000, to = Integer.MAX_VALUE) int minimumVideoDurationInMilliseconds) {
|
||||
anncaConfiguration.minimumVideoDuration = minimumVideoDurationInMilliseconds;
|
||||
return this;
|
||||
}
|
||||
|
||||
/***
|
||||
* @param videoSizeInBytes - file size in bytes
|
||||
* @return
|
||||
*/
|
||||
public Builder setVideoFileSize(@IntRange(from = 1048576, to = Long.MAX_VALUE) long videoSizeInBytes) {
|
||||
anncaConfiguration.videoFileSize = videoSizeInBytes;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFlashMode(@FlashMode int flashMode) {
|
||||
anncaConfiguration.flashMode = flashMode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnncaConfiguration build() throws IllegalArgumentException {
|
||||
if (anncaConfiguration.requestCode < 0)
|
||||
throw new IllegalArgumentException("Wrong request code value. Please set the value > 0.");
|
||||
if (anncaConfiguration.mediaQuality == MEDIA_QUALITY_AUTO && anncaConfiguration.minimumVideoDuration < 0) {
|
||||
throw new IllegalStateException("Please provide minimum video duration in milliseconds to use auto quality.");
|
||||
}
|
||||
|
||||
return anncaConfiguration;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Activity getActivity() {
|
||||
return activity;
|
||||
}
|
||||
|
||||
public Fragment getFragment() {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
public int getRequestCode() {
|
||||
return requestCode;
|
||||
}
|
||||
|
||||
public int getMediaAction() {
|
||||
return mediaAction;
|
||||
}
|
||||
|
||||
public int getMediaQuality() {
|
||||
return mediaQuality;
|
||||
}
|
||||
|
||||
public int getCameraFace() {
|
||||
return cameraFace;
|
||||
}
|
||||
|
||||
public int getMediaResultBehaviour() {
|
||||
return mediaResultBehaviour;
|
||||
}
|
||||
|
||||
public String getOutPutFilePath() {
|
||||
return outPutFilePath;
|
||||
}
|
||||
|
||||
public int getVideoDuration() {
|
||||
return videoDuration;
|
||||
}
|
||||
|
||||
public long getVideoFileSize() {
|
||||
return videoFileSize;
|
||||
}
|
||||
|
||||
public int getMinimumVideoDuration() {
|
||||
return minimumVideoDuration;
|
||||
}
|
||||
|
||||
public int getFlashMode() {
|
||||
return flashMode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package io.github.memfis19.annca.internal.configuration;
|
||||
|
||||
import io.github.memfis19.annca.internal.ui.view.CameraSwitchView;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public interface ConfigurationProvider {
|
||||
|
||||
int getRequestCode();
|
||||
|
||||
@AnncaConfiguration.MediaAction
|
||||
int getMediaAction();
|
||||
|
||||
@AnncaConfiguration.MediaQuality
|
||||
int getMediaQuality();
|
||||
|
||||
int getVideoDuration();
|
||||
|
||||
long getVideoFileSize();
|
||||
|
||||
@AnncaConfiguration.SensorPosition
|
||||
int getSensorPosition();
|
||||
|
||||
int getDegrees();
|
||||
|
||||
int getMinimumVideoDuration();
|
||||
|
||||
@AnncaConfiguration.FlashMode
|
||||
int getFlashMode();
|
||||
|
||||
@CameraSwitchView.CameraType
|
||||
int getCameraFace();
|
||||
|
||||
String getFilePath();
|
||||
|
||||
boolean getShowGrid();
|
||||
|
||||
@AnncaConfiguration.MediaResultBehaviour
|
||||
int getMediaResultBehaviour();
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package io.github.memfis19.annca.internal.controller;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.manager.CameraManager;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public interface CameraController<CameraId> {
|
||||
|
||||
void onCreate(Bundle savedInstanceState);
|
||||
|
||||
void onResume();
|
||||
|
||||
void onPause();
|
||||
|
||||
void onDestroy();
|
||||
|
||||
void takePhoto();
|
||||
|
||||
void startVideoRecord();
|
||||
|
||||
void stopVideoRecord();
|
||||
|
||||
void openCamera();
|
||||
|
||||
boolean isVideoRecording();
|
||||
|
||||
void switchCamera(@AnncaConfiguration.CameraFace int cameraFace);
|
||||
|
||||
void switchQuality();
|
||||
|
||||
void setFlashMode(@AnncaConfiguration.FlashMode int flashMode);
|
||||
|
||||
int getNumberOfCameras();
|
||||
|
||||
@SuppressLint("SupportAnnotationUsage")
|
||||
@AnncaConfiguration.MediaAction
|
||||
|
||||
CameraId getCurrentCameraId();
|
||||
|
||||
int getMediaAction();
|
||||
|
||||
File getOutputFile();
|
||||
|
||||
CameraManager getCameraManager();
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
package io.github.memfis19.annca.internal.controller.impl;
|
||||
|
||||
import android.hardware.Camera;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.controller.view.CameraView;
|
||||
import io.github.memfis19.annca.internal.manager.CameraManager;
|
||||
import io.github.memfis19.annca.internal.manager.impl.Camera1Manager;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraCloseListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraOpenListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraPhotoListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraVideoListener;
|
||||
import io.github.memfis19.annca.internal.ui.view.AutoFitSurfaceView;
|
||||
import io.github.memfis19.annca.internal.ui.view.CameraSwitchView;
|
||||
import io.github.memfis19.annca.internal.utils.CameraHelper;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/7/16.
|
||||
*/
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Camera1Controller implements io.github.memfis19.annca.internal.controller.CameraController<Integer>,
|
||||
CameraOpenListener<Integer, SurfaceHolder.Callback>, CameraPhotoListener, CameraCloseListener<Integer>, CameraVideoListener {
|
||||
|
||||
private final static String TAG = "Camera1Controller";
|
||||
|
||||
private Integer currentCameraId;
|
||||
private ConfigurationProvider configurationProvider;
|
||||
private CameraManager<Integer, SurfaceHolder.Callback, Camera.Parameters, Camera> cameraManager;
|
||||
private CameraView cameraView;
|
||||
|
||||
private File outputFile;
|
||||
|
||||
public Camera1Controller(CameraView cameraView, ConfigurationProvider configurationProvider) {
|
||||
this.cameraView = cameraView;
|
||||
this.configurationProvider = configurationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
cameraManager = Camera1Manager.getInstance();
|
||||
cameraManager.initializeCameraManager(configurationProvider, cameraView.getActivity());
|
||||
if (configurationProvider.getCameraFace() == CameraSwitchView.CAMERA_TYPE_FRONT) {
|
||||
currentCameraId = cameraManager.getFaceFrontCameraId() == null ? cameraManager.getFaceBackCameraId() : cameraManager.getFaceFrontCameraId();
|
||||
} else {
|
||||
currentCameraId = cameraManager.getFaceBackCameraId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openCamera() {
|
||||
cameraManager.openCamera(currentCameraId, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
openCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
cameraManager.closeCamera(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
cameraManager.releaseCameraManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takePhoto() {
|
||||
outputFile = TextUtils.isEmpty(configurationProvider. getFilePath()) ? CameraHelper.getOutputMediaFile(cameraView.getActivity(), AnncaConfiguration.MEDIA_ACTION_PHOTO) : new File(configurationProvider.getFilePath());
|
||||
cameraManager.takePhoto(outputFile, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startVideoRecord() {
|
||||
outputFile = TextUtils.isEmpty(configurationProvider.getFilePath()) ? CameraHelper.getOutputMediaFile(cameraView.getActivity(), AnncaConfiguration.MEDIA_ACTION_VIDEO) : new File(configurationProvider.getFilePath());
|
||||
cameraManager.startVideoRecord(outputFile, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopVideoRecord() {
|
||||
cameraManager.stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVideoRecording() {
|
||||
return cameraManager.isVideoRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchCamera(@AnncaConfiguration.CameraFace final int cameraFace) {
|
||||
currentCameraId = cameraManager.getCurrentCameraId().equals(cameraManager.getFaceFrontCameraId()) ?
|
||||
cameraManager.getFaceBackCameraId() : cameraManager.getFaceFrontCameraId();
|
||||
cameraManager.closeCamera(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlashMode(@AnncaConfiguration.FlashMode int flashMode) {
|
||||
cameraManager.setFlashMode(flashMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchQuality() {
|
||||
cameraManager.closeCamera(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfCameras() {
|
||||
return cameraManager.getNumberOfCameras();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediaAction() {
|
||||
return configurationProvider.getMediaAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutputFile() {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getCurrentCameraId() {
|
||||
return currentCameraId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraOpened(Integer cameraId, Size previewSize, SurfaceHolder.Callback surfaceCallback, Camera camera) {
|
||||
cameraView.updateUiForMediaAction(configurationProvider.getMediaAction());
|
||||
cameraView.updateCameraPreview(previewSize, new AutoFitSurfaceView(cameraView.getActivity(), surfaceCallback, camera, configurationProvider.getShowGrid()));
|
||||
cameraView.updateCameraSwitcher(getNumberOfCameras());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraReady() {
|
||||
cameraView.onCameraReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraOpenError() {
|
||||
//test(TAG, "onCameraOpenError");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraClosed(Integer closedCameraId) {
|
||||
cameraView.releaseCameraPreview();
|
||||
cameraManager.openCamera(currentCameraId, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhotoTaken(File photoFile) {
|
||||
cameraView.onPhotoTaken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhotoTakeError() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordStarted(Size videoSize) {
|
||||
cameraView.onVideoRecordStart(videoSize.getWidth(), videoSize.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordStopped(File videoFile) {
|
||||
cameraView.onVideoRecordStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordError() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraManager getCameraManager() {
|
||||
return cameraManager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package io.github.memfis19.annca.internal.controller.impl;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.hardware.Camera;
|
||||
import android.hardware.camera2.CameraDevice;
|
||||
import android.hardware.camera2.CaptureRequest;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.TextureView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.controller.CameraController;
|
||||
import io.github.memfis19.annca.internal.controller.view.CameraView;
|
||||
import io.github.memfis19.annca.internal.manager.CameraManager;
|
||||
import io.github.memfis19.annca.internal.manager.impl.Camera2Manager;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraCloseListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraOpenListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraPhotoListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraVideoListener;
|
||||
import io.github.memfis19.annca.internal.ui.view.AutoFitTextureView;
|
||||
import io.github.memfis19.annca.internal.ui.view.CameraSwitchView;
|
||||
import io.github.memfis19.annca.internal.utils.CameraHelper;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class Camera2Controller implements CameraController<String>,
|
||||
CameraOpenListener<String, TextureView.SurfaceTextureListener>,
|
||||
CameraPhotoListener, CameraVideoListener, CameraCloseListener<String> {
|
||||
|
||||
private final static String TAG = "Camera2Controller";
|
||||
|
||||
private String currentCameraId;
|
||||
private ConfigurationProvider configurationProvider;
|
||||
private CameraManager<String, TextureView.SurfaceTextureListener, CaptureRequest.Builder, CameraDevice> camera2Manager;
|
||||
private CameraView cameraView;
|
||||
|
||||
private File outputFile;
|
||||
|
||||
public Camera2Controller(CameraView cameraView, ConfigurationProvider configurationProvider) {
|
||||
this.cameraView = cameraView;
|
||||
this.configurationProvider = configurationProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
camera2Manager = Camera2Manager.getInstance();
|
||||
camera2Manager.initializeCameraManager(configurationProvider, cameraView.getActivity());
|
||||
|
||||
if (configurationProvider.getCameraFace() == CameraSwitchView.CAMERA_TYPE_FRONT) {
|
||||
currentCameraId = camera2Manager.getFaceFrontCameraId() == null ? camera2Manager.getFaceBackCameraId() : camera2Manager.getFaceFrontCameraId();
|
||||
} else {
|
||||
currentCameraId = camera2Manager.getFaceBackCameraId();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openCamera() {
|
||||
camera2Manager.closeCamera(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
openCamera();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
camera2Manager.closeCamera(null);
|
||||
cameraView.releaseCameraPreview();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
camera2Manager.releaseCameraManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takePhoto() {
|
||||
outputFile = TextUtils.isEmpty(configurationProvider.getFilePath()) ? CameraHelper.getOutputMediaFile(cameraView.getActivity(), AnncaConfiguration.MEDIA_ACTION_PHOTO) : new File(configurationProvider.getFilePath());
|
||||
camera2Manager.takePhoto(outputFile, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startVideoRecord() {
|
||||
outputFile = TextUtils.isEmpty(configurationProvider.getFilePath()) ? CameraHelper.getOutputMediaFile(cameraView.getActivity(), AnncaConfiguration.MEDIA_ACTION_VIDEO) : new File(configurationProvider.getFilePath());
|
||||
camera2Manager.startVideoRecord(outputFile, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopVideoRecord() {
|
||||
camera2Manager.stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVideoRecording() {
|
||||
return camera2Manager.isVideoRecording();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchCamera(final @AnncaConfiguration.CameraFace int cameraFace) {
|
||||
currentCameraId = camera2Manager.getCurrentCameraId().equals(camera2Manager.getFaceFrontCameraId()) ?
|
||||
camera2Manager.getFaceBackCameraId() : camera2Manager.getFaceFrontCameraId();
|
||||
|
||||
camera2Manager.closeCamera(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlashMode(@AnncaConfiguration.FlashMode int flashMode) {
|
||||
camera2Manager.setFlashMode(flashMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchQuality() {
|
||||
camera2Manager.closeCamera(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumberOfCameras() {
|
||||
return camera2Manager.getNumberOfCameras();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediaAction() {
|
||||
return configurationProvider.getMediaAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getOutputFile() {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentCameraId() {
|
||||
return currentCameraId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraOpened(String openedCameraId, Size previewSize, TextureView.SurfaceTextureListener surfaceTextureListener, Camera camera) {
|
||||
cameraView.updateUiForMediaAction(AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED);
|
||||
cameraView.updateCameraPreview(previewSize, new AutoFitTextureView(cameraView.getActivity(), surfaceTextureListener));
|
||||
cameraView.updateCameraSwitcher(camera2Manager.getNumberOfCameras());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraReady() {
|
||||
cameraView.onCameraReady();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraOpenError() {
|
||||
//test(TAG, "onCameraOpenError");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraClosed(String closedCameraId) {
|
||||
cameraView.releaseCameraPreview();
|
||||
|
||||
camera2Manager.openCamera(currentCameraId, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhotoTaken(File photoFile) {
|
||||
cameraView.onPhotoTaken();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhotoTakeError() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordStarted(Size videoSize) {
|
||||
cameraView.onVideoRecordStart(videoSize.getWidth(), videoSize.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordStopped(File videoFile) {
|
||||
cameraView.onVideoRecordStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordError() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CameraManager getCameraManager() {
|
||||
return camera2Manager;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package io.github.memfis19.annca.internal.controller.view;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.view.View;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public interface CameraView {
|
||||
|
||||
Activity getActivity();
|
||||
|
||||
void updateCameraPreview(Size size, View cameraPreview);
|
||||
|
||||
void updateUiForMediaAction(@AnncaConfiguration.MediaAction int mediaAction);
|
||||
|
||||
void updateCameraSwitcher(int numberOfCameras);
|
||||
|
||||
void onPhotoTaken();
|
||||
|
||||
void onVideoRecordStart(int width, int height);
|
||||
|
||||
void onVideoRecordStop();
|
||||
|
||||
void releaseCameraPreview();
|
||||
|
||||
void onCameraReady();
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.github.memfis19.annca.internal.manager;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.manager.impl.CameraHandler;
|
||||
import io.github.memfis19.annca.internal.manager.impl.ParametersHandler;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraCloseListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraOpenListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraPhotoListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraVideoListener;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
public interface CameraManager<CameraId, SurfaceListener, CameraParameters, Camera> {
|
||||
|
||||
void initializeCameraManager(ConfigurationProvider configurationProvider, Context context);
|
||||
|
||||
void openCamera(CameraId cameraId, CameraOpenListener<CameraId, SurfaceListener> cameraOpenListener);
|
||||
|
||||
void closeCamera(CameraCloseListener<CameraId> cameraCloseListener);
|
||||
|
||||
void setFlashMode(@AnncaConfiguration.FlashMode int flashMode);
|
||||
|
||||
void takePhoto(File photoFile, CameraPhotoListener cameraPhotoListener);
|
||||
|
||||
void startVideoRecord(File videoFile, CameraVideoListener cameraVideoListener);
|
||||
|
||||
Size getPhotoSizeForQuality(@AnncaConfiguration.MediaQuality int mediaQuality);
|
||||
|
||||
void stopVideoRecord();
|
||||
|
||||
void releaseCameraManager();
|
||||
|
||||
CameraId getCurrentCameraId();
|
||||
|
||||
CameraId getFaceFrontCameraId();
|
||||
|
||||
CameraId getFaceBackCameraId();
|
||||
|
||||
int getNumberOfCameras();
|
||||
|
||||
int getFaceFrontCameraOrientation();
|
||||
|
||||
int getFaceBackCameraOrientation();
|
||||
|
||||
boolean isVideoRecording();
|
||||
|
||||
boolean handleParameters(ParametersHandler<CameraParameters> parameters);
|
||||
|
||||
void handleCamera(CameraHandler<Camera> cameraHandler);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package io.github.memfis19.annca.internal.manager.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
abstract class BaseCameraManager<CameraId, SurfaceListener, CameraParameters, Camera>
|
||||
implements io.github.memfis19.annca.internal.manager.CameraManager<CameraId, SurfaceListener, CameraParameters, Camera>, MediaRecorder.OnInfoListener {
|
||||
|
||||
private static final String TAG = "BaseCameraManager";
|
||||
|
||||
protected Context context;
|
||||
ConfigurationProvider configurationProvider;
|
||||
|
||||
MediaRecorder videoRecorder;
|
||||
boolean isVideoRecording = false;
|
||||
|
||||
CameraId currentCameraId = null;
|
||||
CameraId faceFrontCameraId = null;
|
||||
CameraId faceBackCameraId = null;
|
||||
int numberOfCameras = 0;
|
||||
int faceFrontCameraOrientation;
|
||||
int faceBackCameraOrientation;
|
||||
|
||||
CamcorderProfile camcorderProfile;
|
||||
Size photoSize;
|
||||
Size videoSize;
|
||||
Size previewSize;
|
||||
Size windowSize;
|
||||
|
||||
HandlerThread backgroundThread;
|
||||
Handler backgroundHandler;
|
||||
Handler uiHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
@Override
|
||||
public void initializeCameraManager(ConfigurationProvider configurationProvider, Context context) {
|
||||
this.context = context;
|
||||
this.configurationProvider = configurationProvider;
|
||||
startBackgroundThread();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseCameraManager() {
|
||||
this.context = null;
|
||||
stopBackgroundThread();
|
||||
}
|
||||
|
||||
protected abstract void prepareCameraOutputs();
|
||||
|
||||
protected abstract boolean prepareVideoRecorder();
|
||||
|
||||
protected abstract void onMaxDurationReached();
|
||||
|
||||
protected abstract void onMaxFileSizeReached();
|
||||
|
||||
protected abstract int getPhotoOrientation(@AnncaConfiguration.SensorPosition int sensorPosition);
|
||||
|
||||
protected abstract int getVideoOrientation(@AnncaConfiguration.SensorPosition int sensorPosition);
|
||||
|
||||
protected void releaseVideoRecorder() {
|
||||
try {
|
||||
if (videoRecorder != null) {
|
||||
videoRecorder.reset();
|
||||
videoRecorder.release();
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
|
||||
} finally {
|
||||
videoRecorder = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void startBackgroundThread() {
|
||||
backgroundThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
|
||||
backgroundThread.start();
|
||||
backgroundHandler = new Handler(backgroundThread.getLooper());
|
||||
}
|
||||
|
||||
private void stopBackgroundThread() {
|
||||
backgroundThread.quitSafely();
|
||||
|
||||
try {
|
||||
backgroundThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
//test(TAG, "stopBackgroundThread: ", e);
|
||||
} finally {
|
||||
backgroundThread = null;
|
||||
backgroundHandler = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInfo(MediaRecorder mediaRecorder, int what, int extra) {
|
||||
if (MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED == what) {
|
||||
onMaxDurationReached();
|
||||
} else if (MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED == what) {
|
||||
onMaxFileSizeReached();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVideoRecording() {
|
||||
return isVideoRecording;
|
||||
}
|
||||
|
||||
public CameraId getCurrentCameraId() {
|
||||
return currentCameraId;
|
||||
}
|
||||
|
||||
public CameraId getFaceFrontCameraId() {
|
||||
return faceFrontCameraId;
|
||||
}
|
||||
|
||||
public CameraId getFaceBackCameraId() {
|
||||
return faceBackCameraId;
|
||||
}
|
||||
|
||||
public int getNumberOfCameras() {
|
||||
return numberOfCameras;
|
||||
}
|
||||
|
||||
public int getFaceFrontCameraOrientation() {
|
||||
return faceFrontCameraOrientation;
|
||||
}
|
||||
|
||||
public int getFaceBackCameraOrientation() {
|
||||
return faceBackCameraOrientation;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,694 @@
|
||||
package io.github.memfis19.annca.internal.manager.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.hardware.Camera;
|
||||
import android.media.ExifInterface;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraCloseListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraOpenListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraPhotoListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraVideoListener;
|
||||
import io.github.memfis19.annca.internal.utils.CameraHelper;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Camera1Manager extends BaseCameraManager<Integer, SurfaceHolder.Callback, Camera.Parameters, Camera>
|
||||
implements SurfaceHolder.Callback, Camera.PictureCallback {
|
||||
|
||||
private static final String TAG = "Camera1Manager";
|
||||
private static final int MODE_PRIVATE = 1;
|
||||
|
||||
private Camera camera;
|
||||
private Surface surface;
|
||||
|
||||
private static Camera1Manager currentInstance;
|
||||
|
||||
private int orientation;
|
||||
private int displayRotation = 0;
|
||||
|
||||
private File outputPath;
|
||||
private CameraVideoListener videoListener;
|
||||
private CameraPhotoListener photoListener;
|
||||
private CameraOpenListener<Integer, SurfaceHolder.Callback> cameraOpenListener;
|
||||
private boolean safeToTakePicture = false;
|
||||
|
||||
private Camera1Manager() {
|
||||
|
||||
}
|
||||
|
||||
public static Camera1Manager getInstance() {
|
||||
if (currentInstance == null) currentInstance = new Camera1Manager();
|
||||
return currentInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openCamera(final Integer cameraId,
|
||||
final CameraOpenListener<Integer, SurfaceHolder.Callback> cameraOpenListener) {
|
||||
this.currentCameraId = cameraId;
|
||||
this.cameraOpenListener = cameraOpenListener;
|
||||
backgroundHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
camera = Camera.open(cameraId);
|
||||
prepareCameraOutputs();
|
||||
if (cameraOpenListener != null) {
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cameraOpenListener.onCameraOpened(cameraId, previewSize, currentInstance, camera);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception error) {
|
||||
//test(TAG, "Can't open camera: " + error.getMessage());
|
||||
if (cameraOpenListener != null) {
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cameraOpenListener.onCameraOpenError();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeCamera(final CameraCloseListener<Integer> cameraCloseListener) {
|
||||
backgroundHandler.post(() -> {
|
||||
try {
|
||||
if (camera != null) {
|
||||
////changes by jeevan
|
||||
camera.stopPreview();
|
||||
camera.setPreviewCallback(null);
|
||||
camera.release();
|
||||
camera = null;
|
||||
if (cameraCloseListener != null) {
|
||||
uiHandler.post(() -> cameraCloseListener.onCameraClosed(currentCameraId));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlashMode(@AnncaConfiguration.FlashMode int flashMode) {
|
||||
setFlashMode(camera, camera.getParameters(), flashMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takePhoto(File photoFile, CameraPhotoListener cameraPhotoListener) {
|
||||
this.outputPath = photoFile;
|
||||
this.photoListener = cameraPhotoListener;
|
||||
backgroundHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (safeToTakePicture){
|
||||
setCameraPhotoQuality(camera);
|
||||
camera.takePicture(null, null, currentInstance);
|
||||
safeToTakePicture = false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startVideoRecord(final File videoFile, CameraVideoListener cameraVideoListener) {
|
||||
if (isVideoRecording) return;
|
||||
|
||||
this.outputPath = videoFile;
|
||||
this.videoListener = cameraVideoListener;
|
||||
|
||||
if (videoListener != null)
|
||||
backgroundHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (prepareVideoRecorder()) {
|
||||
videoRecorder.start();
|
||||
isVideoRecording = true;
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
videoListener.onVideoRecordStarted(videoSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopVideoRecord() {
|
||||
if (isVideoRecording)
|
||||
backgroundHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
try {
|
||||
if (videoRecorder != null) videoRecorder.stop();
|
||||
} catch (Exception ignore) {
|
||||
// ignore illegal state.
|
||||
// appear in case time or file size reach limit and stop already called.
|
||||
}
|
||||
|
||||
isVideoRecording = false;
|
||||
releaseVideoRecorder();
|
||||
|
||||
if (videoListener != null) {
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
videoListener.onVideoRecordStopped(outputPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseCameraManager() {
|
||||
super.releaseCameraManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeCameraManager(ConfigurationProvider configurationProvider, Context context) {
|
||||
super.initializeCameraManager(configurationProvider, context);
|
||||
|
||||
numberOfCameras = Camera.getNumberOfCameras();
|
||||
|
||||
for (int i = 0; i < numberOfCameras; ++i) {
|
||||
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
||||
|
||||
Camera.getCameraInfo(i, cameraInfo);
|
||||
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
|
||||
faceBackCameraId = i;
|
||||
faceBackCameraOrientation = cameraInfo.orientation;
|
||||
} else if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
faceFrontCameraId = i;
|
||||
faceFrontCameraOrientation = cameraInfo.orientation;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size getPhotoSizeForQuality(@AnncaConfiguration.MediaQuality int mediaQuality) {
|
||||
return CameraHelper.getPictureSize(Size.fromList(camera.getParameters().getSupportedPictureSizes()), mediaQuality);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareCameraOutputs() {
|
||||
try {
|
||||
if (configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_AUTO) {
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(currentCameraId, configurationProvider.getVideoFileSize(), configurationProvider.getMinimumVideoDuration());
|
||||
} else
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(configurationProvider.getMediaQuality(), currentCameraId);
|
||||
|
||||
List<Size> previewSizes = Size.fromList(camera.getParameters().getSupportedPreviewSizes());
|
||||
List<Size> pictureSizes = Size.fromList(camera.getParameters().getSupportedPictureSizes());
|
||||
List<Size> videoSizes;
|
||||
videoSizes = Size.fromList(camera.getParameters().getSupportedVideoSizes());
|
||||
|
||||
videoSize = CameraHelper.getSizeWithClosestRatio(
|
||||
(videoSizes == null || videoSizes.isEmpty()) ? previewSizes : videoSizes,
|
||||
camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
|
||||
|
||||
photoSize = CameraHelper.getPictureSize(
|
||||
(pictureSizes == null || pictureSizes.isEmpty()) ? previewSizes : pictureSizes,
|
||||
configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_AUTO
|
||||
? AnncaConfiguration.MEDIA_QUALITY_HIGHEST : configurationProvider.getMediaQuality());
|
||||
|
||||
if (configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_PHOTO
|
||||
|| configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED) {
|
||||
previewSize = CameraHelper.getSizeWithClosestRatio(previewSizes, photoSize.getWidth(), photoSize.getHeight());
|
||||
} else {
|
||||
previewSize = CameraHelper.getSizeWithClosestRatio(previewSizes, videoSize.getWidth(), videoSize.getHeight());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error while setup camera sizes.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepareVideoRecorder() {
|
||||
videoRecorder = new MediaRecorder();
|
||||
try {
|
||||
camera.lock();
|
||||
camera.unlock();
|
||||
videoRecorder.setCamera(camera);
|
||||
|
||||
videoRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
|
||||
videoRecorder.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
|
||||
|
||||
videoRecorder.setOutputFormat(camcorderProfile.fileFormat);
|
||||
videoRecorder.setVideoFrameRate(camcorderProfile.videoFrameRate);
|
||||
videoRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight());
|
||||
videoRecorder.setVideoEncodingBitRate(camcorderProfile.videoBitRate);
|
||||
videoRecorder.setVideoEncoder(camcorderProfile.videoCodec);
|
||||
|
||||
videoRecorder.setAudioEncodingBitRate(camcorderProfile.audioBitRate);
|
||||
videoRecorder.setAudioChannels(camcorderProfile.audioChannels);
|
||||
videoRecorder.setAudioSamplingRate(camcorderProfile.audioSampleRate);
|
||||
videoRecorder.setAudioEncoder(camcorderProfile.audioCodec);
|
||||
|
||||
videoRecorder.setOutputFile(outputPath.toString());
|
||||
|
||||
if (configurationProvider.getVideoFileSize() > 0) {
|
||||
videoRecorder.setMaxFileSize(configurationProvider.getVideoFileSize());
|
||||
|
||||
videoRecorder.setOnInfoListener(this);
|
||||
}
|
||||
if (configurationProvider.getVideoDuration() > 0) {
|
||||
videoRecorder.setMaxDuration(configurationProvider.getVideoDuration());
|
||||
|
||||
videoRecorder.setOnInfoListener(this);
|
||||
}
|
||||
|
||||
videoRecorder.setOrientationHint(getVideoOrientation(configurationProvider.getSensorPosition()));
|
||||
videoRecorder.setPreviewDisplay(surface);
|
||||
|
||||
videoRecorder.prepare();
|
||||
|
||||
return true;
|
||||
} catch (IllegalStateException error) {
|
||||
//test(TAG, "IllegalStateException preparing MediaRecorder: " + error.getMessage());
|
||||
} catch (IOException error) {
|
||||
//test(TAG, "IOException preparing MediaRecorder: " + error.getMessage());
|
||||
} catch (Throwable error) {
|
||||
//test(TAG, "Error during preparing MediaRecorder: " + error.getMessage());
|
||||
}
|
||||
|
||||
releaseVideoRecorder();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMaxDurationReached() {
|
||||
stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMaxFileSizeReached() {
|
||||
stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void releaseVideoRecorder() {
|
||||
super.releaseVideoRecorder();
|
||||
try {
|
||||
camera.lock(); // lock camera for later use
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------Implementation------------------
|
||||
|
||||
private void startPreview(SurfaceHolder surfaceHolder) {
|
||||
try {
|
||||
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
|
||||
Camera.getCameraInfo(currentCameraId, cameraInfo);
|
||||
int cameraRotationOffset = cameraInfo.orientation;
|
||||
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
|
||||
setAutoFocus(camera, parameters);
|
||||
setFlashMode(configurationProvider.getFlashMode());
|
||||
|
||||
if (configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_PHOTO
|
||||
|| configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED)
|
||||
turnPhotoCameraFeaturesOn(camera, parameters);
|
||||
else if (configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_PHOTO)
|
||||
turnVideoCameraFeaturesOn(camera, parameters);
|
||||
|
||||
int rotation = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
|
||||
int degrees = 0;
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_0:
|
||||
degrees = 0;
|
||||
break; // Natural orientation
|
||||
case Surface.ROTATION_90:
|
||||
degrees = 90;
|
||||
break; // Landscape left
|
||||
case Surface.ROTATION_180:
|
||||
degrees = 180;
|
||||
break;// Upside down
|
||||
case Surface.ROTATION_270:
|
||||
degrees = 270;
|
||||
break;// Landscape right
|
||||
}
|
||||
|
||||
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||
displayRotation = (cameraRotationOffset + degrees) % 360;
|
||||
displayRotation = (360 - displayRotation) % 360; // compensate
|
||||
} else {
|
||||
displayRotation = (cameraRotationOffset - degrees + 360) % 360;
|
||||
}
|
||||
|
||||
this.camera.setDisplayOrientation(displayRotation);
|
||||
|
||||
if (configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_VIDEO || configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED) {
|
||||
// parameters.setRecordingHint(true);
|
||||
}
|
||||
|
||||
if (parameters.isVideoStabilizationSupported() && (configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_VIDEO || configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED)) {
|
||||
parameters.setVideoStabilization(true);
|
||||
}
|
||||
|
||||
parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());
|
||||
parameters.setPictureSize(photoSize.getWidth(), photoSize.getHeight());
|
||||
|
||||
camera.setParameters(parameters);
|
||||
///SurfaceTexture st = new SurfaceTexture(MODE_PRIVATE);
|
||||
// camera.setPreviewTexture(st);
|
||||
|
||||
camera.setPreviewDisplay(surfaceHolder);
|
||||
camera.startPreview();
|
||||
|
||||
if (cameraOpenListener != null) cameraOpenListener.onCameraReady();
|
||||
} catch (IOException error) {
|
||||
//test(TAG, "Error setting camera preview: " + error.getMessage());
|
||||
} catch (Exception ignore) {
|
||||
//test(TAG, "Error starting camera preview: " + ignore.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void turnPhotoCameraFeaturesOn(Camera camera, Camera.Parameters parameters) {
|
||||
if (parameters.getSupportedFocusModes().contains(
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||
}
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
private void turnVideoCameraFeaturesOn(Camera camera, Camera.Parameters parameters) {
|
||||
if (parameters.getSupportedFocusModes().contains(
|
||||
Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
||||
}
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCamera(CameraHandler<Camera> cameraHandler) {
|
||||
cameraHandler.handleCamera(camera);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean handleParameters(ParametersHandler<Camera.Parameters> parameters) {
|
||||
try {
|
||||
camera.setParameters(parameters.getParameters(camera.getParameters()));
|
||||
return true;
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setAutoFocus(Camera camera, Camera.Parameters parameters) {
|
||||
try {
|
||||
if (parameters.getSupportedFocusModes().contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
||||
parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void setFlashMode(Camera camera, Camera.Parameters parameters, @AnncaConfiguration.FlashMode int flashMode) {
|
||||
try {
|
||||
switch (flashMode) {
|
||||
case AnncaConfiguration.FLASH_MODE_AUTO:
|
||||
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_ON:
|
||||
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON);
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_OFF:
|
||||
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
|
||||
break;
|
||||
default:
|
||||
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
|
||||
break;
|
||||
}
|
||||
camera.setParameters(parameters);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void setCameraPhotoQuality(Camera camera) {
|
||||
Camera.Parameters parameters = camera.getParameters();
|
||||
|
||||
parameters.setPictureFormat(PixelFormat.JPEG);
|
||||
|
||||
if (configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_LOW) {
|
||||
parameters.setJpegQuality(50);
|
||||
} else if (configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_MEDIUM) {
|
||||
parameters.setJpegQuality(75);
|
||||
} else if (configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_HIGH) {
|
||||
parameters.setJpegQuality(100);
|
||||
} else if (configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_HIGHEST) {
|
||||
parameters.setJpegQuality(100);
|
||||
}
|
||||
parameters.setPictureSize(photoSize.getWidth(), photoSize.getHeight());
|
||||
|
||||
camera.setParameters(parameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPhotoOrientation(@AnncaConfiguration.SensorPosition int sensorPosition) {
|
||||
int rotate;
|
||||
if (currentCameraId.equals(faceFrontCameraId)) {
|
||||
rotate = (360 + faceFrontCameraOrientation + configurationProvider.getDegrees()) % 360;
|
||||
} else {
|
||||
rotate = (360 + faceBackCameraOrientation - configurationProvider.getDegrees()) % 360;
|
||||
}
|
||||
|
||||
if (rotate == 0) {
|
||||
orientation = ExifInterface.ORIENTATION_NORMAL;
|
||||
} else if (rotate == 90) {
|
||||
orientation = ExifInterface.ORIENTATION_ROTATE_90;
|
||||
} else if (rotate == 180) {
|
||||
orientation = ExifInterface.ORIENTATION_ROTATE_180;
|
||||
} else if (rotate == 270) {
|
||||
orientation = ExifInterface.ORIENTATION_ROTATE_270;
|
||||
}
|
||||
|
||||
return orientation;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVideoOrientation(@AnncaConfiguration.SensorPosition int sensorPosition) {
|
||||
int degrees = 0;
|
||||
switch (sensorPosition) {
|
||||
case AnncaConfiguration.SENSOR_POSITION_UP:
|
||||
degrees = 0;
|
||||
break; // Natural orientation
|
||||
case AnncaConfiguration.SENSOR_POSITION_LEFT:
|
||||
degrees = 90;
|
||||
break; // Landscape left
|
||||
case AnncaConfiguration.SENSOR_POSITION_UP_SIDE_DOWN:
|
||||
degrees = 180;
|
||||
break;// Upside down
|
||||
case AnncaConfiguration.SENSOR_POSITION_RIGHT:
|
||||
degrees = 270;
|
||||
break;// Landscape right
|
||||
}
|
||||
|
||||
int rotate;
|
||||
if (currentCameraId.equals(faceFrontCameraId)) {
|
||||
rotate = (360 + faceFrontCameraOrientation + degrees) % 360;
|
||||
} else {
|
||||
rotate = (360 + faceBackCameraOrientation - degrees) % 360;
|
||||
}
|
||||
return rotate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder surfaceHolder) {
|
||||
if (surfaceHolder.getSurface() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
surface = surfaceHolder.getSurface();
|
||||
|
||||
try {
|
||||
camera.stopPreview();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
startPreview(surfaceHolder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
|
||||
if (surfaceHolder.getSurface() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
surface = surfaceHolder.getSurface();
|
||||
|
||||
try {
|
||||
camera.stopPreview();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
|
||||
startPreview(surfaceHolder);
|
||||
safeToTakePicture = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPictureTaken(byte[] bytes, Camera camera) {
|
||||
File pictureFile = outputPath;
|
||||
if (pictureFile == null) {
|
||||
//test(TAG, "Error creating media file, check storage permissions.");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(pictureFile);
|
||||
fileOutputStream.write(bytes);
|
||||
fileOutputStream.close();
|
||||
|
||||
if (configurationProvider.getShowGrid()) {
|
||||
setGridOnImage(outputPath);
|
||||
}
|
||||
} catch (FileNotFoundException error) {
|
||||
//test(TAG, "File not found: " + error.getMessage());
|
||||
} catch (IOException error) {
|
||||
//test(TAG, "Error accessing file: " + error.getMessage());
|
||||
} catch (Throwable error) {
|
||||
//test(TAG, "Error saving file: " + error.getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
ExifInterface exif = new ExifInterface(pictureFile.getAbsolutePath());
|
||||
exif.setAttribute(ExifInterface.TAG_ORIENTATION, "" + getPhotoOrientation(configurationProvider.getSensorPosition()));
|
||||
exif.saveAttributes();
|
||||
if (photoListener != null) {
|
||||
uiHandler.post(() -> photoListener.onPhotoTaken(outputPath));
|
||||
}
|
||||
//finished saving picture
|
||||
safeToTakePicture = true;
|
||||
} catch (Throwable error) {
|
||||
//finished saving picture
|
||||
safeToTakePicture = true;
|
||||
//test(TAG, "Can't save exif info: " + error.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
void setGridOnImage(File outputPath) {
|
||||
Bitmap b = convertBitmap(outputPath.getAbsolutePath());
|
||||
// Rectangle Objects
|
||||
Paint paint = new Paint();
|
||||
// Create Temp bitmap
|
||||
Bitmap tBitmap = Bitmap.createBitmap(b.getWidth(), b.getHeight(), Bitmap.Config.RGB_565);
|
||||
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
|
||||
float screenWidth = metrics.widthPixels;
|
||||
float screenHeight = (int) (metrics.heightPixels);
|
||||
// Set paint options
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(3);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setColor(Color.argb(255, 255, 125, 000));
|
||||
// Create a new canvas and add Bitmap into it
|
||||
Canvas canvas = new Canvas(tBitmap);
|
||||
//Draw the image bitmap into the canvas
|
||||
canvas.drawBitmap(b, 0, 0, null);
|
||||
|
||||
int width = tBitmap.getWidth();
|
||||
int height = tBitmap.getHeight();
|
||||
|
||||
canvas.drawLine((width / 11) * 3, 0, (width / 11) * 3, height, paint);
|
||||
canvas.drawLine((width / 11) * 8, 0, (width / 11) * 8, height, paint);
|
||||
//test("small==" + (screenWidth / 100) * 27 + " large===" + (screenWidth / 100) * 63);
|
||||
canvas.drawLine(0, (height / 11) * 3, width, (height / 11) * 3, paint);
|
||||
canvas.drawLine(0, (height / 11) * 8, width, (height / 11) * 8, paint);
|
||||
|
||||
BitmapDrawable btmpDr = new BitmapDrawable(tBitmap);
|
||||
Bitmap bmp = btmpDr.getBitmap();
|
||||
try {
|
||||
FileOutputStream outStream = new FileOutputStream(outputPath);
|
||||
bmp.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
|
||||
/* 100 to keep full quality of the image */
|
||||
outStream.flush();
|
||||
outStream.close();
|
||||
} catch (IOException e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Bitmap convertBitmap(String path) {
|
||||
Bitmap bitmap = null;
|
||||
BitmapFactory.Options ourOptions = new BitmapFactory.Options();
|
||||
ourOptions.inDither = false;
|
||||
ourOptions.inPurgeable = true;
|
||||
ourOptions.inInputShareable = true;
|
||||
ourOptions.inTempStorage = new byte[32 * 1024];
|
||||
File file = new File(path);
|
||||
FileInputStream fs = null;
|
||||
try {
|
||||
fs = new FileInputStream(file);
|
||||
} catch (FileNotFoundException e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
try {
|
||||
if (fs != null) {
|
||||
bitmap = BitmapFactory.decodeFileDescriptor(fs.getFD(), null, ourOptions);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.fillInStackTrace();
|
||||
} finally {
|
||||
if (fs != null) {
|
||||
try {
|
||||
fs.close();
|
||||
} catch (IOException e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,804 @@
|
||||
package io.github.memfis19.annca.internal.manager.impl;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.ImageFormat;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.SurfaceTexture;
|
||||
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.CaptureRequest;
|
||||
import android.hardware.camera2.CaptureResult;
|
||||
import android.hardware.camera2.TotalCaptureResult;
|
||||
import android.hardware.camera2.params.StreamConfigurationMap;
|
||||
import android.media.ImageReader;
|
||||
import android.media.MediaRecorder;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.Display;
|
||||
import android.view.Surface;
|
||||
import android.view.TextureView;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraCloseListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraOpenListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraPhotoListener;
|
||||
import io.github.memfis19.annca.internal.manager.listener.CameraVideoListener;
|
||||
import io.github.memfis19.annca.internal.utils.CameraHelper;
|
||||
import io.github.memfis19.annca.internal.utils.ImageSaver;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/9/16.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public final class Camera2Manager extends BaseCameraManager<String, TextureView.SurfaceTextureListener, CaptureRequest.Builder, CameraDevice>
|
||||
implements ImageReader.OnImageAvailableListener, TextureView.SurfaceTextureListener {
|
||||
|
||||
protected int zoomLevel = 1;
|
||||
|
||||
private final static String TAG = "Camera2Manager";
|
||||
|
||||
private static Camera2Manager currentInstance;
|
||||
|
||||
private CameraOpenListener<String, TextureView.SurfaceTextureListener> cameraOpenListener;
|
||||
private CameraPhotoListener cameraPhotoListener;
|
||||
private CameraVideoListener cameraVideoListener;
|
||||
|
||||
private File outputPath;
|
||||
|
||||
@CameraPreviewState
|
||||
private int previewState = STATE_PREVIEW;
|
||||
private static final int STATE_PREVIEW = 0;
|
||||
private static final int STATE_WAITING_LOCK = 1;
|
||||
private static final int STATE_WAITING_PRE_CAPTURE = 2;
|
||||
private static final int STATE_WAITING_NON_PRE_CAPTURE = 3;
|
||||
private static final int STATE_PICTURE_TAKEN = 4;
|
||||
|
||||
@IntDef({STATE_PREVIEW, STATE_WAITING_LOCK, STATE_WAITING_PRE_CAPTURE, STATE_WAITING_NON_PRE_CAPTURE, STATE_PICTURE_TAKEN})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface CameraPreviewState {
|
||||
}
|
||||
|
||||
private CameraManager manager;
|
||||
private CameraDevice cameraDevice;
|
||||
private CaptureRequest previewRequest;
|
||||
private CaptureRequest.Builder previewRequestBuilder;
|
||||
private CameraCaptureSession captureSession;
|
||||
|
||||
private CameraCharacteristics frontCameraCharacteristics;
|
||||
private CameraCharacteristics backCameraCharacteristics;
|
||||
private StreamConfigurationMap frontCameraStreamConfigurationMap;
|
||||
private StreamConfigurationMap backCameraStreamConfigurationMap;
|
||||
|
||||
private SurfaceTexture texture;
|
||||
|
||||
private Surface workingSurface;
|
||||
private ImageReader imageReader;
|
||||
|
||||
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
|
||||
@Override
|
||||
public void onOpened(CameraDevice cameraDevice) {
|
||||
currentInstance.cameraDevice = cameraDevice;
|
||||
if (cameraOpenListener != null) {
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!TextUtils.isEmpty(currentCameraId) && previewSize != null && currentInstance != null) {
|
||||
}
|
||||
//cameraOpenListener.onCameraOpened(currentCameraId, previewSize, currentInstance);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnected(CameraDevice cameraDevice) {
|
||||
cameraDevice.close();
|
||||
currentInstance.cameraDevice = null;
|
||||
|
||||
uiHandler.post(() -> cameraOpenListener.onCameraOpenError());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(CameraDevice cameraDevice, int error) {
|
||||
cameraDevice.close();
|
||||
currentInstance.cameraDevice = null;
|
||||
|
||||
uiHandler.post(() -> cameraOpenListener.onCameraOpenError());
|
||||
}
|
||||
};
|
||||
|
||||
private CameraCaptureSession.CaptureCallback captureCallback
|
||||
= new CameraCaptureSession.CaptureCallback() {
|
||||
|
||||
@Override
|
||||
public void onCaptureProgressed(@NonNull CameraCaptureSession session,
|
||||
@NonNull CaptureRequest request,
|
||||
@NonNull CaptureResult partialResult) {
|
||||
processCaptureResult(partialResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
|
||||
@NonNull CaptureRequest request,
|
||||
@NonNull TotalCaptureResult result) {
|
||||
processCaptureResult(result);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private Camera2Manager() {
|
||||
}
|
||||
|
||||
public static Camera2Manager getInstance() {
|
||||
if (currentInstance == null) currentInstance = new Camera2Manager();
|
||||
return currentInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeCameraManager(ConfigurationProvider configurationProvider, Context context) {
|
||||
super.initializeCameraManager(configurationProvider, context);
|
||||
|
||||
this.manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
|
||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = windowManager.getDefaultDisplay();
|
||||
Point size = new Point();
|
||||
display.getSize(size);
|
||||
windowSize = new Size(size.x, size.y);
|
||||
|
||||
try {
|
||||
String[] ids = manager.getCameraIdList();
|
||||
numberOfCameras = ids.length;
|
||||
for (String id : ids) {
|
||||
CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
|
||||
|
||||
int orientation = characteristics.get(CameraCharacteristics.LENS_FACING);
|
||||
if (orientation == CameraCharacteristics.LENS_FACING_FRONT) {
|
||||
faceFrontCameraId = id;
|
||||
faceFrontCameraOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
|
||||
frontCameraCharacteristics = characteristics;
|
||||
} else {
|
||||
faceBackCameraId = id;
|
||||
faceBackCameraOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
|
||||
backCameraCharacteristics = characteristics;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error during camera init");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void openCamera(String cameraId, final CameraOpenListener<String, TextureView.SurfaceTextureListener> cameraOpenListener) {
|
||||
this.currentCameraId = cameraId;
|
||||
this.cameraOpenListener = cameraOpenListener;
|
||||
backgroundHandler.post(() -> {
|
||||
if (context == null || configurationProvider == null) {
|
||||
//test(TAG, "openCamera: ");
|
||||
if (cameraOpenListener != null) {
|
||||
uiHandler.post(cameraOpenListener::onCameraOpenError);
|
||||
}
|
||||
return;
|
||||
}
|
||||
prepareCameraOutputs();
|
||||
try {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
|
||||
// TODO: Consider calling
|
||||
// ActivityCompat#requestPermissions
|
||||
// here to request the missing permissions, and then overriding
|
||||
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
|
||||
// int[] grantResults)
|
||||
// to handle the case where the user grants the permission. See the documentation
|
||||
// for ActivityCompat#requestPermissions for more details.
|
||||
return;
|
||||
}
|
||||
manager.openCamera(currentCameraId, stateCallback, backgroundHandler);
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "openCamera: ", e);
|
||||
if (cameraOpenListener != null) {
|
||||
uiHandler.post(cameraOpenListener::onCameraOpenError);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeCamera(final CameraCloseListener<String> cameraCloseListener) {
|
||||
backgroundHandler.post(() -> {
|
||||
closeCamera();
|
||||
if (cameraCloseListener != null) {
|
||||
uiHandler.post(() -> cameraCloseListener.onCameraClosed(currentCameraId));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleCamera(CameraHandler<CameraDevice> cameraHandler) {
|
||||
cameraHandler.handleCamera(cameraDevice);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean handleParameters(ParametersHandler<CaptureRequest.Builder> parameters) {
|
||||
try {
|
||||
previewRequestBuilder = parameters.getParameters(previewRequestBuilder);
|
||||
|
||||
previewRequest = previewRequestBuilder.build();
|
||||
captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler);
|
||||
return true;
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFlashMode(@AnncaConfiguration.FlashMode int flashMode) {
|
||||
setFlashModeAndBuildPreviewRequest(flashMode);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void takePhoto(File photoFile, CameraPhotoListener cameraPhotoListener) {
|
||||
this.outputPath = photoFile;
|
||||
this.cameraPhotoListener = cameraPhotoListener;
|
||||
|
||||
backgroundHandler.post(this::lockFocus);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Size getPhotoSizeForQuality(@AnncaConfiguration.MediaQuality int mediaQuality) {
|
||||
StreamConfigurationMap map = currentCameraId.equals(faceBackCameraId) ? backCameraStreamConfigurationMap : frontCameraStreamConfigurationMap;
|
||||
return CameraHelper.getPictureSize(Size.fromArray2(map.getOutputSizes(ImageFormat.JPEG)), mediaQuality);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startVideoRecord(File videoFile, final CameraVideoListener cameraVideoListener) {
|
||||
if (isVideoRecording || texture == null) return;
|
||||
|
||||
this.outputPath = videoFile;
|
||||
this.cameraVideoListener = cameraVideoListener;
|
||||
|
||||
if (cameraVideoListener != null)
|
||||
backgroundHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
closePreviewSession();
|
||||
if (prepareVideoRecorder()) {
|
||||
|
||||
SurfaceTexture texture = currentInstance.texture;
|
||||
texture.setDefaultBufferSize(videoSize.getWidth(), videoSize.getHeight());
|
||||
|
||||
try {
|
||||
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
|
||||
List<Surface> surfaces = new ArrayList<>();
|
||||
|
||||
Surface previewSurface = workingSurface;
|
||||
surfaces.add(previewSurface);
|
||||
previewRequestBuilder.addTarget(previewSurface);
|
||||
|
||||
workingSurface = videoRecorder.getSurface();
|
||||
surfaces.add(workingSurface);
|
||||
previewRequestBuilder.addTarget(workingSurface);
|
||||
|
||||
cameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
|
||||
@Override
|
||||
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
|
||||
captureSession = cameraCaptureSession;
|
||||
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
|
||||
try {
|
||||
captureSession.setRepeatingRequest(previewRequestBuilder.build(), null, backgroundHandler);
|
||||
} catch (Exception e) {
|
||||
}
|
||||
|
||||
try {
|
||||
videoRecorder.start();
|
||||
} catch (Exception ignore) {
|
||||
//test(TAG, "videoRecorder.start(): ", ignore);
|
||||
}
|
||||
|
||||
isVideoRecording = true;
|
||||
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cameraVideoListener.onVideoRecordStarted(videoSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
|
||||
//test(TAG, "onConfigureFailed");
|
||||
}
|
||||
}, backgroundHandler);
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "startVideoRecord: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopVideoRecord() {
|
||||
if (isVideoRecording)
|
||||
backgroundHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
closePreviewSession();
|
||||
|
||||
if (videoRecorder != null) {
|
||||
try {
|
||||
videoRecorder.stop();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
isVideoRecording = false;
|
||||
releaseVideoRecorder();
|
||||
|
||||
if (cameraVideoListener != null) {
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cameraVideoListener.onVideoRecordStopped(outputPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------Internal methods------------------
|
||||
|
||||
private void startPreview(SurfaceTexture texture) {
|
||||
try {
|
||||
if (texture == null) return;
|
||||
|
||||
this.texture = texture;
|
||||
|
||||
texture.setDefaultBufferSize(previewSize.getWidth(), previewSize.getHeight());
|
||||
|
||||
workingSurface = new Surface(texture);
|
||||
|
||||
previewRequestBuilder
|
||||
= cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
|
||||
previewRequestBuilder.addTarget(workingSurface);
|
||||
|
||||
cameraDevice.createCaptureSession(Arrays.asList(workingSurface, imageReader.getSurface()),
|
||||
new CameraCaptureSession.StateCallback() {
|
||||
@Override
|
||||
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
|
||||
updatePreview(cameraCaptureSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConfigureFailed(
|
||||
@NonNull CameraCaptureSession cameraCaptureSession) {
|
||||
//test(TAG, "Fail while starting preview: ");
|
||||
}
|
||||
}, null);
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error while preparing surface for preview: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMaxDurationReached() {
|
||||
stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMaxFileSizeReached() {
|
||||
stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getPhotoOrientation(@AnncaConfiguration.SensorPosition int sensorPosition) {
|
||||
return getVideoOrientation(sensorPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getVideoOrientation(@AnncaConfiguration.SensorPosition int sensorPosition) {
|
||||
int degrees = 0;
|
||||
switch (sensorPosition) {
|
||||
case AnncaConfiguration.SENSOR_POSITION_UP:
|
||||
degrees = 0;
|
||||
break; // Natural orientation
|
||||
case AnncaConfiguration.SENSOR_POSITION_LEFT:
|
||||
degrees = 90;
|
||||
break; // Landscape left
|
||||
case AnncaConfiguration.SENSOR_POSITION_UP_SIDE_DOWN:
|
||||
degrees = 180;
|
||||
break;// Upside down
|
||||
case AnncaConfiguration.SENSOR_POSITION_RIGHT:
|
||||
degrees = 270;
|
||||
break;// Landscape right
|
||||
case AnncaConfiguration.SENSOR_POSITION_UNSPECIFIED:
|
||||
break;
|
||||
}
|
||||
|
||||
int rotate;
|
||||
if (Objects.equals(currentCameraId, faceFrontCameraId)) {
|
||||
rotate = (360 + faceFrontCameraOrientation + degrees) % 360;
|
||||
} else {
|
||||
rotate = (360 + faceBackCameraOrientation - degrees) % 360;
|
||||
}
|
||||
return rotate;
|
||||
}
|
||||
|
||||
private void closeCamera() {
|
||||
closePreviewSession();
|
||||
releaseTexture();
|
||||
closeCameraDevice();
|
||||
closeImageReader();
|
||||
releaseVideoRecorder();
|
||||
}
|
||||
|
||||
private void releaseTexture() {
|
||||
if (null != texture) {
|
||||
texture.release();
|
||||
texture = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeImageReader() {
|
||||
if (null != imageReader) {
|
||||
imageReader.close();
|
||||
imageReader = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void closeCameraDevice() {
|
||||
if (null != cameraDevice) {
|
||||
cameraDevice.close();
|
||||
cameraDevice = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareCameraOutputs() {
|
||||
try {
|
||||
CameraCharacteristics characteristics = currentCameraId.equals(faceBackCameraId) ? backCameraCharacteristics : frontCameraCharacteristics;
|
||||
|
||||
if (currentCameraId.equals(faceFrontCameraId) && frontCameraStreamConfigurationMap == null)
|
||||
frontCameraStreamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
else if (currentCameraId.equals(faceBackCameraId) && backCameraStreamConfigurationMap == null)
|
||||
backCameraStreamConfigurationMap = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
|
||||
|
||||
StreamConfigurationMap map = currentCameraId.equals(faceBackCameraId) ? backCameraStreamConfigurationMap : frontCameraStreamConfigurationMap;
|
||||
if (configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_AUTO) {
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(currentCameraId, configurationProvider.getVideoFileSize(), configurationProvider.getMinimumVideoDuration());
|
||||
} else
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(configurationProvider.getMediaQuality(), currentCameraId);
|
||||
|
||||
videoSize = CameraHelper.chooseOptimalSize(Size.fromArray2(map.getOutputSizes(MediaRecorder.class)),
|
||||
windowSize.getWidth(), windowSize.getHeight(), new Size(camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight));
|
||||
|
||||
if (videoSize == null || videoSize.getWidth() > camcorderProfile.videoFrameWidth
|
||||
|| videoSize.getHeight() > camcorderProfile.videoFrameHeight)
|
||||
videoSize = CameraHelper.getSizeWithClosestRatio(Size.fromArray2(map.getOutputSizes(MediaRecorder.class)), camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
|
||||
else if (videoSize == null || videoSize.getWidth() > camcorderProfile.videoFrameWidth
|
||||
|| videoSize.getHeight() > camcorderProfile.videoFrameHeight)
|
||||
videoSize = CameraHelper.getSizeWithClosestRatio(Size.fromArray2(map.getOutputSizes(MediaRecorder.class)), camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight);
|
||||
|
||||
photoSize = CameraHelper.getPictureSize(Size.fromArray2(map.getOutputSizes(ImageFormat.JPEG)),
|
||||
configurationProvider.getMediaQuality() == AnncaConfiguration.MEDIA_QUALITY_AUTO
|
||||
? AnncaConfiguration.MEDIA_QUALITY_HIGHEST : configurationProvider.getMediaQuality());
|
||||
|
||||
imageReader = ImageReader.newInstance(photoSize.getWidth(), photoSize.getHeight(),
|
||||
ImageFormat.JPEG, 2);
|
||||
imageReader.setOnImageAvailableListener(this, backgroundHandler);
|
||||
|
||||
if (configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_PHOTO
|
||||
|| configurationProvider.getMediaAction() == AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED) {
|
||||
|
||||
if (windowSize.getHeight() * windowSize.getWidth() > photoSize.getWidth() * photoSize.getHeight()) {
|
||||
previewSize = CameraHelper.getOptimalPreviewSize(Size.fromArray2(map.getOutputSizes(SurfaceTexture.class)), photoSize.getWidth(), photoSize.getHeight());
|
||||
} else {
|
||||
previewSize = CameraHelper.getOptimalPreviewSize(Size.fromArray2(map.getOutputSizes(SurfaceTexture.class)), windowSize.getWidth(), windowSize.getHeight());
|
||||
}
|
||||
|
||||
if (previewSize == null)
|
||||
previewSize = CameraHelper.chooseOptimalSize(Size.fromArray2(map.getOutputSizes(SurfaceTexture.class)), windowSize.getWidth(), windowSize.getHeight(), photoSize);
|
||||
|
||||
} else {
|
||||
if (windowSize.getHeight() * windowSize.getWidth() > videoSize.getWidth() * videoSize.getHeight()) {
|
||||
previewSize = CameraHelper.getOptimalPreviewSize(Size.fromArray2(map.getOutputSizes(SurfaceTexture.class)), videoSize.getWidth(), videoSize.getHeight());
|
||||
} else {
|
||||
previewSize = CameraHelper.getOptimalPreviewSize(Size.fromArray2(map.getOutputSizes(SurfaceTexture.class)), windowSize.getWidth(), windowSize.getHeight());
|
||||
}
|
||||
|
||||
if (previewSize == null)
|
||||
previewSize = CameraHelper.getSizeWithClosestRatio(Size.fromArray2(map.getOutputSizes(SurfaceTexture.class)), videoSize.getWidth(), videoSize.getHeight());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error while setup camera sizes.", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean prepareVideoRecorder() {
|
||||
videoRecorder = new MediaRecorder();
|
||||
try {
|
||||
videoRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
|
||||
videoRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
|
||||
|
||||
videoRecorder.setOutputFormat(camcorderProfile.fileFormat);
|
||||
videoRecorder.setVideoFrameRate(camcorderProfile.videoFrameRate);
|
||||
videoRecorder.setVideoSize(videoSize.getWidth(), videoSize.getHeight());
|
||||
videoRecorder.setVideoEncodingBitRate(camcorderProfile.videoBitRate);
|
||||
videoRecorder.setVideoEncoder(camcorderProfile.videoCodec);
|
||||
|
||||
videoRecorder.setAudioEncodingBitRate(camcorderProfile.audioBitRate);
|
||||
videoRecorder.setAudioChannels(camcorderProfile.audioChannels);
|
||||
videoRecorder.setAudioSamplingRate(camcorderProfile.audioSampleRate);
|
||||
videoRecorder.setAudioEncoder(camcorderProfile.audioCodec);
|
||||
|
||||
File outputFile = outputPath;
|
||||
String outputFilePath = outputFile.toString();
|
||||
videoRecorder.setOutputFile(outputFilePath);
|
||||
|
||||
if (configurationProvider.getVideoFileSize() > 0) {
|
||||
videoRecorder.setMaxFileSize(configurationProvider.getVideoFileSize());
|
||||
videoRecorder.setOnInfoListener(this);
|
||||
}
|
||||
if (configurationProvider.getVideoDuration() > 0) {
|
||||
videoRecorder.setMaxDuration(configurationProvider.getVideoDuration());
|
||||
videoRecorder.setOnInfoListener(this);
|
||||
}
|
||||
videoRecorder.setOrientationHint(getVideoOrientation(configurationProvider.getSensorPosition()));
|
||||
|
||||
videoRecorder.prepare();
|
||||
|
||||
return true;
|
||||
} catch (IllegalStateException error) {
|
||||
//test(TAG, "IllegalStateException preparing MediaRecorder: " + error.getMessage());
|
||||
} catch (IOException error) {
|
||||
//test(TAG, "IOException preparing MediaRecorder: " + error.getMessage());
|
||||
} catch (Throwable error) {
|
||||
//test(TAG, "Error during preparing MediaRecorder: " + error.getMessage());
|
||||
}
|
||||
|
||||
releaseVideoRecorder();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updatePreview(CameraCaptureSession cameraCaptureSession) {
|
||||
if (null == cameraDevice) {
|
||||
return;
|
||||
}
|
||||
captureSession = cameraCaptureSession;
|
||||
setFlashModeAndBuildPreviewRequest(configurationProvider.getFlashMode());
|
||||
|
||||
if (cameraOpenListener != null) cameraOpenListener.onCameraReady();
|
||||
}
|
||||
|
||||
private void closePreviewSession() {
|
||||
if (captureSession != null) {
|
||||
captureSession.close();
|
||||
try {
|
||||
captureSession.abortCaptures();
|
||||
} catch (Exception ignore) {
|
||||
} finally {
|
||||
captureSession = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void lockFocus() {
|
||||
try {
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START);
|
||||
|
||||
previewState = STATE_WAITING_LOCK;
|
||||
captureSession.capture(previewRequestBuilder.build(), captureCallback, backgroundHandler);
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
private void processCaptureResult(CaptureResult result) {
|
||||
switch (previewState) {
|
||||
case STATE_PREVIEW: {
|
||||
break;
|
||||
}
|
||||
case STATE_WAITING_LOCK: {
|
||||
Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
|
||||
if (afState == null) {
|
||||
captureStillPicture();
|
||||
} else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState
|
||||
|| CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState
|
||||
|| CaptureResult.CONTROL_AF_STATE_INACTIVE == afState
|
||||
|| CaptureResult.CONTROL_AF_STATE_PASSIVE_SCAN == afState) {
|
||||
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
|
||||
if (aeState == null ||
|
||||
aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
|
||||
previewState = STATE_PICTURE_TAKEN;
|
||||
captureStillPicture();
|
||||
} else {
|
||||
runPreCaptureSequence();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_WAITING_PRE_CAPTURE: {
|
||||
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
|
||||
if (aeState == null ||
|
||||
aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE ||
|
||||
aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
|
||||
previewState = STATE_WAITING_NON_PRE_CAPTURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_WAITING_NON_PRE_CAPTURE: {
|
||||
Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
|
||||
if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
|
||||
previewState = STATE_PICTURE_TAKEN;
|
||||
captureStillPicture();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case STATE_PICTURE_TAKEN:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void runPreCaptureSequence() {
|
||||
try {
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
|
||||
previewState = STATE_WAITING_PRE_CAPTURE;
|
||||
captureSession.capture(previewRequestBuilder.build(), captureCallback, backgroundHandler);
|
||||
} catch (CameraAccessException e) {
|
||||
}
|
||||
}
|
||||
|
||||
private void captureStillPicture() {
|
||||
try {
|
||||
if (null == cameraDevice) {
|
||||
return;
|
||||
}
|
||||
final CaptureRequest.Builder captureBuilder =
|
||||
cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
|
||||
captureBuilder.addTarget(imageReader.getSurface());
|
||||
|
||||
captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
|
||||
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getPhotoOrientation(configurationProvider.getSensorPosition()));
|
||||
|
||||
CameraCaptureSession.CaptureCallback CaptureCallback = new CameraCaptureSession.CaptureCallback() {
|
||||
@Override
|
||||
public void onCaptureCompleted(@NonNull CameraCaptureSession session,
|
||||
@NonNull CaptureRequest request,
|
||||
@NonNull TotalCaptureResult result) {
|
||||
//test(TAG, "onCaptureCompleted: ");
|
||||
}
|
||||
};
|
||||
|
||||
captureSession.stopRepeating();
|
||||
captureSession.capture(captureBuilder.build(), CaptureCallback, null);
|
||||
|
||||
} catch (CameraAccessException e) {
|
||||
//test(TAG, "Error during capturing picture");
|
||||
}
|
||||
}
|
||||
|
||||
private void unlockFocus() {
|
||||
try {
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
|
||||
captureSession.capture(previewRequestBuilder.build(), captureCallback, backgroundHandler);
|
||||
previewState = STATE_PREVIEW;
|
||||
captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler);
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error during focus unlocking");
|
||||
}
|
||||
}
|
||||
|
||||
private void setFlashModeAndBuildPreviewRequest(@AnncaConfiguration.FlashMode int flashMode) {
|
||||
try {
|
||||
|
||||
switch (flashMode) {
|
||||
case AnncaConfiguration.FLASH_MODE_AUTO:
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
previewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_ON:
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
|
||||
previewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_OFF:
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
|
||||
previewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_OFF);
|
||||
break;
|
||||
default:
|
||||
previewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
|
||||
previewRequestBuilder.set(CaptureRequest.FLASH_MODE, CameraMetadata.FLASH_MODE_SINGLE);
|
||||
break;
|
||||
}
|
||||
|
||||
previewRequest = previewRequestBuilder.build();
|
||||
|
||||
try {
|
||||
captureSession.setRepeatingRequest(previewRequest, captureCallback, backgroundHandler);
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error updating preview: ", e);
|
||||
}
|
||||
} catch (Exception ignore) {
|
||||
//test(TAG, "Error setting flash: ", ignore);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onImageAvailable(ImageReader imageReader) {
|
||||
File outputFile = outputPath;
|
||||
backgroundHandler.post(new ImageSaver(imageReader.acquireNextImage(), outputFile, new ImageSaver.ImageSaverCallback() {
|
||||
@Override
|
||||
public void onSuccessFinish() {
|
||||
//test(TAG, "onPhotoSuccessFinish: ");
|
||||
if (cameraPhotoListener != null) {
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cameraPhotoListener.onPhotoTaken(outputPath);
|
||||
}
|
||||
});
|
||||
}
|
||||
unlockFocus();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError() {
|
||||
//test(TAG, "onPhotoError: ");
|
||||
uiHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cameraPhotoListener.onPhotoTakeError();
|
||||
}
|
||||
});
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (surfaceTexture != null) startPreview(surfaceTexture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (surfaceTexture != null) startPreview(surfaceTexture);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.github.memfis19.annca.internal.manager.impl;
|
||||
|
||||
/**
|
||||
* Created by memfis on 1/13/17.
|
||||
*/
|
||||
|
||||
public interface CameraHandler<Camera> {
|
||||
void handleCamera(Camera camera);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.github.memfis19.annca.internal.manager.impl;
|
||||
|
||||
/**
|
||||
* Created by memfis on 1/13/17.
|
||||
*/
|
||||
public interface ParametersHandler<CameraParameters> {
|
||||
CameraParameters getParameters(CameraParameters cameraParameters);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.github.memfis19.annca.internal.manager.listener;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
public interface CameraCloseListener<CameraId> {
|
||||
void onCameraClosed(CameraId closedCameraId);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.github.memfis19.annca.internal.manager.listener;
|
||||
|
||||
import android.hardware.Camera;
|
||||
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
public interface CameraOpenListener<CameraId, SurfaceListener> {
|
||||
void onCameraOpened(CameraId openedCameraId, Size previewSize, SurfaceListener surfaceListener, Camera camera);
|
||||
|
||||
void onCameraReady();
|
||||
|
||||
void onCameraOpenError();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.github.memfis19.annca.internal.manager.listener;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
public interface CameraPhotoListener {
|
||||
void onPhotoTaken(File photoFile);
|
||||
|
||||
void onPhotoTakeError();
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.github.memfis19.annca.internal.manager.listener;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/14/16.
|
||||
*/
|
||||
public interface CameraVideoListener {
|
||||
void onVideoRecordStarted(Size videoSize);
|
||||
|
||||
void onVideoRecordStopped(File videoFile);
|
||||
|
||||
void onVideoRecordError();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
package io.github.memfis19.annca.internal.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.controller.CameraController;
|
||||
import io.github.memfis19.annca.internal.controller.view.CameraView;
|
||||
import io.github.memfis19.annca.internal.ui.view.AspectFrameLayout;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
import io.github.memfis19.annca.internal.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 12/1/16.
|
||||
*/
|
||||
|
||||
abstract public class AnncaCameraActivity<CameraId> extends Activity
|
||||
implements ConfigurationProvider, CameraView, SensorEventListener {
|
||||
|
||||
private SensorManager sensorManager = null;
|
||||
|
||||
protected AspectFrameLayout previewContainer;
|
||||
protected ViewGroup userContainer;
|
||||
|
||||
private CameraController<CameraId> cameraController;
|
||||
|
||||
@AnncaConfiguration.SensorPosition
|
||||
protected int sensorPosition = AnncaConfiguration.SENSOR_POSITION_UNSPECIFIED;
|
||||
@AnncaConfiguration.DeviceDefaultOrientation
|
||||
protected int deviceDefaultOrientation;
|
||||
private int degrees = -1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
onProcessBundle(savedInstanceState);
|
||||
|
||||
cameraController = createCameraController(this, this);
|
||||
cameraController.onCreate(savedInstanceState);
|
||||
|
||||
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
|
||||
int defaultOrientation = Utils.getDeviceDefaultOrientation(this);
|
||||
|
||||
if (defaultOrientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
deviceDefaultOrientation = AnncaConfiguration.ORIENTATION_LANDSCAPE;
|
||||
} else if (defaultOrientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||
deviceDefaultOrientation = AnncaConfiguration.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
|
||||
View decorView = getWindow().getDecorView();
|
||||
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
setContentView(R.layout.generic_camera_layout);
|
||||
previewContainer = (AspectFrameLayout) findViewById(R.id.previewContainer);
|
||||
userContainer = (ViewGroup) findViewById(R.id.userContainer);
|
||||
|
||||
setUserContent();
|
||||
}
|
||||
|
||||
protected void onProcessBundle(Bundle savedInstanceState) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
cameraController.onResume();
|
||||
sensorManager.registerListener(this, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
cameraController.onPause();
|
||||
sensorManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
cameraController.onDestroy();
|
||||
}
|
||||
|
||||
public final CameraController<CameraId> getCameraController() {
|
||||
return cameraController;
|
||||
}
|
||||
|
||||
public abstract CameraController<CameraId> createCameraController(CameraView cameraView, ConfigurationProvider configurationProvider);
|
||||
|
||||
private void setUserContent() {
|
||||
userContainer.removeAllViews();
|
||||
userContainer.addView(getUserContentView(LayoutInflater.from(this), userContainer));
|
||||
}
|
||||
|
||||
public final void setCameraPreview(View preview, Size previewSize) {
|
||||
try {
|
||||
if (previewContainer == null || preview == null) return;
|
||||
previewContainer.removeAllViews();
|
||||
previewContainer.addView(preview);
|
||||
previewContainer.setAspectRatio(previewSize.getHeight() / (double) previewSize.getWidth());
|
||||
} catch (Exception e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public final void setCustomCameraPreview(View preview, Size previewSize, Size customSize) {
|
||||
if (previewContainer == null || preview == null) return;
|
||||
previewContainer.removeAllViews();
|
||||
previewContainer.addView(preview);
|
||||
|
||||
previewContainer.setCustomSize(customSize, previewSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraReady() {
|
||||
onCameraControllerReady();
|
||||
}
|
||||
|
||||
public final void clearCameraPreview() {
|
||||
if (previewContainer != null)
|
||||
previewContainer.removeAllViews();
|
||||
}
|
||||
|
||||
protected abstract View getUserContentView(LayoutInflater layoutInflater, ViewGroup parent);
|
||||
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent sensorEvent) {
|
||||
synchronized (this) {
|
||||
if (sensorEvent.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
|
||||
if (sensorEvent.values[0] < 4 && sensorEvent.values[0] > -4) {
|
||||
if (sensorEvent.values[1] > 0) {
|
||||
// UP
|
||||
sensorPosition = AnncaConfiguration.SENSOR_POSITION_UP;
|
||||
degrees = deviceDefaultOrientation == AnncaConfiguration.ORIENTATION_PORTRAIT ? 0 : 90;
|
||||
} else if (sensorEvent.values[1] < 0) {
|
||||
// UP SIDE DOWN
|
||||
sensorPosition = AnncaConfiguration.SENSOR_POSITION_UP_SIDE_DOWN;
|
||||
degrees = deviceDefaultOrientation == AnncaConfiguration.ORIENTATION_PORTRAIT ? 180 : 270;
|
||||
}
|
||||
} else if (sensorEvent.values[1] < 4 && sensorEvent.values[1] > -4) {
|
||||
if (sensorEvent.values[0] > 0) {
|
||||
// LEFT
|
||||
sensorPosition = AnncaConfiguration.SENSOR_POSITION_LEFT;
|
||||
degrees = deviceDefaultOrientation == AnncaConfiguration.ORIENTATION_PORTRAIT ? 90 : 180;
|
||||
} else if (sensorEvent.values[0] < 0) {
|
||||
// RIGHT
|
||||
sensorPosition = AnncaConfiguration.SENSOR_POSITION_RIGHT;
|
||||
degrees = deviceDefaultOrientation == AnncaConfiguration.ORIENTATION_PORTRAIT ? 270 : 0;
|
||||
}
|
||||
}
|
||||
onScreenRotation(degrees);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getSensorPosition() {
|
||||
return sensorPosition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getDegrees() {
|
||||
return degrees;
|
||||
}
|
||||
|
||||
protected abstract void onScreenRotation(int degrees);
|
||||
|
||||
protected void onCameraControllerReady() {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,536 @@
|
||||
package io.github.memfis19.annca.internal.ui;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.ui.model.PhotoQualityOption;
|
||||
import io.github.memfis19.annca.internal.ui.model.VideoQualityOption;
|
||||
import io.github.memfis19.annca.internal.ui.preview.PreviewActivity;
|
||||
import io.github.memfis19.annca.internal.ui.view.CameraControlPanel;
|
||||
import io.github.memfis19.annca.internal.ui.view.CameraSwitchView;
|
||||
import io.github.memfis19.annca.internal.ui.view.FlashSwitchView;
|
||||
import io.github.memfis19.annca.internal.ui.view.MediaActionSwitchView;
|
||||
import io.github.memfis19.annca.internal.ui.view.RecordButton;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
import io.github.memfis19.annca.internal.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 12/1/16.
|
||||
*/
|
||||
|
||||
public abstract class BaseAnncaActivity<CameraId> extends AnncaCameraActivity<CameraId>
|
||||
implements
|
||||
RecordButton.RecordButtonListener,
|
||||
FlashSwitchView.FlashModeSwitchListener,
|
||||
MediaActionSwitchView.OnMediaActionStateChangeListener,
|
||||
CameraSwitchView.OnCameraTypeChangeListener, CameraControlPanel.SettingsClickListener {
|
||||
|
||||
private CameraControlPanel cameraControlPanel;
|
||||
private AlertDialog settingsDialog;
|
||||
|
||||
protected static final int REQUEST_PREVIEW_CODE = 1001;
|
||||
|
||||
public static final int ACTION_CONFIRM = 900;
|
||||
public static final int ACTION_RETAKE = 901;
|
||||
public static final int ACTION_CANCEL = 902;
|
||||
|
||||
protected int requestCode = -1;
|
||||
|
||||
@AnncaConfiguration.MediaAction
|
||||
protected int mediaAction = AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED;
|
||||
@AnncaConfiguration.MediaQuality
|
||||
protected int mediaQuality = AnncaConfiguration.MEDIA_QUALITY_MEDIUM;
|
||||
@AnncaConfiguration.MediaQuality
|
||||
protected int passedMediaQuality = AnncaConfiguration.MEDIA_QUALITY_MEDIUM;
|
||||
|
||||
@AnncaConfiguration.FlashMode
|
||||
protected int flashMode = AnncaConfiguration.FLASH_MODE_AUTO;
|
||||
|
||||
protected CharSequence[] videoQualities;
|
||||
protected CharSequence[] photoQualities;
|
||||
|
||||
protected int videoDuration = -1;
|
||||
protected long videoFileSize = -1;
|
||||
protected int minimumVideoDuration = -1;
|
||||
protected String filePath = "";
|
||||
protected boolean showGrid;
|
||||
|
||||
@MediaActionSwitchView.MediaActionState
|
||||
protected int currentMediaActionState;
|
||||
|
||||
@CameraSwitchView.CameraType
|
||||
protected int currentCameraType = CameraSwitchView.CAMERA_TYPE_FRONT;
|
||||
|
||||
@AnncaConfiguration.MediaResultBehaviour
|
||||
private int mediaResultBehaviour = AnncaConfiguration.PREVIEW;
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
@AnncaConfiguration.MediaQuality
|
||||
protected int newQuality = -1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProcessBundle(Bundle savedInstanceState) {
|
||||
super.onProcessBundle(savedInstanceState);
|
||||
|
||||
extractConfiguration(getIntent().getExtras());
|
||||
currentMediaActionState = mediaAction == AnncaConfiguration.MEDIA_ACTION_VIDEO ?
|
||||
MediaActionSwitchView.ACTION_VIDEO : MediaActionSwitchView.ACTION_PHOTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCameraControllerReady() {
|
||||
super.onCameraControllerReady();
|
||||
|
||||
videoQualities = getVideoQualityOptions();
|
||||
photoQualities = getPhotoQualityOptions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
|
||||
cameraControlPanel.lockControls();
|
||||
cameraControlPanel.allowRecord(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
|
||||
cameraControlPanel.lockControls();
|
||||
cameraControlPanel.allowRecord(false);
|
||||
}
|
||||
|
||||
private void extractConfiguration(Bundle bundle) {
|
||||
if (bundle != null) {
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.REQUEST_CODE))
|
||||
requestCode = bundle.getInt(AnncaConfiguration.Arguments.REQUEST_CODE);
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.MEDIA_ACTION)) {
|
||||
switch (bundle.getInt(AnncaConfiguration.Arguments.MEDIA_ACTION)) {
|
||||
case AnncaConfiguration.MEDIA_ACTION_PHOTO:
|
||||
mediaAction = AnncaConfiguration.MEDIA_ACTION_PHOTO;
|
||||
break;
|
||||
case AnncaConfiguration.MEDIA_ACTION_VIDEO:
|
||||
mediaAction = AnncaConfiguration.MEDIA_ACTION_VIDEO;
|
||||
break;
|
||||
default:
|
||||
mediaAction = AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.MEDIA_RESULT_BEHAVIOUR)) {
|
||||
switch (bundle.getInt(AnncaConfiguration.Arguments.MEDIA_RESULT_BEHAVIOUR)) {
|
||||
case AnncaConfiguration.CLOSE:
|
||||
mediaResultBehaviour = AnncaConfiguration.CLOSE;
|
||||
break;
|
||||
case AnncaConfiguration.PREVIEW:
|
||||
mediaResultBehaviour = AnncaConfiguration.PREVIEW;
|
||||
break;
|
||||
case AnncaConfiguration.CONTINUE:
|
||||
mediaResultBehaviour = AnncaConfiguration.CONTINUE;
|
||||
break;
|
||||
default:
|
||||
mediaResultBehaviour = AnncaConfiguration.PREVIEW;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.CAMERA_FACE)) {
|
||||
switch (bundle.getInt(AnncaConfiguration.Arguments.CAMERA_FACE)) {
|
||||
case AnncaConfiguration.CAMERA_FACE_FRONT:
|
||||
currentCameraType = CameraSwitchView.CAMERA_TYPE_FRONT;
|
||||
CameraSwitchView.currentCameraType = CameraSwitchView.CAMERA_TYPE_FRONT;
|
||||
break;
|
||||
case AnncaConfiguration.CAMERA_FACE_REAR:
|
||||
currentCameraType = CameraSwitchView.CAMERA_TYPE_REAR;
|
||||
CameraSwitchView.currentCameraType = CameraSwitchView.CAMERA_TYPE_REAR;
|
||||
break;
|
||||
default:
|
||||
currentCameraType = CameraSwitchView.CAMERA_TYPE_REAR;
|
||||
CameraSwitchView.currentCameraType = CameraSwitchView.CAMERA_TYPE_REAR;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.FILE_PATH)) {
|
||||
filePath = bundle.getString(AnncaConfiguration.Arguments.FILE_PATH);
|
||||
}
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.SHOW_GRID)) {
|
||||
showGrid = bundle.getBoolean(AnncaConfiguration.Arguments.SHOW_GRID);
|
||||
}
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.MEDIA_QUALITY)) {
|
||||
switch (bundle.getInt(AnncaConfiguration.Arguments.MEDIA_QUALITY)) {
|
||||
case AnncaConfiguration.MEDIA_QUALITY_AUTO:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_AUTO;
|
||||
break;
|
||||
case AnncaConfiguration.MEDIA_QUALITY_HIGHEST:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_HIGHEST;
|
||||
break;
|
||||
case AnncaConfiguration.MEDIA_QUALITY_HIGH:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_HIGH;
|
||||
break;
|
||||
case AnncaConfiguration.MEDIA_QUALITY_MEDIUM:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_MEDIUM;
|
||||
break;
|
||||
case AnncaConfiguration.MEDIA_QUALITY_LOW:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_LOW;
|
||||
break;
|
||||
case AnncaConfiguration.MEDIA_QUALITY_LOWEST:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_LOWEST;
|
||||
break;
|
||||
default:
|
||||
mediaQuality = AnncaConfiguration.MEDIA_QUALITY_MEDIUM;
|
||||
break;
|
||||
}
|
||||
passedMediaQuality = mediaQuality;
|
||||
}
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.VIDEO_DURATION))
|
||||
videoDuration = bundle.getInt(AnncaConfiguration.Arguments.VIDEO_DURATION);
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.VIDEO_FILE_SIZE))
|
||||
videoFileSize = bundle.getLong(AnncaConfiguration.Arguments.VIDEO_FILE_SIZE);
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.MINIMUM_VIDEO_DURATION))
|
||||
minimumVideoDuration = bundle.getInt(AnncaConfiguration.Arguments.MINIMUM_VIDEO_DURATION);
|
||||
|
||||
if (bundle.containsKey(AnncaConfiguration.Arguments.FLASH_MODE))
|
||||
switch (bundle.getInt(AnncaConfiguration.Arguments.FLASH_MODE)) {
|
||||
case AnncaConfiguration.FLASH_MODE_AUTO:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_AUTO;
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_ON:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_ON;
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_OFF:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_OFF;
|
||||
break;
|
||||
default:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_AUTO;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getUserContentView(LayoutInflater layoutInflater, ViewGroup parent) {
|
||||
cameraControlPanel = (CameraControlPanel) layoutInflater.inflate(R.layout.user_control_layout, parent, false);
|
||||
|
||||
if (cameraControlPanel != null) {
|
||||
cameraControlPanel.setup(getMediaAction());
|
||||
|
||||
switch (flashMode) {
|
||||
case AnncaConfiguration.FLASH_MODE_AUTO:
|
||||
cameraControlPanel.setFlasMode(FlashSwitchView.FLASH_AUTO);
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_ON:
|
||||
cameraControlPanel.setFlasMode(FlashSwitchView.FLASH_ON);
|
||||
break;
|
||||
case AnncaConfiguration.FLASH_MODE_OFF:
|
||||
cameraControlPanel.setFlasMode(FlashSwitchView.FLASH_OFF);
|
||||
break;
|
||||
}
|
||||
|
||||
cameraControlPanel.setRecordButtonListener(this);
|
||||
cameraControlPanel.setFlashModeSwitchListener(this);
|
||||
cameraControlPanel.setOnMediaActionStateChangeListener(this);
|
||||
cameraControlPanel.setOnCameraTypeChangeListener(this);
|
||||
cameraControlPanel.setMaxVideoDuration(getVideoDuration());
|
||||
cameraControlPanel.setMaxVideoFileSize(getVideoFileSize());
|
||||
cameraControlPanel.setSettingsClickListener(this);
|
||||
}
|
||||
|
||||
return cameraControlPanel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSettingsClick() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
|
||||
if (currentMediaActionState == MediaActionSwitchView.ACTION_VIDEO) {
|
||||
builder.setSingleChoiceItems(videoQualities, getVideoOptionCheckedIndex(), getVideoOptionSelectedListener());
|
||||
if (getVideoFileSize() > 0)
|
||||
builder.setTitle(String.format(getString(R.string.settings_video_quality_title),
|
||||
"(Max " + String.valueOf(getVideoFileSize() / (1024 * 1024) + " MB)")));
|
||||
else
|
||||
builder.setTitle(String.format(getString(R.string.settings_video_quality_title), ""));
|
||||
} else {
|
||||
builder.setSingleChoiceItems(photoQualities, getPhotoOptionCheckedIndex(), getPhotoOptionSelectedListener());
|
||||
builder.setTitle(R.string.settings_photo_quality_title);
|
||||
}
|
||||
|
||||
builder.setPositiveButton(R.string.ok_label, (dialogInterface, i) -> {
|
||||
if (newQuality > 0 && newQuality != mediaQuality) {
|
||||
mediaQuality = newQuality;
|
||||
dialogInterface.dismiss();
|
||||
cameraControlPanel.lockControls();
|
||||
getCameraController().switchQuality();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(R.string.cancel_label, (dialogInterface, i) -> dialogInterface.dismiss());
|
||||
settingsDialog = builder.create();
|
||||
settingsDialog.show();
|
||||
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
|
||||
layoutParams.copyFrom(settingsDialog.getWindow().getAttributes());
|
||||
layoutParams.width = Utils.convertDipToPixels(this, 350);
|
||||
layoutParams.height = Utils.convertDipToPixels(this, 350);
|
||||
settingsDialog.getWindow().setAttributes(layoutParams);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraTypeChanged(@CameraSwitchView.CameraType int cameraType) {
|
||||
if (currentCameraType == cameraType) return;
|
||||
currentCameraType = cameraType;
|
||||
|
||||
cameraControlPanel.lockControls();
|
||||
cameraControlPanel.allowRecord(false);
|
||||
|
||||
int cameraFace = cameraType == CameraSwitchView.CAMERA_TYPE_FRONT
|
||||
? AnncaConfiguration.CAMERA_FACE_FRONT : AnncaConfiguration.CAMERA_FACE_REAR;
|
||||
|
||||
getCameraController().switchCamera(cameraFace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFlashModeChanged(@FlashSwitchView.FlashMode int mode) {
|
||||
switch (mode) {
|
||||
case FlashSwitchView.FLASH_AUTO:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_AUTO;
|
||||
getCameraController().setFlashMode(AnncaConfiguration.FLASH_MODE_AUTO);
|
||||
break;
|
||||
case FlashSwitchView.FLASH_ON:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_ON;
|
||||
getCameraController().setFlashMode(AnncaConfiguration.FLASH_MODE_ON);
|
||||
break;
|
||||
case FlashSwitchView.FLASH_OFF:
|
||||
flashMode = AnncaConfiguration.FLASH_MODE_OFF;
|
||||
getCameraController().setFlashMode(AnncaConfiguration.FLASH_MODE_OFF);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaActionChanged(int mediaActionState) {
|
||||
if (currentMediaActionState == mediaActionState) return;
|
||||
currentMediaActionState = mediaActionState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTakePhotoButtonPressed() {
|
||||
getCameraController().takePhoto();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartRecordingButtonPressed() {
|
||||
getCameraController().startVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopRecordingButtonPressed() {
|
||||
getCameraController().stopVideoRecord();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onScreenRotation(int degrees) {
|
||||
cameraControlPanel.rotateControls(degrees);
|
||||
rotateSettingsDialog(degrees);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRequestCode() {
|
||||
return requestCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediaAction() {
|
||||
return mediaAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediaQuality() {
|
||||
return mediaQuality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getVideoDuration() {
|
||||
return videoDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getVideoFileSize() {
|
||||
return videoFileSize;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getMinimumVideoDuration() {
|
||||
return minimumVideoDuration / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFlashMode() {
|
||||
return flashMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Activity getActivity() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCameraFace() {
|
||||
return currentCameraType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMediaResultBehaviour() {
|
||||
return mediaResultBehaviour;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilePath() {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getShowGrid () {
|
||||
return showGrid;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void updateCameraPreview(Size size, View cameraPreview) {
|
||||
cameraControlPanel.unLockControls();
|
||||
cameraControlPanel.allowRecord(true);
|
||||
|
||||
setCameraPreview(cameraPreview, size);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateUiForMediaAction(@AnncaConfiguration.MediaAction int mediaAction) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateCameraSwitcher(int numberOfCameras) {
|
||||
cameraControlPanel.allowCameraSwitching(numberOfCameras > 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPhotoTaken() {
|
||||
startPreviewActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordStart(int width, int height) {
|
||||
cameraControlPanel.onStartVideoRecord(getCameraController().getOutputFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVideoRecordStop() {
|
||||
cameraControlPanel.allowRecord(false);
|
||||
cameraControlPanel.onStopVideoRecord();
|
||||
startPreviewActivity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void releaseCameraPreview() {
|
||||
clearCameraPreview();
|
||||
}
|
||||
|
||||
private void startPreviewActivity() {
|
||||
if (mediaResultBehaviour == AnncaConfiguration.PREVIEW) {
|
||||
Intent intent = PreviewActivity.newIntent(this,
|
||||
getMediaAction(), getCameraController().getOutputFile().toString());
|
||||
startActivityForResult(intent, REQUEST_PREVIEW_CODE);
|
||||
} else if (mediaResultBehaviour == AnncaConfiguration.CONTINUE) {
|
||||
getCameraController().openCamera();
|
||||
} else if (mediaResultBehaviour == AnncaConfiguration.CLOSE) {
|
||||
finish();
|
||||
} else {
|
||||
Intent intent = PreviewActivity.newIntent(this,
|
||||
getMediaAction(), getCameraController().getOutputFile().toString());
|
||||
startActivityForResult(intent, REQUEST_PREVIEW_CODE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
if (requestCode == REQUEST_PREVIEW_CODE) {
|
||||
if (PreviewActivity.isResultConfirm(data)) {
|
||||
Intent resultIntent = new Intent();
|
||||
resultIntent.putExtra(AnncaConfiguration.Arguments.FILE_PATH, PreviewActivity.getMediaFilePatch(data));
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
} else if (PreviewActivity.isResultCancel(data)) {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
} else if (PreviewActivity.isResultRetake(data)) {
|
||||
//ignore, just proceed the camera
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void rotateSettingsDialog(int degrees) {
|
||||
if (settingsDialog != null && settingsDialog.isShowing()) {
|
||||
ViewGroup dialogView = (ViewGroup) settingsDialog.getWindow().getDecorView();
|
||||
for (int i = 0; i < dialogView.getChildCount(); i++) {
|
||||
dialogView.getChildAt(i).setRotation(degrees);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract CharSequence[] getVideoQualityOptions();
|
||||
|
||||
protected abstract CharSequence[] getPhotoQualityOptions();
|
||||
|
||||
protected int getVideoOptionCheckedIndex() {
|
||||
int checkedIndex = -1;
|
||||
if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_AUTO) checkedIndex = 0;
|
||||
else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGH) checkedIndex = 1;
|
||||
else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_MEDIUM) checkedIndex = 2;
|
||||
else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOW) checkedIndex = 3;
|
||||
|
||||
if (passedMediaQuality != AnncaConfiguration.MEDIA_QUALITY_AUTO) checkedIndex--;
|
||||
|
||||
return checkedIndex;
|
||||
}
|
||||
|
||||
protected int getPhotoOptionCheckedIndex() {
|
||||
int checkedIndex = -1;
|
||||
if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGHEST) checkedIndex = 0;
|
||||
else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGH) checkedIndex = 1;
|
||||
else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_MEDIUM) checkedIndex = 2;
|
||||
else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOWEST) checkedIndex = 3;
|
||||
return checkedIndex;
|
||||
}
|
||||
|
||||
protected DialogInterface.OnClickListener getVideoOptionSelectedListener() {
|
||||
return (dialogInterface, index) -> newQuality = ((VideoQualityOption) videoQualities[index]).getMediaQuality();
|
||||
}
|
||||
|
||||
protected DialogInterface.OnClickListener getPhotoOptionSelectedListener() {
|
||||
return (dialogInterface, index) -> newQuality = ((PhotoQualityOption) photoQualities[index]).getMediaQuality();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package io.github.memfis19.annca.internal.ui.camera;
|
||||
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.hardware.Camera;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.controller.CameraController;
|
||||
import io.github.memfis19.annca.internal.controller.impl.Camera1Controller;
|
||||
import io.github.memfis19.annca.internal.controller.view.CameraView;
|
||||
import io.github.memfis19.annca.internal.ui.BaseAnncaActivity;
|
||||
import io.github.memfis19.annca.internal.ui.model.PhotoQualityOption;
|
||||
import io.github.memfis19.annca.internal.ui.model.VideoQualityOption;
|
||||
import io.github.memfis19.annca.internal.utils.CameraHelper;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class Camera1Activity extends BaseAnncaActivity<Integer> {
|
||||
|
||||
@Override
|
||||
public CameraController<Integer> createCameraController(CameraView cameraView, ConfigurationProvider configurationProvider) {
|
||||
return new Camera1Controller(cameraView, configurationProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence[] getVideoQualityOptions() {
|
||||
List<CharSequence> videoQualities = new ArrayList<>();
|
||||
|
||||
if (getMinimumVideoDuration() > 0)
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_AUTO, CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_AUTO, getCameraController().getCurrentCameraId()), getMinimumVideoDuration()));
|
||||
|
||||
CamcorderProfile camcorderProfile = CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_HIGH, getCameraController().getCurrentCameraId());
|
||||
double videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, getVideoFileSize());
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_HIGH, camcorderProfile, videoDuration));
|
||||
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_MEDIUM, getCameraController().getCurrentCameraId());
|
||||
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, getVideoFileSize());
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_MEDIUM, camcorderProfile, videoDuration));
|
||||
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_LOW, getCameraController().getCurrentCameraId());
|
||||
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, getVideoFileSize());
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_LOW, camcorderProfile, videoDuration));
|
||||
|
||||
CharSequence[] array = new CharSequence[videoQualities.size()];
|
||||
videoQualities.toArray(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence[] getPhotoQualityOptions() {
|
||||
List<CharSequence> photoQualities = new ArrayList<>();
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_HIGHEST, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_HIGHEST)));
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_HIGH, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_HIGH)));
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_MEDIUM, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_MEDIUM)));
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_LOWEST, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_LOWEST)));
|
||||
|
||||
CharSequence[] array = new CharSequence[photoQualities.size()];
|
||||
photoQualities.toArray(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package io.github.memfis19.annca.internal.ui.camera2;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.configuration.ConfigurationProvider;
|
||||
import io.github.memfis19.annca.internal.controller.CameraController;
|
||||
import io.github.memfis19.annca.internal.controller.impl.Camera2Controller;
|
||||
import io.github.memfis19.annca.internal.controller.view.CameraView;
|
||||
import io.github.memfis19.annca.internal.ui.BaseAnncaActivity;
|
||||
import io.github.memfis19.annca.internal.ui.model.PhotoQualityOption;
|
||||
import io.github.memfis19.annca.internal.ui.model.VideoQualityOption;
|
||||
import io.github.memfis19.annca.internal.utils.CameraHelper;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class Camera2Activity extends BaseAnncaActivity<String> {
|
||||
|
||||
@Override
|
||||
public CameraController<String> createCameraController(CameraView cameraView, ConfigurationProvider configurationProvider) {
|
||||
return new Camera2Controller(cameraView, configurationProvider);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence[] getVideoQualityOptions() {
|
||||
List<CharSequence> videoQualities = new ArrayList<>();
|
||||
|
||||
if (getMinimumVideoDuration() > 0)
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_AUTO, CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_AUTO, getCameraController().getCurrentCameraId()), getMinimumVideoDuration()));
|
||||
|
||||
|
||||
CamcorderProfile camcorderProfile = CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_HIGH, getCameraController().getCurrentCameraId());
|
||||
double videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, getVideoFileSize());
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_HIGH, camcorderProfile, videoDuration));
|
||||
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_MEDIUM, getCameraController().getCurrentCameraId());
|
||||
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, getVideoFileSize());
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_MEDIUM, camcorderProfile, videoDuration));
|
||||
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_LOW, getCameraController().getCurrentCameraId());
|
||||
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, getVideoFileSize());
|
||||
videoQualities.add(new VideoQualityOption(AnncaConfiguration.MEDIA_QUALITY_LOW, camcorderProfile, videoDuration));
|
||||
|
||||
CharSequence[] array = new CharSequence[videoQualities.size()];
|
||||
videoQualities.toArray(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CharSequence[] getPhotoQualityOptions() {
|
||||
List<CharSequence> photoQualities = new ArrayList<>();
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_HIGHEST, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_HIGHEST)));
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_HIGH, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_HIGH)));
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_MEDIUM, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_MEDIUM)));
|
||||
photoQualities.add(new PhotoQualityOption(AnncaConfiguration.MEDIA_QUALITY_LOWEST, getCameraController().getCameraManager().getPhotoSizeForQuality(AnncaConfiguration.MEDIA_QUALITY_LOWEST)));
|
||||
|
||||
CharSequence[] array = new CharSequence[photoQualities.size()];
|
||||
photoQualities.toArray(array);
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.github.memfis19.annca.internal.ui.model;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Created by memfis on 12/1/16.
|
||||
*/
|
||||
|
||||
public class PhotoQualityOption implements CharSequence {
|
||||
|
||||
@AnncaConfiguration.MediaQuality
|
||||
private int mediaQuality;
|
||||
private String title;
|
||||
|
||||
public PhotoQualityOption(@AnncaConfiguration.MediaQuality int mediaQuality, Size size) {
|
||||
this.mediaQuality = mediaQuality;
|
||||
|
||||
title = String.valueOf(size.getWidth()) + " x " + String.valueOf(size.getHeight());
|
||||
}
|
||||
|
||||
@AnncaConfiguration.MediaQuality
|
||||
public int getMediaQuality() {
|
||||
return mediaQuality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return title.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return title.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return title.subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package io.github.memfis19.annca.internal.ui.model;
|
||||
|
||||
import android.media.CamcorderProfile;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
|
||||
/**
|
||||
* Created by memfis on 12/1/16.
|
||||
*/
|
||||
|
||||
public class VideoQualityOption implements CharSequence {
|
||||
|
||||
private String title;
|
||||
|
||||
@AnncaConfiguration.MediaQuality
|
||||
private int mediaQuality;
|
||||
|
||||
public VideoQualityOption(@AnncaConfiguration.MediaQuality int mediaQuality, CamcorderProfile camcorderProfile, double videoDuration) {
|
||||
this.mediaQuality = mediaQuality;
|
||||
|
||||
long minutes = TimeUnit.SECONDS.toMinutes((long) videoDuration);
|
||||
long seconds = ((long) videoDuration) - minutes * 60;
|
||||
|
||||
if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_AUTO) {
|
||||
title = "Auto " + ", (" + (minutes > 10 ? minutes : ("0" + minutes)) + ":" + (seconds > 10 ? seconds : ("0" + seconds)) + " min)";
|
||||
} else {
|
||||
title = camcorderProfile.videoFrameWidth
|
||||
+ " x " + camcorderProfile.videoFrameHeight
|
||||
+ (videoDuration <= 0 ? "" : ", (" + (minutes > 10 ? minutes : ("0" + minutes)) + ":" + (seconds > 10 ? seconds : ("0" + seconds)) + " min)");
|
||||
}
|
||||
}
|
||||
|
||||
@AnncaConfiguration.MediaQuality
|
||||
public int getMediaQuality() {
|
||||
return mediaQuality;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return title.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return title.charAt(index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return title.subSequence(start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,365 @@
|
||||
package io.github.memfis19.annca.internal.ui.preview;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaPlayer;
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.MediaController;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.ui.BaseAnncaActivity;
|
||||
import io.github.memfis19.annca.internal.ui.view.AspectFrameLayout;
|
||||
import io.github.memfis19.annca.internal.utils.AnncaImageLoader;
|
||||
import io.github.memfis19.annca.internal.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class PreviewActivity extends AppCompatActivity implements View.OnClickListener {
|
||||
|
||||
private static final String TAG = "PreviewActivity";
|
||||
|
||||
private final static String MEDIA_ACTION_ARG = "media_action_arg";
|
||||
private final static String FILE_PATH_ARG = "file_path_arg";
|
||||
private final static String RESPONSE_CODE_ARG = "response_code_arg";
|
||||
private final static String VIDEO_POSITION_ARG = "current_video_position";
|
||||
private final static String VIDEO_IS_PLAYED_ARG = "is_played";
|
||||
private final static String MIME_TYPE_VIDEO = "video";
|
||||
private final static String MIME_TYPE_IMAGE = "image";
|
||||
|
||||
private int mediaAction;
|
||||
private String previewFilePath;
|
||||
|
||||
private SurfaceView surfaceView;
|
||||
private FrameLayout photoPreviewContainer;
|
||||
private ImageView imagePreview;
|
||||
private ViewGroup buttonPanel;
|
||||
private AspectFrameLayout videoPreviewContainer;
|
||||
private View cropMediaAction;
|
||||
private TextView ratioChanger;
|
||||
|
||||
private MediaController mediaController;
|
||||
private MediaPlayer mediaPlayer;
|
||||
|
||||
private int currentPlaybackPosition = 0;
|
||||
private boolean isVideoPlaying = true;
|
||||
|
||||
private int currentRatioIndex = 0;
|
||||
private float[] ratios;
|
||||
private String[] ratioLabels;
|
||||
|
||||
private SurfaceHolder.Callback surfaceCallbacks = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
showVideoPreview(holder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
private MediaController.MediaPlayerControl MediaPlayerControlImpl = new MediaController.MediaPlayerControl() {
|
||||
@Override
|
||||
public void start() {
|
||||
mediaPlayer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() {
|
||||
mediaPlayer.pause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDuration() {
|
||||
return mediaPlayer.getDuration();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPosition() {
|
||||
return mediaPlayer.getCurrentPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void seekTo(int pos) {
|
||||
mediaPlayer.seekTo(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlaying() {
|
||||
return mediaPlayer.isPlaying();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferPercentage() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPause() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSeekBackward() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSeekForward() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAudioSessionId() {
|
||||
return mediaPlayer.getAudioSessionId();
|
||||
}
|
||||
};
|
||||
|
||||
public static Intent newIntent(Context context,
|
||||
@AnncaConfiguration.MediaAction int mediaAction,
|
||||
String filePath) {
|
||||
|
||||
return new Intent(context, PreviewActivity.class)
|
||||
.putExtra(MEDIA_ACTION_ARG, mediaAction)
|
||||
.putExtra(FILE_PATH_ARG, filePath);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_preview);
|
||||
|
||||
String originalRatioLabel = getString(R.string.preview_controls_original_ratio_label);
|
||||
ratioLabels = new String[]{originalRatioLabel, "1:1", "4:3", "16:9"};
|
||||
ratios = new float[]{0f, 1f, 4f / 3f, 16f / 9f};
|
||||
|
||||
surfaceView = (SurfaceView) findViewById(R.id.video_preview);
|
||||
surfaceView.setOnTouchListener((v, event) -> {
|
||||
if (mediaController == null) return false;
|
||||
if (mediaController.isShowing()) {
|
||||
mediaController.hide();
|
||||
showButtonPanel(true);
|
||||
} else {
|
||||
showButtonPanel(false);
|
||||
mediaController.show();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
videoPreviewContainer = (AspectFrameLayout) findViewById(R.id.previewAspectFrameLayout);
|
||||
photoPreviewContainer = (FrameLayout) findViewById(R.id.photo_preview_container);
|
||||
buttonPanel = (ViewGroup) findViewById(R.id.preview_control_panel);
|
||||
View confirmMediaResult = findViewById(R.id.confirm_media_result);
|
||||
View reTakeMedia = findViewById(R.id.re_take_media);
|
||||
View cancelMediaAction = findViewById(R.id.cancel_media_action);
|
||||
cropMediaAction = findViewById(R.id.crop_image);
|
||||
ratioChanger = (TextView) findViewById(R.id.ratio_image);
|
||||
ratioChanger.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
currentRatioIndex = (currentRatioIndex + 1) % ratios.length;
|
||||
ratioChanger.setText(ratioLabels[currentRatioIndex]);
|
||||
}
|
||||
});
|
||||
|
||||
cropMediaAction.setVisibility(View.GONE);
|
||||
ratioChanger.setVisibility(View.GONE);
|
||||
|
||||
if (cropMediaAction != null)
|
||||
cropMediaAction.setOnClickListener(view -> {
|
||||
});
|
||||
|
||||
if (confirmMediaResult != null)
|
||||
confirmMediaResult.setOnClickListener(this);
|
||||
|
||||
if (reTakeMedia != null)
|
||||
reTakeMedia.setOnClickListener(this);
|
||||
|
||||
if (cancelMediaAction != null)
|
||||
cancelMediaAction.setOnClickListener(this);
|
||||
|
||||
Bundle args = getIntent().getExtras();
|
||||
|
||||
mediaAction = args.getInt(MEDIA_ACTION_ARG);
|
||||
previewFilePath = args.getString(FILE_PATH_ARG);
|
||||
|
||||
if (mediaAction == AnncaConfiguration.MEDIA_ACTION_VIDEO) {
|
||||
displayVideo(savedInstanceState);
|
||||
} else if (mediaAction == AnncaConfiguration.MEDIA_ACTION_PHOTO) {
|
||||
displayImage();
|
||||
} else {
|
||||
String mimeType = Utils.getMimeType(previewFilePath);
|
||||
if (mimeType.contains(MIME_TYPE_VIDEO)) {
|
||||
displayVideo(savedInstanceState);
|
||||
} else if (mimeType.contains(MIME_TYPE_IMAGE)) {
|
||||
displayImage();
|
||||
} else finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
saveVideoParams(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (mediaPlayer != null) {
|
||||
mediaPlayer.release();
|
||||
mediaPlayer = null;
|
||||
}
|
||||
if (mediaController != null) {
|
||||
mediaController.hide();
|
||||
mediaController = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void displayImage() {
|
||||
videoPreviewContainer.setVisibility(View.GONE);
|
||||
surfaceView.setVisibility(View.GONE);
|
||||
showImagePreview();
|
||||
ratioChanger.setText(ratioLabels[currentRatioIndex]);
|
||||
}
|
||||
|
||||
private void showImagePreview() {
|
||||
imagePreview = new ImageView(this);
|
||||
AnncaImageLoader.Builder builder = new AnncaImageLoader.Builder(this);
|
||||
builder.load(previewFilePath).build().into(imagePreview);
|
||||
photoPreviewContainer.removeAllViews();
|
||||
photoPreviewContainer.addView(imagePreview);
|
||||
}
|
||||
|
||||
private void displayVideo(Bundle savedInstanceState) {
|
||||
cropMediaAction.setVisibility(View.GONE);
|
||||
ratioChanger.setVisibility(View.GONE);
|
||||
if (savedInstanceState != null) {
|
||||
loadVideoParams(savedInstanceState);
|
||||
}
|
||||
photoPreviewContainer.setVisibility(View.GONE);
|
||||
surfaceView.getHolder().addCallback(surfaceCallbacks);
|
||||
}
|
||||
|
||||
private void showVideoPreview(SurfaceHolder holder) {
|
||||
try {
|
||||
mediaPlayer = new MediaPlayer();
|
||||
mediaPlayer.setDataSource(previewFilePath);
|
||||
mediaPlayer.setDisplay(holder);
|
||||
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
|
||||
mediaPlayer.setOnPreparedListener(mp -> {
|
||||
mediaController = new MediaController(PreviewActivity.this);
|
||||
mediaController.setAnchorView(surfaceView);
|
||||
mediaController.setMediaPlayer(MediaPlayerControlImpl);
|
||||
|
||||
int videoWidth = mp.getVideoWidth();
|
||||
int videoHeight = mp.getVideoHeight();
|
||||
|
||||
videoPreviewContainer.setAspectRatio((double) videoWidth / videoHeight);
|
||||
|
||||
mediaPlayer.start();
|
||||
mediaPlayer.seekTo(currentPlaybackPosition);
|
||||
|
||||
if (!isVideoPlaying)
|
||||
mediaPlayer.pause();
|
||||
});
|
||||
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
|
||||
finish();
|
||||
return true;
|
||||
});
|
||||
mediaPlayer.prepareAsync();
|
||||
} catch (Exception e) {
|
||||
//test(TAG, "Error media player playing video.");
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void saveVideoParams(Bundle outState) {
|
||||
if (mediaPlayer != null) {
|
||||
outState.putInt(VIDEO_POSITION_ARG, mediaPlayer.getCurrentPosition());
|
||||
outState.putBoolean(VIDEO_IS_PLAYED_ARG, mediaPlayer.isPlaying());
|
||||
}
|
||||
}
|
||||
|
||||
private void loadVideoParams(Bundle savedInstanceState) {
|
||||
currentPlaybackPosition = savedInstanceState.getInt(VIDEO_POSITION_ARG, 0);
|
||||
isVideoPlaying = savedInstanceState.getBoolean(VIDEO_IS_PLAYED_ARG, true);
|
||||
}
|
||||
|
||||
private void showButtonPanel(boolean show) {
|
||||
if (show) {
|
||||
buttonPanel.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
buttonPanel.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Intent resultIntent = new Intent();
|
||||
if (view.getId() == R.id.confirm_media_result) {
|
||||
resultIntent.putExtra(RESPONSE_CODE_ARG, BaseAnncaActivity.ACTION_CONFIRM).putExtra(FILE_PATH_ARG, previewFilePath);
|
||||
} else if (view.getId() == R.id.re_take_media) {
|
||||
deleteMediaFile();
|
||||
resultIntent.putExtra(RESPONSE_CODE_ARG, BaseAnncaActivity.ACTION_RETAKE);
|
||||
} else if (view.getId() == R.id.cancel_media_action) {
|
||||
deleteMediaFile();
|
||||
resultIntent.putExtra(RESPONSE_CODE_ARG, BaseAnncaActivity.ACTION_CANCEL);
|
||||
}
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
super.onBackPressed();
|
||||
deleteMediaFile();
|
||||
}
|
||||
|
||||
private boolean deleteMediaFile() {
|
||||
File mediaFile = new File(previewFilePath);
|
||||
return mediaFile.delete();
|
||||
}
|
||||
|
||||
public static boolean isResultConfirm(@NonNull Intent resultIntent) {
|
||||
return BaseAnncaActivity.ACTION_CONFIRM == resultIntent.getIntExtra(RESPONSE_CODE_ARG, -1);
|
||||
}
|
||||
|
||||
public static String getMediaFilePatch(@NonNull Intent resultIntent) {
|
||||
return resultIntent.getStringExtra(FILE_PATH_ARG);
|
||||
}
|
||||
|
||||
public static boolean isResultRetake(@NonNull Intent resultIntent) {
|
||||
return BaseAnncaActivity.ACTION_RETAKE == resultIntent.getIntExtra(RESPONSE_CODE_ARG, -1);
|
||||
}
|
||||
|
||||
public static boolean isResultCancel(@NonNull Intent resultIntent) {
|
||||
return BaseAnncaActivity.ACTION_CANCEL == resultIntent.getIntExtra(RESPONSE_CODE_ARG, -1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import io.github.memfis19.annca.internal.utils.Size;
|
||||
|
||||
/**
|
||||
* Layout that adjusts to maintain a specific aspect ratio.
|
||||
*/
|
||||
public class AspectFrameLayout extends FrameLayout {
|
||||
|
||||
private static final String TAG = "AspectFrameLayout";
|
||||
|
||||
private double targetAspectRatio = -1.0; // initially use default window size
|
||||
|
||||
private Size size = null;
|
||||
private int actualPreviewWidth;
|
||||
private int actualPreviewHeight;
|
||||
|
||||
public AspectFrameLayout(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AspectFrameLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void setAspectRatio(double aspectRatio) {
|
||||
if (aspectRatio < 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (targetAspectRatio != aspectRatio) {
|
||||
targetAspectRatio = aspectRatio;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (size != null) {
|
||||
setMeasuredDimension(size.getWidth(), size.getHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
if (targetAspectRatio > 0) {
|
||||
int initialWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
int initialHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
// padding
|
||||
int horizontalPadding = getPaddingLeft() + getPaddingRight();
|
||||
int verticalPadding = getPaddingTop() + getPaddingBottom();
|
||||
initialWidth -= horizontalPadding;
|
||||
initialHeight -= verticalPadding;
|
||||
|
||||
double viewAspectRatio = (double) initialWidth / initialHeight;
|
||||
double aspectDifference = targetAspectRatio / viewAspectRatio - 1;
|
||||
|
||||
if (Math.abs(aspectDifference) < 0.01) {
|
||||
//no changes
|
||||
} else {
|
||||
if (aspectDifference > 0) {
|
||||
initialHeight = (int) (initialWidth / targetAspectRatio);
|
||||
} else {
|
||||
initialWidth = (int) (initialHeight * targetAspectRatio);
|
||||
}
|
||||
initialWidth += horizontalPadding;
|
||||
initialHeight += verticalPadding;
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(initialWidth, MeasureSpec.EXACTLY);
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(initialHeight, MeasureSpec.EXACTLY);
|
||||
}
|
||||
}
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
if (size != null && getChildAt(0) != null) {
|
||||
getChildAt(0).layout(0, 0, actualPreviewWidth, actualPreviewHeight);
|
||||
} else super.onLayout(changed, l, t, r, b);
|
||||
}
|
||||
|
||||
public void setCustomSize(final Size size, Size previewSize) {
|
||||
if (targetAspectRatio <= 0) {
|
||||
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
getViewTreeObserver().removeGlobalOnLayoutListener(this);
|
||||
// AspectFrameLayout.this.size = size;
|
||||
|
||||
actualPreviewWidth = getMeasuredWidth();
|
||||
actualPreviewHeight = getMeasuredHeight();
|
||||
|
||||
if (actualPreviewHeight < actualPreviewWidth)
|
||||
AspectFrameLayout.this.size = new Size(actualPreviewHeight, actualPreviewHeight);
|
||||
else
|
||||
AspectFrameLayout.this.size = new Size(actualPreviewWidth, actualPreviewWidth);
|
||||
|
||||
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
layoutParams.width = size.getWidth();
|
||||
layoutParams.height = size.getHeight();
|
||||
|
||||
setLayoutParams(layoutParams);
|
||||
requestLayout();
|
||||
}
|
||||
});
|
||||
setAspectRatio(previewSize.getHeight() / (double) previewSize.getWidth());
|
||||
}
|
||||
}
|
||||
|
||||
public int getCroppSize() {
|
||||
return size.getHeight();
|
||||
}
|
||||
|
||||
public void getCameraViewLocation(int[] location) {
|
||||
if (getChildAt(0) != null) {
|
||||
getChildAt(0).getLocationInWindow(location);
|
||||
} else getLocationInWindow(location);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.hardware.Camera;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.github.memfis19.annca.internal.ui.zoom.ZoomController;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class AutoFitSurfaceView extends SurfaceView implements ZoomController {
|
||||
private final static String TAG = "AutoFitSurfaceView";
|
||||
private final SurfaceHolder surfaceHolder;
|
||||
|
||||
private int ratioWidth;
|
||||
private int ratioHeight;
|
||||
|
||||
private Camera mCamera;
|
||||
float mDist = 0;
|
||||
Paint paint;
|
||||
TextView tv_zoom;
|
||||
|
||||
public AutoFitSurfaceView(@NonNull Context context, SurfaceHolder.Callback callback, Camera camera, boolean showGrid) {
|
||||
super(context);
|
||||
|
||||
this.surfaceHolder = getHolder();
|
||||
|
||||
this.surfaceHolder.addCallback(callback);
|
||||
this.surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||
|
||||
this.mCamera = camera;
|
||||
paint = new Paint();
|
||||
tv_zoom = MyTextview.zoomTextView;
|
||||
if (showGrid) {
|
||||
this.setWillNotDraw(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
|
||||
* calculated fromList the parameters. Note that the actual sizes of parameters don't matter, that
|
||||
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
|
||||
*
|
||||
* @param width Relative horizontal size
|
||||
* @param height Relative vertical size
|
||||
*/
|
||||
public void setAspectRatio(int width, int height) {
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Size cannot be negative.");
|
||||
}
|
||||
ratioWidth = width;
|
||||
ratioHeight = height;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
try {
|
||||
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
|
||||
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
|
||||
|
||||
if (0 == ratioWidth || 0 == ratioHeight) {
|
||||
setMeasuredDimension(width, height);
|
||||
} else {
|
||||
if (width < height * (ratioWidth / (float) ratioHeight)) {
|
||||
setMeasuredDimension(width, (int) (width * (ratioWidth / (float) ratioHeight)));
|
||||
} else {
|
||||
setMeasuredDimension((int) (height * (ratioWidth / (float) ratioHeight)), height);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// Get the pointer ID
|
||||
try {
|
||||
Camera.Parameters params = mCamera.getParameters();
|
||||
int action = event.getAction();
|
||||
|
||||
if (event.getPointerCount() > 1) {
|
||||
// handle multi-touch events
|
||||
if (action == MotionEvent.ACTION_POINTER_DOWN) {
|
||||
mDist = getFingerSpacing(event);
|
||||
} else if (action == MotionEvent.ACTION_MOVE
|
||||
&& params.isZoomSupported()) {
|
||||
mCamera.cancelAutoFocus();
|
||||
handleZoom(event, params);
|
||||
}
|
||||
} else {
|
||||
// handle single touch events
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
handleFocus(event, params);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void handleZoom(MotionEvent event, Camera.Parameters params) {
|
||||
int maxZoom = params.getMaxZoom();
|
||||
int zoom = params.getZoom();
|
||||
float newDist = getFingerSpacing(event);
|
||||
if (newDist > mDist) {
|
||||
// zoom in
|
||||
if (zoom < maxZoom)
|
||||
zoom++;
|
||||
} else if (newDist < mDist) {
|
||||
// zoom out
|
||||
if (zoom > 0)
|
||||
zoom--;
|
||||
}
|
||||
mDist = newDist;
|
||||
params.setZoom(zoom);
|
||||
|
||||
//zoom text
|
||||
setZoomVisibility(zoom != 0);
|
||||
if (zoom > 0) {
|
||||
double data = Math.round(zoom * 1.0) / 10.0;
|
||||
setZoomData(data);
|
||||
}
|
||||
|
||||
|
||||
mCamera.setParameters(params);
|
||||
}
|
||||
|
||||
public void handleFocus(MotionEvent event, Camera.Parameters params) {
|
||||
try {
|
||||
int pointerId = event.getPointerId(0);
|
||||
int pointerIndex = event.findPointerIndex(pointerId);
|
||||
// Get the pointer's current position
|
||||
float x = event.getX(pointerIndex);
|
||||
float y = event.getY(pointerIndex);
|
||||
|
||||
List<String> supportedFocusModes = params.getSupportedFocusModes();
|
||||
if (supportedFocusModes != null && supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
|
||||
mCamera.autoFocus(new Camera.AutoFocusCallback() {
|
||||
@Override
|
||||
public void onAutoFocus(boolean b, Camera camera) {
|
||||
// currently set to auto-focus on single touch
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the space between the first two fingers
|
||||
*/
|
||||
private float getFingerSpacing(MotionEvent event) {
|
||||
// ...
|
||||
float x = event.getX(0) - event.getX(1);
|
||||
float y = event.getY(0) - event.getY(1);
|
||||
return (float) Math.sqrt(x * x + y * y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZoomVisibility(boolean visibility) {
|
||||
|
||||
if (tv_zoom != null)
|
||||
if (visibility) {
|
||||
tv_zoom.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
tv_zoom.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void setZoomData(double zoom) {
|
||||
if (tv_zoom != null)
|
||||
tv_zoom.setText("x " + zoom);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull Canvas canvas) {
|
||||
// Find Screen size first
|
||||
try {
|
||||
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
|
||||
float screenWidth = metrics.widthPixels;
|
||||
float screenHeight = metrics.heightPixels;
|
||||
|
||||
// Set paint options
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStrokeWidth(5);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setColor(Color.argb(255, 255, 125, 000));
|
||||
|
||||
canvas.drawLine((screenWidth / 110) * 27, 0, (screenWidth / 110) * 27, screenHeight, paint);
|
||||
canvas.drawLine((screenWidth / 110) * 72, 0, (screenWidth / 110) * 72, screenHeight, paint);
|
||||
//test("small==" + (screenWidth / 100) * 27 + " large===" + (screenWidth / 100) * 63);
|
||||
canvas.drawLine(0, (screenHeight / 11) * 3, screenWidth, (screenHeight / 11) * 3, paint);
|
||||
canvas.drawLine(0, (screenHeight / 11) * 8, screenWidth, (screenHeight / 11) * 8, paint);
|
||||
} catch (Exception e) {
|
||||
e.fillInStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.view.TextureView;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class AutoFitTextureView extends TextureView {
|
||||
private final static String TAG = "AutoFitTextureView";
|
||||
private int ratioWidth = 0;
|
||||
private int ratioHeight = 0;
|
||||
public AutoFitTextureView(Context context, TextureView.SurfaceTextureListener surfaceTextureListener) {
|
||||
super(context, null);
|
||||
setSurfaceTextureListener(surfaceTextureListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the aspect ratio for this view. The size of the view will be measured based on the ratio
|
||||
* calculated fromList the parameters. Note that the actual sizes of parameters don't matter, that
|
||||
* is, calling setAspectRatio(2, 3) and setAspectRatio(4, 6) make the same result.
|
||||
*
|
||||
* @param width Relative horizontal size
|
||||
* @param height Relative vertical size
|
||||
*/
|
||||
public void setAspectRatio(int width, int height) {
|
||||
if (width < 0 || height < 0) {
|
||||
throw new IllegalArgumentException("Size cannot be negative.");
|
||||
}
|
||||
ratioWidth = width;
|
||||
ratioHeight = height;
|
||||
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int width = resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec);
|
||||
final int height = resolveSize(getSuggestedMinimumHeight(), heightMeasureSpec);
|
||||
|
||||
if (0 == ratioWidth || 0 == ratioHeight) {
|
||||
setMeasuredDimension(width, height);
|
||||
} else {
|
||||
if (width < height * (ratioWidth / (float) ratioHeight)) {
|
||||
setMeasuredDimension(width, (int) (width * (ratioWidth / (float) ratioHeight)));
|
||||
} else {
|
||||
setMeasuredDimension((int) (height * (ratioWidth / (float) ratioHeight)), height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,388 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.FileObserver;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.utils.DateTimeUtils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class CameraControlPanel extends RelativeLayout
|
||||
implements RecordButton.RecordButtonListener,
|
||||
MediaActionSwitchView.OnMediaActionStateChangeListener {
|
||||
|
||||
private Context context;
|
||||
private CameraSwitchView cameraSwitchView;
|
||||
private RecordButton recordButton;
|
||||
private MediaActionSwitchView mediaActionSwitchView;
|
||||
private FlashSwitchView flashSwitchView;
|
||||
private TextView recordDurationText;
|
||||
private TextView recordSizeText;
|
||||
private CameraSettingsView settingsButton;
|
||||
//for zoom text
|
||||
private TextView zoomText;
|
||||
|
||||
private RecordButton.RecordButtonListener recordButtonListener;
|
||||
private MediaActionSwitchView.OnMediaActionStateChangeListener onMediaActionStateChangeListener;
|
||||
private CameraSwitchView.OnCameraTypeChangeListener onCameraTypeChangeListener;
|
||||
private FlashSwitchView.FlashModeSwitchListener flashModeSwitchListener;
|
||||
private SettingsClickListener settingsClickListener;
|
||||
|
||||
private TimerTaskBase countDownTimer;
|
||||
private long maxVideoFileSize = 0;
|
||||
private String mediaFilePath;
|
||||
|
||||
public interface SettingsClickListener {
|
||||
void onSettingsClick();
|
||||
}
|
||||
|
||||
private boolean hasFlash = false;
|
||||
|
||||
private
|
||||
@MediaActionSwitchView.MediaActionState
|
||||
int mediaActionState;
|
||||
|
||||
private int mediaAction;
|
||||
|
||||
private FileObserver fileObserver;
|
||||
|
||||
public CameraControlPanel(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CameraControlPanel(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
hasFlash = context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);
|
||||
LayoutInflater.from(context).inflate(R.layout.camera_control_panel_layout, this);
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
settingsButton = (CameraSettingsView) findViewById(R.id.settings_view);
|
||||
cameraSwitchView = (CameraSwitchView) findViewById(R.id.front_back_camera_switcher);
|
||||
mediaActionSwitchView = (MediaActionSwitchView) findViewById(R.id.photo_video_camera_switcher);
|
||||
recordButton = (RecordButton) findViewById(R.id.record_button);
|
||||
flashSwitchView = (FlashSwitchView) findViewById(R.id.flash_switch_view);
|
||||
recordDurationText = (TextView) findViewById(R.id.record_duration_text);
|
||||
recordSizeText = (TextView) findViewById(R.id.record_size_mb_text);
|
||||
zoomText = (TextView) findViewById(R.id.tv_zoom);
|
||||
|
||||
MyTextview.zoomTextView = zoomText;
|
||||
|
||||
cameraSwitchView.setOnCameraTypeChangeListener(onCameraTypeChangeListener);
|
||||
mediaActionSwitchView.setOnMediaActionStateChangeListener(this);
|
||||
setOnCameraTypeChangeListener(onCameraTypeChangeListener);
|
||||
setOnMediaActionStateChangeListener(onMediaActionStateChangeListener);
|
||||
setFlashModeSwitchListener(flashModeSwitchListener);
|
||||
setRecordButtonListener(recordButtonListener);
|
||||
settingsButton.setOnClickListener(view -> {
|
||||
if (settingsClickListener != null) settingsClickListener.onSettingsClick();
|
||||
});
|
||||
|
||||
if (hasFlash)
|
||||
flashSwitchView.setVisibility(VISIBLE);
|
||||
else flashSwitchView.setVisibility(GONE);
|
||||
|
||||
countDownTimer = new TimerTask(recordDurationText);
|
||||
}
|
||||
|
||||
public void lockControls() {
|
||||
cameraSwitchView.setEnabled(false);
|
||||
recordButton.setEnabled(false);
|
||||
settingsButton.setEnabled(false);
|
||||
flashSwitchView.setEnabled(false);
|
||||
}
|
||||
|
||||
public void unLockControls() {
|
||||
cameraSwitchView.setEnabled(true);
|
||||
recordButton.setEnabled(true);
|
||||
settingsButton.setEnabled(true);
|
||||
flashSwitchView.setEnabled(true);
|
||||
}
|
||||
|
||||
public void setup(int mediaAction) {
|
||||
this.mediaAction = mediaAction;
|
||||
if (AnncaConfiguration.MEDIA_ACTION_VIDEO == mediaAction) {
|
||||
recordButton.setup(mediaAction, this);
|
||||
flashSwitchView.setVisibility(GONE);
|
||||
} else {
|
||||
recordButton.setup(AnncaConfiguration.MEDIA_ACTION_PHOTO, this);
|
||||
}
|
||||
|
||||
if (AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED != mediaAction) {
|
||||
mediaActionSwitchView.setVisibility(GONE);
|
||||
} //else mediaActionSwitchView.setVisibility(VISIBLE);
|
||||
}
|
||||
|
||||
public void setFlasMode(@FlashSwitchView.FlashMode int flashMode) {
|
||||
flashSwitchView.setFlashMode(flashMode);
|
||||
}
|
||||
|
||||
public void setMediaFilePath(final File mediaFile) {
|
||||
this.mediaFilePath = mediaFile.toString();
|
||||
}
|
||||
|
||||
public void setMaxVideoFileSize(long maxVideoFileSize) {
|
||||
this.maxVideoFileSize = maxVideoFileSize;
|
||||
}
|
||||
|
||||
public void setMaxVideoDuration(int maxVideoDurationInMillis) {
|
||||
if (maxVideoDurationInMillis > 0)
|
||||
countDownTimer = new CountdownTask(recordDurationText, maxVideoDurationInMillis);
|
||||
else countDownTimer = new TimerTask(recordDurationText);
|
||||
}
|
||||
|
||||
public void setMediaActionState(@MediaActionSwitchView.MediaActionState int actionState) {
|
||||
if (mediaActionState == actionState) return;
|
||||
if (MediaActionSwitchView.ACTION_PHOTO == actionState) {
|
||||
recordButton.setMediaAction(AnncaConfiguration.MEDIA_ACTION_PHOTO);
|
||||
if (hasFlash)
|
||||
flashSwitchView.setVisibility(VISIBLE);
|
||||
} else {
|
||||
recordButton.setMediaAction(AnncaConfiguration.MEDIA_ACTION_VIDEO);
|
||||
flashSwitchView.setVisibility(GONE);
|
||||
}
|
||||
mediaActionState = actionState;
|
||||
mediaActionSwitchView.setMediaActionState(actionState);
|
||||
}
|
||||
|
||||
public void setRecordButtonListener(RecordButton.RecordButtonListener recordButtonListener) {
|
||||
this.recordButtonListener = recordButtonListener;
|
||||
}
|
||||
|
||||
public void rotateControls(int rotation) {
|
||||
cameraSwitchView.setRotation(rotation);
|
||||
mediaActionSwitchView.setRotation(rotation);
|
||||
flashSwitchView.setRotation(rotation);
|
||||
recordDurationText.setRotation(rotation);
|
||||
recordSizeText.setRotation(rotation);
|
||||
zoomText.setRotation(rotation);
|
||||
}
|
||||
|
||||
public void setOnMediaActionStateChangeListener(MediaActionSwitchView.OnMediaActionStateChangeListener onMediaActionStateChangeListener) {
|
||||
this.onMediaActionStateChangeListener = onMediaActionStateChangeListener;
|
||||
}
|
||||
|
||||
public void setOnCameraTypeChangeListener(CameraSwitchView.OnCameraTypeChangeListener onCameraTypeChangeListener) {
|
||||
this.onCameraTypeChangeListener = onCameraTypeChangeListener;
|
||||
if (cameraSwitchView != null)
|
||||
cameraSwitchView.setOnCameraTypeChangeListener(this.onCameraTypeChangeListener);
|
||||
}
|
||||
|
||||
public void setFlashModeSwitchListener(FlashSwitchView.FlashModeSwitchListener flashModeSwitchListener) {
|
||||
this.flashModeSwitchListener = flashModeSwitchListener;
|
||||
if (flashSwitchView != null)
|
||||
flashSwitchView.setFlashSwitchListener(this.flashModeSwitchListener);
|
||||
}
|
||||
|
||||
public void setSettingsClickListener(SettingsClickListener settingsClickListener) {
|
||||
this.settingsClickListener = settingsClickListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTakePhotoButtonPressed() {
|
||||
if (recordButtonListener != null)
|
||||
recordButtonListener.onTakePhotoButtonPressed();
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
public void onStartVideoRecord(final File mediaFile) {
|
||||
setMediaFilePath(mediaFile);
|
||||
if (maxVideoFileSize > 0) {
|
||||
recordSizeText.setText("1Mb" + " / " + maxVideoFileSize / (1024 * 1024) + "Mb");
|
||||
recordSizeText.setVisibility(VISIBLE);
|
||||
try {
|
||||
fileObserver = new FileObserver(this.mediaFilePath) {
|
||||
private long lastUpdateSize = 0;
|
||||
|
||||
@Override
|
||||
public void onEvent(int event, String path) {
|
||||
final long fileSize = mediaFile.length() / (1024 * 1024);
|
||||
if ((fileSize - lastUpdateSize) >= 1) {
|
||||
lastUpdateSize = fileSize;
|
||||
recordSizeText.post(() -> recordSizeText.setText(fileSize + "Mb" + " / " + maxVideoFileSize / (1024 * 1024) + "Mb"));
|
||||
}
|
||||
}
|
||||
};
|
||||
fileObserver.startWatching();
|
||||
} catch (Exception e) {
|
||||
//test("FileObserver", "setMediaFilePath: ", e);
|
||||
}
|
||||
}
|
||||
countDownTimer.start();
|
||||
|
||||
}
|
||||
|
||||
public void allowRecord(boolean isAllowed) {
|
||||
recordButton.setEnabled(isAllowed);
|
||||
}
|
||||
|
||||
public void allowCameraSwitching(boolean isAllowed) {
|
||||
cameraSwitchView.setVisibility(isAllowed ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
public void onStopVideoRecord() {
|
||||
if (fileObserver != null)
|
||||
fileObserver.stopWatching();
|
||||
countDownTimer.stop();
|
||||
|
||||
recordSizeText.setVisibility(GONE);
|
||||
cameraSwitchView.setVisibility(View.VISIBLE);
|
||||
settingsButton.setVisibility(VISIBLE);
|
||||
|
||||
if (AnncaConfiguration.MEDIA_ACTION_UNSPECIFIED != mediaAction) {
|
||||
mediaActionSwitchView.setVisibility(GONE);
|
||||
} //else mediaActionSwitchView.setVisibility(VISIBLE);
|
||||
recordButton.setRecordState(RecordButton.READY_FOR_RECORD_STATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartRecordingButtonPressed() {
|
||||
|
||||
cameraSwitchView.setVisibility(View.GONE);
|
||||
mediaActionSwitchView.setVisibility(GONE);
|
||||
settingsButton.setVisibility(GONE);
|
||||
|
||||
if (recordButtonListener != null)
|
||||
recordButtonListener.onStartRecordingButtonPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopRecordingButtonPressed() {
|
||||
onStopVideoRecord();
|
||||
if (recordButtonListener != null)
|
||||
recordButtonListener.onStopRecordingButtonPressed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaActionChanged(int mediaActionState) {
|
||||
setMediaActionState(mediaActionState);
|
||||
if (onMediaActionStateChangeListener != null)
|
||||
onMediaActionStateChangeListener.onMediaActionChanged(this.mediaActionState);
|
||||
}
|
||||
|
||||
abstract class TimerTaskBase {
|
||||
Handler handler = new Handler(Looper.getMainLooper());
|
||||
TextView timerView;
|
||||
boolean alive = false;
|
||||
long recordingTimeSeconds = 0;
|
||||
long recordingTimeMinutes = 0;
|
||||
|
||||
TimerTaskBase(TextView timerView) {
|
||||
this.timerView = timerView;
|
||||
}
|
||||
|
||||
abstract void stop();
|
||||
|
||||
abstract void start();
|
||||
}
|
||||
|
||||
private class CountdownTask extends TimerTaskBase implements Runnable {
|
||||
|
||||
private int maxDurationMilliseconds = 0;
|
||||
|
||||
public CountdownTask(TextView timerView, int maxDurationMilliseconds) {
|
||||
super(timerView);
|
||||
this.maxDurationMilliseconds = maxDurationMilliseconds;
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public void run() {
|
||||
recordingTimeSeconds--;
|
||||
int millis = (int) recordingTimeSeconds * 1000;
|
||||
timerView.setText(
|
||||
String.format("%02d:%02d",
|
||||
TimeUnit.MILLISECONDS.toMinutes(millis),
|
||||
TimeUnit.MILLISECONDS.toSeconds(millis) -
|
||||
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis))
|
||||
));
|
||||
|
||||
if (recordingTimeSeconds < 10) {
|
||||
timerView.setTextColor(Color.RED);
|
||||
}
|
||||
|
||||
if (alive && recordingTimeSeconds > 0) handler.postDelayed(this, DateTimeUtils.SECOND);
|
||||
}
|
||||
|
||||
@Override
|
||||
void stop() {
|
||||
timerView.setVisibility(View.INVISIBLE);
|
||||
alive = false;
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
void start() {
|
||||
alive = true;
|
||||
recordingTimeSeconds = maxDurationMilliseconds / 1000;
|
||||
timerView.setTextColor(Color.WHITE);
|
||||
timerView.setText(
|
||||
String.format("%02d:%02d",
|
||||
TimeUnit.MILLISECONDS.toMinutes(maxDurationMilliseconds),
|
||||
TimeUnit.MILLISECONDS.toSeconds(maxDurationMilliseconds) -
|
||||
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(maxDurationMilliseconds))
|
||||
));
|
||||
timerView.setVisibility(View.VISIBLE);
|
||||
handler.postDelayed(this, DateTimeUtils.SECOND);
|
||||
}
|
||||
}
|
||||
|
||||
private class TimerTask extends TimerTaskBase implements Runnable {
|
||||
|
||||
public TimerTask(TextView timerView) {
|
||||
super(timerView);
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
@Override
|
||||
public void run() {
|
||||
recordingTimeSeconds++;
|
||||
|
||||
if (recordingTimeSeconds == 60) {
|
||||
recordingTimeSeconds = 0;
|
||||
recordingTimeMinutes++;
|
||||
}
|
||||
timerView.setText(
|
||||
String.format("%02d:%02d", recordingTimeMinutes, recordingTimeSeconds));
|
||||
if (alive) handler.postDelayed(this, DateTimeUtils.SECOND);
|
||||
}
|
||||
|
||||
@SuppressLint("DefaultLocale")
|
||||
public void start() {
|
||||
alive = true;
|
||||
recordingTimeMinutes = 0;
|
||||
recordingTimeSeconds = 0;
|
||||
timerView.setText(
|
||||
String.format("%02d:%02d", recordingTimeMinutes, recordingTimeSeconds));
|
||||
timerView.setVisibility(View.VISIBLE);
|
||||
handler.postDelayed(this, DateTimeUtils.SECOND);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
timerView.setVisibility(View.INVISIBLE);
|
||||
alive = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
/**
|
||||
* Created by memfis on 8/23/16.
|
||||
*/
|
||||
public class CameraSettingsView extends ImageButton {
|
||||
public CameraSettingsView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public CameraSettingsView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public CameraSettingsView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public CameraSettingsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (enabled) {
|
||||
setAlpha(1f);
|
||||
} else {
|
||||
setAlpha(0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import androidx.appcompat.widget.AppCompatImageButton;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 6/24/16.
|
||||
*/
|
||||
public class CameraSwitchView extends AppCompatImageButton {
|
||||
|
||||
public static final int CAMERA_TYPE_FRONT = 0;
|
||||
public static final int CAMERA_TYPE_REAR = 1;
|
||||
|
||||
@IntDef({CAMERA_TYPE_FRONT, CAMERA_TYPE_REAR})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface CameraType {
|
||||
}
|
||||
|
||||
private OnCameraTypeChangeListener onCameraTypeChangeListener;
|
||||
|
||||
public interface OnCameraTypeChangeListener {
|
||||
void onCameraTypeChanged(@CameraType int cameraType);
|
||||
}
|
||||
|
||||
private Context context;
|
||||
private Drawable frontCameraDrawable;
|
||||
private Drawable rearCameraDrawable;
|
||||
private int padding = 5;
|
||||
|
||||
public
|
||||
@CameraType
|
||||
static int currentCameraType = CAMERA_TYPE_FRONT;
|
||||
|
||||
public CameraSwitchView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CameraSwitchView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
initializeView();
|
||||
}
|
||||
|
||||
public CameraSwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
frontCameraDrawable = ContextCompat.getDrawable(context, R.drawable.ic_camera_front_white_24dp);
|
||||
frontCameraDrawable = DrawableCompat.wrap(frontCameraDrawable);
|
||||
DrawableCompat.setTintList(frontCameraDrawable.mutate(), ContextCompat.getColorStateList(context, R.drawable.switch_camera_mode_selector));
|
||||
|
||||
rearCameraDrawable = ContextCompat.getDrawable(context, R.drawable.ic_camera_rear_white_24dp);
|
||||
rearCameraDrawable = DrawableCompat.wrap(rearCameraDrawable);
|
||||
DrawableCompat.setTintList(rearCameraDrawable.mutate(), ContextCompat.getColorStateList(context, R.drawable.switch_camera_mode_selector));
|
||||
|
||||
setBackgroundResource(R.drawable.circle_frame_background_dark);
|
||||
setOnClickListener(new CameraTypeClickListener());
|
||||
setIcons();
|
||||
padding = Utils.convertDipToPixels(context, padding);
|
||||
setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
private void setIcons() {
|
||||
if (currentCameraType == CAMERA_TYPE_REAR) {
|
||||
setImageDrawable(rearCameraDrawable);
|
||||
} else setImageDrawable(frontCameraDrawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (enabled) {
|
||||
setAlpha(1f);
|
||||
} else {
|
||||
setAlpha(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public
|
||||
@CameraType
|
||||
int getCameraType() {
|
||||
return currentCameraType;
|
||||
}
|
||||
|
||||
public void setOnCameraTypeChangeListener(OnCameraTypeChangeListener onCameraTypeChangeListener) {
|
||||
this.onCameraTypeChangeListener = onCameraTypeChangeListener;
|
||||
}
|
||||
|
||||
private class CameraTypeClickListener implements OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (currentCameraType == CAMERA_TYPE_REAR) {
|
||||
currentCameraType = CAMERA_TYPE_FRONT;
|
||||
} else currentCameraType = CAMERA_TYPE_REAR;
|
||||
|
||||
setIcons();
|
||||
|
||||
if (onCameraTypeChangeListener != null)
|
||||
onCameraTypeChangeListener.onCameraTypeChanged(currentCameraType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class FlashSwitchView extends ImageButton {
|
||||
@FlashMode
|
||||
private int currentMode = FLASH_AUTO;
|
||||
|
||||
private FlashModeSwitchListener switchListener;
|
||||
private Drawable flashOnDrawable;
|
||||
private Drawable flashOffDrawable;
|
||||
private Drawable flashAutoDrawable;
|
||||
|
||||
private int tintColor = Color.WHITE;
|
||||
|
||||
public static final int FLASH_ON = 0;
|
||||
public static final int FLASH_OFF = 1;
|
||||
public static final int FLASH_AUTO = 2;
|
||||
|
||||
@IntDef({FLASH_ON, FLASH_OFF, FLASH_AUTO})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FlashMode {
|
||||
}
|
||||
|
||||
public interface FlashModeSwitchListener {
|
||||
void onFlashModeChanged(@FlashMode int mode);
|
||||
}
|
||||
|
||||
public FlashSwitchView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public FlashSwitchView(@NonNull Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
flashOnDrawable = ContextCompat.getDrawable(context, R.drawable.ic_flash_on_white_24dp);
|
||||
flashOffDrawable = ContextCompat.getDrawable(context, R.drawable.ic_flash_off_white_24dp);
|
||||
flashAutoDrawable = ContextCompat.getDrawable(context, R.drawable.ic_flash_auto_white_24dp);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setBackgroundColor(Color.TRANSPARENT);
|
||||
setOnClickListener(new FlashButtonClickListener());
|
||||
setIcon();
|
||||
}
|
||||
|
||||
private void setIcon() {
|
||||
if (FLASH_OFF == currentMode) {
|
||||
setImageDrawable(flashOffDrawable);
|
||||
} else if (FLASH_ON == currentMode) {
|
||||
setImageDrawable(flashOnDrawable);
|
||||
} else setImageDrawable(flashAutoDrawable);
|
||||
|
||||
}
|
||||
|
||||
private void setIconsTint(@ColorInt int tintColor) {
|
||||
this.tintColor = tintColor;
|
||||
flashOnDrawable.setColorFilter(tintColor, PorterDuff.Mode.MULTIPLY);
|
||||
flashOffDrawable.setColorFilter(tintColor, PorterDuff.Mode.MULTIPLY);
|
||||
flashAutoDrawable.setColorFilter(tintColor, PorterDuff.Mode.MULTIPLY);
|
||||
}
|
||||
|
||||
public void setFlashMode(@FlashMode int mode) {
|
||||
this.currentMode = mode;
|
||||
setIcon();
|
||||
}
|
||||
|
||||
@FlashMode
|
||||
public int getCurrentFlasMode() {
|
||||
return currentMode;
|
||||
}
|
||||
|
||||
public void setFlashSwitchListener(@NonNull FlashModeSwitchListener switchListener) {
|
||||
this.switchListener = switchListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (enabled) {
|
||||
setAlpha(1f);
|
||||
} else {
|
||||
setAlpha(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
private class FlashButtonClickListener implements OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (FLASH_AUTO == currentMode) {
|
||||
currentMode = FLASH_OFF;
|
||||
} else if (FLASH_OFF == currentMode) {
|
||||
currentMode = FLASH_ON;
|
||||
} else if (FLASH_ON == currentMode) {
|
||||
currentMode = FLASH_AUTO;
|
||||
}
|
||||
setIcon();
|
||||
if (switchListener != null) {
|
||||
switchListener.onFlashModeChanged(currentMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.core.graphics.drawable.DrawableCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 6/24/16.
|
||||
*/
|
||||
public class MediaActionSwitchView extends ImageButton {
|
||||
public final static int ACTION_PHOTO = 0;
|
||||
public final static int ACTION_VIDEO = 1;
|
||||
|
||||
@IntDef({ACTION_PHOTO, ACTION_VIDEO})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface MediaActionState {
|
||||
}
|
||||
|
||||
private int currentMediaActionState = ACTION_PHOTO;
|
||||
private OnMediaActionStateChangeListener onMediaActionStateChangeListener;
|
||||
|
||||
public interface OnMediaActionStateChangeListener {
|
||||
void onMediaActionChanged(int mediaActionState);
|
||||
}
|
||||
|
||||
private Context context;
|
||||
private Drawable photoDrawable;
|
||||
private Drawable videoDrawable;
|
||||
private int padding = 5;
|
||||
|
||||
public MediaActionSwitchView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MediaActionSwitchView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.context = context;
|
||||
initializeView();
|
||||
}
|
||||
|
||||
public MediaActionSwitchView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs);
|
||||
}
|
||||
|
||||
private void initializeView() {
|
||||
photoDrawable = ContextCompat.getDrawable(context, R.drawable.ic_photo_camera_white_24dp);
|
||||
photoDrawable = DrawableCompat.wrap(photoDrawable);
|
||||
DrawableCompat.setTintList(photoDrawable.mutate(), ContextCompat.getColorStateList(context, R.drawable.switch_camera_mode_selector));
|
||||
|
||||
videoDrawable = ContextCompat.getDrawable(context, R.drawable.ic_videocam_white_24dp);
|
||||
videoDrawable = DrawableCompat.wrap(videoDrawable);
|
||||
DrawableCompat.setTintList(videoDrawable.mutate(), ContextCompat.getColorStateList(context, R.drawable.switch_camera_mode_selector));
|
||||
|
||||
setBackgroundResource(R.drawable.circle_frame_background_dark);
|
||||
setOnClickListener(new MediaActionClickListener());
|
||||
setIcons();
|
||||
padding = Utils.convertDipToPixels(context, padding);
|
||||
setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
private void setIcons() {
|
||||
if (currentMediaActionState == ACTION_PHOTO) {
|
||||
setImageDrawable(videoDrawable);
|
||||
} else setImageDrawable(photoDrawable);
|
||||
}
|
||||
|
||||
public void setMediaActionState(@MediaActionState int currentMediaActionState) {
|
||||
this.currentMediaActionState = currentMediaActionState;
|
||||
setIcons();
|
||||
}
|
||||
|
||||
public void setOnMediaActionStateChangeListener(OnMediaActionStateChangeListener onMediaActionStateChangeListener) {
|
||||
this.onMediaActionStateChangeListener = onMediaActionStateChangeListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (enabled) {
|
||||
setAlpha(1f);
|
||||
} else {
|
||||
setAlpha(0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
private class MediaActionClickListener implements OnClickListener {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (currentMediaActionState == ACTION_PHOTO) {
|
||||
currentMediaActionState = ACTION_VIDEO;
|
||||
} else currentMediaActionState = ACTION_PHOTO;
|
||||
|
||||
setIcons();
|
||||
if (onMediaActionStateChangeListener != null)
|
||||
onMediaActionStateChangeListener.onMediaActionChanged(currentMediaActionState);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class MyTextview {
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public static TextView zoomTextView;
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package io.github.memfis19.annca.internal.ui.view;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.media.MediaActionSound;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import io.github.memfis19.annca.R;
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
import io.github.memfis19.annca.internal.utils.Utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class RecordButton extends ImageButton {
|
||||
|
||||
public static final int TAKE_PHOTO_STATE = 0;
|
||||
public static final int READY_FOR_RECORD_STATE = 1;
|
||||
public static final int RECORD_IN_PROGRESS_STATE = 2;
|
||||
|
||||
@IntDef({TAKE_PHOTO_STATE, READY_FOR_RECORD_STATE, RECORD_IN_PROGRESS_STATE})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RecordState {
|
||||
}
|
||||
|
||||
public interface RecordButtonListener {
|
||||
|
||||
void onTakePhotoButtonPressed();
|
||||
|
||||
void onStartRecordingButtonPressed();
|
||||
|
||||
void onStopRecordingButtonPressed();
|
||||
}
|
||||
|
||||
private Context context;
|
||||
private int mediaAction = AnncaConfiguration.MEDIA_ACTION_PHOTO;
|
||||
|
||||
private
|
||||
@RecordState
|
||||
int currentState = TAKE_PHOTO_STATE;
|
||||
|
||||
private Drawable takePhotoDrawable;
|
||||
private Drawable startRecordDrawable;
|
||||
private Drawable stopRecordDrawable;
|
||||
private int iconPadding = 8;
|
||||
private int iconPaddingStop = 18;
|
||||
|
||||
private RecordButtonListener listener;
|
||||
|
||||
public RecordButton(@NonNull Context context) {
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public RecordButton(@NonNull Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public RecordButton(@NonNull Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.context = context;
|
||||
takePhotoDrawable = ContextCompat.getDrawable(context, R.drawable.take_photo_button);
|
||||
startRecordDrawable = ContextCompat.getDrawable(context, R.drawable.start_video_record_button);
|
||||
stopRecordDrawable = ContextCompat.getDrawable(context, R.drawable.stop_button_background);
|
||||
}
|
||||
|
||||
public void setup(@AnncaConfiguration.MediaAction int mediaAction, @NonNull RecordButtonListener listener) {
|
||||
setMediaAction(mediaAction);
|
||||
this.listener = listener;
|
||||
setBackground(ContextCompat.getDrawable(context, R.drawable.circle_frame_background));
|
||||
setIcon();
|
||||
setOnClickListener(new RecordClickListener());
|
||||
setSoundEffectsEnabled(false);
|
||||
setIconPadding(iconPadding);
|
||||
}
|
||||
|
||||
private void setIconPadding(int paddingDP) {
|
||||
int padding = Utils.convertDipToPixels(context, paddingDP);
|
||||
setPadding(padding, padding, padding, padding);
|
||||
}
|
||||
|
||||
public void setMediaAction(@AnncaConfiguration.MediaAction int mediaAction) {
|
||||
this.mediaAction = mediaAction;
|
||||
if (AnncaConfiguration.MEDIA_ACTION_PHOTO == mediaAction)
|
||||
currentState = TAKE_PHOTO_STATE;
|
||||
else currentState = READY_FOR_RECORD_STATE;
|
||||
setRecordState(currentState);
|
||||
setIcon();
|
||||
}
|
||||
|
||||
public void setRecordState(@RecordState int state) {
|
||||
currentState = state;
|
||||
setIcon();
|
||||
}
|
||||
|
||||
public
|
||||
@RecordState
|
||||
int getRecordState() {
|
||||
return currentState;
|
||||
}
|
||||
|
||||
public void setRecordButtonListener(@NonNull RecordButtonListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void setIcon() {
|
||||
if (AnncaConfiguration.MEDIA_ACTION_VIDEO == mediaAction) {
|
||||
if (READY_FOR_RECORD_STATE == currentState) {
|
||||
setImageDrawable(startRecordDrawable);
|
||||
setIconPadding(iconPadding);
|
||||
} else if (RECORD_IN_PROGRESS_STATE == currentState) {
|
||||
setImageDrawable(stopRecordDrawable);
|
||||
setIconPadding(iconPaddingStop);
|
||||
}
|
||||
} else {
|
||||
setImageDrawable(takePhotoDrawable);
|
||||
setIconPadding(iconPadding);
|
||||
}
|
||||
}
|
||||
|
||||
private void takePhoto(MediaActionSound sound) {
|
||||
//sound.play(MediaActionSound.SHUTTER_CLICK);
|
||||
takePhoto();
|
||||
}
|
||||
|
||||
private void takePhoto() {
|
||||
if (listener != null)
|
||||
listener.onTakePhotoButtonPressed();
|
||||
}
|
||||
|
||||
private void startRecording(MediaActionSound sound) {
|
||||
//sound.play(MediaActionSound.START_VIDEO_RECORDING);
|
||||
startRecording();
|
||||
}
|
||||
|
||||
private void startRecording() {
|
||||
currentState = RECORD_IN_PROGRESS_STATE;
|
||||
if (listener != null) {
|
||||
listener.onStartRecordingButtonPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private void stopRecording(MediaActionSound sound) {
|
||||
//sound.play(MediaActionSound.STOP_VIDEO_RECORDING);
|
||||
stopRecording();
|
||||
}
|
||||
|
||||
private void stopRecording() {
|
||||
currentState = READY_FOR_RECORD_STATE;
|
||||
if (listener != null) {
|
||||
listener.onStopRecordingButtonPressed();
|
||||
}
|
||||
}
|
||||
|
||||
private class RecordClickListener implements OnClickListener {
|
||||
private final static int CLICK_DELAY = 1000;
|
||||
private long lastClickTime = 0;
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (System.currentTimeMillis() - lastClickTime < CLICK_DELAY) {
|
||||
return;
|
||||
} else lastClickTime = System.currentTimeMillis();
|
||||
|
||||
MediaActionSound sound = new MediaActionSound();
|
||||
if (TAKE_PHOTO_STATE == currentState) {
|
||||
takePhoto(sound);
|
||||
} else if (READY_FOR_RECORD_STATE == currentState) {
|
||||
startRecording(sound);
|
||||
} else if (RECORD_IN_PROGRESS_STATE == currentState) {
|
||||
stopRecording(sound);
|
||||
}
|
||||
setIcon();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.github.memfis19.annca.internal.ui.zoom;
|
||||
|
||||
public interface ZoomController {
|
||||
|
||||
void setZoomVisibility(boolean visibility);
|
||||
void setZoomData(double zoom);
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
package io.github.memfis19.annca.internal.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Point;
|
||||
import android.media.ExifInterface;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.Display;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/18/16.
|
||||
*/
|
||||
public final class AnncaImageLoader {
|
||||
|
||||
private static final String TAG = "AnncaImageLoader";
|
||||
|
||||
private Context context;
|
||||
private String url;
|
||||
|
||||
private AnncaImageLoader(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private AnncaImageLoader anncaImageLoader;
|
||||
|
||||
public Builder(@NonNull Context context) {
|
||||
anncaImageLoader = new AnncaImageLoader(context);
|
||||
}
|
||||
|
||||
public Builder load(String url) {
|
||||
anncaImageLoader.url = url;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnncaImageLoader build() {
|
||||
return anncaImageLoader;
|
||||
}
|
||||
}
|
||||
|
||||
public void into(final ImageView target) {
|
||||
ViewTreeObserver viewTreeObserver = target.getViewTreeObserver();
|
||||
viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
|
||||
public boolean onPreDraw() {
|
||||
target.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
|
||||
new ImageLoaderThread(target, url).start();
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class ImageLoaderThread extends Thread {
|
||||
|
||||
private ImageView target;
|
||||
private String url;
|
||||
private Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
|
||||
private ImageLoaderThread(ImageView target, String url) {
|
||||
this.target = target;
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
||||
|
||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = windowManager.getDefaultDisplay();
|
||||
|
||||
int imageViewHeight;
|
||||
int imageViewWidth;
|
||||
|
||||
if (Build.VERSION.SDK_INT < 13) {
|
||||
imageViewHeight = display.getHeight();
|
||||
imageViewWidth = display.getWidth();
|
||||
} else {
|
||||
Point size = new Point();
|
||||
display.getSize(size);
|
||||
imageViewHeight = size.y;
|
||||
imageViewWidth = size.x;
|
||||
}
|
||||
|
||||
// int imageViewHeight = target.getMeasuredHeight();
|
||||
// int imageViewWidth = target.getMeasuredWidth();
|
||||
|
||||
Bitmap decodedBitmap = decodeSampledBitmapFromResource(url, imageViewWidth, imageViewHeight);
|
||||
final Bitmap resultBitmap = rotateBitmap(decodedBitmap, getExifOrientation());
|
||||
|
||||
mainHandler.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
target.setImageBitmap(resultBitmap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Bitmap rotateBitmap(Bitmap bitmap, int orientation) {
|
||||
Matrix matrix = new Matrix();
|
||||
switch (orientation) {
|
||||
case ExifInterface.ORIENTATION_NORMAL:
|
||||
return bitmap;
|
||||
case ExifInterface.ORIENTATION_FLIP_HORIZONTAL:
|
||||
matrix.setScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||
matrix.setRotate(180);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_FLIP_VERTICAL:
|
||||
matrix.setRotate(180);
|
||||
matrix.postScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_TRANSPOSE:
|
||||
matrix.setRotate(90);
|
||||
matrix.postScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||
matrix.setRotate(90);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_TRANSVERSE:
|
||||
matrix.setRotate(-90);
|
||||
matrix.postScale(-1, 1);
|
||||
break;
|
||||
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||
matrix.setRotate(-90);
|
||||
break;
|
||||
default:
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
try {
|
||||
Bitmap bmRotated = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
bitmap.recycle();
|
||||
return bmRotated;
|
||||
} catch (OutOfMemoryError ignore) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private int getExifOrientation() {
|
||||
ExifInterface exif = null;
|
||||
try {
|
||||
exif = new ExifInterface(url);
|
||||
} catch (IOException ignore) {
|
||||
}
|
||||
return exif == null ? ExifInterface.ORIENTATION_UNDEFINED :
|
||||
exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);
|
||||
}
|
||||
|
||||
private Bitmap decodeSampledBitmapFromResource(String url,
|
||||
int requestedWidth, int requestedHeight) {
|
||||
|
||||
final BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(url, options);
|
||||
|
||||
options.inSampleSize = calculateInSampleSize(options, requestedWidth, requestedHeight);
|
||||
options.inJustDecodeBounds = false;
|
||||
options.inPreferredConfig = Bitmap.Config.RGB_565;
|
||||
return BitmapFactory.decodeFile(url, options);
|
||||
}
|
||||
|
||||
private int calculateInSampleSize(BitmapFactory.Options options,
|
||||
int requestedWidth, int requestedHeight) {
|
||||
|
||||
final int height = options.outHeight;
|
||||
final int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > requestedHeight || width > requestedWidth) {
|
||||
|
||||
final int halfHeight = height / inSampleSize;
|
||||
final int halfWidth = width / inSampleSize;
|
||||
|
||||
while ((halfHeight / inSampleSize) > requestedHeight
|
||||
&& (halfWidth / inSampleSize) > requestedWidth) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
}
|
||||
return inSampleSize;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,393 @@
|
||||
package io.github.memfis19.annca.internal.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.hardware.camera2.CameraCharacteristics;
|
||||
import android.hardware.camera2.CameraManager;
|
||||
import android.media.CamcorderProfile;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import io.github.memfis19.annca.internal.configuration.AnncaConfiguration;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
* <p/>
|
||||
* Class with some common methods to work with camera.
|
||||
*/
|
||||
public final class CameraHelper {
|
||||
|
||||
public final static String TAG = "CameraHelper";
|
||||
|
||||
|
||||
private CameraHelper() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean hasCamera(Context context) {
|
||||
return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY) ||
|
||||
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
|
||||
}
|
||||
|
||||
public static boolean hasCamera2(Context context) {
|
||||
if (context == null) return false;
|
||||
try {
|
||||
CameraManager manager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
|
||||
String[] idList = manager.getCameraIdList();
|
||||
boolean notNull = true;
|
||||
if (idList.length == 0) {
|
||||
notNull = false;
|
||||
} else {
|
||||
for (final String str : idList) {
|
||||
if (str == null || str.trim().isEmpty()) {
|
||||
notNull = false;
|
||||
break;
|
||||
}
|
||||
final CameraCharacteristics characteristics = manager.getCameraCharacteristics(str);
|
||||
final int supportLevel = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
|
||||
if (supportLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
|
||||
notNull = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return notNull;
|
||||
} catch (Throwable ignore) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static File getOutputMediaFile(Context context, @AnncaConfiguration.MediaAction int mediaAction) {
|
||||
File mediaStorageDir = new File(Environment.getExternalStorageDirectory() + "/MyCustomCamera");
|
||||
if (!mediaStorageDir.exists()) {
|
||||
if (!mediaStorageDir.mkdirs()) {
|
||||
//test(TAG, "Failed to create directory.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SimpleDateFormat") String timeStamp = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date());
|
||||
File mediaFile;
|
||||
if (mediaAction == AnncaConfiguration.MEDIA_ACTION_PHOTO) {
|
||||
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");
|
||||
} else if (mediaAction == AnncaConfiguration.MEDIA_ACTION_VIDEO) {
|
||||
mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mediaFile;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Size getPictureSize(List<Size> choices, @AnncaConfiguration.MediaQuality int mediaQuality) {
|
||||
if (choices == null || choices.isEmpty()) return null;
|
||||
if (choices.size() == 1) return choices.get(0);
|
||||
|
||||
Size result = null;
|
||||
Size maxPictureSize = Collections.max(choices, new CompareSizesByArea2());
|
||||
Size minPictureSize = Collections.min(choices, new CompareSizesByArea2());
|
||||
|
||||
Collections.sort(choices, new CompareSizesByArea2());
|
||||
|
||||
if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGHEST) {
|
||||
result = maxPictureSize;
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOW) {
|
||||
if (choices.size() == 2) result = minPictureSize;
|
||||
else {
|
||||
int half = choices.size() / 2;
|
||||
int lowQualityIndex = (choices.size() - half) / 2;
|
||||
result = choices.get(lowQualityIndex + 1);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGH) {
|
||||
if (choices.size() == 2) result = maxPictureSize;
|
||||
else {
|
||||
int half = choices.size() / 2;
|
||||
int highQualityIndex = (choices.size() - half) / 2;
|
||||
result = choices.get(choices.size() - highQualityIndex - 1);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_MEDIUM) {
|
||||
if (choices.size() == 2) result = minPictureSize;
|
||||
else {
|
||||
int mediumQualityIndex = choices.size() / 2;
|
||||
result = choices.get(mediumQualityIndex);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOWEST) {
|
||||
result = minPictureSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static Size getPictureSize(Size[] sizes, @AnncaConfiguration.MediaQuality int mediaQuality) {
|
||||
if (sizes == null || sizes.length == 0) return null;
|
||||
|
||||
List<Size> choices = Arrays.asList(sizes);
|
||||
|
||||
if (choices.size() == 1) return choices.get(0);
|
||||
|
||||
Size result = null;
|
||||
Size maxPictureSize = Collections.max(choices, new CompareSizesByArea2());
|
||||
Size minPictureSize = Collections.min(choices, new CompareSizesByArea2());
|
||||
|
||||
Collections.sort(choices, new CompareSizesByArea2());
|
||||
|
||||
if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGHEST) {
|
||||
result = maxPictureSize;
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOW) {
|
||||
if (choices.size() == 2) result = minPictureSize;
|
||||
else {
|
||||
int half = choices.size() / 2;
|
||||
int lowQualityIndex = (choices.size() - half) / 2;
|
||||
result = choices.get(lowQualityIndex + 1);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGH) {
|
||||
if (choices.size() == 2) result = maxPictureSize;
|
||||
else {
|
||||
int half = choices.size() / 2;
|
||||
int highQualityIndex = (choices.size() - half) / 2;
|
||||
result = choices.get(choices.size() - highQualityIndex - 1);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_MEDIUM) {
|
||||
if (choices.size() == 2) result = minPictureSize;
|
||||
else {
|
||||
int mediumQualityIndex = choices.size() / 2;
|
||||
result = choices.get(mediumQualityIndex);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOWEST) {
|
||||
result = minPictureSize;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Size getSizeWithClosestRatio(List<Size> sizes, int width, int height) {
|
||||
if (sizes == null) return null;
|
||||
double MIN_TOLERANCE = 100;
|
||||
double targetRatio = (double) height / width;
|
||||
Size optimalSize = null;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
int targetHeight = height;
|
||||
for (Size size : sizes) {
|
||||
if (size.getWidth() == width && size.getHeight() == height)
|
||||
return size;
|
||||
|
||||
double ratio = (double) size.getHeight() / size.getWidth();
|
||||
|
||||
if (Math.abs(ratio - targetRatio) < MIN_TOLERANCE) MIN_TOLERANCE = ratio;
|
||||
else continue;
|
||||
|
||||
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.getHeight() - targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (optimalSize == null) {
|
||||
minDiff = Double.MAX_VALUE;
|
||||
for (Size size : sizes) {
|
||||
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.getHeight() - targetHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
return optimalSize;
|
||||
}
|
||||
|
||||
public static Size getOptimalPreviewSize(Size[] sizes, int width, int height) {
|
||||
if (sizes == null) return null;
|
||||
final double ASPECT_TOLERANCE = 0.1;
|
||||
double targetRatio = (double) height / width;
|
||||
Size optimalSize = null;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
int targetHeight = height;
|
||||
for (Size size : sizes) {
|
||||
double ratio = (double) size.getWidth() / size.getHeight();
|
||||
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
|
||||
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.getHeight() - targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (optimalSize == null) {
|
||||
minDiff = Double.MAX_VALUE;
|
||||
for (Size size : sizes) {
|
||||
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.getHeight() - targetHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
return optimalSize;
|
||||
}
|
||||
|
||||
public static Size getSizeWithClosestRatio(Size[] sizes, int width, int height) {
|
||||
|
||||
if (sizes == null) return null;
|
||||
|
||||
double MIN_TOLERANCE = 100;
|
||||
double targetRatio = (double) height / width;
|
||||
Size optimalSize = null;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
|
||||
int targetHeight = height;
|
||||
|
||||
for (Size size : sizes) {
|
||||
// if (size.getWidth() == width && size.getHeight() == height)
|
||||
// return size;
|
||||
|
||||
double ratio = (double) size.getHeight() / size.getWidth();
|
||||
|
||||
if (Math.abs(ratio - targetRatio) < MIN_TOLERANCE) MIN_TOLERANCE = ratio;
|
||||
else continue;
|
||||
|
||||
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.getHeight() - targetHeight);
|
||||
}
|
||||
}
|
||||
|
||||
if (optimalSize == null) {
|
||||
minDiff = Double.MAX_VALUE;
|
||||
for (Size size : sizes) {
|
||||
if (Math.abs(size.getHeight() - targetHeight) < minDiff) {
|
||||
optimalSize = size;
|
||||
minDiff = Math.abs(size.getHeight() - targetHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
return optimalSize;
|
||||
}
|
||||
|
||||
public static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
|
||||
// Collect the supported resolutions that are at least as big as the preview Surface
|
||||
List<Size> bigEnough = new ArrayList<>();
|
||||
int w = aspectRatio.getWidth();
|
||||
int h = aspectRatio.getHeight();
|
||||
for (Size option : choices) {
|
||||
if (option.getHeight() == option.getWidth() * h / w &&
|
||||
option.getWidth() >= width && option.getHeight() >= height) {
|
||||
bigEnough.add(option);
|
||||
}
|
||||
}
|
||||
|
||||
// Pick the smallest of those, assuming we found any
|
||||
if (!bigEnough.isEmpty()) {
|
||||
return Collections.min(bigEnough, new CompareSizesByArea2());
|
||||
} else {
|
||||
//test(TAG, "Couldn't find any suitable preview size");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static double calculateApproximateVideoSize(CamcorderProfile camcorderProfile, int seconds) {
|
||||
return ((camcorderProfile.videoBitRate / (float) 1 + camcorderProfile.audioBitRate / (float) 1) * seconds) / (float) 8;
|
||||
}
|
||||
|
||||
public static double calculateApproximateVideoDuration(CamcorderProfile camcorderProfile, long maxFileSize) {
|
||||
return 8 * maxFileSize / (camcorderProfile.videoBitRate + camcorderProfile.audioBitRate);
|
||||
}
|
||||
|
||||
private static long calculateMinimumRequiredBitRate(CamcorderProfile camcorderProfile, long maxFileSize, int seconds) {
|
||||
return 8 * maxFileSize / seconds - camcorderProfile.audioBitRate;
|
||||
}
|
||||
|
||||
public static CamcorderProfile getCamcorderProfile(String cameraId, long maximumFileSize, int minimumDurationInSeconds) {
|
||||
if (TextUtils.isEmpty(cameraId)) {
|
||||
return null;
|
||||
}
|
||||
int cameraIdInt = Integer.parseInt(cameraId);
|
||||
return getCamcorderProfile(cameraIdInt, maximumFileSize, minimumDurationInSeconds);
|
||||
}
|
||||
|
||||
public static CamcorderProfile getCamcorderProfile(int currentCameraId, long maximumFileSize, int minimumDurationInSeconds) {
|
||||
if (maximumFileSize <= 0)
|
||||
return CamcorderProfile.get(currentCameraId, AnncaConfiguration.MEDIA_QUALITY_HIGHEST);
|
||||
|
||||
int[] qualities = new int[]{AnncaConfiguration.MEDIA_QUALITY_HIGHEST,
|
||||
AnncaConfiguration.MEDIA_QUALITY_HIGH, AnncaConfiguration.MEDIA_QUALITY_MEDIUM,
|
||||
AnncaConfiguration.MEDIA_QUALITY_LOW, AnncaConfiguration.MEDIA_QUALITY_LOWEST};
|
||||
|
||||
CamcorderProfile camcorderProfile;
|
||||
for (int i = 0; i < qualities.length; ++i) {
|
||||
camcorderProfile = CameraHelper.getCamcorderProfile(qualities[i], currentCameraId);
|
||||
double fileSize = CameraHelper.calculateApproximateVideoSize(camcorderProfile, minimumDurationInSeconds);
|
||||
|
||||
if (fileSize > maximumFileSize) {
|
||||
long minimumRequiredBitRate = calculateMinimumRequiredBitRate(camcorderProfile, maximumFileSize, minimumDurationInSeconds);
|
||||
|
||||
if (minimumRequiredBitRate >= camcorderProfile.videoBitRate / 4 && minimumRequiredBitRate <= camcorderProfile.videoBitRate) {
|
||||
camcorderProfile.videoBitRate = (int) minimumRequiredBitRate;
|
||||
return camcorderProfile;
|
||||
}
|
||||
} else return camcorderProfile;
|
||||
}
|
||||
return CameraHelper.getCamcorderProfile(AnncaConfiguration.MEDIA_QUALITY_LOWEST, currentCameraId);
|
||||
}
|
||||
|
||||
public static CamcorderProfile getCamcorderProfile(@AnncaConfiguration.MediaQuality int mediaQuality, String cameraId) {
|
||||
if (TextUtils.isEmpty(cameraId)) {
|
||||
return null;
|
||||
}
|
||||
int cameraIdInt = Integer.parseInt(cameraId);
|
||||
return getCamcorderProfile(mediaQuality, cameraIdInt);
|
||||
}
|
||||
|
||||
public static CamcorderProfile getCamcorderProfile(@AnncaConfiguration.MediaQuality int mediaQuality, int cameraId) {
|
||||
if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGHEST) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_HIGH) {
|
||||
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_1080P)) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_1080P);
|
||||
} else if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_720P);
|
||||
} else {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_MEDIUM) {
|
||||
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_720P)) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_720P);
|
||||
} else if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_480P);
|
||||
} else {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOW) {
|
||||
if (CamcorderProfile.hasProfile(cameraId, CamcorderProfile.QUALITY_480P)) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_480P);
|
||||
} else {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
|
||||
}
|
||||
} else if (mediaQuality == AnncaConfiguration.MEDIA_QUALITY_LOWEST) {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW);
|
||||
} else {
|
||||
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH);
|
||||
}
|
||||
}
|
||||
|
||||
private static class CompareSizesByArea2 implements Comparator<Size> {
|
||||
@Override
|
||||
public int compare(Size lhs, Size rhs) {
|
||||
// We cast here to ensure the multiplications won't overflow
|
||||
return Long.signum((long) lhs.getWidth() * lhs.getHeight() -
|
||||
(long) rhs.getWidth() * rhs.getHeight());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.github.memfis19.annca.internal.utils;
|
||||
|
||||
/**
|
||||
* Created by memfis on 6/27/16.
|
||||
*/
|
||||
public final class DateTimeUtils {
|
||||
|
||||
public static final long SECOND = 1000;
|
||||
public static final long MINUTE = 60 * SECOND;
|
||||
public static final long HOUR = 60 * MINUTE;
|
||||
public static final long DAY = 24 * HOUR;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package io.github.memfis19.annca.internal.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.media.Image;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/6/16.
|
||||
*/
|
||||
public class ImageSaver implements Runnable {
|
||||
|
||||
private final static String TAG = "ImageSaver";
|
||||
|
||||
private final Image image;
|
||||
private final File file;
|
||||
private ImageSaverCallback imageSaverCallback;
|
||||
|
||||
public interface ImageSaverCallback {
|
||||
void onSuccessFinish();
|
||||
|
||||
void onError();
|
||||
}
|
||||
|
||||
public ImageSaver(Image image, File file, ImageSaverCallback imageSaverCallback) {
|
||||
this.image = image;
|
||||
this.file = file;
|
||||
this.imageSaverCallback = imageSaverCallback;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
@Override
|
||||
public void run() {
|
||||
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
|
||||
byte[] bytes = new byte[buffer.remaining()];
|
||||
buffer.get(bytes);
|
||||
FileOutputStream output = null;
|
||||
try {
|
||||
output = new FileOutputStream(file);
|
||||
output.write(bytes);
|
||||
imageSaverCallback.onSuccessFinish();
|
||||
} catch (IOException ignore) {
|
||||
//test(TAG, "Can't save_icon the image file.");
|
||||
imageSaverCallback.onError();
|
||||
} finally {
|
||||
image.close();
|
||||
if (null != output) {
|
||||
try {
|
||||
output.close();
|
||||
} catch (IOException e) {
|
||||
//test(TAG, "Can't release image or close the output stream.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package io.github.memfis19.annca.internal.utils;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.hardware.Camera;
|
||||
import android.os.Build;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by memfis on 12/1/16.
|
||||
*/
|
||||
|
||||
public class Size {
|
||||
|
||||
private int width;
|
||||
private int height;
|
||||
|
||||
public Size() {
|
||||
width = 0;
|
||||
height = 0;
|
||||
}
|
||||
|
||||
public Size(int width, int height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public Size(android.util.Size size) {
|
||||
this.width = size.getWidth();
|
||||
this.height = size.getHeight();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public Size(Camera.Size size) {
|
||||
this.width = size.width;
|
||||
this.height = size.height;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return height;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public static List<Size> fromList2(List<android.util.Size> sizes) {
|
||||
if (sizes == null) return null;
|
||||
List<Size> result = new ArrayList<>(sizes.size());
|
||||
|
||||
for (android.util.Size size : sizes) {
|
||||
result.add(new Size(size));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static List<Size> fromList(List<Camera.Size> sizes) {
|
||||
if (sizes == null) return null;
|
||||
List<Size> result = new ArrayList<>(sizes.size());
|
||||
|
||||
for (Camera.Size size : sizes) {
|
||||
result.add(new Size(size));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
|
||||
public static Size[] fromArray2(android.util.Size[] sizes) {
|
||||
if (sizes == null) return null;
|
||||
Size[] result = new Size[sizes.length];
|
||||
|
||||
for (int i = 0; i < sizes.length; ++i) {
|
||||
result[i] = new Size(sizes[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Size[] fromArray(Camera.Size[] sizes) {
|
||||
if (sizes == null) return null;
|
||||
Size[] result = new Size[sizes.length];
|
||||
|
||||
for (int i = 0; i < sizes.length; ++i) {
|
||||
result[i] = new Size(sizes[i]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package io.github.memfis19.annca.internal.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Surface;
|
||||
import android.view.WindowManager;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
/**
|
||||
* Created by memfis on 7/18/16.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
public static int getDeviceDefaultOrientation(Context context) {
|
||||
WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||
Configuration config = context.getResources().getConfiguration();
|
||||
|
||||
int rotation = windowManager.getDefaultDisplay().getRotation();
|
||||
|
||||
if (((rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) &&
|
||||
config.orientation == Configuration.ORIENTATION_LANDSCAPE)
|
||||
|| ((rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) &&
|
||||
config.orientation == Configuration.ORIENTATION_PORTRAIT)) {
|
||||
return Configuration.ORIENTATION_LANDSCAPE;
|
||||
} else {
|
||||
return Configuration.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
}
|
||||
|
||||
public static String getMimeType(String url) {
|
||||
String type = "";
|
||||
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
|
||||
if (!TextUtils.isEmpty(extension)) {
|
||||
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
|
||||
} else {
|
||||
String reCheckExtension = MimeTypeMap.getFileExtensionFromUrl(url.replaceAll("\\s+", ""));
|
||||
if (!TextUtils.isEmpty(reCheckExtension)) {
|
||||
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(reCheckExtension);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
public static int convertDipToPixels(Context context, int dip) {
|
||||
Resources resources = context.getResources();
|
||||
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, resources.getDisplayMetrics());
|
||||
return (int) px;
|
||||
}
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 274 B |
|
After Width: | Height: | Size: 224 B |
|
After Width: | Height: | Size: 221 B |
|
After Width: | Height: | Size: 406 B |
|
After Width: | Height: | Size: 188 B |
|
After Width: | Height: | Size: 188 B |
|
After Width: | Height: | Size: 379 B |
|
After Width: | Height: | Size: 306 B |
|
After Width: | Height: | Size: 217 B |
|
After Width: | Height: | Size: 150 B |
|
After Width: | Height: | Size: 364 B |
|
After Width: | Height: | Size: 391 B |
|
After Width: | Height: | Size: 460 B |
|
After Width: | Height: | Size: 173 B |
|
After Width: | Height: | Size: 196 B |
|
After Width: | Height: | Size: 193 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 269 B |
|
After Width: | Height: | Size: 119 B |
|
After Width: | Height: | Size: 139 B |
|
After Width: | Height: | Size: 263 B |
|
After Width: | Height: | Size: 223 B |
|
After Width: | Height: | Size: 158 B |
|
After Width: | Height: | Size: 127 B |
|
After Width: | Height: | Size: 240 B |
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 326 B |
|
After Width: | Height: | Size: 131 B |
|
After Width: | Height: | Size: 317 B |
|
After Width: | Height: | Size: 266 B |
|
After Width: | Height: | Size: 257 B |
|
After Width: | Height: | Size: 500 B |
|
After Width: | Height: | Size: 155 B |
|
After Width: | Height: | Size: 199 B |
|
After Width: | Height: | Size: 459 B |
|
After Width: | Height: | Size: 345 B |
|
After Width: | Height: | Size: 239 B |
|
After Width: | Height: | Size: 175 B |
|
After Width: | Height: | Size: 446 B |
|
After Width: | Height: | Size: 457 B |
|
After Width: | Height: | Size: 562 B |
|
After Width: | Height: | Size: 178 B |
|
After Width: | Height: | Size: 444 B |
|
After Width: | Height: | Size: 377 B |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 705 B |
|
After Width: | Height: | Size: 198 B |
|
After Width: | Height: | Size: 255 B |
|
After Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 485 B |
|
After Width: | Height: | Size: 318 B |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 666 B |
|
After Width: | Height: | Size: 675 B |