Initial commit

pull/17/head
Florent CHAMPIGNY 8 years ago
parent 3f2fa8bc13
commit c3b0214cd0
  1. 30
      .gitignore
  2. 21
      LICENSE.md
  3. 117
      README.md
  4. 1
      app/.gitignore
  5. 28
      app/build.gradle
  6. 17
      app/proguard-rules.pro
  7. 37
      app/src/main/AndroidManifest.xml
  8. 333
      app/src/main/java/com/github/florent37/camerafragment/sample/MainActivity.java
  9. 122
      app/src/main/res/layout/activity_fragment.xml
  10. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  11. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  12. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  13. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  14. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  15. 6
      app/src/main/res/values/colors.xml
  16. 3
      app/src/main/res/values/strings.xml
  17. 15
      app/src/main/res/values/styles.xml
  18. BIN
      art/default_camera.png
  19. BIN
      art/settings_for_video_limitation.png
  20. BIN
      art/video_camera.png
  21. BIN
      art/video_low_quality.png
  22. 22
      build.gradle
  23. 1
      camerafragment/.gitignore
  24. 32
      camerafragment/build.gradle
  25. 17
      camerafragment/proguard-rules.pro
  26. 5
      camerafragment/src/main/AndroidManifest.xml
  27. 15
      camerafragment/src/main/java/com/github/florent37/camerafragment/CameraFragment.java
  28. 386
      camerafragment/src/main/java/com/github/florent37/camerafragment/PreviewActivity.java
  29. 200
      camerafragment/src/main/java/com/github/florent37/camerafragment/configuration/Configuration.java
  30. 53
      camerafragment/src/main/java/com/github/florent37/camerafragment/configuration/ConfigurationProvider.java
  31. 207
      camerafragment/src/main/java/com/github/florent37/camerafragment/configuration/ConfigurationProviderImpl.java
  32. 51
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/controller/CameraController.java
  33. 194
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/controller/impl/Camera1Controller.java
  34. 195
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/controller/impl/Camera2Controller.java
  35. 27
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/controller/view/CameraView.java
  36. 17
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/enums/Camera.java
  37. 17
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/enums/Flash.java
  38. 17
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/enums/MediaAction.java
  39. 19
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/enums/Record.java
  40. 57
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/CameraManager.java
  41. 148
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/impl/BaseCameraManager.java
  42. 627
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/impl/Camera1Manager.java
  43. 844
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/impl/Camera2Manager.java
  44. 8
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/listener/CameraCloseListener.java
  45. 12
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/listener/CameraOpenListener.java
  46. 12
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/listener/CameraPhotoListener.java
  47. 16
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/manager/listener/CameraVideoListener.java
  48. 64
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/timer/CountdownTask.java
  49. 48
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/timer/TimerTask.java
  50. 31
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/timer/TimerTaskBase.java
  51. 703
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/ui/BaseAnncaFragment.java
  52. 46
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/ui/model/PhotoQualityOption.java
  53. 59
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/ui/model/VideoQualityOption.java
  54. 67
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/ui/view/AspectFrameLayout.java
  55. 64
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/ui/view/AutoFitSurfaceView.java
  56. 59
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/ui/view/AutoFitTextureView.java
  57. 465
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/utils/CameraHelper.java
  58. 12
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/utils/DateTimeUtils.java
  59. 196
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/utils/ImageLoader.java
  60. 62
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/utils/ImageSaver.java
  61. 104
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/utils/Size.java
  62. 53
      camerafragment/src/main/java/com/github/florent37/camerafragment/internal/utils/Utils.java
  63. 13
      camerafragment/src/main/java/com/github/florent37/camerafragment/listeners/CameraFragmentControlsListener.java
  64. 14
      camerafragment/src/main/java/com/github/florent37/camerafragment/listeners/CameraFragmentResultListener.java
  65. 42
      camerafragment/src/main/java/com/github/florent37/camerafragment/listeners/CameraFragmentStateListener.java
  66. 13
      camerafragment/src/main/java/com/github/florent37/camerafragment/listeners/CameraFragmentVideoRecordTextListener.java
  67. 42
      camerafragment/src/main/java/com/github/florent37/camerafragment/widgets/CameraSettingsView.java
  68. 93
      camerafragment/src/main/java/com/github/florent37/camerafragment/widgets/CameraSwitchView.java
  69. 85
      camerafragment/src/main/java/com/github/florent37/camerafragment/widgets/FlashSwitchView.java
  70. 96
      camerafragment/src/main/java/com/github/florent37/camerafragment/widgets/MediaActionSwitchView.java
  71. 112
      camerafragment/src/main/java/com/github/florent37/camerafragment/widgets/RecordButton.java
  72. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_camera_front_white_24dp.png
  73. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_camera_rear_white_24dp.png
  74. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_clear_white_24dp.png
  75. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_content_cut_white_24dp.png
  76. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_crop_white_24dp.png
  77. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_done_white_24dp.png
  78. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_flash_auto_white_24dp.png
  79. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_flash_off_white_24dp.png
  80. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_flash_on_white_24dp.png
  81. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_image_aspect_ratio_white_24dp.png
  82. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_photo_camera_white_24dp.png
  83. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_replay_white_24dp.png
  84. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_settings_white_24dp.png
  85. BIN
      camerafragment/src/main/res/drawable-hdpi/ic_videocam_white_24dp.png
  86. BIN
      camerafragment/src/main/res/drawable-hdpi/tile.9.png
  87. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_camera_front_white_24dp.png
  88. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_camera_rear_white_24dp.png
  89. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_clear_white_24dp.png
  90. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_content_cut_white_24dp.png
  91. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_crop_white_24dp.png
  92. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_done_white_24dp.png
  93. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_flash_auto_white_24dp.png
  94. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_flash_off_white_24dp.png
  95. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_flash_on_white_24dp.png
  96. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_image_aspect_ratio_white_24dp.png
  97. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_photo_camera_white_24dp.png
  98. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_replay_white_24dp.png
  99. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_settings_white_24dp.png
  100. BIN
      camerafragment/src/main/res/drawable-mdpi/ic_videocam_white_24dp.png
  101. Some files were not shown because too many files have changed in this diff Show More

30
.gitignore vendored

@ -38,3 +38,33 @@ captures/
# Keystore files
*.jks
# Built application files
*.apk
*.ap_
# Files for the Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
# Gradle files
.gradle/
build/
/*/build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
.idea/*

@ -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
[![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`

1
app/.gitignore vendored

@ -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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.5 KiB

Binary file not shown.

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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

Binary file not shown.

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);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 391 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

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

Loading…
Cancel
Save