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