12 Commits

Author SHA1 Message Date
ravik 3634dc0fa9 initial_commit 2026-05-15 16:30:56 +05:30
ravik f1bdf73db2 geofence update code 2026-03-28 18:11:58 +05:30
jeevanp 5801aa3e6f added qr-generator in ba-assessment 2025-12-30 12:58:54 +05:30
jeevanp 9142924800 Added BA assessment and fixed baskinGss 2025-12-19 17:39:01 +05:30
ravik a1f14ac912 minsdk23 2025-09-26 19:03:00 +05:30
ravik cd4c4cc8e0 lockout-preference 2025-09-12 18:25:46 +05:30
ravik acb841cc11 Range_Promotion and locked feature 2025-09-09 19:01:59 +05:30
ravik d3033db48c Geotag and Geofence changes 2025-08-06 15:02:25 +05:30
ravik fb03926973 Geotag_geofence_checkout_profilemandate 2025-08-04 17:09:08 +05:30
Jeevan Prasad a263278382 Added Profile Mandate Alert Popup 2025-07-04 12:40:00 +05:30
Jeevan Prasad e68ddd7133 Added checkout and mandate profile 2025-07-03 19:02:40 +05:30
Jeevan Prasad 917fee86de Add new changes GeoFencing-GeoTaging and hide Manual Inword Option etc 2025-06-26 15:10:22 +05:30
1782 changed files with 196208 additions and 2 deletions
+13
View File
@@ -0,0 +1,13 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
+3
View File
@@ -0,0 +1,3 @@
# Default ignored files
/shelf/
/workspace.xml
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>
+16
View File
File diff suppressed because one or more lines are too long
+40
View File
@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AppInsightsSettings">
<option name="tabSettings">
<map>
<entry key="Android Vitals">
<value>
<InsightsFilterSettings>
<option name="connection">
<ConnectionSetting>
<option name="appId" value="com.cpm.abbott" />
</ConnectionSetting>
</option>
<option name="signal" value="SIGNAL_UNSPECIFIED" />
<option name="timeIntervalDays" value="SEVEN_DAYS" />
<option name="visibilityType" value="ALL" />
</InsightsFilterSettings>
</value>
</entry>
<entry key="Firebase Crashlytics">
<value>
<InsightsFilterSettings>
<option name="connection">
<ConnectionSetting>
<option name="appId" value="com.cpm.lorealbaMabeline" />
<option name="mobileSdkAppId" value="1:629309347341:android:79b6975868639be937a744" />
<option name="projectId" value="silicon-airlock-129611" />
<option name="projectNumber" value="629309347341" />
</ConnectionSetting>
</option>
<option name="signal" value="SIGNAL_UNSPECIFIED" />
<option name="timeIntervalDays" value="THIRTY_DAYS" />
<option name="visibilityType" value="ALL" />
</InsightsFilterSettings>
</value>
</entry>
</map>
</option>
</component>
</project>
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="17" />
</component>
</project>
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-04-17T05:11:06.218957500Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\ravik\.android\avd\Pixel_2.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState>
</selectionStates>
</component>
</project>
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
</list>
</option>
</component>
</project>
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/annca" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/circleimageview" />
<option value="$PROJECT_DIR$/library" />
<option value="$PROJECT_DIR$/pinview" />
</set>
</option>
</GradleProjectSettings>
</option>
</component>
</project>
+8
View File
@@ -0,0 +1,8 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,java.lang.foreign.Arena,ofAuto,java.lang.foreign.Arena,global,retrofit2.Response,body,android.content.Context,obtainStyledAttributes" />
</inspection_tool>
</profile>
</component>
+10
View File
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>
+9
View File
@@ -0,0 +1,9 @@
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
+17
View File
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>
Generated
+6
View File
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
-2
View File
@@ -1,2 +0,0 @@
# LorealBa-Mny
+1
View File
@@ -0,0 +1 @@
/build
+80
View File
@@ -0,0 +1,80 @@
apply plugin: 'com.android.library'
buildscript {
repositories {
jcenter()
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
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 = 23
}
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.7.1'
androidTestImplementation 'androidx.annotation:annotation:1.9.1'
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
tasks.register('wrapper', Wrapper) {
gradleVersion = '2.4'
}
+17
View File
@@ -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 *;
#}
+11
View File
@@ -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;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Some files were not shown because too many files have changed in this diff Show More