diff --git a/.gitignore b/.gitignore index f6b286c..8ef66a9 100644 --- a/.gitignore +++ b/.gitignore @@ -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/* \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..b940ba7 --- /dev/null +++ b/LICENSE.md @@ -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. diff --git a/README.md b/README.md index 447f65f..db59d50 100644 --- a/README.md +++ b/README.md @@ -1 +1,118 @@ -# CameraFragment \ No newline at end of file +# 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` \ No newline at end of file diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..c61e543 --- /dev/null +++ b/app/build.gradle @@ -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') +} diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..aa55aa4 --- /dev/null +++ b/app/proguard-rules.pro @@ -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 *; +#} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..6ef4e12 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/com/github/florent37/camerafragment/sample/MainActivity.java b/app/src/main/java/com/github/florent37/camerafragment/sample/MainActivity.java new file mode 100644 index 0000000..4cfb750 --- /dev/null +++ b/app/src/main/java/com/github/florent37/camerafragment/sample/MainActivity.java @@ -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 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); + } +} diff --git a/app/src/main/res/layout/activity_fragment.xml b/app/src/main/res/layout/activity_fragment.xml new file mode 100644 index 0000000..7b73767 --- /dev/null +++ b/app/src/main/res/layout/activity_fragment.xml @@ -0,0 +1,122 @@ + + + + + +