@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2016 Florent37 |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -1 +1,118 @@ |
|||||||
# CameraFragment |
# CameraFragment |
||||||
|
|
||||||
|
[![gif](https://raw.githubusercontent.com/florent37/CameraFragment/master/media/cameraFragment.gif)](https://github.com/florent37/CameraFragment) |
||||||
|
|
||||||
|
```java |
||||||
|
//you can configure the fragment by the configuration builder |
||||||
|
CameraFragment cameraFragment = CameraFragment.newInstance(new Configuration.Builder().build()); |
||||||
|
|
||||||
|
getSupportFragmentManager().beginTransaction() |
||||||
|
.replace(R.id.content, cameraFragment, FRAGMENT_TAG) |
||||||
|
.commit(); |
||||||
|
``` |
||||||
|
|
||||||
|
## Actions |
||||||
|
|
||||||
|
You can directly take a photo / video with |
||||||
|
```java |
||||||
|
cameraFragment.takePhotoOrCaptureVideo(); |
||||||
|
``` |
||||||
|
|
||||||
|
[![gif](https://raw.githubusercontent.com/florent37/CameraFragment/master/media/take_photo.gif)](https://github.com/florent37/CameraFragment) |
||||||
|
|
||||||
|
Flash can be enable / disabled ( `AUTO` / `OFF` / `ON` ) with |
||||||
|
|
||||||
|
```java |
||||||
|
cameraFragment.toggleFlashMode(); |
||||||
|
``` |
||||||
|
|
||||||
|
[![gif](https://raw.githubusercontent.com/florent37/CameraFragment/master/media/switch_flash.gif)](https://github.com/florent37/CameraFragment) |
||||||
|
|
||||||
|
Camera Type can be modified ( `BACK` / `FRONT` ) with |
||||||
|
|
||||||
|
```java |
||||||
|
cameraFragment.switchCameraType(); |
||||||
|
``` |
||||||
|
|
||||||
|
[![gif](https://raw.githubusercontent.com/florent37/CameraFragment/master/media/switch_camera.gif)](https://github.com/florent37/CameraFragment) |
||||||
|
|
||||||
|
Camera action ( `PHOTO` / `VIDEO` ) can be modified with |
||||||
|
|
||||||
|
```java |
||||||
|
cameraFragment.switchAction(); |
||||||
|
``` |
||||||
|
|
||||||
|
[![gif](https://raw.githubusercontent.com/florent37/CameraFragment/master/media/switch_action.gif)](https://github.com/florent37/CameraFragment) |
||||||
|
|
||||||
|
And you can change the captured photo / video size with |
||||||
|
|
||||||
|
```java |
||||||
|
cameraFragment.openSettingDialog(); |
||||||
|
``` |
||||||
|
|
||||||
|
[![gif](https://raw.githubusercontent.com/florent37/CameraFragment/master/media/settings.gif)](https://github.com/florent37/CameraFragment) |
||||||
|
|
||||||
|
# Listeners |
||||||
|
|
||||||
|
## Result |
||||||
|
|
||||||
|
Get back the result of the camera record / photo in the `CameraFragmentResultListener` |
||||||
|
|
||||||
|
```java |
||||||
|
cameraFragment.setResultListener(new CameraFragmentResultListener() { |
||||||
|
@Override |
||||||
|
public void onVideoRecorded(byte[] bytes, String filePath) { |
||||||
|
//called when the video record is finished and saved |
||||||
|
|
||||||
|
startActivityForResult(PreviewActivity.newIntentVideo(MainActivity.this, filePath)); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPhotoTaken(byte[] bytes, String filePath) { |
||||||
|
//called when the photo is taken and saved |
||||||
|
|
||||||
|
startActivity(PreviewActivity.newIntentPhoto(MainActivity.this, filePath)); |
||||||
|
} |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
## Camera Listener |
||||||
|
|
||||||
|
```java |
||||||
|
cameraFragment.setStateListener(new CameraFragmentStateListener() { |
||||||
|
|
||||||
|
//when the current displayed camera is the back |
||||||
|
void onCurrentCameraBack(); |
||||||
|
//when the current displayed camera is the front |
||||||
|
void onCurrentCameraFront(); |
||||||
|
|
||||||
|
//when the flash is at mode auto |
||||||
|
void onFlashAuto(); |
||||||
|
//when the flash is at on |
||||||
|
void onFlashOn(); |
||||||
|
//when the flash is off |
||||||
|
void onFlashOff(); |
||||||
|
|
||||||
|
//if the camera is ready to take a photo |
||||||
|
void onCameraSetupForPhoto(); |
||||||
|
//if the camera is ready to take a video |
||||||
|
void onCameraSetupForVideo(); |
||||||
|
|
||||||
|
//when the camera state is "ready to record a video" |
||||||
|
void onRecordStateVideoReadyForRecord(); |
||||||
|
//when the camera state is "recording a video" |
||||||
|
void onRecordStateVideoInProgress(); |
||||||
|
//when the camera state is "ready to take a photo" |
||||||
|
void onRecordStatePhoto(); |
||||||
|
|
||||||
|
//after the rotation of the screen / camera |
||||||
|
void shouldRotateControls(int degrees); |
||||||
|
|
||||||
|
void onStartVideoRecord(File outputFile); |
||||||
|
void onStopVideoRecord(); |
||||||
|
}); |
||||||
|
``` |
||||||
|
|
||||||
|
## Text |
||||||
|
|
||||||
|
CameraFragment can ping you with the current record duration with `CameraFragmentTextListener` |
@ -0,0 +1 @@ |
|||||||
|
/build |
@ -0,0 +1,28 @@ |
|||||||
|
apply plugin: 'com.android.application' |
||||||
|
|
||||||
|
ext { |
||||||
|
PUBLISH_VERSION = '1.2.0' |
||||||
|
PUBLISH_VERSION_CODE = 3 |
||||||
|
SUPPORT_VERSION = '25.1.0' |
||||||
|
TARGET_SDK = 24 |
||||||
|
MIN_SDK = 10 |
||||||
|
BUILD_TOOLS = "24.0.3" |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion TARGET_SDK |
||||||
|
buildToolsVersion BUILD_TOOLS |
||||||
|
|
||||||
|
defaultConfig { |
||||||
|
minSdkVersion MIN_SDK |
||||||
|
targetSdkVersion TARGET_SDK |
||||||
|
versionCode PUBLISH_VERSION_CODE |
||||||
|
versionName PUBLISH_VERSION |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
compile "com.android.support:appcompat-v7:$SUPPORT_VERSION" |
||||||
|
compile 'com.jakewharton:butterknife:7.0.1' |
||||||
|
compile project(':camerafragment') |
||||||
|
} |
@ -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,37 @@ |
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="com.github.florent37.camerafragment.sample"> |
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" /> |
||||||
|
<uses-permission android:name="android.permission.FLASHLIGHT" /> |
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" /> |
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> |
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
||||||
|
|
||||||
|
|
||||||
|
<application |
||||||
|
android:allowBackup="true" |
||||||
|
android:icon="@mipmap/ic_launcher" |
||||||
|
android:label="@string/app_name" |
||||||
|
android:supportsRtl="true" |
||||||
|
android:theme="@style/AppTheme"> |
||||||
|
|
||||||
|
<activity |
||||||
|
android:name="com.github.florent37.camerafragment.PreviewActivity" |
||||||
|
android:screenOrientation="portrait" |
||||||
|
android:theme="@style/ThemeFullscreen" /> |
||||||
|
|
||||||
|
<activity |
||||||
|
android:name="com.github.florent37.camerafragment.sample.MainActivity" |
||||||
|
android:screenOrientation="portrait" |
||||||
|
android:theme="@style/ThemeFullscreen" > |
||||||
|
<intent-filter> |
||||||
|
<action android:name="android.intent.action.MAIN" /> |
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" /> |
||||||
|
</intent-filter> |
||||||
|
</activity> |
||||||
|
|
||||||
|
</application> |
||||||
|
|
||||||
|
|
||||||
|
</manifest> |
@ -0,0 +1,333 @@ |
|||||||
|
package com.github.florent37.camerafragment.sample; |
||||||
|
|
||||||
|
import android.Manifest; |
||||||
|
import android.content.Intent; |
||||||
|
import android.content.pm.PackageManager; |
||||||
|
import android.os.Build; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
import android.support.annotation.RequiresPermission; |
||||||
|
import android.support.v4.app.ActivityCompat; |
||||||
|
import android.support.v4.view.ViewCompat; |
||||||
|
import android.support.v7.app.AppCompatActivity; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.TextView; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import butterknife.Bind; |
||||||
|
import butterknife.ButterKnife; |
||||||
|
import butterknife.OnClick; |
||||||
|
import com.github.florent37.camerafragment.CameraFragment; |
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.PreviewActivity; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentStateListener; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentControlsListener; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentResultListener; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentVideoRecordTextListener; |
||||||
|
import com.github.florent37.camerafragment.widgets.CameraSettingsView; |
||||||
|
import com.github.florent37.camerafragment.widgets.CameraSwitchView; |
||||||
|
import com.github.florent37.camerafragment.widgets.FlashSwitchView; |
||||||
|
import com.github.florent37.camerafragment.widgets.MediaActionSwitchView; |
||||||
|
import com.github.florent37.camerafragment.widgets.RecordButton; |
||||||
|
|
||||||
|
public class MainActivity extends AppCompatActivity { |
||||||
|
|
||||||
|
private static final int REQUEST_CAMERA_PERMISSIONS = 931; |
||||||
|
private static final int REQUEST_PREVIEW_CODE = 1001; |
||||||
|
|
||||||
|
public static final String FRAGMENT_TAG = "camera"; |
||||||
|
|
||||||
|
@Bind(R.id.settings_view) CameraSettingsView settingsView; |
||||||
|
@Bind(R.id.flash_switch_view) FlashSwitchView flashSwitchView; |
||||||
|
@Bind(R.id.front_back_camera_switcher) CameraSwitchView cameraSwitchView; |
||||||
|
@Bind(R.id.record_button) RecordButton recordButton; |
||||||
|
@Bind(R.id.photo_video_camera_switcher) MediaActionSwitchView mediaActionSwitchView; |
||||||
|
|
||||||
|
@Bind(R.id.record_duration_text) TextView recordDurationText; |
||||||
|
@Bind(R.id.record_size_mb_text) TextView recordSizeText; |
||||||
|
|
||||||
|
@Bind(R.id.cameraLayout) View cameraLayout; |
||||||
|
@Bind(R.id.addCameraButton) View addCameraButton; |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onCreate(Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
setContentView(R.layout.activity_fragment); |
||||||
|
ButterKnife.bind(this); |
||||||
|
|
||||||
|
recordButton.setVisibility(View.GONE); |
||||||
|
settingsView.setVisibility(View.GONE); |
||||||
|
flashSwitchView.setVisibility(View.GONE); |
||||||
|
cameraSwitchView.setVisibility(View.GONE); |
||||||
|
mediaActionSwitchView.setVisibility(View.GONE); |
||||||
|
|
||||||
|
setupViewListeners(); |
||||||
|
} |
||||||
|
|
||||||
|
@OnClick(R.id.take_photo) |
||||||
|
public void takePhotoClicked(){ |
||||||
|
final CameraFragment cameraFragment = getCameraFragment(); |
||||||
|
if (cameraFragment != null) { |
||||||
|
cameraFragment.takePhotoOrCaptureVideo(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void setupViewListeners() { |
||||||
|
flashSwitchView.setFlashSwitchListener(new FlashSwitchView.FlashModeSwitchListener() { |
||||||
|
@Override |
||||||
|
public void toggleFlashMode() { |
||||||
|
final CameraFragment cameraFragment = getCameraFragment(); |
||||||
|
if (cameraFragment != null) { |
||||||
|
cameraFragment.toggleFlashMode(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
cameraSwitchView.setOnCameraTypeChangeListener(new CameraSwitchView.OnCameraTypeChangeListener() { |
||||||
|
@Override |
||||||
|
public void switchCameraType() { |
||||||
|
final CameraFragment cameraFragment = getCameraFragment(); |
||||||
|
if (cameraFragment != null) { |
||||||
|
cameraFragment.switchCameraType(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
recordButton.setRecordButtonListener(new RecordButton.RecordButtonListener() { |
||||||
|
@Override |
||||||
|
public void onRecordButtonClicked() { |
||||||
|
final CameraFragment cameraFragment = getCameraFragment(); |
||||||
|
if (cameraFragment != null) { |
||||||
|
cameraFragment.takePhotoOrCaptureVideo(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
mediaActionSwitchView.setOnMediaActionStateChangeListener(new MediaActionSwitchView.OnMediaActionStateChangeListener() { |
||||||
|
@Override |
||||||
|
public void switchAction() { |
||||||
|
final CameraFragment cameraFragment = getCameraFragment(); |
||||||
|
if (cameraFragment != null) { |
||||||
|
cameraFragment.switchAction(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
settingsView.setOnClickListener(new View.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View view) { |
||||||
|
final CameraFragment cameraFragment = getCameraFragment(); |
||||||
|
if (cameraFragment != null) { |
||||||
|
cameraFragment.openSettingDialog(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@OnClick(R.id.addCameraButton) |
||||||
|
public void onAddCameraClicked(){ |
||||||
|
if (Build.VERSION.SDK_INT > 15) { |
||||||
|
final String[] permissions = { |
||||||
|
Manifest.permission.CAMERA, |
||||||
|
Manifest.permission.RECORD_AUDIO, |
||||||
|
Manifest.permission.WRITE_EXTERNAL_STORAGE, |
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE}; |
||||||
|
|
||||||
|
final List<String> permissionsToRequest = new ArrayList<>(); |
||||||
|
for (String permission : permissions) { |
||||||
|
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { |
||||||
|
permissionsToRequest.add(permission); |
||||||
|
} |
||||||
|
} |
||||||
|
if (!permissionsToRequest.isEmpty()) { |
||||||
|
ActivityCompat.requestPermissions(this, permissionsToRequest.toArray(new String[permissionsToRequest.size()]), REQUEST_CAMERA_PERMISSIONS); |
||||||
|
} else addCamera(); |
||||||
|
} else { |
||||||
|
addCamera(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { |
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults); |
||||||
|
if (grantResults.length != 0) { |
||||||
|
addCamera(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@RequiresPermission(Manifest.permission.CAMERA) |
||||||
|
public void addCamera() { |
||||||
|
addCameraButton.setVisibility(View.GONE); |
||||||
|
cameraLayout.setVisibility(View.VISIBLE); |
||||||
|
|
||||||
|
final CameraFragment cameraFragment = CameraFragment.newInstance(new Configuration.Builder().build()); |
||||||
|
getSupportFragmentManager().beginTransaction() |
||||||
|
.replace(R.id.content, cameraFragment, FRAGMENT_TAG) |
||||||
|
.commit(); |
||||||
|
|
||||||
|
if (cameraFragment != null) { |
||||||
|
|
||||||
|
cameraFragment.setResultListener(new CameraFragmentResultListener() { |
||||||
|
@Override |
||||||
|
public void onVideoRecorded(String filePath) { |
||||||
|
Intent intent = PreviewActivity.newIntentVideo(MainActivity.this, filePath); |
||||||
|
startActivityForResult(intent, REQUEST_PREVIEW_CODE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPhotoTaken(byte[] bytes, String filePath) { |
||||||
|
Intent intent = PreviewActivity.newIntentPhoto(MainActivity.this, filePath); |
||||||
|
startActivityForResult(intent, REQUEST_PREVIEW_CODE); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
cameraFragment.setStateListener(new CameraFragmentStateListener() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCurrentCameraBack() { |
||||||
|
cameraSwitchView.displayBackCamera(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCurrentCameraFront() { |
||||||
|
cameraSwitchView.displayFrontCamera(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFlashAuto() { |
||||||
|
flashSwitchView.displayFlashAuto(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFlashOn() { |
||||||
|
flashSwitchView.displayFlashOn(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onFlashOff() { |
||||||
|
flashSwitchView.displayFlashOff(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCameraSetupForPhoto() { |
||||||
|
mediaActionSwitchView.displayActionWillSwitchVideo(); |
||||||
|
|
||||||
|
recordButton.displayPhotoState(); |
||||||
|
flashSwitchView.setVisibility(View.VISIBLE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCameraSetupForVideo() { |
||||||
|
mediaActionSwitchView.displayActionWillSwitchPhoto(); |
||||||
|
|
||||||
|
recordButton.displayVideoRecordStateReady(); |
||||||
|
flashSwitchView.setVisibility(View.GONE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void shouldRotateControls(int degrees) { |
||||||
|
ViewCompat.setRotation(cameraSwitchView, degrees); |
||||||
|
ViewCompat.setRotation(mediaActionSwitchView, degrees); |
||||||
|
ViewCompat.setRotation(flashSwitchView, degrees); |
||||||
|
ViewCompat.setRotation(recordDurationText, degrees); |
||||||
|
ViewCompat.setRotation(recordSizeText, degrees); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRecordStateVideoReadyForRecord() { |
||||||
|
recordButton.displayVideoRecordStateReady(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRecordStateVideoInProgress() { |
||||||
|
recordButton.displayVideoRecordStateInProgress(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onRecordStatePhoto() { |
||||||
|
recordButton.displayPhotoState(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStopVideoRecord() { |
||||||
|
recordSizeText.setVisibility(View.GONE); |
||||||
|
//cameraSwitchView.setVisibility(View.VISIBLE);
|
||||||
|
settingsView.setVisibility(View.VISIBLE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onStartVideoRecord(File outputFile) { |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
cameraFragment.setControlsListener(new CameraFragmentControlsListener() { |
||||||
|
@Override |
||||||
|
public void lockControls() { |
||||||
|
cameraSwitchView.setEnabled(false); |
||||||
|
recordButton.setEnabled(false); |
||||||
|
settingsView.setEnabled(false); |
||||||
|
flashSwitchView.setEnabled(false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void unLockControls() { |
||||||
|
cameraSwitchView.setEnabled(true); |
||||||
|
recordButton.setEnabled(true); |
||||||
|
settingsView.setEnabled(true); |
||||||
|
flashSwitchView.setEnabled(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void allowCameraSwitching(boolean allow) { |
||||||
|
//cameraSwitchView.setVisibility(allow ? View.VISIBLE : View.GONE);
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void allowRecord(boolean allow) { |
||||||
|
recordButton.setEnabled(allow); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setMediaActionSwitchVisible(boolean visible) { |
||||||
|
//mediaActionSwitchView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
cameraFragment.setTextListener(new CameraFragmentVideoRecordTextListener() { |
||||||
|
@Override |
||||||
|
public void setRecordSizeText(long size, String text) { |
||||||
|
recordSizeText.setText(text); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setRecordSizeTextVisible(boolean visible) { |
||||||
|
recordSizeText.setVisibility(visible ? View.VISIBLE : View.GONE); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setRecordDurationText(String text) { |
||||||
|
recordDurationText.setText(text); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setRecordDurationTextVisible(boolean visible) { |
||||||
|
recordDurationText.setVisibility(visible ? View.VISIBLE : View.GONE); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
cameraSwitchView.displayBackCamera(); |
||||||
|
flashSwitchView.displayFlashAuto(); |
||||||
|
recordButton.displayPhotoState(); |
||||||
|
mediaActionSwitchView.displayActionWillSwitchVideo(); |
||||||
|
} |
||||||
|
|
||||||
|
private CameraFragment getCameraFragment() { |
||||||
|
return (CameraFragment) getSupportFragmentManager().findFragmentByTag(FRAGMENT_TAG); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,122 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
xmlns:tools="http://schemas.android.com/tools" |
||||||
|
android:orientation="vertical" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent"> |
||||||
|
|
||||||
|
<FrameLayout |
||||||
|
android:id="@+id/content" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent"/> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/addCameraButton" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_margin="15dp" |
||||||
|
android:text="Add camera" /> |
||||||
|
|
||||||
|
<RelativeLayout |
||||||
|
android:id="@+id/cameraLayout" |
||||||
|
android:visibility="gone" |
||||||
|
tools:visibility="visible" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="match_parent" |
||||||
|
> |
||||||
|
|
||||||
|
<RelativeLayout |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="60dp" |
||||||
|
android:layout_alignParentEnd="true" |
||||||
|
android:layout_alignParentRight="true" |
||||||
|
android:layout_alignParentTop="true" |
||||||
|
android:paddingTop="10dp"> |
||||||
|
|
||||||
|
<com.github.florent37.camerafragment.widgets.CameraSettingsView |
||||||
|
android:id="@+id/settings_view" |
||||||
|
android:layout_width="40dp" |
||||||
|
android:layout_height="40dp" |
||||||
|
android:layout_alignParentLeft="true" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_marginLeft="15dp" |
||||||
|
android:background="@drawable/circle_frame_background_dark" |
||||||
|
android:scaleType="center" |
||||||
|
android:src="@drawable/ic_settings_white_24dp" /> |
||||||
|
|
||||||
|
<com.github.florent37.camerafragment.widgets.FlashSwitchView |
||||||
|
android:id="@+id/flash_switch_view" |
||||||
|
android:layout_width="40dp" |
||||||
|
android:layout_height="40dp" |
||||||
|
android:layout_centerInParent="true" /> |
||||||
|
|
||||||
|
<com.github.florent37.camerafragment.widgets.CameraSwitchView |
||||||
|
android:id="@+id/front_back_camera_switcher" |
||||||
|
android:layout_width="40dp" |
||||||
|
android:layout_height="40dp" |
||||||
|
android:layout_alignParentRight="true" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_marginRight="15dp" |
||||||
|
/> |
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
|
||||||
|
<!--android:background="#82000000"--> |
||||||
|
<RelativeLayout |
||||||
|
android:id="@+id/record_panel" |
||||||
|
android:layout_width="match_parent" |
||||||
|
android:layout_height="150dp" |
||||||
|
android:layout_alignParentBottom="true" |
||||||
|
android:background="@android:color/transparent"> |
||||||
|
|
||||||
|
<com.github.florent37.camerafragment.widgets.RecordButton |
||||||
|
android:id="@+id/record_button" |
||||||
|
android:layout_width="75dp" |
||||||
|
android:layout_height="75dp" |
||||||
|
android:visibility="gone" |
||||||
|
android:layout_centerInParent="true" |
||||||
|
android:layout_marginLeft="50dp" |
||||||
|
android:layout_marginRight="50dp" /> |
||||||
|
|
||||||
|
<Button |
||||||
|
android:id="@+id/take_photo" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerInParent="true" |
||||||
|
android:text="take photo"/> |
||||||
|
|
||||||
|
<com.github.florent37.camerafragment.widgets.MediaActionSwitchView |
||||||
|
android:id="@+id/photo_video_camera_switcher" |
||||||
|
android:layout_width="40dp" |
||||||
|
android:layout_height="40dp" |
||||||
|
android:layout_alignParentRight="true" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_marginRight="40dp" |
||||||
|
android:checked="false" |
||||||
|
android:gravity="center" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/record_duration_text" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_toLeftOf="@id/record_button" |
||||||
|
android:textColor="@android:color/white" |
||||||
|
android:textSize="14dp" |
||||||
|
android:visibility="invisible" /> |
||||||
|
|
||||||
|
<TextView |
||||||
|
android:id="@+id/record_size_mb_text" |
||||||
|
android:layout_width="wrap_content" |
||||||
|
android:layout_height="wrap_content" |
||||||
|
android:layout_centerVertical="true" |
||||||
|
android:layout_toRightOf="@id/record_button" |
||||||
|
android:textColor="@android:color/white" |
||||||
|
android:textSize="14dp" |
||||||
|
android:visibility="invisible" /> |
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
|
||||||
|
</RelativeLayout> |
||||||
|
|
||||||
|
</FrameLayout> |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 10 KiB |
@ -0,0 +1,6 @@ |
|||||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||||
|
<resources> |
||||||
|
<color name="colorPrimary">#3F51B5</color> |
||||||
|
<color name="colorPrimaryDark">#303F9F</color> |
||||||
|
<color name="colorAccent">#FF4081</color> |
||||||
|
</resources> |
@ -0,0 +1,3 @@ |
|||||||
|
<resources> |
||||||
|
<string name="app_name">CameraFragment</string> |
||||||
|
</resources> |
@ -0,0 +1,15 @@ |
|||||||
|
<resources> |
||||||
|
|
||||||
|
<!-- Base application theme. --> |
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar"> |
||||||
|
<!-- Customize your theme here. --> |
||||||
|
<item name="colorPrimary">@color/colorPrimary</item> |
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item> |
||||||
|
<item name="colorAccent">@color/colorAccent</item> |
||||||
|
</style> |
||||||
|
|
||||||
|
<style name="ThemeFullscreen" parent="AppTheme"> |
||||||
|
<item name="android:windowFullscreen">true</item> |
||||||
|
</style> |
||||||
|
|
||||||
|
</resources> |
After Width: | Height: | Size: 4.1 MiB |
After Width: | Height: | Size: 1.2 MiB |
After Width: | Height: | Size: 3.9 MiB |
After Width: | Height: | Size: 2.7 MiB |
@ -0,0 +1,22 @@ |
|||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules. |
||||||
|
|
||||||
|
buildscript { |
||||||
|
repositories { |
||||||
|
jcenter() |
||||||
|
} |
||||||
|
dependencies { |
||||||
|
classpath 'com.android.tools.build:gradle:2.2.3' |
||||||
|
// NOTE: Do not place your application dependencies here; they belong |
||||||
|
// in the individual module build.gradle files |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
allprojects { |
||||||
|
repositories { |
||||||
|
jcenter() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
task clean(type: Delete) { |
||||||
|
delete rootProject.buildDir |
||||||
|
} |
@ -0,0 +1 @@ |
|||||||
|
/build |
@ -0,0 +1,32 @@ |
|||||||
|
apply plugin: 'com.android.library' |
||||||
|
|
||||||
|
|
||||||
|
ext { |
||||||
|
PUBLISH_VERSION = '0.3.2' |
||||||
|
PUBLISH_VERSION_CODE = 6 |
||||||
|
SUPPORT_VERSION = '25.1.0' |
||||||
|
TARGET_SDK = 24 |
||||||
|
MIN_SDK = 10 |
||||||
|
BUILD_TOOLS = "24.0.3" |
||||||
|
} |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion TARGET_SDK |
||||||
|
buildToolsVersion BUILD_TOOLS |
||||||
|
|
||||||
|
defaultConfig { |
||||||
|
minSdkVersion MIN_SDK |
||||||
|
targetSdkVersion TARGET_SDK |
||||||
|
versionCode PUBLISH_VERSION_CODE |
||||||
|
versionName PUBLISH_VERSION |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
compile "com.android.support:support-v4:$SUPPORT_VERSION" |
||||||
|
compile "com.android.support:appcompat-v7:$SUPPORT_VERSION" |
||||||
|
} |
||||||
|
|
||||||
|
task wrapper(type: 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,5 @@ |
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="com.github.florent37.camerafragment"> |
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" /> |
||||||
|
</manifest> |
@ -0,0 +1,15 @@ |
|||||||
|
package com.github.florent37.camerafragment; |
||||||
|
|
||||||
|
import android.Manifest; |
||||||
|
import android.support.annotation.RequiresPermission; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.BaseAnncaFragment; |
||||||
|
|
||||||
|
public class CameraFragment extends BaseAnncaFragment { |
||||||
|
|
||||||
|
@RequiresPermission(Manifest.permission.CAMERA) |
||||||
|
public static CameraFragment newInstance(Configuration configuration) { |
||||||
|
return (CameraFragment) BaseAnncaFragment.newInstance(new CameraFragment(), configuration); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,386 @@ |
|||||||
|
package com.github.florent37.camerafragment; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.content.Intent; |
||||||
|
import android.media.AudioManager; |
||||||
|
import android.media.MediaPlayer; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v7.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 com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.internal.enums.MediaAction; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.view.AspectFrameLayout; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.ImageLoader; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
public class PreviewActivity extends AppCompatActivity implements View.OnClickListener { |
||||||
|
|
||||||
|
private static final String TAG = "PreviewActivity"; |
||||||
|
|
||||||
|
public static final int ACTION_CONFIRM = 900; |
||||||
|
public static final int ACTION_RETAKE = 901; |
||||||
|
public static final int ACTION_CANCEL = 902; |
||||||
|
|
||||||
|
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 newIntentPhoto(Context context, |
||||||
|
String filePath) { |
||||||
|
|
||||||
|
return new Intent(context, PreviewActivity.class) |
||||||
|
.putExtra(MEDIA_ACTION_ARG, MediaAction.ACTION_PHOTO) |
||||||
|
.putExtra(FILE_PATH_ARG, filePath); |
||||||
|
} |
||||||
|
|
||||||
|
public static Intent newIntentVideo(Context context, |
||||||
|
String filePath) { |
||||||
|
|
||||||
|
return new Intent(context, PreviewActivity.class) |
||||||
|
.putExtra(MEDIA_ACTION_ARG, MediaAction.ACTION_VIDEO) |
||||||
|
.putExtra(FILE_PATH_ARG, filePath); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@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(new View.OnTouchListener() { |
||||||
|
@Override |
||||||
|
public boolean onTouch(View v, MotionEvent 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(new View.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View 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 == Configuration.MEDIA_ACTION_VIDEO) { |
||||||
|
displayVideo(savedInstanceState); |
||||||
|
} else if (mediaAction == Configuration.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(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); |
||||||
|
ImageLoader.Builder builder = new ImageLoader.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(new MediaPlayer.OnPreparedListener() { |
||||||
|
@Override |
||||||
|
public void onPrepared(MediaPlayer 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(new MediaPlayer.OnErrorListener() { |
||||||
|
@Override |
||||||
|
public boolean onError(MediaPlayer mp, int what, int extra) { |
||||||
|
finish(); |
||||||
|
return true; |
||||||
|
} |
||||||
|
}); |
||||||
|
mediaPlayer.prepareAsync(); |
||||||
|
} catch (Exception e) { |
||||||
|
Log.e(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, ACTION_CONFIRM).putExtra(FILE_PATH_ARG, previewFilePath); |
||||||
|
} else if (view.getId() == R.id.re_take_media) { |
||||||
|
deleteMediaFile(); |
||||||
|
resultIntent.putExtra(RESPONSE_CODE_ARG, ACTION_RETAKE); |
||||||
|
} else if (view.getId() == R.id.cancel_media_action) { |
||||||
|
deleteMediaFile(); |
||||||
|
resultIntent.putExtra(RESPONSE_CODE_ARG, 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 String getMediaFilePatch(@NonNull Intent resultIntent) { |
||||||
|
return resultIntent.getStringExtra(FILE_PATH_ARG); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isResultConfirm(@NonNull Intent resultIntent) { |
||||||
|
return ACTION_CONFIRM == resultIntent.getIntExtra(RESPONSE_CODE_ARG, -1); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isResultRetake(@NonNull Intent resultIntent) { |
||||||
|
return ACTION_RETAKE == resultIntent.getIntExtra(RESPONSE_CODE_ARG, -1); |
||||||
|
} |
||||||
|
|
||||||
|
public static boolean isResultCancel(@NonNull Intent resultIntent) { |
||||||
|
return ACTION_CANCEL == resultIntent.getIntExtra(RESPONSE_CODE_ARG, -1); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,200 @@ |
|||||||
|
package com.github.florent37.camerafragment.configuration; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
import android.support.annotation.IntRange; |
||||||
|
|
||||||
|
import java.io.Serializable; |
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
* Updated by Florent37 |
||||||
|
*/ |
||||||
|
public final class Configuration implements Serializable { |
||||||
|
|
||||||
|
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 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"; |
||||||
|
} |
||||||
|
|
||||||
|
@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({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 { |
||||||
|
} |
||||||
|
|
||||||
|
@MediaAction |
||||||
|
private int mediaAction = -1; |
||||||
|
|
||||||
|
@MediaQuality |
||||||
|
private int mediaQuality = -1; |
||||||
|
|
||||||
|
@CameraFace |
||||||
|
private int cameraFace = -1; |
||||||
|
|
||||||
|
private int videoDuration = -1; |
||||||
|
|
||||||
|
private long videoFileSize = -1; |
||||||
|
|
||||||
|
private int minimumVideoDuration = -1; |
||||||
|
|
||||||
|
@FlashMode |
||||||
|
private int flashMode = FLASH_MODE_AUTO; |
||||||
|
|
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private Configuration configuration; |
||||||
|
|
||||||
|
public Builder() { |
||||||
|
configuration = new Configuration(); |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setMediaAction(@MediaAction int mediaAction) { |
||||||
|
configuration.mediaAction = mediaAction; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setMediaQuality(@MediaQuality int mediaQuality) { |
||||||
|
configuration.mediaQuality = mediaQuality; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* @param videoDurationInMilliseconds - video duration in milliseconds |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Builder setVideoDuration(@IntRange(from = 1000, to = Integer.MAX_VALUE) int videoDurationInMilliseconds) { |
||||||
|
configuration.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) { |
||||||
|
configuration.minimumVideoDuration = minimumVideoDurationInMilliseconds; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/*** |
||||||
|
* @param videoSizeInBytes - file size in bytes |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public Builder setVideoFileSize(@IntRange(from = 1048576, to = Long.MAX_VALUE) long videoSizeInBytes) { |
||||||
|
configuration.videoFileSize = videoSizeInBytes; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Builder setFlashMode(@FlashMode int flashMode) { |
||||||
|
configuration.flashMode = flashMode; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public Configuration build() throws IllegalArgumentException { |
||||||
|
if (configuration.mediaQuality == MEDIA_QUALITY_AUTO && configuration.minimumVideoDuration < 0) { |
||||||
|
throw new IllegalStateException("Please provide minimum video duration in milliseconds to use auto quality."); |
||||||
|
} |
||||||
|
|
||||||
|
return configuration; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public int getMediaAction() { |
||||||
|
return mediaAction; |
||||||
|
} |
||||||
|
|
||||||
|
public int getMediaQuality() { |
||||||
|
return mediaQuality; |
||||||
|
} |
||||||
|
|
||||||
|
public int getCameraFace() { |
||||||
|
return cameraFace; |
||||||
|
} |
||||||
|
|
||||||
|
public int getVideoDuration() { |
||||||
|
return videoDuration; |
||||||
|
} |
||||||
|
|
||||||
|
public long getVideoFileSize() { |
||||||
|
return videoFileSize; |
||||||
|
} |
||||||
|
|
||||||
|
public int getMinimumVideoDuration() { |
||||||
|
return minimumVideoDuration; |
||||||
|
} |
||||||
|
|
||||||
|
public int getFlashMode() { |
||||||
|
return flashMode; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package com.github.florent37.camerafragment.configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
public interface ConfigurationProvider { |
||||||
|
|
||||||
|
@Configuration.MediaAction |
||||||
|
int getMediaAction(); |
||||||
|
|
||||||
|
@Configuration.MediaQuality |
||||||
|
int getMediaQuality(); |
||||||
|
|
||||||
|
int getVideoDuration(); |
||||||
|
|
||||||
|
long getVideoFileSize(); |
||||||
|
|
||||||
|
@Configuration.SensorPosition |
||||||
|
int getSensorPosition(); |
||||||
|
|
||||||
|
int getDegrees(); |
||||||
|
|
||||||
|
int getMinimumVideoDuration(); |
||||||
|
|
||||||
|
@Configuration.FlashMode |
||||||
|
int getFlashMode(); |
||||||
|
|
||||||
|
void setMediaQuality(int mediaQuality); |
||||||
|
|
||||||
|
void setPassedMediaQuality(int mediaQuality); |
||||||
|
|
||||||
|
void setVideoDuration(int videoDuration); |
||||||
|
|
||||||
|
void setVideoFileSize(long videoFileSize); |
||||||
|
|
||||||
|
void setMinimumVideoDuration(int minimumVideoDuration); |
||||||
|
|
||||||
|
void setFlashMode(int flashMode); |
||||||
|
|
||||||
|
void setSensorPosition(int sensorPosition); |
||||||
|
|
||||||
|
int getDeviceDefaultOrientation(); |
||||||
|
|
||||||
|
void setDegrees(int degrees); |
||||||
|
|
||||||
|
void setMediaAction(int mediaAction); |
||||||
|
|
||||||
|
void setDeviceDefaultOrientation(int deviceDefaultOrientation); |
||||||
|
|
||||||
|
int getPassedMediaQuality(); |
||||||
|
|
||||||
|
void setupWithAnnaConfiguration(Configuration configuration); |
||||||
|
} |
@ -0,0 +1,207 @@ |
|||||||
|
package com.github.florent37.camerafragment.configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 12/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class ConfigurationProviderImpl implements ConfigurationProvider { |
||||||
|
|
||||||
|
protected long videoFileSize = -1; |
||||||
|
protected int videoDuration = -1; |
||||||
|
protected int minimumVideoDuration = -1; |
||||||
|
|
||||||
|
@Configuration.MediaAction |
||||||
|
protected int mediaAction = Configuration.MEDIA_ACTION_UNSPECIFIED; |
||||||
|
|
||||||
|
@Configuration.MediaQuality |
||||||
|
protected int mediaQuality = Configuration.MEDIA_QUALITY_MEDIUM; |
||||||
|
|
||||||
|
@Configuration.MediaQuality |
||||||
|
protected int passedMediaQuality = Configuration.MEDIA_QUALITY_MEDIUM; |
||||||
|
|
||||||
|
@Configuration.FlashMode |
||||||
|
protected int flashMode = Configuration.FLASH_MODE_AUTO; |
||||||
|
|
||||||
|
@Configuration.SensorPosition |
||||||
|
protected int sensorPosition = Configuration.SENSOR_POSITION_UNSPECIFIED; |
||||||
|
|
||||||
|
@Configuration.DeviceDefaultOrientation |
||||||
|
protected int deviceDefaultOrientation; |
||||||
|
|
||||||
|
private int degrees = -1; |
||||||
|
|
||||||
|
@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 final int getSensorPosition() { |
||||||
|
return sensorPosition; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public final int getDegrees() { |
||||||
|
return degrees; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getFlashMode() { |
||||||
|
return flashMode; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setMediaQuality(int mediaQuality) { |
||||||
|
this.mediaQuality = mediaQuality; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setPassedMediaQuality(int mediaQuality) { |
||||||
|
this.passedMediaQuality = mediaQuality; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setVideoDuration(int videoDuration) { |
||||||
|
this.videoDuration = videoDuration; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setVideoFileSize(long videoFileSize) { |
||||||
|
this.videoFileSize = videoFileSize; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setMinimumVideoDuration(int minimumVideoDuration) { |
||||||
|
this.minimumVideoDuration = minimumVideoDuration; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setFlashMode(int flashMode) { |
||||||
|
this.flashMode = flashMode; |
||||||
|
} |
||||||
|
|
||||||
|
public void setMediaAction(int mediaAction) { |
||||||
|
this.mediaAction = mediaAction; |
||||||
|
} |
||||||
|
|
||||||
|
public int getPassedMediaQuality() { |
||||||
|
return passedMediaQuality; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setupWithAnnaConfiguration(Configuration configuration) { |
||||||
|
if (configuration != null) { |
||||||
|
|
||||||
|
final int mediaAction = configuration.getMediaAction(); |
||||||
|
if (mediaAction != -1) { |
||||||
|
switch (mediaAction) { |
||||||
|
case Configuration.MEDIA_ACTION_PHOTO: |
||||||
|
setMediaAction(Configuration.MEDIA_ACTION_PHOTO); |
||||||
|
break; |
||||||
|
case Configuration.MEDIA_ACTION_VIDEO: |
||||||
|
setMediaAction(Configuration.MEDIA_ACTION_VIDEO); |
||||||
|
break; |
||||||
|
default: |
||||||
|
setMediaAction(Configuration.MEDIA_ACTION_UNSPECIFIED); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
final int mediaQuality = configuration.getMediaQuality(); |
||||||
|
if (mediaQuality != -1) { |
||||||
|
switch (mediaQuality) { |
||||||
|
case Configuration.MEDIA_QUALITY_AUTO: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_AUTO); |
||||||
|
break; |
||||||
|
case Configuration.MEDIA_QUALITY_HIGHEST: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_HIGHEST); |
||||||
|
break; |
||||||
|
case Configuration.MEDIA_QUALITY_HIGH: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_HIGH); |
||||||
|
break; |
||||||
|
case Configuration.MEDIA_QUALITY_MEDIUM: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_MEDIUM); |
||||||
|
break; |
||||||
|
case Configuration.MEDIA_QUALITY_LOW: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_LOW); |
||||||
|
break; |
||||||
|
case Configuration.MEDIA_QUALITY_LOWEST: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_LOWEST); |
||||||
|
break; |
||||||
|
default: |
||||||
|
setMediaQuality(Configuration.MEDIA_QUALITY_MEDIUM); |
||||||
|
break; |
||||||
|
} |
||||||
|
setPassedMediaQuality(getMediaQuality()); |
||||||
|
} |
||||||
|
|
||||||
|
final int videoDuration = configuration.getVideoDuration(); |
||||||
|
if (videoDuration != -1) { |
||||||
|
setVideoDuration(videoDuration); |
||||||
|
} |
||||||
|
|
||||||
|
final long videoFileSize = configuration.getVideoFileSize(); |
||||||
|
if (videoFileSize != -1) { |
||||||
|
setVideoFileSize(videoFileSize); |
||||||
|
} |
||||||
|
|
||||||
|
final int minimumVideoDuration = configuration.getMinimumVideoDuration(); |
||||||
|
if (minimumVideoDuration != -1) { |
||||||
|
setMinimumVideoDuration(minimumVideoDuration); |
||||||
|
} |
||||||
|
|
||||||
|
final int flashMode = configuration.getFlashMode(); |
||||||
|
if (flashMode != -1) |
||||||
|
switch (flashMode) { |
||||||
|
case Configuration.FLASH_MODE_AUTO: |
||||||
|
setFlashMode(Configuration.FLASH_MODE_AUTO); |
||||||
|
break; |
||||||
|
case Configuration.FLASH_MODE_ON: |
||||||
|
setFlashMode(Configuration.FLASH_MODE_ON); |
||||||
|
break; |
||||||
|
case Configuration.FLASH_MODE_OFF: |
||||||
|
setFlashMode(Configuration.FLASH_MODE_OFF); |
||||||
|
break; |
||||||
|
default: |
||||||
|
setFlashMode(Configuration.FLASH_MODE_AUTO); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setSensorPosition(int sensorPosition) { |
||||||
|
this.sensorPosition = sensorPosition; |
||||||
|
} |
||||||
|
|
||||||
|
public int getDeviceDefaultOrientation() { |
||||||
|
return deviceDefaultOrientation; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDeviceDefaultOrientation(int deviceDefaultOrientation) { |
||||||
|
this.deviceDefaultOrientation = deviceDefaultOrientation; |
||||||
|
} |
||||||
|
|
||||||
|
public void setDegrees(int degrees) { |
||||||
|
this.degrees = degrees; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,51 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.controller; |
||||||
|
|
||||||
|
import android.os.Bundle; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.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(); |
||||||
|
|
||||||
|
boolean isVideoRecording(); |
||||||
|
|
||||||
|
void switchCamera(@Configuration.CameraFace int cameraFace); |
||||||
|
|
||||||
|
void switchQuality(); |
||||||
|
|
||||||
|
void setFlashMode(@Configuration.FlashMode int flashMode); |
||||||
|
|
||||||
|
int getNumberOfCameras(); |
||||||
|
|
||||||
|
@Configuration.MediaAction |
||||||
|
int getMediaAction(); |
||||||
|
|
||||||
|
CameraId getCurrentCameraId(); |
||||||
|
|
||||||
|
File getOutputFile(); |
||||||
|
|
||||||
|
CameraManager getCameraManager(); |
||||||
|
|
||||||
|
CharSequence[] getVideoQualityOptions(); |
||||||
|
|
||||||
|
CharSequence[] getPhotoQualityOptions(); |
||||||
|
} |
@ -0,0 +1,194 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.controller.impl; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.SurfaceHolder; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.CameraController; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.view.CameraView; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.CameraManager; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.impl.Camera1Manager; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraCloseListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraOpenListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraPhotoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraVideoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.view.AutoFitSurfaceView; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.CameraHelper; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/7/16. |
||||||
|
*/ |
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public class Camera1Controller implements CameraController<Integer>, |
||||||
|
CameraOpenListener<Integer, SurfaceHolder.Callback>, CameraPhotoListener, CameraCloseListener<Integer>, CameraVideoListener { |
||||||
|
|
||||||
|
private final static String TAG = "Camera1Controller"; |
||||||
|
|
||||||
|
private final Context context; |
||||||
|
|
||||||
|
private Integer currentCameraId; |
||||||
|
private ConfigurationProvider configurationProvider; |
||||||
|
private CameraManager<Integer, SurfaceHolder.Callback> cameraManager; |
||||||
|
private CameraView cameraView; |
||||||
|
|
||||||
|
private File outputFile; |
||||||
|
|
||||||
|
public Camera1Controller(Context context, CameraView cameraView, ConfigurationProvider configurationProvider) { |
||||||
|
this.context = context; |
||||||
|
this.cameraView = cameraView; |
||||||
|
this.configurationProvider = configurationProvider; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCreate(Bundle savedInstanceState) { |
||||||
|
cameraManager = Camera1Manager.getInstance(); |
||||||
|
cameraManager.initializeCameraManager(configurationProvider, context); |
||||||
|
setCurrentCameraId(cameraManager.getFaceBackCameraId()); |
||||||
|
} |
||||||
|
|
||||||
|
private void setCurrentCameraId(Integer cameraId) { |
||||||
|
this.currentCameraId = cameraId; |
||||||
|
cameraManager.setCameraId(cameraId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onResume() { |
||||||
|
cameraManager.openCamera(currentCameraId, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPause() { |
||||||
|
cameraManager.closeCamera(null); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroy() { |
||||||
|
cameraManager.releaseCameraManager(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void takePhoto() { |
||||||
|
outputFile = CameraHelper.getOutputMediaFile(context, Configuration.MEDIA_ACTION_PHOTO); |
||||||
|
cameraManager.takePhoto(outputFile, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void startVideoRecord() { |
||||||
|
outputFile = CameraHelper.getOutputMediaFile(context, Configuration.MEDIA_ACTION_VIDEO); |
||||||
|
cameraManager.startVideoRecord(outputFile, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stopVideoRecord() { |
||||||
|
cameraManager.stopVideoRecord(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isVideoRecording() { |
||||||
|
return cameraManager.isVideoRecording(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void switchCamera(@Configuration.CameraFace final int cameraFace) { |
||||||
|
setCurrentCameraId(cameraManager.getCurrentCameraId().equals(cameraManager.getFaceFrontCameraId()) ? |
||||||
|
cameraManager.getFaceBackCameraId() : cameraManager.getFaceFrontCameraId()); |
||||||
|
|
||||||
|
cameraManager.closeCamera(this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setFlashMode(@Configuration.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) { |
||||||
|
cameraView.updateUiForMediaAction(configurationProvider.getMediaAction()); |
||||||
|
cameraView.updateCameraPreview(previewSize, new AutoFitSurfaceView(context, surfaceCallback)); |
||||||
|
cameraView.updateCameraSwitcher(getNumberOfCameras()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCameraOpenError() { |
||||||
|
Log.e(TAG, "onCameraOpenError"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCameraClosed(Integer closedCameraId) { |
||||||
|
cameraView.releaseCameraPreview(); |
||||||
|
|
||||||
|
cameraManager.openCamera(currentCameraId, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPhotoTaken(byte[] bytes, File photoFile) { |
||||||
|
cameraView.onPhotoTaken(bytes); |
||||||
|
} |
||||||
|
|
||||||
|
@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; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getVideoQualityOptions() { |
||||||
|
return cameraManager.getVideoQualityOptions(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getPhotoQualityOptions() { |
||||||
|
return cameraManager.getPhotoQualityOptions(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.controller.impl; |
||||||
|
|
||||||
|
import android.annotation.TargetApi; |
||||||
|
import android.content.Context; |
||||||
|
import android.os.Build; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.TextureView; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.CameraController; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.view.CameraView; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.CameraManager; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.impl.Camera2Manager; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraCloseListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraOpenListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraPhotoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraVideoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.view.AutoFitTextureView; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.CameraHelper; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public class Camera2Controller implements CameraController<String>, |
||||||
|
CameraOpenListener<String, TextureView.SurfaceTextureListener>, |
||||||
|
CameraPhotoListener, CameraVideoListener, CameraCloseListener<String> { |
||||||
|
|
||||||
|
private final static String TAG = "Camera2Controller"; |
||||||
|
|
||||||
|
private final Context context; |
||||||
|
private String currentCameraId; |
||||||
|
private ConfigurationProvider configurationProvider; |
||||||
|
private CameraManager<String, TextureView.SurfaceTextureListener> camera2Manager; |
||||||
|
private CameraView cameraView; |
||||||
|
|
||||||
|
private File outputFile; |
||||||
|
|
||||||
|
public Camera2Controller(Context context, CameraView cameraView, ConfigurationProvider configurationProvider) { |
||||||
|
this.context = context; |
||||||
|
this.cameraView = cameraView; |
||||||
|
this.configurationProvider = configurationProvider; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCreate(Bundle savedInstanceState) { |
||||||
|
camera2Manager = Camera2Manager.getInstance(); |
||||||
|
camera2Manager.initializeCameraManager(configurationProvider, context); |
||||||
|
setCurrentCameraId(camera2Manager.getFaceBackCameraId()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onResume() { |
||||||
|
camera2Manager.openCamera(currentCameraId, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPause() { |
||||||
|
camera2Manager.closeCamera(null); |
||||||
|
cameraView.releaseCameraPreview(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroy() { |
||||||
|
camera2Manager.releaseCameraManager(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void takePhoto() { |
||||||
|
outputFile = CameraHelper.getOutputMediaFile(context, Configuration.MEDIA_ACTION_PHOTO); |
||||||
|
camera2Manager.takePhoto(outputFile, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void startVideoRecord() { |
||||||
|
outputFile = CameraHelper.getOutputMediaFile(context, Configuration.MEDIA_ACTION_VIDEO); |
||||||
|
camera2Manager.startVideoRecord(outputFile, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stopVideoRecord() { |
||||||
|
camera2Manager.stopVideoRecord(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isVideoRecording() { |
||||||
|
return camera2Manager.isVideoRecording(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void switchCamera(final @Configuration.CameraFace int cameraFace) { |
||||||
|
setCurrentCameraId(camera2Manager.getCurrentCameraId().equals(camera2Manager.getFaceFrontCameraId()) ? |
||||||
|
camera2Manager.getFaceBackCameraId() : camera2Manager.getFaceFrontCameraId()); |
||||||
|
|
||||||
|
camera2Manager.closeCamera(this); |
||||||
|
} |
||||||
|
|
||||||
|
private void setCurrentCameraId(String currentCameraId){ |
||||||
|
this.currentCameraId = currentCameraId; |
||||||
|
camera2Manager.setCameraId(currentCameraId); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setFlashMode(@Configuration.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) { |
||||||
|
cameraView.updateUiForMediaAction(Configuration.MEDIA_ACTION_UNSPECIFIED); |
||||||
|
cameraView.updateCameraPreview(previewSize, new AutoFitTextureView(context, surfaceTextureListener)); |
||||||
|
cameraView.updateCameraSwitcher(camera2Manager.getNumberOfCameras()); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCameraOpenError() { |
||||||
|
Log.e(TAG, "onCameraOpenError"); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCameraClosed(String closedCameraId) { |
||||||
|
cameraView.releaseCameraPreview(); |
||||||
|
|
||||||
|
camera2Manager.openCamera(currentCameraId, this); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPhotoTaken(byte[] bytes, File photoFile) { |
||||||
|
cameraView.onPhotoTaken(bytes); |
||||||
|
} |
||||||
|
|
||||||
|
@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; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getVideoQualityOptions() { |
||||||
|
return camera2Manager.getVideoQualityOptions(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getPhotoQualityOptions() { |
||||||
|
return camera2Manager.getPhotoQualityOptions(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,27 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.controller.view; |
||||||
|
|
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
public interface CameraView { |
||||||
|
|
||||||
|
void updateCameraPreview(Size size, View cameraPreview); |
||||||
|
|
||||||
|
void updateUiForMediaAction(@Configuration.MediaAction int mediaAction); |
||||||
|
|
||||||
|
void updateCameraSwitcher(int numberOfCameras); |
||||||
|
|
||||||
|
void onPhotoTaken(byte[] bytes); |
||||||
|
|
||||||
|
void onVideoRecordStart(int width, int height); |
||||||
|
|
||||||
|
void onVideoRecordStop(); |
||||||
|
|
||||||
|
void releaseCameraPreview(); |
||||||
|
|
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.enums; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
|
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
public class Camera { |
||||||
|
|
||||||
|
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 { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.enums; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
|
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
public class Flash { |
||||||
|
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 { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.enums; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
|
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
public class MediaAction { |
||||||
|
|
||||||
|
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 { |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.enums; |
||||||
|
|
||||||
|
import android.support.annotation.IntDef; |
||||||
|
|
||||||
|
import java.lang.annotation.Retention; |
||||||
|
import java.lang.annotation.RetentionPolicy; |
||||||
|
|
||||||
|
public class Record { |
||||||
|
|
||||||
|
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 { |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,57 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraCloseListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraOpenListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraPhotoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraVideoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/14/16. |
||||||
|
*/ |
||||||
|
public interface CameraManager<CameraId, SurfaceListener> { |
||||||
|
|
||||||
|
void initializeCameraManager(ConfigurationProvider configurationProvider, Context context); |
||||||
|
|
||||||
|
void openCamera(CameraId cameraId, CameraOpenListener<CameraId, SurfaceListener> cameraOpenListener); |
||||||
|
|
||||||
|
void closeCamera(CameraCloseListener<CameraId> cameraCloseListener); |
||||||
|
|
||||||
|
void setFlashMode(@Configuration.FlashMode int flashMode); |
||||||
|
|
||||||
|
void takePhoto(File photoFile, CameraPhotoListener cameraPhotoListener); |
||||||
|
|
||||||
|
void startVideoRecord(File videoFile, CameraVideoListener cameraVideoListener); |
||||||
|
|
||||||
|
Size getPhotoSizeForQuality(@Configuration.MediaQuality int mediaQuality); |
||||||
|
|
||||||
|
void stopVideoRecord(); |
||||||
|
|
||||||
|
void releaseCameraManager(); |
||||||
|
|
||||||
|
CameraId getCurrentCameraId(); |
||||||
|
|
||||||
|
CameraId getFaceFrontCameraId(); |
||||||
|
|
||||||
|
CameraId getFaceBackCameraId(); |
||||||
|
|
||||||
|
int getNumberOfCameras(); |
||||||
|
|
||||||
|
int getFaceFrontCameraOrientation(); |
||||||
|
|
||||||
|
int getFaceBackCameraOrientation(); |
||||||
|
|
||||||
|
boolean isVideoRecording(); |
||||||
|
|
||||||
|
CharSequence[] getVideoQualityOptions(); |
||||||
|
|
||||||
|
CharSequence[] getPhotoQualityOptions(); |
||||||
|
|
||||||
|
void setCameraId(CameraId currentCameraId); |
||||||
|
} |
@ -0,0 +1,148 @@ |
|||||||
|
package com.github.florent37.camerafragment.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 com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.CameraManager; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/14/16. |
||||||
|
*/ |
||||||
|
abstract class BaseCameraManager<CameraId, SurfaceListener> |
||||||
|
implements CameraManager<CameraId, SurfaceListener>, 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(@Configuration.SensorPosition int sensorPosition); |
||||||
|
|
||||||
|
protected abstract int getVideoOrientation(@Configuration.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() { |
||||||
|
if (Build.VERSION.SDK_INT > 17) { |
||||||
|
backgroundThread.quitSafely(); |
||||||
|
} else backgroundThread.quit(); |
||||||
|
|
||||||
|
try { |
||||||
|
backgroundThread.join(); |
||||||
|
} catch (InterruptedException e) { |
||||||
|
Log.e(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; |
||||||
|
} |
||||||
|
|
||||||
|
public void setCameraId(CameraId currentCameraId) { |
||||||
|
this.currentCameraId = currentCameraId; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,627 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager.impl; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.PixelFormat; |
||||||
|
import android.hardware.Camera; |
||||||
|
import android.media.CamcorderProfile; |
||||||
|
import android.media.ExifInterface; |
||||||
|
import android.media.MediaRecorder; |
||||||
|
import android.os.Build; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.Surface; |
||||||
|
import android.view.SurfaceHolder; |
||||||
|
import android.view.WindowManager; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileNotFoundException; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraCloseListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraOpenListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraPhotoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraVideoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.model.PhotoQualityOption; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.model.VideoQualityOption; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.CameraHelper; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/14/16. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public class Camera1Manager extends BaseCameraManager<Integer, SurfaceHolder.Callback> |
||||||
|
implements SurfaceHolder.Callback, Camera.PictureCallback { |
||||||
|
|
||||||
|
private static final String TAG = "Camera1Manager"; |
||||||
|
|
||||||
|
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 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; |
||||||
|
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); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} catch (Exception error) { |
||||||
|
Log.d(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(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (camera != null) { |
||||||
|
camera.release(); |
||||||
|
camera = null; |
||||||
|
if (cameraCloseListener != null) { |
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraCloseListener.onCameraClosed(currentCameraId); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setFlashMode(@Configuration.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() { |
||||||
|
setCameraPhotoQuality(camera); |
||||||
|
camera.takePicture(null, null, currentInstance); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@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(@Configuration.MediaQuality int mediaQuality) { |
||||||
|
return CameraHelper.getPictureSize(Size.fromList(camera.getParameters().getSupportedPictureSizes()), mediaQuality); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void prepareCameraOutputs() { |
||||||
|
try { |
||||||
|
if (configurationProvider.getMediaQuality() == Configuration.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; |
||||||
|
if (Build.VERSION.SDK_INT > 10) |
||||||
|
videoSizes = Size.fromList(camera.getParameters().getSupportedVideoSizes()); |
||||||
|
else videoSizes = previewSizes; |
||||||
|
|
||||||
|
videoSize = CameraHelper.getSizeWithClosestRatio( |
||||||
|
(videoSizes == null || videoSizes.isEmpty()) ? previewSizes : videoSizes, |
||||||
|
camcorderProfile.videoFrameWidth, camcorderProfile.videoFrameHeight); |
||||||
|
|
||||||
|
photoSize = CameraHelper.getPictureSize( |
||||||
|
(pictureSizes == null || pictureSizes.isEmpty()) ? previewSizes : pictureSizes, |
||||||
|
configurationProvider.getMediaQuality() == Configuration.MEDIA_QUALITY_AUTO |
||||||
|
? Configuration.MEDIA_QUALITY_HIGHEST : configurationProvider.getMediaQuality()); |
||||||
|
|
||||||
|
if (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_PHOTO |
||||||
|
|| configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED) { |
||||||
|
previewSize = CameraHelper.getSizeWithClosestRatio(previewSizes, photoSize.getWidth(), photoSize.getHeight()); |
||||||
|
} else { |
||||||
|
previewSize = CameraHelper.getSizeWithClosestRatio(previewSizes, videoSize.getWidth(), videoSize.getHeight()); |
||||||
|
} |
||||||
|
} catch (Exception e) { |
||||||
|
Log.e(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) { |
||||||
|
Log.e(TAG, "IllegalStateException preparing MediaRecorder: " + error.getMessage()); |
||||||
|
} catch (IOException error) { |
||||||
|
Log.e(TAG, "IOException preparing MediaRecorder: " + error.getMessage()); |
||||||
|
} catch (Throwable error) { |
||||||
|
Log.e(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() == Configuration.MEDIA_ACTION_PHOTO |
||||||
|
|| configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED) |
||||||
|
turnPhotoCameraFeaturesOn(camera, parameters); |
||||||
|
else if (configurationProvider.getMediaAction() == Configuration.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 (Build.VERSION.SDK_INT > 13 |
||||||
|
&& (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_VIDEO |
||||||
|
|| configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED)) { |
||||||
|
// parameters.setRecordingHint(true);
|
||||||
|
} |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT > 14 |
||||||
|
&& parameters.isVideoStabilizationSupported() |
||||||
|
&& (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_VIDEO |
||||||
|
|| configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_UNSPECIFIED)) { |
||||||
|
parameters.setVideoStabilization(true); |
||||||
|
} |
||||||
|
|
||||||
|
parameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight()); |
||||||
|
parameters.setPictureSize(photoSize.getWidth(), photoSize.getHeight()); |
||||||
|
|
||||||
|
camera.setParameters(parameters); |
||||||
|
camera.setPreviewDisplay(surfaceHolder); |
||||||
|
camera.startPreview(); |
||||||
|
|
||||||
|
} catch (IOException error) { |
||||||
|
Log.d(TAG, "Error setting camera preview: " + error.getMessage()); |
||||||
|
} catch (Exception ignore) { |
||||||
|
Log.d(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); |
||||||
|
} |
||||||
|
|
||||||
|
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, @Configuration.FlashMode int flashMode) { |
||||||
|
try { |
||||||
|
switch (flashMode) { |
||||||
|
case Configuration.FLASH_MODE_AUTO: |
||||||
|
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO); |
||||||
|
break; |
||||||
|
case Configuration.FLASH_MODE_ON: |
||||||
|
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_ON); |
||||||
|
break; |
||||||
|
case Configuration.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() == Configuration.MEDIA_QUALITY_LOW) { |
||||||
|
parameters.setJpegQuality(50); |
||||||
|
} else if (configurationProvider.getMediaQuality() == Configuration.MEDIA_QUALITY_MEDIUM) { |
||||||
|
parameters.setJpegQuality(75); |
||||||
|
} else if (configurationProvider.getMediaQuality() == Configuration.MEDIA_QUALITY_HIGH) { |
||||||
|
parameters.setJpegQuality(100); |
||||||
|
} else if (configurationProvider.getMediaQuality() == Configuration.MEDIA_QUALITY_HIGHEST) { |
||||||
|
parameters.setJpegQuality(100); |
||||||
|
} |
||||||
|
parameters.setPictureSize(photoSize.getWidth(), photoSize.getHeight()); |
||||||
|
|
||||||
|
camera.setParameters(parameters); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getPhotoOrientation(@Configuration.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(@Configuration.SensorPosition int sensorPosition) { |
||||||
|
int degrees = 0; |
||||||
|
switch (sensorPosition) { |
||||||
|
case Configuration.SENSOR_POSITION_UP: |
||||||
|
degrees = 0; |
||||||
|
break; // Natural orientation
|
||||||
|
case Configuration.SENSOR_POSITION_LEFT: |
||||||
|
degrees = 90; |
||||||
|
break; // Landscape left
|
||||||
|
case Configuration.SENSOR_POSITION_UP_SIDE_DOWN: |
||||||
|
degrees = 180; |
||||||
|
break;// Upside down
|
||||||
|
case Configuration.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); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void surfaceDestroyed(SurfaceHolder surfaceHolder) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPictureTaken(final byte[] bytes, Camera camera) { |
||||||
|
File pictureFile = outputPath; |
||||||
|
if (pictureFile == null) { |
||||||
|
Log.d(TAG, "Error creating media file, check storage permissions."); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
FileOutputStream fileOutputStream = new FileOutputStream(pictureFile); |
||||||
|
fileOutputStream.write(bytes); |
||||||
|
fileOutputStream.close(); |
||||||
|
} catch (FileNotFoundException error) { |
||||||
|
Log.e(TAG, "File not found: " + error.getMessage()); |
||||||
|
} catch (IOException error) { |
||||||
|
Log.e(TAG, "Error accessing file: " + error.getMessage()); |
||||||
|
} catch (Throwable error) { |
||||||
|
Log.e(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(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
photoListener.onPhotoTaken(bytes, outputPath); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} catch (Throwable error) { |
||||||
|
Log.e(TAG, "Can't save exif info: " + error.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getVideoQualityOptions() { |
||||||
|
List<CharSequence> videoQualities = new ArrayList<>(); |
||||||
|
|
||||||
|
if (configurationProvider.getMinimumVideoDuration() > 0) |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_AUTO, CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_AUTO, getCurrentCameraId()), configurationProvider.getMinimumVideoDuration())); |
||||||
|
|
||||||
|
CamcorderProfile camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_HIGH, getCurrentCameraId()); |
||||||
|
double videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, configurationProvider.getVideoFileSize()); |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_HIGH, camcorderProfile, videoDuration)); |
||||||
|
|
||||||
|
camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_MEDIUM, getCurrentCameraId()); |
||||||
|
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, configurationProvider.getVideoFileSize()); |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_MEDIUM, camcorderProfile, videoDuration)); |
||||||
|
|
||||||
|
camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_LOW, getCurrentCameraId()); |
||||||
|
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, configurationProvider.getVideoFileSize()); |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_LOW, camcorderProfile, videoDuration)); |
||||||
|
|
||||||
|
CharSequence[] array = new CharSequence[videoQualities.size()]; |
||||||
|
videoQualities.toArray(array); |
||||||
|
|
||||||
|
return array; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getPhotoQualityOptions() { |
||||||
|
List<CharSequence> photoQualities = new ArrayList<>(); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_HIGHEST, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGHEST))); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_HIGH, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGH))); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_MEDIUM, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_MEDIUM))); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_LOWEST, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_LOWEST))); |
||||||
|
|
||||||
|
CharSequence[] array = new CharSequence[photoQualities.size()]; |
||||||
|
photoQualities.toArray(array); |
||||||
|
|
||||||
|
return array; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,844 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager.impl; |
||||||
|
|
||||||
|
import android.annotation.TargetApi; |
||||||
|
import android.content.Context; |
||||||
|
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.CamcorderProfile; |
||||||
|
import android.media.ImageReader; |
||||||
|
import android.media.MediaRecorder; |
||||||
|
import android.os.Build; |
||||||
|
import android.support.annotation.IntDef; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
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 com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraCloseListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraOpenListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraPhotoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.manager.listener.CameraVideoListener; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.model.PhotoQualityOption; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.model.VideoQualityOption; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.CameraHelper; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.ImageSaver; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/9/16. |
||||||
|
*/ |
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public final class Camera2Manager extends BaseCameraManager<String, TextureView.SurfaceTextureListener> |
||||||
|
implements ImageReader.OnImageAvailableListener, TextureView.SurfaceTextureListener { |
||||||
|
|
||||||
|
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(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraOpenListener.onCameraOpenError(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError(CameraDevice cameraDevice, int error) { |
||||||
|
cameraDevice.close(); |
||||||
|
currentInstance.cameraDevice = null; |
||||||
|
|
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
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) { |
||||||
|
Log.e(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(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (context == null || configurationProvider == null) { |
||||||
|
Log.e(TAG, "openCamera: "); |
||||||
|
if (cameraOpenListener != null) { |
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraOpenListener.onCameraOpenError(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
prepareCameraOutputs(); |
||||||
|
try { |
||||||
|
manager.openCamera(currentCameraId, stateCallback, backgroundHandler); |
||||||
|
} catch (Exception e) { |
||||||
|
Log.e(TAG, "openCamera: ", e); |
||||||
|
if (cameraOpenListener != null) { |
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraOpenListener.onCameraOpenError(); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void closeCamera(final CameraCloseListener<String> cameraCloseListener) { |
||||||
|
backgroundHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
closeCamera(); |
||||||
|
if (cameraCloseListener != null) { |
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraCloseListener.onCameraClosed(currentCameraId); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setFlashMode(@Configuration.FlashMode int flashMode) { |
||||||
|
setFlashModeAndBuildPreviewRequest(flashMode); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void takePhoto(File photoFile, CameraPhotoListener cameraPhotoListener) { |
||||||
|
this.outputPath = photoFile; |
||||||
|
this.cameraPhotoListener = cameraPhotoListener; |
||||||
|
|
||||||
|
backgroundHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
lockFocus(); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public Size getPhotoSizeForQuality(@Configuration.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) { |
||||||
|
Log.e(TAG, "videoRecorder.start(): ", ignore); |
||||||
|
} |
||||||
|
|
||||||
|
isVideoRecording = true; |
||||||
|
|
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraVideoListener.onVideoRecordStarted(videoSize); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) { |
||||||
|
Log.d(TAG, "onConfigureFailed"); |
||||||
|
} |
||||||
|
}, backgroundHandler); |
||||||
|
} catch (Exception e) { |
||||||
|
Log.e(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) { |
||||||
|
Log.d(TAG, "Fail while starting preview: "); |
||||||
|
} |
||||||
|
}, null); |
||||||
|
} catch (Exception e) { |
||||||
|
Log.e(TAG, "Error while preparing surface for preview: ", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onMaxDurationReached() { |
||||||
|
stopVideoRecord(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onMaxFileSizeReached() { |
||||||
|
stopVideoRecord(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getPhotoOrientation(@Configuration.SensorPosition int sensorPosition) { |
||||||
|
return getVideoOrientation(sensorPosition); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected int getVideoOrientation(@Configuration.SensorPosition int sensorPosition) { |
||||||
|
int degrees = 0; |
||||||
|
switch (sensorPosition) { |
||||||
|
case Configuration.SENSOR_POSITION_UP: |
||||||
|
degrees = 0; |
||||||
|
break; // Natural orientation
|
||||||
|
case Configuration.SENSOR_POSITION_LEFT: |
||||||
|
degrees = 90; |
||||||
|
break; // Landscape left
|
||||||
|
case Configuration.SENSOR_POSITION_UP_SIDE_DOWN: |
||||||
|
degrees = 180; |
||||||
|
break;// Upside down
|
||||||
|
case Configuration.SENSOR_POSITION_RIGHT: |
||||||
|
degrees = 270; |
||||||
|
break;// Landscape right
|
||||||
|
case Configuration.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() == Configuration.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() == Configuration.MEDIA_QUALITY_AUTO |
||||||
|
? Configuration.MEDIA_QUALITY_HIGHEST : configurationProvider.getMediaQuality()); |
||||||
|
|
||||||
|
imageReader = ImageReader.newInstance(photoSize.getWidth(), photoSize.getHeight(), |
||||||
|
ImageFormat.JPEG, 2); |
||||||
|
imageReader.setOnImageAvailableListener(this, backgroundHandler); |
||||||
|
|
||||||
|
if (configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_PHOTO |
||||||
|
|| configurationProvider.getMediaAction() == Configuration.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) { |
||||||
|
Log.e(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) { |
||||||
|
Log.e(TAG, "IllegalStateException preparing MediaRecorder: " + error.getMessage()); |
||||||
|
} catch (IOException error) { |
||||||
|
Log.e(TAG, "IOException preparing MediaRecorder: " + error.getMessage()); |
||||||
|
} catch (Throwable error) { |
||||||
|
Log.e(TAG, "Error during preparing MediaRecorder: " + error.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
releaseVideoRecorder(); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
private void updatePreview(CameraCaptureSession cameraCaptureSession) { |
||||||
|
if (null == cameraDevice) { |
||||||
|
return; |
||||||
|
} |
||||||
|
captureSession = cameraCaptureSession; |
||||||
|
|
||||||
|
setFlashModeAndBuildPreviewRequest(configurationProvider.getFlashMode()); |
||||||
|
} |
||||||
|
|
||||||
|
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) { |
||||||
|
Log.d(TAG, "onCaptureCompleted: "); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
captureSession.stopRepeating(); |
||||||
|
captureSession.capture(captureBuilder.build(), CaptureCallback, null); |
||||||
|
|
||||||
|
} catch (CameraAccessException e) { |
||||||
|
Log.e(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) { |
||||||
|
Log.e(TAG, "Error during focus unlocking"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void setFlashModeAndBuildPreviewRequest(@Configuration.FlashMode int flashMode) { |
||||||
|
try { |
||||||
|
|
||||||
|
switch (flashMode) { |
||||||
|
case Configuration.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 Configuration.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 Configuration.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) { |
||||||
|
Log.e(TAG, "Error updating preview: ", e); |
||||||
|
} |
||||||
|
} catch (Exception ignore) { |
||||||
|
Log.e(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(final byte[] bytes) { |
||||||
|
Log.d(TAG, "onPhotoSuccessFinish: "); |
||||||
|
if (cameraPhotoListener != null) { |
||||||
|
uiHandler.post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
cameraPhotoListener.onPhotoTaken(bytes, outputPath); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
unlockFocus(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onError() { |
||||||
|
Log.d(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) { |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getVideoQualityOptions() { |
||||||
|
List<CharSequence> videoQualities = new ArrayList<>(); |
||||||
|
|
||||||
|
if (configurationProvider.getMinimumVideoDuration() > 0) |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_AUTO, CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_AUTO, getCurrentCameraId()), configurationProvider.getMinimumVideoDuration())); |
||||||
|
|
||||||
|
|
||||||
|
CamcorderProfile camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_HIGH, currentCameraId); |
||||||
|
double videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, configurationProvider.getVideoFileSize()); |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_HIGH, camcorderProfile, videoDuration)); |
||||||
|
|
||||||
|
camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_MEDIUM, currentCameraId); |
||||||
|
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, configurationProvider.getVideoFileSize()); |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_MEDIUM, camcorderProfile, videoDuration)); |
||||||
|
|
||||||
|
camcorderProfile = CameraHelper.getCamcorderProfile(Configuration.MEDIA_QUALITY_LOW, currentCameraId); |
||||||
|
videoDuration = CameraHelper.calculateApproximateVideoDuration(camcorderProfile, configurationProvider.getVideoFileSize()); |
||||||
|
videoQualities.add(new VideoQualityOption(Configuration.MEDIA_QUALITY_LOW, camcorderProfile, videoDuration)); |
||||||
|
|
||||||
|
CharSequence[] array = new CharSequence[videoQualities.size()]; |
||||||
|
videoQualities.toArray(array); |
||||||
|
|
||||||
|
return array; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public CharSequence[] getPhotoQualityOptions() { |
||||||
|
List<CharSequence> photoQualities = new ArrayList<>(); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_HIGHEST, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGHEST))); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_HIGH, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_HIGH))); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_MEDIUM, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_MEDIUM))); |
||||||
|
photoQualities.add(new PhotoQualityOption(Configuration.MEDIA_QUALITY_LOWEST, getPhotoSizeForQuality(Configuration.MEDIA_QUALITY_LOWEST))); |
||||||
|
|
||||||
|
CharSequence[] array = new CharSequence[photoQualities.size()]; |
||||||
|
photoQualities.toArray(array); |
||||||
|
|
||||||
|
return array; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,8 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager.listener; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/14/16. |
||||||
|
*/ |
||||||
|
public interface CameraCloseListener<CameraId> { |
||||||
|
void onCameraClosed(CameraId closedCameraId); |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager.listener; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/14/16. |
||||||
|
*/ |
||||||
|
public interface CameraOpenListener<CameraId, SurfaceListener> { |
||||||
|
void onCameraOpened(CameraId openedCameraId, Size previewSize, SurfaceListener surfaceListener); |
||||||
|
|
||||||
|
void onCameraOpenError(); |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager.listener; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 8/14/16. |
||||||
|
*/ |
||||||
|
public interface CameraPhotoListener { |
||||||
|
void onPhotoTaken(byte[] bytes, File photoFile); |
||||||
|
|
||||||
|
void onPhotoTakeError(); |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.manager.listener; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.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,64 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.timer; |
||||||
|
|
||||||
|
import android.graphics.Color; |
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.internal.utils.DateTimeUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class CountdownTask extends TimerTaskBase implements Runnable { |
||||||
|
|
||||||
|
private int maxDurationMilliseconds = 0; |
||||||
|
|
||||||
|
public CountdownTask(Callback callback, int maxDurationMilliseconds) { |
||||||
|
super(callback); |
||||||
|
this.maxDurationMilliseconds = maxDurationMilliseconds; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
|
||||||
|
recordingTimeSeconds--; |
||||||
|
|
||||||
|
int millis = (int) recordingTimeSeconds * 1000; |
||||||
|
|
||||||
|
if (callback != null) { |
||||||
|
callback.setText( |
||||||
|
String.format("%02d:%02d", |
||||||
|
TimeUnit.MILLISECONDS.toMinutes(millis), |
||||||
|
TimeUnit.MILLISECONDS.toSeconds(millis) - |
||||||
|
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millis)) |
||||||
|
)); |
||||||
|
} |
||||||
|
|
||||||
|
if (alive && recordingTimeSeconds > 0) handler.postDelayed(this, DateTimeUtils.SECOND); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void stop() { |
||||||
|
if (callback != null) { |
||||||
|
callback.setTextVisible(false); |
||||||
|
} |
||||||
|
alive = false; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void start() { |
||||||
|
alive = true; |
||||||
|
recordingTimeSeconds = maxDurationMilliseconds / 1000; |
||||||
|
if (callback != null) { |
||||||
|
callback.setText( |
||||||
|
String.format("%02d:%02d", |
||||||
|
TimeUnit.MILLISECONDS.toMinutes(maxDurationMilliseconds), |
||||||
|
TimeUnit.MILLISECONDS.toSeconds(maxDurationMilliseconds) - |
||||||
|
TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(maxDurationMilliseconds)) |
||||||
|
)); |
||||||
|
callback.setTextVisible(true); |
||||||
|
} |
||||||
|
handler.postDelayed(this, DateTimeUtils.SECOND); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,48 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.timer; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.internal.utils.DateTimeUtils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class TimerTask extends TimerTaskBase implements Runnable { |
||||||
|
|
||||||
|
public TimerTask(TimerTaskBase.Callback callback) { |
||||||
|
super(callback); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
recordingTimeSeconds++; |
||||||
|
|
||||||
|
if (recordingTimeSeconds == 60) { |
||||||
|
recordingTimeSeconds = 0; |
||||||
|
recordingTimeMinutes++; |
||||||
|
} |
||||||
|
if(callback != null) { |
||||||
|
callback.setText( |
||||||
|
String.format("%02d:%02d", recordingTimeMinutes, recordingTimeSeconds)); |
||||||
|
} |
||||||
|
if (alive) handler.postDelayed(this, DateTimeUtils.SECOND); |
||||||
|
} |
||||||
|
|
||||||
|
public void start() { |
||||||
|
alive = true; |
||||||
|
recordingTimeMinutes = 0; |
||||||
|
recordingTimeSeconds = 0; |
||||||
|
if(callback != null) { |
||||||
|
callback.setText( |
||||||
|
String.format("%02d:%02d", recordingTimeMinutes, recordingTimeSeconds)); |
||||||
|
callback.setTextVisible(true); |
||||||
|
} |
||||||
|
handler.postDelayed(this, DateTimeUtils.SECOND); |
||||||
|
} |
||||||
|
|
||||||
|
public void stop() { |
||||||
|
if(callback != null){ |
||||||
|
callback.setTextVisible(false); |
||||||
|
} |
||||||
|
alive = false; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,31 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.timer; |
||||||
|
|
||||||
|
import android.os.Handler; |
||||||
|
import android.os.Looper; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public abstract class TimerTaskBase { |
||||||
|
|
||||||
|
public interface Callback { |
||||||
|
void setText(String text); |
||||||
|
void setTextVisible(boolean visible); |
||||||
|
} |
||||||
|
|
||||||
|
protected Handler handler = new Handler(Looper.getMainLooper()); |
||||||
|
protected boolean alive = false; |
||||||
|
protected long recordingTimeSeconds = 0; |
||||||
|
protected long recordingTimeMinutes = 0; |
||||||
|
|
||||||
|
protected Callback callback; |
||||||
|
|
||||||
|
protected TimerTaskBase(Callback callback) { |
||||||
|
this.callback = callback; |
||||||
|
} |
||||||
|
|
||||||
|
public abstract void stop(); |
||||||
|
|
||||||
|
public abstract void start(); |
||||||
|
} |
@ -0,0 +1,703 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.ui; |
||||||
|
|
||||||
|
import android.app.Activity; |
||||||
|
import android.app.AlertDialog; |
||||||
|
import android.content.Context; |
||||||
|
import android.content.DialogInterface; |
||||||
|
import android.hardware.Sensor; |
||||||
|
import android.hardware.SensorEvent; |
||||||
|
import android.hardware.SensorEventListener; |
||||||
|
import android.hardware.SensorManager; |
||||||
|
import android.media.MediaActionSound; |
||||||
|
import android.os.Build; |
||||||
|
import android.os.Bundle; |
||||||
|
import android.os.FileObserver; |
||||||
|
import android.os.Handler; |
||||||
|
import android.os.Looper; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v4.app.Fragment; |
||||||
|
import android.util.Log; |
||||||
|
import android.view.LayoutInflater; |
||||||
|
import android.view.View; |
||||||
|
import android.view.ViewGroup; |
||||||
|
import android.view.WindowManager; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.R; |
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProvider; |
||||||
|
import com.github.florent37.camerafragment.configuration.ConfigurationProviderImpl; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.CameraController; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.impl.Camera1Controller; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.impl.Camera2Controller; |
||||||
|
import com.github.florent37.camerafragment.internal.controller.view.CameraView; |
||||||
|
import com.github.florent37.camerafragment.internal.enums.Camera; |
||||||
|
import com.github.florent37.camerafragment.internal.enums.Flash; |
||||||
|
import com.github.florent37.camerafragment.internal.enums.MediaAction; |
||||||
|
import com.github.florent37.camerafragment.internal.enums.Record; |
||||||
|
import com.github.florent37.camerafragment.internal.timer.CountdownTask; |
||||||
|
import com.github.florent37.camerafragment.internal.timer.TimerTask; |
||||||
|
import com.github.florent37.camerafragment.internal.timer.TimerTaskBase; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.model.PhotoQualityOption; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.model.VideoQualityOption; |
||||||
|
import com.github.florent37.camerafragment.internal.ui.view.AspectFrameLayout; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.CameraHelper; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Utils; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentControlsListener; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentResultListener; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentStateListener; |
||||||
|
import com.github.florent37.camerafragment.listeners.CameraFragmentVideoRecordTextListener; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 12/1/16. |
||||||
|
* Updated by Florent37 |
||||||
|
*/ |
||||||
|
|
||||||
|
public abstract class BaseAnncaFragment<CameraId> extends Fragment { |
||||||
|
|
||||||
|
public static final String ARG_CONFIGURATION = "configuration"; |
||||||
|
public static final int MIN_VERSION_ICECREAM = Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1; |
||||||
|
|
||||||
|
protected CharSequence[] videoQualities; |
||||||
|
protected CharSequence[] photoQualities; |
||||||
|
protected AspectFrameLayout previewContainer; |
||||||
|
|
||||||
|
protected ConfigurationProvider configurationProvider; |
||||||
|
@Configuration.MediaQuality |
||||||
|
protected int newQuality = -1; |
||||||
|
private Configuration configuration; |
||||||
|
private SensorManager sensorManager = null; |
||||||
|
private CameraController cameraController; |
||||||
|
private AlertDialog settingsDialog; |
||||||
|
private CameraFragmentControlsListener cameraFragmentControlsListener; |
||||||
|
private CameraFragmentVideoRecordTextListener cameraFragmentVideoRecordTextListener; |
||||||
|
private CameraFragmentStateListener cameraFragmentStateListener; |
||||||
|
@Flash.FlashMode |
||||||
|
private int currentFlashMode = Flash.FLASH_AUTO; |
||||||
|
|
||||||
|
@Camera.CameraType |
||||||
|
private int currentCameraType = Camera.CAMERA_TYPE_REAR; |
||||||
|
|
||||||
|
@MediaAction.MediaActionState |
||||||
|
private int currentMediaActionState = MediaAction.ACTION_PHOTO; |
||||||
|
|
||||||
|
@Record.RecordState |
||||||
|
private int currentRecordState = Record.TAKE_PHOTO_STATE; |
||||||
|
private String mediaFilePath; |
||||||
|
private FileObserver fileObserver; |
||||||
|
private long maxVideoFileSize = 0; |
||||||
|
private TimerTaskBase countDownTimer; |
||||||
|
private SensorEventListener sensorEventListener = new SensorEventListener() { |
||||||
|
@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
|
||||||
|
configurationProvider.setSensorPosition(Configuration.SENSOR_POSITION_UP); |
||||||
|
configurationProvider.setDegrees(configurationProvider.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT ? 0 : 90); |
||||||
|
} else if (sensorEvent.values[1] < 0) { |
||||||
|
// UP SIDE DOWN
|
||||||
|
configurationProvider.setSensorPosition(Configuration.SENSOR_POSITION_UP_SIDE_DOWN); |
||||||
|
configurationProvider.setDegrees(configurationProvider.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT ? 180 : 270); |
||||||
|
} |
||||||
|
} else if (sensorEvent.values[1] < 4 && sensorEvent.values[1] > -4) { |
||||||
|
if (sensorEvent.values[0] > 0) { |
||||||
|
// LEFT
|
||||||
|
configurationProvider.setSensorPosition(Configuration.SENSOR_POSITION_LEFT); |
||||||
|
configurationProvider.setDegrees(configurationProvider.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT ? 90 : 180); |
||||||
|
} else if (sensorEvent.values[0] < 0) { |
||||||
|
// RIGHT
|
||||||
|
configurationProvider.setSensorPosition(Configuration.SENSOR_POSITION_RIGHT); |
||||||
|
configurationProvider.setDegrees(configurationProvider.getDeviceDefaultOrientation() == Configuration.ORIENTATION_PORTRAIT ? 270 : 0); |
||||||
|
} |
||||||
|
} |
||||||
|
onScreenRotation(configurationProvider.getDegrees()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onAccuracyChanged(Sensor sensor, int i) { |
||||||
|
|
||||||
|
} |
||||||
|
}; |
||||||
|
private CameraFragmentResultListener cameraFragmentResultListener; |
||||||
|
|
||||||
|
protected static Fragment newInstance(Fragment fragment, Configuration configuration) { |
||||||
|
Bundle args = new Bundle(); |
||||||
|
args.putSerializable(ARG_CONFIGURATION, configuration); |
||||||
|
fragment.setArguments(args); |
||||||
|
return fragment; |
||||||
|
} |
||||||
|
|
||||||
|
@Nullable |
||||||
|
@Override |
||||||
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { |
||||||
|
View decorView = ((Activity) container.getContext()).getWindow().getDecorView(); |
||||||
|
if (Build.VERSION.SDK_INT > MIN_VERSION_ICECREAM) { |
||||||
|
int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; |
||||||
|
decorView.setSystemUiVisibility(uiOptions); |
||||||
|
} |
||||||
|
|
||||||
|
return inflater.inflate(R.layout.generic_camera_layout, container, false); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onCreate(@Nullable Bundle savedInstanceState) { |
||||||
|
super.onCreate(savedInstanceState); |
||||||
|
final Bundle arguments = getArguments(); |
||||||
|
if (arguments != null) { |
||||||
|
configuration = (Configuration) arguments.getSerializable(ARG_CONFIGURATION); |
||||||
|
} |
||||||
|
this.configurationProvider = new ConfigurationProviderImpl(); |
||||||
|
this.configurationProvider.setupWithAnnaConfiguration(configuration); |
||||||
|
|
||||||
|
//onProcessBundle
|
||||||
|
currentMediaActionState = configurationProvider.getMediaAction() == Configuration.MEDIA_ACTION_VIDEO ? |
||||||
|
MediaAction.ACTION_VIDEO : MediaAction.ACTION_PHOTO; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { |
||||||
|
super.onViewCreated(view, savedInstanceState); |
||||||
|
previewContainer = (AspectFrameLayout) view.findViewById(R.id.previewContainer); |
||||||
|
|
||||||
|
final CameraView cameraView = new CameraView() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateCameraPreview(Size size, View cameraPreview) { |
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.unLockControls(); |
||||||
|
cameraFragmentControlsListener.allowRecord(true); |
||||||
|
} |
||||||
|
|
||||||
|
setCameraPreview(cameraPreview, size); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateUiForMediaAction(@Configuration.MediaAction int mediaAction) { |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void updateCameraSwitcher(int numberOfCameras) { |
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.allowCameraSwitching(numberOfCameras > 1); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPhotoTaken(byte[] bytes) { |
||||||
|
if (cameraFragmentResultListener != null) { |
||||||
|
final String filePath = getCameraController().getOutputFile().toString(); |
||||||
|
cameraFragmentResultListener.onPhotoTaken(bytes, filePath); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onVideoRecordStart(int width, int height) { |
||||||
|
final File outputFile = getCameraController().getOutputFile(); |
||||||
|
onStartVideoRecord(outputFile); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onVideoRecordStop() { |
||||||
|
BaseAnncaFragment.this.onStopVideoRecord(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void releaseCameraPreview() { |
||||||
|
clearCameraPreview(); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
if (CameraHelper.hasCamera2(getContext())) { |
||||||
|
cameraController = new Camera2Controller(getActivity(), cameraView, configurationProvider); |
||||||
|
} else { |
||||||
|
cameraController = new Camera1Controller(getActivity(), cameraView, configurationProvider); |
||||||
|
} |
||||||
|
cameraController.onCreate(savedInstanceState); |
||||||
|
|
||||||
|
sensorManager = (SensorManager) getContext().getSystemService(Activity.SENSOR_SERVICE); |
||||||
|
final int defaultOrientation = Utils.getDeviceDefaultOrientation(getContext()); |
||||||
|
switch (defaultOrientation) { |
||||||
|
case android.content.res.Configuration.ORIENTATION_LANDSCAPE: |
||||||
|
configurationProvider.setDeviceDefaultOrientation(Configuration.ORIENTATION_LANDSCAPE); |
||||||
|
break; |
||||||
|
default: |
||||||
|
configurationProvider.setDeviceDefaultOrientation(Configuration.ORIENTATION_PORTRAIT); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
switch (configurationProvider.getFlashMode()) { |
||||||
|
case Configuration.FLASH_MODE_AUTO: |
||||||
|
setFlashMode(Flash.FLASH_AUTO); |
||||||
|
break; |
||||||
|
case Configuration.FLASH_MODE_ON: |
||||||
|
setFlashMode(Flash.FLASH_ON); |
||||||
|
break; |
||||||
|
case Configuration.FLASH_MODE_OFF: |
||||||
|
setFlashMode(Flash.FLASH_OFF); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
setMaxVideoDuration(configurationProvider.getVideoDuration()); |
||||||
|
setMaxVideoFileSize(configurationProvider.getVideoFileSize()); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
public void takePhotoOrCaptureVideo() { |
||||||
|
switch (currentMediaActionState) { |
||||||
|
case MediaAction.ACTION_PHOTO: |
||||||
|
takePhoto(); |
||||||
|
break; |
||||||
|
case MediaAction.ACTION_VIDEO: |
||||||
|
switch (currentRecordState) { |
||||||
|
case Record.RECORD_IN_PROGRESS_STATE: |
||||||
|
stopRecording(); |
||||||
|
break; |
||||||
|
default: |
||||||
|
startRecording(); |
||||||
|
break; |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onResume() { |
||||||
|
super.onResume(); |
||||||
|
|
||||||
|
cameraController.onResume(); |
||||||
|
sensorManager.registerListener(sensorEventListener, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL); |
||||||
|
|
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.lockControls(); |
||||||
|
cameraFragmentControlsListener.allowRecord(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onPause() { |
||||||
|
super.onPause(); |
||||||
|
|
||||||
|
cameraController.onPause(); |
||||||
|
sensorManager.unregisterListener(sensorEventListener); |
||||||
|
|
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.lockControls(); |
||||||
|
cameraFragmentControlsListener.allowRecord(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onDestroy() { |
||||||
|
super.onDestroy(); |
||||||
|
|
||||||
|
cameraController.onDestroy(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setMaxVideoFileSize(long maxVideoFileSize) { |
||||||
|
this.maxVideoFileSize = maxVideoFileSize; |
||||||
|
} |
||||||
|
|
||||||
|
public void setMaxVideoDuration(int maxVideoDurationInMillis) { |
||||||
|
final TimerTaskBase.Callback callback = new TimerTaskBase.Callback() { |
||||||
|
@Override |
||||||
|
public void setText(String text) { |
||||||
|
if (cameraFragmentVideoRecordTextListener != null) { |
||||||
|
cameraFragmentVideoRecordTextListener.setRecordDurationText(text); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setTextVisible(boolean visible) { |
||||||
|
if (cameraFragmentVideoRecordTextListener != null) { |
||||||
|
cameraFragmentVideoRecordTextListener.setRecordDurationTextVisible(visible); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
if (maxVideoDurationInMillis > 0) { |
||||||
|
this.countDownTimer = new CountdownTask(callback, maxVideoDurationInMillis); |
||||||
|
} else { |
||||||
|
this.countDownTimer = new TimerTask(callback); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void openSettingDialog() { |
||||||
|
final Context context = getContext(); |
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(context); |
||||||
|
|
||||||
|
if (currentMediaActionState == MediaAction.ACTION_VIDEO) { |
||||||
|
builder.setSingleChoiceItems(videoQualities, getVideoOptionCheckedIndex(), getVideoOptionSelectedListener()); |
||||||
|
if (configurationProvider.getVideoFileSize() > 0) |
||||||
|
builder.setTitle(String.format(getString(R.string.settings_video_quality_title), |
||||||
|
"(Max " + String.valueOf(configurationProvider.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, new DialogInterface.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(DialogInterface dialogInterface, int i) { |
||||||
|
if (newQuality > 0 && newQuality != configurationProvider.getMediaQuality()) { |
||||||
|
configurationProvider.setMediaQuality(newQuality); |
||||||
|
dialogInterface.dismiss(); |
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.lockControls(); |
||||||
|
} |
||||||
|
getCameraController().switchQuality(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
builder.setNegativeButton(R.string.cancel_label, new DialogInterface.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(DialogInterface dialogInterface, int i) { |
||||||
|
dialogInterface.dismiss(); |
||||||
|
} |
||||||
|
}); |
||||||
|
settingsDialog = builder.create(); |
||||||
|
settingsDialog.show(); |
||||||
|
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); |
||||||
|
layoutParams.copyFrom(settingsDialog.getWindow().getAttributes()); |
||||||
|
layoutParams.width = Utils.convertDipToPixels(context, 350); |
||||||
|
layoutParams.height = Utils.convertDipToPixels(context, 350); |
||||||
|
settingsDialog.getWindow().setAttributes(layoutParams); |
||||||
|
} |
||||||
|
|
||||||
|
public void switchCameraType() { |
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.lockControls(); |
||||||
|
cameraFragmentControlsListener.allowRecord(false); |
||||||
|
} |
||||||
|
|
||||||
|
int cameraFace = Configuration.CAMERA_FACE_REAR; |
||||||
|
switch (currentCameraType) { |
||||||
|
case Camera.CAMERA_TYPE_FRONT: |
||||||
|
currentCameraType = Camera.CAMERA_TYPE_REAR; |
||||||
|
cameraFace = Configuration.CAMERA_FACE_REAR; |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onCurrentCameraBack(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case Camera.CAMERA_TYPE_REAR: |
||||||
|
currentCameraType = Camera.CAMERA_TYPE_FRONT; |
||||||
|
cameraFace = Configuration.CAMERA_FACE_FRONT; |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onCurrentCameraFront(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
getCameraController().switchCamera(cameraFace); |
||||||
|
} |
||||||
|
|
||||||
|
public void switchAction() { |
||||||
|
switch (currentMediaActionState) { |
||||||
|
case MediaAction.ACTION_PHOTO: |
||||||
|
currentMediaActionState = MediaAction.ACTION_VIDEO; |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onCameraSetupForVideo(); |
||||||
|
} |
||||||
|
break; |
||||||
|
case MediaAction.ACTION_VIDEO: |
||||||
|
currentMediaActionState = MediaAction.ACTION_PHOTO; |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onCameraSetupForPhoto(); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void toggleFlashMode() { |
||||||
|
switch (currentFlashMode) { |
||||||
|
case Flash.FLASH_AUTO: |
||||||
|
currentFlashMode = Flash.FLASH_OFF; |
||||||
|
break; |
||||||
|
case Flash.FLASH_OFF: |
||||||
|
currentFlashMode = Flash.FLASH_ON; |
||||||
|
break; |
||||||
|
case Flash.FLASH_ON: |
||||||
|
currentFlashMode = Flash.FLASH_AUTO; |
||||||
|
break; |
||||||
|
} |
||||||
|
onFlashModeChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
private void onFlashModeChanged() { |
||||||
|
switch (currentFlashMode) { |
||||||
|
case Flash.FLASH_AUTO: |
||||||
|
if (cameraFragmentStateListener != null) cameraFragmentStateListener.onFlashAuto(); |
||||||
|
configurationProvider.setFlashMode(Configuration.FLASH_MODE_AUTO); |
||||||
|
getCameraController().setFlashMode(Configuration.FLASH_MODE_AUTO); |
||||||
|
break; |
||||||
|
case Flash.FLASH_ON: |
||||||
|
if (cameraFragmentStateListener != null) cameraFragmentStateListener.onFlashOn(); |
||||||
|
configurationProvider.setFlashMode(Configuration.FLASH_MODE_ON); |
||||||
|
getCameraController().setFlashMode(Configuration.FLASH_MODE_ON); |
||||||
|
break; |
||||||
|
case Flash.FLASH_OFF: |
||||||
|
if (cameraFragmentStateListener != null) cameraFragmentStateListener.onFlashOff(); |
||||||
|
configurationProvider.setFlashMode(Configuration.FLASH_MODE_OFF); |
||||||
|
getCameraController().setFlashMode(Configuration.FLASH_MODE_OFF); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected void onScreenRotation(int degrees) { |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.shouldRotateControls(degrees); |
||||||
|
} |
||||||
|
rotateSettingsDialog(degrees); |
||||||
|
} |
||||||
|
|
||||||
|
@Record.RecordState |
||||||
|
public int getRecordState() { |
||||||
|
return currentRecordState; |
||||||
|
} |
||||||
|
|
||||||
|
public void setRecordState(@Record.RecordState int recordState) { |
||||||
|
this.currentRecordState = recordState; |
||||||
|
} |
||||||
|
|
||||||
|
//@Override
|
||||||
|
//public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
|
// if (resultCode == Activity.RESULT_OK) {
|
||||||
|
// if (requestCode == REQUEST_PREVIEW_CODE) {
|
||||||
|
// final FragmentActivity activity = getActivity();
|
||||||
|
// if (activity != null) {
|
||||||
|
// if (PreviewActivity.isResultConfirm(data)) {
|
||||||
|
// Intent resultIntent = new Intent();
|
||||||
|
// resultIntent.putExtra(Configuration.Arguments.FILE_PATH,
|
||||||
|
// PreviewActivity.getMediaFilePatch(data));
|
||||||
|
// activity.setResult(Activity.RESULT_OK, resultIntent);
|
||||||
|
// activity.finish();
|
||||||
|
// } else if (PreviewActivity.isResultCancel(data)) {
|
||||||
|
// activity.setResult(Activity.RESULT_CANCELED);
|
||||||
|
// activity.finish();
|
||||||
|
// } else if (PreviewActivity.isResultRetake(data)) {
|
||||||
|
// //ignore, just proceed the camera
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public void setFlashMode(@Flash.FlashMode int mode) { |
||||||
|
this.currentFlashMode = mode; |
||||||
|
onFlashModeChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
private void rotateSettingsDialog(int degrees) { |
||||||
|
if (settingsDialog != null && settingsDialog.isShowing() && Build.VERSION.SDK_INT > 10) { |
||||||
|
ViewGroup dialogView = (ViewGroup) settingsDialog.getWindow().getDecorView(); |
||||||
|
for (int i = 0; i < dialogView.getChildCount(); i++) { |
||||||
|
dialogView.getChildAt(i).setRotation(degrees); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
protected int getVideoOptionCheckedIndex() { |
||||||
|
int checkedIndex = -1; |
||||||
|
|
||||||
|
final int mediaQuality = configurationProvider.getMediaQuality(); |
||||||
|
final int passedMediaQuality = configurationProvider.getPassedMediaQuality(); |
||||||
|
|
||||||
|
if (mediaQuality == Configuration.MEDIA_QUALITY_AUTO) checkedIndex = 0; |
||||||
|
else if (mediaQuality == Configuration.MEDIA_QUALITY_HIGH) checkedIndex = 1; |
||||||
|
else if (mediaQuality == Configuration.MEDIA_QUALITY_MEDIUM) checkedIndex = 2; |
||||||
|
else if (mediaQuality == Configuration.MEDIA_QUALITY_LOW) checkedIndex = 3; |
||||||
|
|
||||||
|
if (passedMediaQuality != Configuration.MEDIA_QUALITY_AUTO) checkedIndex--; |
||||||
|
|
||||||
|
return checkedIndex; |
||||||
|
} |
||||||
|
|
||||||
|
protected int getPhotoOptionCheckedIndex() { |
||||||
|
int checkedIndex = -1; |
||||||
|
|
||||||
|
final int mediaQuality = configurationProvider.getMediaQuality(); |
||||||
|
|
||||||
|
if (mediaQuality == Configuration.MEDIA_QUALITY_HIGHEST) checkedIndex = 0; |
||||||
|
else if (mediaQuality == Configuration.MEDIA_QUALITY_HIGH) checkedIndex = 1; |
||||||
|
else if (mediaQuality == Configuration.MEDIA_QUALITY_MEDIUM) checkedIndex = 2; |
||||||
|
else if (mediaQuality == Configuration.MEDIA_QUALITY_LOWEST) checkedIndex = 3; |
||||||
|
return checkedIndex; |
||||||
|
} |
||||||
|
|
||||||
|
protected DialogInterface.OnClickListener getVideoOptionSelectedListener() { |
||||||
|
return new DialogInterface.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(DialogInterface dialogInterface, int index) { |
||||||
|
newQuality = ((VideoQualityOption) videoQualities[index]).getMediaQuality(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
protected DialogInterface.OnClickListener getPhotoOptionSelectedListener() { |
||||||
|
return new DialogInterface.OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(DialogInterface dialogInterface, int index) { |
||||||
|
newQuality = ((PhotoQualityOption) photoQualities[index]).getMediaQuality(); |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
public void takePhoto() { |
||||||
|
if (Build.VERSION.SDK_INT > MIN_VERSION_ICECREAM) { |
||||||
|
new MediaActionSound().play(MediaActionSound.SHUTTER_CLICK); |
||||||
|
} |
||||||
|
setRecordState(Record.TAKE_PHOTO_STATE); |
||||||
|
getCameraController().takePhoto(); |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onRecordStatePhoto(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void startRecording() { |
||||||
|
if (Build.VERSION.SDK_INT > MIN_VERSION_ICECREAM) { |
||||||
|
new MediaActionSound().play(MediaActionSound.START_VIDEO_RECORDING); |
||||||
|
} |
||||||
|
|
||||||
|
setRecordState(Record.RECORD_IN_PROGRESS_STATE); |
||||||
|
getCameraController().startVideoRecord(); |
||||||
|
|
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onRecordStateVideoInProgress(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void stopRecording() { |
||||||
|
if (Build.VERSION.SDK_INT > MIN_VERSION_ICECREAM) { |
||||||
|
new MediaActionSound().play(MediaActionSound.STOP_VIDEO_RECORDING); |
||||||
|
} |
||||||
|
|
||||||
|
setRecordState(Record.READY_FOR_RECORD_STATE); |
||||||
|
getCameraController().stopVideoRecord(); |
||||||
|
|
||||||
|
this.onStopVideoRecord(); |
||||||
|
|
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onRecordStateVideoReadyForRecord(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public CameraController<CameraId> getCameraController() { |
||||||
|
return cameraController; |
||||||
|
} |
||||||
|
|
||||||
|
public void clearCameraPreview() { |
||||||
|
if (previewContainer != null) |
||||||
|
previewContainer.removeAllViews(); |
||||||
|
} |
||||||
|
|
||||||
|
public void setCameraPreview(View preview, Size previewSize) { |
||||||
|
//onCameraControllerReady()
|
||||||
|
videoQualities = cameraController.getVideoQualityOptions(); |
||||||
|
photoQualities = cameraController.getPhotoQualityOptions(); |
||||||
|
|
||||||
|
if (previewContainer == null || preview == null) return; |
||||||
|
previewContainer.removeAllViews(); |
||||||
|
previewContainer.addView(preview); |
||||||
|
|
||||||
|
previewContainer.setAspectRatio(previewSize.getHeight() / (double) previewSize.getWidth()); |
||||||
|
} |
||||||
|
|
||||||
|
public void setMediaFilePath(final File mediaFile) { |
||||||
|
this.mediaFilePath = mediaFile.toString(); |
||||||
|
} |
||||||
|
|
||||||
|
public void onStartVideoRecord(final File mediaFile) { |
||||||
|
setMediaFilePath(mediaFile); |
||||||
|
if (maxVideoFileSize > 0) { |
||||||
|
|
||||||
|
if (cameraFragmentVideoRecordTextListener != null) { |
||||||
|
cameraFragmentVideoRecordTextListener.setRecordSizeText(maxVideoFileSize, "1Mb" + " / " + maxVideoFileSize / (1024 * 1024) + "Mb"); |
||||||
|
cameraFragmentVideoRecordTextListener.setRecordSizeTextVisible(true); |
||||||
|
} |
||||||
|
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; |
||||||
|
new Handler(Looper.getMainLooper()).post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
if (cameraFragmentVideoRecordTextListener != null) { |
||||||
|
cameraFragmentVideoRecordTextListener.setRecordSizeText(maxVideoFileSize, fileSize + "Mb" + " / " + maxVideoFileSize / (1024 * 1024) + "Mb"); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
fileObserver.startWatching(); |
||||||
|
} catch (Exception e) { |
||||||
|
Log.e("FileObserver", "setMediaFilePath: ", e); |
||||||
|
} |
||||||
|
} |
||||||
|
countDownTimer.start(); |
||||||
|
|
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onStartVideoRecord(mediaFile); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void onStopVideoRecord() { |
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
cameraFragmentControlsListener.allowRecord(false); |
||||||
|
} |
||||||
|
if (cameraFragmentStateListener != null) { |
||||||
|
cameraFragmentStateListener.onStopVideoRecord(); |
||||||
|
} |
||||||
|
setRecordState(Record.READY_FOR_RECORD_STATE); |
||||||
|
|
||||||
|
if (fileObserver != null) |
||||||
|
fileObserver.stopWatching(); |
||||||
|
countDownTimer.stop(); |
||||||
|
|
||||||
|
final int mediaAction = configurationProvider.getMediaAction(); |
||||||
|
if (cameraFragmentControlsListener != null) { |
||||||
|
if (mediaAction != Configuration.MEDIA_ACTION_UNSPECIFIED) { |
||||||
|
cameraFragmentControlsListener.setMediaActionSwitchVisible(false); |
||||||
|
} else { |
||||||
|
cameraFragmentControlsListener.setMediaActionSwitchVisible(true); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (cameraFragmentResultListener != null) { |
||||||
|
final String filePath = getCameraController().getOutputFile().toString(); |
||||||
|
cameraFragmentResultListener.onVideoRecorded(filePath); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setStateListener(CameraFragmentStateListener cameraFragmentStateListener) { |
||||||
|
this.cameraFragmentStateListener = cameraFragmentStateListener; |
||||||
|
} |
||||||
|
|
||||||
|
public void setTextListener(CameraFragmentVideoRecordTextListener cameraFragmentVideoRecordTextListener) { |
||||||
|
this.cameraFragmentVideoRecordTextListener = cameraFragmentVideoRecordTextListener; |
||||||
|
} |
||||||
|
|
||||||
|
public void setControlsListener(CameraFragmentControlsListener cameraFragmentControlsListener) { |
||||||
|
this.cameraFragmentControlsListener = cameraFragmentControlsListener; |
||||||
|
} |
||||||
|
|
||||||
|
public void setResultListener(CameraFragmentResultListener cameraFragmentResultListener) { |
||||||
|
this.cameraFragmentResultListener = cameraFragmentResultListener; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.ui.model; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Size; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 12/1/16. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class PhotoQualityOption implements CharSequence { |
||||||
|
|
||||||
|
@Configuration.MediaQuality |
||||||
|
private int mediaQuality; |
||||||
|
private String title; |
||||||
|
|
||||||
|
public PhotoQualityOption(@Configuration.MediaQuality int mediaQuality, Size size) { |
||||||
|
this.mediaQuality = mediaQuality; |
||||||
|
|
||||||
|
title = String.valueOf(size.getWidth()) + " x " + String.valueOf(size.getHeight()); |
||||||
|
} |
||||||
|
|
||||||
|
@Configuration.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 com.github.florent37.camerafragment.internal.ui.model; |
||||||
|
|
||||||
|
import android.media.CamcorderProfile; |
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 12/1/16. |
||||||
|
*/ |
||||||
|
|
||||||
|
public class VideoQualityOption implements CharSequence { |
||||||
|
|
||||||
|
private String title; |
||||||
|
|
||||||
|
@Configuration.MediaQuality |
||||||
|
private int mediaQuality; |
||||||
|
|
||||||
|
public VideoQualityOption(@Configuration.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 == Configuration.MEDIA_QUALITY_AUTO) { |
||||||
|
title = "Auto " + ", (" + (minutes > 10 ? minutes : ("0" + minutes)) + ":" + (seconds > 10 ? seconds : ("0" + seconds)) + " min)"; |
||||||
|
} else { |
||||||
|
title = String.valueOf(camcorderProfile.videoFrameWidth) |
||||||
|
+ " x " + String.valueOf(camcorderProfile.videoFrameHeight) |
||||||
|
+ (videoDuration <= 0 ? "" : ", (" + (minutes > 10 ? minutes : ("0" + minutes)) + ":" + (seconds > 10 ? seconds : ("0" + seconds)) + " min)"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Configuration.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,67 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.ui.view; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.widget.FrameLayout; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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
|
||||||
|
|
||||||
|
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 (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); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,64 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.ui.view; |
||||||
|
|
||||||
|
import android.annotation.SuppressLint; |
||||||
|
import android.content.Context; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
import android.view.SurfaceHolder; |
||||||
|
import android.view.SurfaceView; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
@SuppressLint("ViewConstructor") |
||||||
|
public class AutoFitSurfaceView extends SurfaceView { |
||||||
|
|
||||||
|
private final static String TAG = "AutoFitSurfaceView"; |
||||||
|
|
||||||
|
private final SurfaceHolder surfaceHolder; |
||||||
|
|
||||||
|
private int ratioWidth; |
||||||
|
private int ratioHeight; |
||||||
|
|
||||||
|
public AutoFitSurfaceView(@NonNull Context context, SurfaceHolder.Callback callback) { |
||||||
|
super(context); |
||||||
|
|
||||||
|
this.surfaceHolder = getHolder(); |
||||||
|
|
||||||
|
this.surfaceHolder.addCallback(callback); |
||||||
|
this.surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 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,59 @@ |
|||||||
|
package com.github.florent37.camerafragment.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") |
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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,465 @@ |
|||||||
|
package com.github.florent37.camerafragment.internal.utils; |
||||||
|
|
||||||
|
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 com.github.florent37.camerafragment.configuration.Configuration; |
||||||
|
|
||||||
|
/** |
||||||
|
* 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) || |
||||||
|
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT); |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public static boolean hasCamera2(Context context) { |
||||||
|
if (context == null) return false; |
||||||
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) 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, @Configuration.MediaAction int mediaAction) { |
||||||
|
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory( |
||||||
|
Environment.DIRECTORY_PICTURES), context.getPackageName()); |
||||||
|
|
||||||
|
if (!mediaStorageDir.exists()) { |
||||||
|
if (!mediaStorageDir.mkdirs()) { |
||||||
|
Log.d(TAG, "Failed to create directory."); |
||||||
|
return null; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
String timeStamp = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss").format(new Date()); |
||||||
|
File mediaFile; |
||||||
|
if (mediaAction == Configuration.MEDIA_ACTION_PHOTO) { |
||||||
|
mediaFile = new File(mediaStorageDir.getPath() + File.separator + |
||||||
|
"IMG_" + timeStamp + ".jpg"); |
||||||
|
} else if (mediaAction == Configuration.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, @Configuration.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 == Configuration.MEDIA_QUALITY_HIGHEST) { |
||||||
|
result = maxPictureSize; |
||||||
|
} else if (mediaQuality == Configuration.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 == Configuration.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 == Configuration.MEDIA_QUALITY_MEDIUM) { |
||||||
|
if (choices.size() == 2) result = minPictureSize; |
||||||
|
else { |
||||||
|
int mediumQualityIndex = choices.size() / 2; |
||||||
|
result = choices.get(mediumQualityIndex); |
||||||
|
} |
||||||
|
} else if (mediaQuality == Configuration.MEDIA_QUALITY_LOWEST) { |
||||||
|
result = minPictureSize; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public static Size getPictureSize(Size[] sizes, @Configuration.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 == Configuration.MEDIA_QUALITY_HIGHEST) { |
||||||
|
result = maxPictureSize; |
||||||
|
} else if (mediaQuality == Configuration.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 == Configuration.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 == Configuration.MEDIA_QUALITY_MEDIUM) { |
||||||
|
if (choices.size() == 2) result = minPictureSize; |
||||||
|
else { |
||||||
|
int mediumQualityIndex = choices.size() / 2; |
||||||
|
result = choices.get(mediumQualityIndex); |
||||||
|
} |
||||||
|
} else if (mediaQuality == Configuration.MEDIA_QUALITY_LOWEST) { |
||||||
|
result = minPictureSize; |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") |
||||||
|
public static Size getOptimalPreviewSize(List<Size> sizes, int width, int height) { |
||||||
|
final double ASPECT_TOLERANCE = 0.1; |
||||||
|
double targetRatio = (double) height / width; |
||||||
|
|
||||||
|
if (sizes == null) return null; |
||||||
|
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
@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; |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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) { |
||||||
|
// if (size.getWidth() == width && size.getHeight() == height)
|
||||||
|
// return size;
|
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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; |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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.size() > 0) { |
||||||
|
return Collections.min(bigEnough, new CompareSizesByArea2()); |
||||||
|
} else { |
||||||
|
Log.e(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; |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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, Configuration.MEDIA_QUALITY_HIGHEST); |
||||||
|
|
||||||
|
int[] qualities = new int[]{Configuration.MEDIA_QUALITY_HIGHEST, |
||||||
|
Configuration.MEDIA_QUALITY_HIGH, Configuration.MEDIA_QUALITY_MEDIUM, |
||||||
|
Configuration.MEDIA_QUALITY_LOW, Configuration.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(Configuration.MEDIA_QUALITY_LOWEST, currentCameraId); |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
public static CamcorderProfile getCamcorderProfile(@Configuration.MediaQuality int mediaQuality, String cameraId) { |
||||||
|
if (TextUtils.isEmpty(cameraId)) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
int cameraIdInt = Integer.parseInt(cameraId); |
||||||
|
return getCamcorderProfile(mediaQuality, cameraIdInt); |
||||||
|
} |
||||||
|
|
||||||
|
public static CamcorderProfile getCamcorderProfile(@Configuration.MediaQuality int mediaQuality, int cameraId) { |
||||||
|
if (Build.VERSION.SDK_INT > 10) { |
||||||
|
if (mediaQuality == Configuration.MEDIA_QUALITY_HIGHEST) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); |
||||||
|
} else if (mediaQuality == Configuration.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 == Configuration.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 == Configuration.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 == Configuration.MEDIA_QUALITY_LOWEST) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); |
||||||
|
} else { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); |
||||||
|
} |
||||||
|
} else { |
||||||
|
if (mediaQuality == Configuration.MEDIA_QUALITY_HIGHEST) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); |
||||||
|
} else if (mediaQuality == Configuration.MEDIA_QUALITY_HIGH) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); |
||||||
|
} else if (mediaQuality == Configuration.MEDIA_QUALITY_MEDIUM) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); |
||||||
|
} else if (mediaQuality == Configuration.MEDIA_QUALITY_LOW) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); |
||||||
|
} else if (mediaQuality == Configuration.MEDIA_QUALITY_LOWEST) { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_LOW); |
||||||
|
} else { |
||||||
|
return CamcorderProfile.get(cameraId, CamcorderProfile.QUALITY_HIGH); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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 com.github.florent37.camerafragment.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,196 @@ |
|||||||
|
package com.github.florent37.camerafragment.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 android.support.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 ImageLoader { |
||||||
|
|
||||||
|
private Context context; |
||||||
|
private String url; |
||||||
|
|
||||||
|
private ImageLoader(Context context) { |
||||||
|
this.context = context; |
||||||
|
} |
||||||
|
|
||||||
|
public static class Builder { |
||||||
|
|
||||||
|
private ImageLoader imageLoader; |
||||||
|
|
||||||
|
public Builder(@NonNull Context context) { |
||||||
|
imageLoader = new ImageLoader(context); |
||||||
|
} |
||||||
|
|
||||||
|
public Builder load(String url) { |
||||||
|
imageLoader.url = url; |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
public ImageLoader build() { |
||||||
|
return imageLoader; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
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,62 @@ |
|||||||
|
package com.github.florent37.camerafragment.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(byte[] bytes); |
||||||
|
|
||||||
|
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(bytes); |
||||||
|
} catch (IOException ignore) { |
||||||
|
Log.e(TAG, "Can't save the image file."); |
||||||
|
imageSaverCallback.onError(); |
||||||
|
} finally { |
||||||
|
image.close(); |
||||||
|
if (null != output) { |
||||||
|
try { |
||||||
|
output.close(); |
||||||
|
} catch (IOException e) { |
||||||
|
Log.e(TAG, "Can't release image or close the output stream."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
package com.github.florent37.camerafragment.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 com.github.florent37.camerafragment.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; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.github.florent37.camerafragment.listeners; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface CameraFragmentControlsListener { |
||||||
|
void lockControls(); |
||||||
|
void unLockControls(); |
||||||
|
void allowCameraSwitching(boolean allow); |
||||||
|
void allowRecord(boolean allow); |
||||||
|
void setMediaActionSwitchVisible(boolean visible); |
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package com.github.florent37.camerafragment.listeners; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface CameraFragmentResultListener { |
||||||
|
|
||||||
|
//Called when the video record is finished and saved
|
||||||
|
void onVideoRecorded(String filePath); |
||||||
|
|
||||||
|
//called when the photo is taken and saved
|
||||||
|
void onPhotoTaken(byte[] bytes, String filePath); |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package com.github.florent37.camerafragment.listeners; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface CameraFragmentStateListener { |
||||||
|
|
||||||
|
//when the current displayed camera is the back
|
||||||
|
void onCurrentCameraBack(); |
||||||
|
|
||||||
|
//when the current displayed camera is the front
|
||||||
|
void onCurrentCameraFront(); |
||||||
|
|
||||||
|
//when the flash is at mode auto
|
||||||
|
void onFlashAuto(); |
||||||
|
//when the flash is at on
|
||||||
|
void onFlashOn(); |
||||||
|
//when the flash is off
|
||||||
|
void onFlashOff(); |
||||||
|
|
||||||
|
//if the camera is ready to take a photo
|
||||||
|
void onCameraSetupForPhoto(); |
||||||
|
|
||||||
|
//if the camera is ready to take a video
|
||||||
|
void onCameraSetupForVideo(); |
||||||
|
|
||||||
|
//when the camera state is "ready to record a video"
|
||||||
|
void onRecordStateVideoReadyForRecord(); |
||||||
|
//when the camera state is "recording a video"
|
||||||
|
void onRecordStateVideoInProgress(); |
||||||
|
//when the camera state is "ready to take a photo"
|
||||||
|
void onRecordStatePhoto(); |
||||||
|
|
||||||
|
//after the rotation of the screen / camera
|
||||||
|
void shouldRotateControls(int degrees); |
||||||
|
|
||||||
|
void onStartVideoRecord(File outputFile); |
||||||
|
void onStopVideoRecord(); |
||||||
|
} |
@ -0,0 +1,13 @@ |
|||||||
|
package com.github.florent37.camerafragment.listeners; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by florentchampigny on 13/01/2017. |
||||||
|
*/ |
||||||
|
|
||||||
|
public interface CameraFragmentVideoRecordTextListener { |
||||||
|
void setRecordSizeText(long size, String text); |
||||||
|
void setRecordSizeTextVisible(boolean visible); |
||||||
|
|
||||||
|
void setRecordDurationText(String text); |
||||||
|
void setRecordDurationTextVisible(boolean visible); |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package com.github.florent37.camerafragment.widgets; |
||||||
|
|
||||||
|
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); |
||||||
|
} |
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.LOLLIPOP) |
||||||
|
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 (Build.VERSION.SDK_INT > 10) { |
||||||
|
if (enabled) { |
||||||
|
setAlpha(1f); |
||||||
|
} else { |
||||||
|
setAlpha(0.5f); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,93 @@ |
|||||||
|
package com.github.florent37.camerafragment.widgets; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.drawable.Drawable; |
||||||
|
import android.os.Build; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v4.content.ContextCompat; |
||||||
|
import android.support.v4.graphics.drawable.DrawableCompat; |
||||||
|
import android.support.v7.widget.AppCompatImageButton; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.R; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 6/24/16. |
||||||
|
*/ |
||||||
|
public class CameraSwitchView extends AppCompatImageButton { |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private OnCameraTypeChangeListener onCameraTypeChangeListener; |
||||||
|
|
||||||
|
public interface OnCameraTypeChangeListener { |
||||||
|
void switchCameraType(); |
||||||
|
} |
||||||
|
|
||||||
|
private Drawable frontCameraDrawable; |
||||||
|
private Drawable rearCameraDrawable; |
||||||
|
private int padding = 5; |
||||||
|
|
||||||
|
public CameraSwitchView(Context context) { |
||||||
|
this(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
public CameraSwitchView(Context context, AttributeSet attrs) { |
||||||
|
super(context, attrs); |
||||||
|
initializeView(); |
||||||
|
} |
||||||
|
|
||||||
|
public CameraSwitchView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||||
|
this(context, attrs); |
||||||
|
} |
||||||
|
|
||||||
|
private void initializeView() { |
||||||
|
Context context = getContext(); |
||||||
|
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 OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View view) { |
||||||
|
if (onCameraTypeChangeListener != null) { |
||||||
|
onCameraTypeChangeListener.switchCameraType(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
displayBackCamera(); |
||||||
|
|
||||||
|
padding = Utils.convertDipToPixels(context, padding); |
||||||
|
setPadding(padding, padding, padding, padding); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayFrontCamera() { |
||||||
|
setImageDrawable(frontCameraDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayBackCamera() { |
||||||
|
setImageDrawable(rearCameraDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setEnabled(boolean enabled) { |
||||||
|
super.setEnabled(enabled); |
||||||
|
if (Build.VERSION.SDK_INT > 10) { |
||||||
|
if (enabled) { |
||||||
|
setAlpha(1f); |
||||||
|
} else { |
||||||
|
setAlpha(0.5f); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public void setOnCameraTypeChangeListener(OnCameraTypeChangeListener listener) { |
||||||
|
this.onCameraTypeChangeListener = listener; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
package com.github.florent37.camerafragment.widgets; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.Color; |
||||||
|
import android.graphics.drawable.Drawable; |
||||||
|
import android.os.Build; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v4.content.ContextCompat; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.ImageButton; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.R; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
public class FlashSwitchView extends ImageButton { |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private FlashModeSwitchListener switchListener; |
||||||
|
|
||||||
|
private Drawable flashOnDrawable; |
||||||
|
private Drawable flashOffDrawable; |
||||||
|
private Drawable flashAutoDrawable; |
||||||
|
|
||||||
|
public interface FlashModeSwitchListener { |
||||||
|
void toggleFlashMode(); |
||||||
|
} |
||||||
|
|
||||||
|
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 OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View view) { |
||||||
|
if (switchListener != null) { |
||||||
|
switchListener.toggleFlashMode(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayFlashOff() { |
||||||
|
setImageDrawable(flashOffDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayFlashOn() { |
||||||
|
setImageDrawable(flashOnDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayFlashAuto() { |
||||||
|
setImageDrawable(flashAutoDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
public void setFlashSwitchListener(@NonNull FlashModeSwitchListener switchListener) { |
||||||
|
this.switchListener = switchListener; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setEnabled(boolean enabled) { |
||||||
|
super.setEnabled(enabled); |
||||||
|
if (Build.VERSION.SDK_INT > 10) { |
||||||
|
if (enabled) { |
||||||
|
setAlpha(1f); |
||||||
|
} else { |
||||||
|
setAlpha(0.5f); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
package com.github.florent37.camerafragment.widgets; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.drawable.Drawable; |
||||||
|
import android.os.Build; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v4.content.ContextCompat; |
||||||
|
import android.support.v4.graphics.drawable.DrawableCompat; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.ImageButton; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.R; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 6/24/16. |
||||||
|
*/ |
||||||
|
public class MediaActionSwitchView extends ImageButton { |
||||||
|
|
||||||
|
@Nullable |
||||||
|
private OnMediaActionStateChangeListener onMediaActionStateChangeListener; |
||||||
|
|
||||||
|
public interface OnMediaActionStateChangeListener { |
||||||
|
void switchAction(); |
||||||
|
} |
||||||
|
|
||||||
|
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); |
||||||
|
initializeView(); |
||||||
|
} |
||||||
|
|
||||||
|
public MediaActionSwitchView(Context context, AttributeSet attrs, int defStyleAttr) { |
||||||
|
this(context, attrs); |
||||||
|
} |
||||||
|
|
||||||
|
private void initializeView() { |
||||||
|
Context context = getContext(); |
||||||
|
|
||||||
|
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); |
||||||
|
// setBackgroundResource(R.drawable.circle_frame_background);
|
||||||
|
|
||||||
|
setOnClickListener(new OnClickListener() { |
||||||
|
@Override |
||||||
|
public void onClick(View view) { |
||||||
|
if(onMediaActionStateChangeListener != null) { |
||||||
|
onMediaActionStateChangeListener.switchAction(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
padding = Utils.convertDipToPixels(context, padding); |
||||||
|
setPadding(padding, padding, padding, padding); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayActionWillSwitchPhoto(){ |
||||||
|
setImageDrawable(photoDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayActionWillSwitchVideo(){ |
||||||
|
setImageDrawable(videoDrawable); |
||||||
|
} |
||||||
|
|
||||||
|
public void setOnMediaActionStateChangeListener(OnMediaActionStateChangeListener onMediaActionStateChangeListener) { |
||||||
|
this.onMediaActionStateChangeListener = onMediaActionStateChangeListener; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void setEnabled(boolean enabled) { |
||||||
|
super.setEnabled(enabled); |
||||||
|
if (Build.VERSION.SDK_INT > 10) { |
||||||
|
if (enabled) { |
||||||
|
setAlpha(1f); |
||||||
|
} else { |
||||||
|
setAlpha(0.5f); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,112 @@ |
|||||||
|
package com.github.florent37.camerafragment.widgets; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.graphics.drawable.Drawable; |
||||||
|
import android.os.Build; |
||||||
|
import android.support.annotation.NonNull; |
||||||
|
import android.support.annotation.Nullable; |
||||||
|
import android.support.v4.content.ContextCompat; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.View; |
||||||
|
import android.widget.ImageButton; |
||||||
|
|
||||||
|
import com.github.florent37.camerafragment.R; |
||||||
|
import com.github.florent37.camerafragment.internal.utils.Utils; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by memfis on 7/6/16. |
||||||
|
*/ |
||||||
|
public class RecordButton extends ImageButton { |
||||||
|
|
||||||
|
public interface RecordButtonListener { |
||||||
|
void onRecordButtonClicked(); |
||||||
|
} |
||||||
|
|
||||||
|
private Drawable takePhotoDrawable; |
||||||
|
private Drawable startRecordDrawable; |
||||||
|
private Drawable stopRecordDrawable; |
||||||
|
private int iconPadding = 8; |
||||||
|
private int iconPaddingStop = 18; |
||||||
|
|
||||||
|
@Nullable |
||||||
|
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); |
||||||
|
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); |
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT > 15) |
||||||
|
setBackground(ContextCompat.getDrawable(context, R.drawable.circle_frame_background)); |
||||||
|
else |
||||||
|
setBackgroundDrawable(ContextCompat.getDrawable(context, R.drawable.circle_frame_background)); |
||||||
|
|
||||||
|
setOnClickListener(new 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(); |
||||||
|
|
||||||
|
if(listener != null) { |
||||||
|
listener.onRecordButtonClicked(); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
setSoundEffectsEnabled(false); |
||||||
|
setIconPadding(iconPadding); |
||||||
|
} |
||||||
|
|
||||||
|
//public void setup(@Configuration.MediaAction int mediaAction) {
|
||||||
|
// setMediaAction(mediaAction);
|
||||||
|
//}
|
||||||
|
|
||||||
|
private void setIconPadding(int paddingDP) { |
||||||
|
int padding = Utils.convertDipToPixels(getContext(), paddingDP); |
||||||
|
setPadding(padding, padding, padding, padding); |
||||||
|
} |
||||||
|
|
||||||
|
//public void setMediaAction(@Configuration.MediaAction int mediaAction) {
|
||||||
|
// this.mediaAction = mediaAction;
|
||||||
|
// if (listener != null) {
|
||||||
|
// if (Configuration.MEDIA_ACTION_PHOTO == mediaAction) {
|
||||||
|
// listener.setRecordState(Record.TAKE_PHOTO_STATE);
|
||||||
|
// } else {
|
||||||
|
// listener.setRecordState(Record.READY_FOR_RECORD_STATE);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
public void setRecordButtonListener(@NonNull RecordButtonListener listener) { |
||||||
|
this.listener = listener; |
||||||
|
} |
||||||
|
|
||||||
|
public void displayVideoRecordStateReady(){ |
||||||
|
setImageDrawable(startRecordDrawable); |
||||||
|
setIconPadding(iconPadding); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayVideoRecordStateInProgress(){ |
||||||
|
setImageDrawable(stopRecordDrawable); |
||||||
|
setIconPadding(iconPaddingStop); |
||||||
|
} |
||||||
|
|
||||||
|
public void displayPhotoState(){ |
||||||
|
setImageDrawable(takePhotoDrawable); |
||||||
|
setIconPadding(iconPadding); |
||||||
|
} |
||||||
|
|
||||||
|
} |
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 |