pull/789/head
oded 5 years ago
parent 2d979b3f31
commit 163bae9a69
  1. 2
      build.gradle
  2. 5
      demo/build.gradle
  3. 7
      demo/src/main/AndroidManifest.xml
  4. 7
      demo/src/main/java/com/otaliastudios/cameraview/demo/AlbumStorageDirFactory.java
  5. 21
      demo/src/main/java/com/otaliastudios/cameraview/demo/BaseAlbumDirFactory.java
  6. 239
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  7. 95
      demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
  8. 33
      demo/src/main/res/layout/activity_camera.xml
  9. 1
      demo/src/main/res/values/strings.xml

@ -20,7 +20,7 @@ allprojects {
ext { ext {
compileSdkVersion = 29 compileSdkVersion = 29
minSdkVersion = 15 minSdkVersion = 18
targetSdkVersion = 29 targetSdkVersion = 29
} }

@ -23,5 +23,8 @@ android {
dependencies { dependencies {
implementation project(':cameraview') implementation project(':cameraview')
implementation 'androidx.appcompat:appcompat:1.1.0' implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.google.android.material:material:1.1.0-beta01' implementation 'com.google.android.material:material:1.1.0-alpha09'
implementation 'com.otaliastudios:transcoder:0.8.0'
implementation 'com.otaliastudios.opengl:egloo:0.4.0'
} }

@ -4,6 +4,10 @@
package="com.otaliastudios.cameraview.demo"> package="com.otaliastudios.cameraview.demo">
<uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-sdk tools:overrideLibrary="com.otaliastudios.opengl" />
<application <application
android:allowBackup="false" android:allowBackup="false"
@ -11,6 +15,7 @@
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:requestLegacyExternalStorage="true"
tools:ignore="GoogleAppIndexingWarning"> tools:ignore="GoogleAppIndexingWarning">
<activity <activity
@ -41,6 +46,8 @@
android:resource="@xml/filepaths" /> android:resource="@xml/filepaths" />
</provider> </provider>
</application> </application>
</manifest> </manifest>

@ -0,0 +1,7 @@
package com.otaliastudios.cameraview.demo;
import java.io.File;
public abstract class AlbumStorageDirFactory {
public abstract File getAlbumStorageDir(String albumName);
}

@ -0,0 +1,21 @@
package com.otaliastudios.cameraview.demo;
import android.os.Environment;
import java.io.File;
public final class BaseAlbumDirFactory extends AlbumStorageDirFactory {
// Standard storage location for digital camera files
private static final String CAMERA_DIR = "/Pictures/";
@Override
public File getAlbumStorageDir(String albumName) {
return new File(
Environment.getExternalStorageDirectory()
+ CAMERA_DIR
+ albumName
);
}
}

@ -1,26 +1,33 @@
package com.otaliastudios.cameraview.demo; package com.otaliastudios.cameraview.demo;
import android.Manifest;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.ImageFormat; import android.graphics.ImageFormat;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.YuvImage; import android.graphics.YuvImage;
import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.bottomsheet.BottomSheetBehavior;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import android.os.Environment;
import android.os.Handler;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver; import android.view.ViewTreeObserver;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import com.otaliastudios.cameraview.CameraException; import com.otaliastudios.cameraview.CameraException;
@ -33,16 +40,21 @@ import com.otaliastudios.cameraview.controls.Mode;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.controls.Preview; import com.otaliastudios.cameraview.controls.Preview;
import com.otaliastudios.cameraview.filter.Filters; import com.otaliastudios.cameraview.filter.Filters;
import com.otaliastudios.cameraview.filter.MultiFilter;
import com.otaliastudios.cameraview.filters.BrightnessFilter;
import com.otaliastudios.cameraview.filters.DuotoneFilter;
import com.otaliastudios.cameraview.frame.Frame; import com.otaliastudios.cameraview.frame.Frame;
import com.otaliastudios.cameraview.frame.FrameProcessor; import com.otaliastudios.cameraview.frame.FrameProcessor;
import com.otaliastudios.transcoder.Transcoder;
import com.otaliastudios.transcoder.TranscoderListener;
import com.otaliastudios.transcoder.TranscoderOptions;
import com.otaliastudios.transcoder.sink.DataSink;
import com.otaliastudios.transcoder.sink.DefaultDataSink;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale;
public class CameraActivity extends AppCompatActivity implements View.OnClickListener, OptionView.Callback { public class CameraActivity extends AppCompatActivity implements View.OnClickListener, OptionView.Callback {
@ -54,16 +66,25 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
private CameraView camera; private CameraView camera;
private ViewGroup controlPanel; private ViewGroup controlPanel;
private long mCaptureTime; private long mCaptureTime;
private long mVideoCaptureTime;
private int mCurrentFilter = 0; private int mCurrentFilter = 0;
private final Filters[] mAllFilters = Filters.values(); private final Filters[] mAllFilters = Filters.values();
private TextView mTvTimer;
private ImageButton mImgPlayPauseVideo; private ImageButton mImgPlayPauseVideo;
private ImageButton mImgStopVideo; private ImageButton mImgStopVideo;
private String videoFilePath;
private static final int MAX_RECORDING_TIME = 2 * 60 * 1000; // 2 MINUTES! private static final int MAX_RECORDING_TIME = 2 * 60 * 1000; // 2 MINUTES!
private Handler mUpdateDurationHandler;
private static final String TAG = "CameraActivity_";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -77,6 +98,9 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
mImgPlayPauseVideo = findViewById(R.id.imgPlayPauseVideo); mImgPlayPauseVideo = findViewById(R.id.imgPlayPauseVideo);
mImgStopVideo = findViewById(R.id.imgStopVideo); mImgStopVideo = findViewById(R.id.imgStopVideo);
mTvTimer = findViewById(R.id.tvTimer);
mTvTimer.setText("");
mImgStopVideo.setVisibility(View.GONE); mImgStopVideo.setVisibility(View.GONE);
@ -86,14 +110,52 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
camera.setMode(Mode.VIDEO); camera.setMode(Mode.VIDEO);
mImgStopVideo.setVisibility(View.VISIBLE); mImgStopVideo.setVisibility(View.VISIBLE);
if (camera.isTakingVideo()) { if (camera.isTakingVideo()) {
// PAUSE THE VIDEO
mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp); mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
camera.pauseVideoRecording(); camera.pauseVideoRecording();
killVideoDurationElapsedTimeHandler();
mVideoCaptureTime = System.currentTimeMillis(); // saves the pause time!
long secondsElapsed = (System.currentTimeMillis() - mVideoCaptureTime)/1000;
} else if (camera.isRecordingPaused()) { } else if (camera.isRecordingPaused()) {
// RESUME VIDEO RECORDING
mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp); mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
camera.resumeVideoRecording(); camera.resumeVideoRecording();
long pauseTime = System.currentTimeMillis() - mVideoCaptureTime;
mVideoCaptureTime = mVideoCaptureTime+pauseTime;
// mVideoCaptureTime = System.currentTimeMillis() + mVideoCaptureTime;
// mVideoCaptureTime = System.currentTimeMillis() -(System.currentTimeMillis() - mVideoCaptureTime);
// mVideoCaptureTime = System.currentTimeMillis() - mVideoCaptureTime;
long secondsElapsed = (System.currentTimeMillis() - mVideoCaptureTime)/1000;
updateVideoDurationElapsedTime();
} else { } else {
mVideoCaptureTime = System.currentTimeMillis();
updateVideoDurationElapsedTime();
mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp); mImgPlayPauseVideo.setImageResource(R.drawable.ic_pause_circle_outline_black_24dp);
camera.takeVideo(new File(getFilesDir(), "video.mp4"), MAX_RECORDING_TIME); File albumF = getAlbumDir();
String videoFileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
videoFileName = videoFileName+".mp4";
File videoFile = new File(albumF,videoFileName);
videoFilePath = videoFile.getAbsolutePath();
camera.takeVideo(videoFile, MAX_RECORDING_TIME);
// camera.takeVideoSnapshot(videoFile, MAX_RECORDING_TIME);
// camera.takeVideo(new File(getFilesDir(), "video.mp4"), MAX_RECORDING_TIME);
} }
} }
@ -103,6 +165,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
mImgStopVideo.setOnClickListener(new View.OnClickListener() { mImgStopVideo.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View view) { public void onClick(View view) {
killVideoDurationElapsedTimeHandler(); // make sure not to init more then one instance
camera.stopVideo(); camera.stopVideo();
} }
}); });
@ -230,6 +293,42 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
animator.start(); animator.start();
} }
private final String TEMP_FILE_DIR = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/" +getPackageName() + "/temp";
public File getTempMediaFileDirectory() throws NullPointerException {
File result = new File(TEMP_FILE_DIR);
if (!result.exists()){
result.mkdirs();
}
if (result != null){
return result;
} else {
throw new NullPointerException();
}
}
private File getAlbumDir() {
File storageDir = null;
AlbumStorageDirFactory mAlbumStorageDirFactory = new BaseAlbumDirFactory();
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
storageDir = mAlbumStorageDirFactory.getAlbumStorageDir(getString(R.string.album_name));
if (!storageDir.mkdirs()) {
if (!storageDir.exists()) {
// Logger.Log("failed to create directory");
// return storageDir;
return null;
}
}
} else {
// Logger.Log("External storage is not mounted READ/WRITE.");
}
return storageDir;
}
private void message(@NonNull String content, boolean important) { private void message(@NonNull String content, boolean important) {
if (important) { if (important) {
LOG.w(content); LOG.w(content);
@ -285,6 +384,30 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
Intent intent = new Intent(CameraActivity.this, VideoPreviewActivity.class); Intent intent = new Intent(CameraActivity.this, VideoPreviewActivity.class);
startActivity(intent); startActivity(intent);
LOG.w("onVideoTaken called! Launched activity."); LOG.w("onVideoTaken called! Launched activity.");
// notify yhe gallery after a new video added
if (videoFilePath != null) {
// check if android M has access to write external storage
if (ActivityCompat.checkSelfPermission(CameraActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
return;
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(videoFilePath);
Uri contentUri;
contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
// transcodeVideo();
}
} }
@Override @Override
@ -300,6 +423,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
LOG.w("onVideoRecordingEnd!"); LOG.w("onVideoRecordingEnd!");
mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp); mImgPlayPauseVideo.setImageResource(R.drawable.ic_play_circle_outline_black_24dp);
mImgStopVideo.setVisibility(View.GONE); mImgStopVideo.setVisibility(View.GONE);
mTvTimer.setText("");
} }
@ -331,6 +455,64 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
} }
} }
private void transcodeVideo() {
// File f = new File(videoFilePath);
// Uri contentUri;
//
// contentUri = Uri.fromFile(f);
File albumF = getAlbumDir();
String videoFileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
videoFileName = videoFileName+".mp4";
File videoFile = new File(albumF,videoFileName);
final String videoFilePathCoded = videoFile.getAbsolutePath();
Transcoder.into(videoFilePathCoded).addDataSource(videoFilePath).setListener(new TranscoderListener() {
@Override
public void onTranscodeProgress(double progress) {
Log.d(TAG, "onTranscodeProgress: "+progress);
}
@Override
public void onTranscodeCompleted(int successCode) {
if (successCode == Transcoder.SUCCESS_TRANSCODED) {
// notify yhe gallery after a new video added
// check if android M has access to write external storage
if (ActivityCompat.checkSelfPermission(CameraActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
return;
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(videoFilePathCoded);
Uri contentUri;
contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
}else{
// error
}
}
@Override
public void onTranscodeCanceled() {
Log.d(TAG, "onTranscodeCanceled: ");
}
@Override
public void onTranscodeFailed(@NonNull Throwable exception) {
// error!
Log.d(TAG, "onTranscodeFailed: "+exception.getMessage());
}
}).transcode();
}
@Override @Override
public void onClick(View view) { public void onClick(View view) {
switch (view.getId()) { switch (view.getId()) {
@ -440,6 +622,37 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
// camera.setFilter(new MultiFilter(duotone, filter.newInstance())); // camera.setFilter(new MultiFilter(duotone, filter.newInstance()));
} }
private void updateVideoDurationElapsedTime(){
killVideoDurationElapsedTimeHandler(); // make sure not to init more then one instance
mUpdateDurationHandler = new Handler();
mUpdateDurationHandler.postDelayed(mVideoDurationRunnable,1000);
long secondsElapsed = (System.currentTimeMillis() - mVideoCaptureTime)/1000;
mTvTimer.setText(DateUtils.formatElapsedTime(secondsElapsed));
}
private Runnable mVideoDurationRunnable = new Runnable() {
@Override
public void run() {
updateVideoDurationElapsedTime();
}
};
private void killVideoDurationElapsedTimeHandler(){
if (mUpdateDurationHandler!=null){
mUpdateDurationHandler.removeCallbacks(mVideoDurationRunnable);
}
}
@Override @Override
public <T> boolean onValueChanged(@NonNull Option<T> option, @NonNull T value, @NonNull String name) { public <T> boolean onValueChanged(@NonNull Option<T> option, @NonNull T value, @NonNull String name) {
if ((option instanceof Option.Width || option instanceof Option.Height)) { if ((option instanceof Option.Width || option instanceof Option.Height)) {
@ -473,4 +686,18 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
} }
//endregion //endregion
@Override
protected void onPause() {
super.onPause();
killVideoDurationElapsedTimeHandler();
}
@Override
protected void onDestroy() {
super.onDestroy();
killVideoDurationElapsedTimeHandler();
}
} }

@ -1,15 +1,21 @@
package com.otaliastudios.cameraview.demo; package com.otaliastudios.cameraview.demo;
import android.Manifest;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.FileProvider; import androidx.core.content.FileProvider;
import android.os.Environment;
import android.util.Log; import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -23,8 +29,13 @@ import com.otaliastudios.cameraview.CameraUtils;
import com.otaliastudios.cameraview.FileCallback; import com.otaliastudios.cameraview.FileCallback;
import com.otaliastudios.cameraview.VideoResult; import com.otaliastudios.cameraview.VideoResult;
import com.otaliastudios.cameraview.size.AspectRatio; import com.otaliastudios.cameraview.size.AspectRatio;
import com.otaliastudios.transcoder.Transcoder;
import com.otaliastudios.transcoder.TranscoderListener;
import java.io.File; import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class VideoPreviewActivity extends AppCompatActivity { public class VideoPreviewActivity extends AppCompatActivity {
@ -33,6 +44,8 @@ public class VideoPreviewActivity extends AppCompatActivity {
private static VideoResult videoResult; private static VideoResult videoResult;
private static final String TAG = "CameraActivity_";
public static void setVideoResult(@Nullable VideoResult result) { public static void setVideoResult(@Nullable VideoResult result) {
videoResult = result; videoResult = result;
} }
@ -93,6 +106,10 @@ public class VideoPreviewActivity extends AppCompatActivity {
} }
} }
}); });
transcodeVideo();
} }
void playVideo() { void playVideo() {
@ -101,6 +118,84 @@ public class VideoPreviewActivity extends AppCompatActivity {
} }
} }
private void transcodeVideo() {
// File f = new File(videoFilePath);
// Uri contentUri;
//
// contentUri = Uri.fromFile(f);
File albumF = getAlbumDir();
String videoFileName = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
videoFileName = videoFileName+".mp4";
File videoFile = new File(albumF,videoFileName);
final String videoFilePathCoded = videoFile.getAbsolutePath();
Transcoder.into(videoFilePathCoded).addDataSource(videoResult.getFile().getAbsolutePath()).setListener(new TranscoderListener() {
@Override
public void onTranscodeProgress(double progress) {
Log.d(TAG, "onTranscodeProgress: "+progress);
}
@Override
public void onTranscodeCompleted(int successCode) {
if (successCode == Transcoder.SUCCESS_TRANSCODED) {
// notify yhe gallery after a new video added
// check if android M has access to write external storage
if (ActivityCompat.checkSelfPermission(VideoPreviewActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
return;
}
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File(videoFilePathCoded);
Uri contentUri;
contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
sendBroadcast(mediaScanIntent);
}else{
// error
}
}
@Override
public void onTranscodeCanceled() {
Log.d(TAG, "onTranscodeCanceled: ");
}
@Override
public void onTranscodeFailed(@NonNull Throwable exception) {
// error!
Log.d(TAG, "onTranscodeFailed: "+exception.getMessage());
}
}).transcode();
}
private File getAlbumDir() {
File storageDir = null;
AlbumStorageDirFactory mAlbumStorageDirFactory = new BaseAlbumDirFactory();
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
storageDir = mAlbumStorageDirFactory.getAlbumStorageDir(getString(R.string.album_name));
if (!storageDir.mkdirs()) {
if (!storageDir.exists()) {
// Logger.Log("failed to create directory");
// return storageDir;
return null;
}
}
} else {
// Logger.Log("External storage is not mounted READ/WRITE.");
}
return storageDir;
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();

@ -7,13 +7,31 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<!-- Camera --> <!-- Camera -->
<com.otaliastudios.cameraview.CameraView <com.otaliastudios.cameraview.CameraView
android:id="@+id/camera" android:id="@+id/camera"
android:layout_width="wrap_content" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"
android:layout_marginBottom="88dp" android:layout_marginBottom="88dp"
app:cameraVideoCodec="h264"
app:cameraVideoSizeMinWidth="100"
app:cameraVideoSizeMinHeight="100"
app:cameraVideoSizeMaxWidth="780"
app:cameraVideoSizeMaxHeight="1280"
app:cameraSnapshotMaxWidth="1080"
app:cameraSnapshotMaxHeight="1920"
app:cameraUseDeviceOrientation="true"
app:cameraPictureSnapshotMetering="true"
app:cameraPreviewFrameRate="30"
android:keepScreenOn="true" android:keepScreenOn="true"
app:cameraExperimental="true" app:cameraExperimental="true"
app:cameraEngine="camera2" app:cameraEngine="camera2"
@ -26,6 +44,7 @@
app:cameraGestureTap="autoFocus" app:cameraGestureTap="autoFocus"
app:cameraGestureLongTap="none" app:cameraGestureLongTap="none"
app:cameraGesturePinch="zoom" app:cameraGesturePinch="zoom"
app:cameraRequestPermissions="true"
app:cameraGestureScrollHorizontal="filterControl1" app:cameraGestureScrollHorizontal="filterControl1"
app:cameraGestureScrollVertical="exposureCorrection" app:cameraGestureScrollVertical="exposureCorrection"
app:cameraMode="picture" app:cameraMode="picture"
@ -179,7 +198,15 @@
android:text="SNAP"/> android:text="SNAP"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<TextView
android:id="@+id/tvTimer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TIME"
android:layout_margin="10dp"
android:textSize="20sp"
android:textColor="#FFFFFF"
/>
<!-- Edit --> <!-- Edit -->
<androidx.core.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/controls" android:id="@+id/controls"

@ -1,3 +1,4 @@
<resources> <resources>
<string name="app_name">CameraView</string> <string name="app_name">CameraView</string>
<string name="album_name" translatable="false">CameraView Album</string>
</resources> </resources>

Loading…
Cancel
Save