From 5964080db465f90ae626e7bac4ee48f3e5973ffd Mon Sep 17 00:00:00 2001 From: Mattia Iavarone Date: Fri, 29 Sep 2017 13:30:05 +0200 Subject: [PATCH] New demo app (#53) * New demo app * UI fixes * Refactor preview activities * Add gestures control --- demo/build.gradle | 3 +- demo/src/main/AndroidManifest.xml | 2 +- .../cameraview/demo/AutoUnfocusEditText.java | 39 -- .../cameraview/demo/CameraActivity.java | 211 +++++++ .../cameraview/demo/Control.java | 176 ++++++ .../cameraview/demo/ControlView.java | 103 ++++ .../cameraview/demo/MainActivity.java | 375 ------------ .../cameraview/demo/MessageView.java | 50 ++ .../demo/PicturePreviewActivity.java | 38 +- .../cameraview/demo/VideoPreviewActivity.java | 24 +- demo/src/main/res/drawable/background.xml | 8 +- .../drawable/{ic_flash.xml => ic_edit.xml} | 2 +- demo/src/main/res/layout/activity_camera.xml | 100 ++++ demo/src/main/res/layout/activity_main.xml | 555 ------------------ .../res/layout/activity_picture_preview.xml | 183 +----- .../res/layout/activity_video_preview.xml | 51 +- demo/src/main/res/layout/control_view.xml | 31 + demo/src/main/res/layout/spinner_text.xml | 8 + demo/src/main/res/values/colors.xml | 2 +- demo/src/main/res/values/styles.xml | 8 +- 20 files changed, 745 insertions(+), 1224 deletions(-) delete mode 100644 demo/src/main/java/com/otaliastudios/cameraview/demo/AutoUnfocusEditText.java create mode 100644 demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java create mode 100644 demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java create mode 100644 demo/src/main/java/com/otaliastudios/cameraview/demo/ControlView.java delete mode 100644 demo/src/main/java/com/otaliastudios/cameraview/demo/MainActivity.java create mode 100644 demo/src/main/java/com/otaliastudios/cameraview/demo/MessageView.java rename demo/src/main/res/drawable/{ic_flash.xml => ic_edit.xml} (57%) create mode 100644 demo/src/main/res/layout/activity_camera.xml delete mode 100644 demo/src/main/res/layout/activity_main.xml create mode 100644 demo/src/main/res/layout/control_view.xml create mode 100644 demo/src/main/res/layout/spinner_text.xml diff --git a/demo/build.gradle b/demo/build.gradle index 72c1e043..fb063b1f 100644 --- a/demo/build.gradle +++ b/demo/build.gradle @@ -23,6 +23,5 @@ android { dependencies { compile project(':cameraview') compile "com.android.support:appcompat-v7:$supportLibVersion" - compile 'com.jakewharton:butterknife:8.4.0' - annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' + compile "com.android.support:design:$supportLibVersion" } diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml index f3c04d1c..9447b52c 100644 --- a/demo/src/main/AndroidManifest.xml +++ b/demo/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ android:theme="@style/AppTheme"> diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/AutoUnfocusEditText.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/AutoUnfocusEditText.java deleted file mode 100644 index 59c651b6..00000000 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/AutoUnfocusEditText.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.otaliastudios.cameraview.demo; - -import android.content.Context; -import android.support.v7.widget.AppCompatEditText; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.view.inputmethod.InputMethodManager; - -public class AutoUnfocusEditText extends AppCompatEditText { - - public AutoUnfocusEditText(Context context) { - super(context); - } - - public AutoUnfocusEditText(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public AutoUnfocusEditText(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - closeKeyboard(); - clearFocus(); - return true; - } - - return super.dispatchKeyEvent(event); - } - - private void closeKeyboard() { - InputMethodManager imm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE); - imm.hideSoftInputFromWindow(getWindowToken(), 0); - } - -} diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java new file mode 100644 index 00000000..7be67483 --- /dev/null +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java @@ -0,0 +1,211 @@ +package com.otaliastudios.cameraview.demo; + +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.design.widget.BottomSheetBehavior; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.view.ViewTreeObserver; +import android.widget.Toast; + +import com.otaliastudios.cameraview.Audio; +import com.otaliastudios.cameraview.CameraListener; +import com.otaliastudios.cameraview.CameraLogger; +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.CameraView; +import com.otaliastudios.cameraview.Flash; +import com.otaliastudios.cameraview.Grid; +import com.otaliastudios.cameraview.SessionType; +import com.otaliastudios.cameraview.Size; +import com.otaliastudios.cameraview.VideoQuality; +import com.otaliastudios.cameraview.WhiteBalance; + +import java.io.File; + + +public class CameraActivity extends AppCompatActivity implements View.OnClickListener, ControlView.Callback { + + private CameraView camera; + private ViewGroup controlPanel; + + private boolean mCapturingPicture; + private boolean mCapturingVideo; + + // To show stuff in the callback + private Size mCaptureNativeSize; + private long mCaptureTime; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_camera); + CameraLogger.setLogLevel(CameraLogger.LEVEL_VERBOSE); + + camera = findViewById(R.id.camera); + camera.addCameraListener(new CameraListener() { + public void onCameraOpened(CameraOptions options) { onOpened(); } + public void onPictureTaken(byte[] jpeg) { onPicture(jpeg); } + public void onVideoTaken(File video) { onVideo(video); } + }); + + findViewById(R.id.edit).setOnClickListener(this); + findViewById(R.id.capturePhoto).setOnClickListener(this); + findViewById(R.id.captureVideo).setOnClickListener(this); + findViewById(R.id.toggleCamera).setOnClickListener(this); + + controlPanel = findViewById(R.id.controls); + ViewGroup group = (ViewGroup) controlPanel.getChildAt(0); + Control[] controls = Control.values(); + for (Control control : controls) { + ControlView view = new ControlView(this, control, this); + group.addView(view, ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT); + } + + controlPanel.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + BottomSheetBehavior b = BottomSheetBehavior.from(controlPanel); + b.setState(BottomSheetBehavior.STATE_HIDDEN); + } + }); + } + + private void message(String content, boolean important) { + int length = important ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT; + Toast.makeText(this, content, length).show(); + } + + private void onOpened() { + ViewGroup group = (ViewGroup) controlPanel.getChildAt(0); + for (int i = 0; i < group.getChildCount(); i++) { + ControlView view = (ControlView) group.getChildAt(i); + view.onCameraOpened(camera); + } + } + + private void onPicture(byte[] jpeg) { + mCapturingPicture = false; + long callbackTime = System.currentTimeMillis(); + if (mCapturingVideo) { + message("Captured while taking video. Size="+mCaptureNativeSize, false); + return; + } + + // This can happen if picture was taken with a gesture. + if (mCaptureTime == 0) mCaptureTime = callbackTime - 300; + if (mCaptureNativeSize == null) mCaptureNativeSize = camera.getCaptureSize(); + + PicturePreviewActivity.setImage(jpeg); + Intent intent = new Intent(CameraActivity.this, PicturePreviewActivity.class); + intent.putExtra("delay", callbackTime - mCaptureTime); + intent.putExtra("nativeWidth", mCaptureNativeSize.getWidth()); + intent.putExtra("nativeHeight", mCaptureNativeSize.getHeight()); + startActivity(intent); + + mCaptureTime = 0; + mCaptureNativeSize = null; + } + + private void onVideo(File video) { + mCapturingVideo = false; + Intent intent = new Intent(CameraActivity.this, VideoPreviewActivity.class); + intent.putExtra("video", Uri.fromFile(video)); + startActivity(intent); + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.edit: edit(); break; + case R.id.capturePhoto: capturePhoto(); break; + case R.id.captureVideo: captureVideo(); break; + case R.id.toggleCamera: toggleCamera(); break; + } + } + + private void edit() { + BottomSheetBehavior b = BottomSheetBehavior.from(controlPanel); + b.setState(BottomSheetBehavior.STATE_COLLAPSED); + } + + private void capturePhoto() { + if (mCapturingPicture) return; + mCapturingPicture = true; + mCaptureTime = System.currentTimeMillis(); + mCaptureNativeSize = camera.getCaptureSize(); + message("Capturing picture...", false); + camera.capturePicture(); + } + + private void captureVideo() { + if (camera.getSessionType() != SessionType.VIDEO) { + message("Can't record video while session type is 'picture'.", false); + return; + } + if (mCapturingPicture || mCapturingVideo) return; + mCapturingVideo = true; + message("Recording for 8 seconds...", true); + camera.startCapturingVideo(null, 8000); + } + + private void toggleCamera() { + if (mCapturingPicture) return; + switch (camera.toggleFacing()) { + case BACK: + message("Switched to back camera!", false); + break; + + case FRONT: + message("Switched to front camera!", false); + break; + } + } + + @Override + public void onValueChanged(Control control, Object value, String name) { + control.applyValue(camera, value); + BottomSheetBehavior b = BottomSheetBehavior.from(controlPanel); + b.setState(BottomSheetBehavior.STATE_HIDDEN); + message("Changed " + control.getName() + " to " + name, false); + } + + //region Boilerplate + + @Override + protected void onResume() { + super.onResume(); + camera.start(); + } + + @Override + protected void onPause() { + super.onPause(); + camera.stop(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + camera.destroy(); + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + boolean valid = true; + for (int grantResult : grantResults) { + valid = valid && grantResult == PackageManager.PERMISSION_GRANTED; + } + if (valid && !camera.isStarted()) { + camera.start(); + } + } + + //endregion +} diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java new file mode 100644 index 00000000..74fbccb3 --- /dev/null +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java @@ -0,0 +1,176 @@ +package com.otaliastudios.cameraview.demo; + +import android.view.View; +import android.view.ViewGroup; + +import com.otaliastudios.cameraview.Audio; +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.CameraView; +import com.otaliastudios.cameraview.Flash; +import com.otaliastudios.cameraview.Gesture; +import com.otaliastudios.cameraview.GestureAction; +import com.otaliastudios.cameraview.Grid; +import com.otaliastudios.cameraview.SessionType; +import com.otaliastudios.cameraview.VideoQuality; +import com.otaliastudios.cameraview.WhiteBalance; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +/** + * Controls that we want to display in a ControlView. + */ +public enum Control { + + WIDTH("Width", false), + HEIGHT("Height", true), + SESSION("Session type", false), + CROP_OUTPUT("Crop output", true), + FLASH("Flash", false), + WHITE_BALANCE("White balance", false), + GRID("Grid", true), + VIDEO_QUALITY("Video quality", false), + AUDIO("Audio", true), + PINCH("Pinch gesture", false), + HSCROLL("Horizontal scroll gesture", false), + VSCROLL("Vertical scroll gesture", false), + TAP("Single tap gesture", false), + LONG_TAP("Long tap gesture", true); + + private String name; + private boolean last; + + Control(String n, boolean l) { + name = n; + last = l; + } + + public String getName() { + return name; + } + + public boolean isSectionLast() { + return last; + } + + public Collection getValues(CameraView view) { + CameraOptions options = view.getCameraOptions(); + switch (this) { + case WIDTH: + case HEIGHT: + View root = (View) view.getParent(); + ArrayList list = new ArrayList<>(); + int boundary = this == WIDTH ? root.getWidth() : root.getHeight(); + if (boundary == 0) boundary = 1000; + int step = boundary / 10; + list.add(ViewGroup.LayoutParams.WRAP_CONTENT); + list.add(ViewGroup.LayoutParams.MATCH_PARENT); + for (int i = step; i < boundary; i += step) { + list.add(i); + } + return list; + case SESSION: return Arrays.asList(SessionType.values()); + case CROP_OUTPUT: return Arrays.asList(true, false); + case FLASH: return options.getSupportedFlash(); + case WHITE_BALANCE: return options.getSupportedWhiteBalance(); + case GRID: return Arrays.asList(Grid.values()); + case VIDEO_QUALITY: return Arrays.asList(VideoQuality.values()); + case AUDIO: return Arrays.asList(Audio.values()); + case PINCH: + case HSCROLL: + case VSCROLL: + ArrayList list1 = new ArrayList<>(); + addIfSupported(options, list1, GestureAction.NONE); + addIfSupported(options, list1, GestureAction.ZOOM); + addIfSupported(options, list1, GestureAction.EXPOSURE_CORRECTION); + return list1; + case TAP: + case LONG_TAP: + ArrayList list2 = new ArrayList<>(); + addIfSupported(options, list2, GestureAction.NONE); + addIfSupported(options, list2, GestureAction.CAPTURE); + addIfSupported(options, list2, GestureAction.FOCUS); + addIfSupported(options, list2, GestureAction.FOCUS_WITH_MARKER); + return list2; + + } + return null; + } + + private void addIfSupported(CameraOptions options, List list, GestureAction value) { + if (options.supports(value)) list.add(value); + } + + public Object getCurrentValue(CameraView view) { + switch (this) { + case WIDTH: return view.getLayoutParams().width; + case HEIGHT: return view.getLayoutParams().height; + case SESSION: return view.getSessionType(); + case CROP_OUTPUT: return view.getCropOutput(); + case FLASH: return view.getFlash(); + case WHITE_BALANCE: return view.getWhiteBalance(); + case GRID: return view.getGrid(); + case VIDEO_QUALITY: return view.getVideoQuality(); + case AUDIO: return view.getAudio(); + case PINCH: return view.getGestureAction(Gesture.PINCH); + case HSCROLL: return view.getGestureAction(Gesture.SCROLL_HORIZONTAL); + case VSCROLL: return view.getGestureAction(Gesture.SCROLL_VERTICAL); + case TAP: return view.getGestureAction(Gesture.TAP); + case LONG_TAP: return view.getGestureAction(Gesture.LONG_TAP); + } + return null; + } + + public void applyValue(CameraView camera, Object value) { + switch (this) { + case WIDTH: + camera.getLayoutParams().width = (int) value; + camera.setLayoutParams(camera.getLayoutParams()); + break; + case HEIGHT: + camera.getLayoutParams().height = (int) value; + camera.setLayoutParams(camera.getLayoutParams()); + break; + case SESSION: + camera.setSessionType((SessionType) value); + break; + case CROP_OUTPUT: + camera.setCropOutput((boolean) value); + break; + case FLASH: + camera.setFlash((Flash) value); + break; + case WHITE_BALANCE: + camera.setWhiteBalance((WhiteBalance) value); + break; + case GRID: + camera.setGrid((Grid) value); + break; + case VIDEO_QUALITY: + camera.setVideoQuality((VideoQuality) value); + break; + case AUDIO: + camera.setAudio((Audio) value); + break; + case PINCH: + camera.mapGesture(Gesture.PINCH, (GestureAction) value); + break; + case HSCROLL: + camera.mapGesture(Gesture.SCROLL_HORIZONTAL, (GestureAction) value); + break; + case VSCROLL: + camera.mapGesture(Gesture.SCROLL_VERTICAL, (GestureAction) value); + break; + case TAP: + camera.mapGesture(Gesture.TAP, (GestureAction) value); + break; + case LONG_TAP: + camera.mapGesture(Gesture.LONG_TAP, (GestureAction) value); + break; + } + } + + +} diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/ControlView.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/ControlView.java new file mode 100644 index 00000000..bab55baa --- /dev/null +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/ControlView.java @@ -0,0 +1,103 @@ +package com.otaliastudios.cameraview.demo; + + +import android.annotation.SuppressLint; +import android.content.Context; +import android.text.TextUtils; +import android.util.Log; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.Spinner; +import android.widget.SpinnerAdapter; +import android.widget.TextView; + +import com.otaliastudios.cameraview.CameraOptions; +import com.otaliastudios.cameraview.CameraView; + +import java.util.ArrayList; +import java.util.Collection; + +public class ControlView extends LinearLayout implements Spinner.OnItemSelectedListener { + + interface Callback { + void onValueChanged(Control control, Object value, String name); + } + + private Value value; + private ArrayList values; + private ArrayList valuesStrings; + private Control control; + private Callback callback; + private Spinner spinner; + + public ControlView(Context context, Control control, Callback callback) { + super(context); + this.control = control; + this.callback = callback; + setOrientation(VERTICAL); + + inflate(context, R.layout.control_view, this); + TextView title = findViewById(R.id.title); + title.setText(control.getName()); + View divider = findViewById(R.id.divider); + divider.setVisibility(control.isSectionLast() ? View.VISIBLE : View.GONE); + + ViewGroup content = findViewById(R.id.content); + spinner = new Spinner(context, Spinner.MODE_DROPDOWN); + content.addView(spinner); + } + + public Value getValue() { + return value; + } + + @SuppressWarnings("all") + public void onCameraOpened(CameraView view) { + values = new ArrayList(control.getValues(view)); + value = (Value) control.getCurrentValue(view); + valuesStrings = new ArrayList<>(); + for (Value value : values) { + valuesStrings.add(stringify(value)); + } + + if (values.isEmpty()) { + spinner.setOnItemSelectedListener(null); + spinner.setEnabled(false); + spinner.setAlpha(0.8f); + spinner.setAdapter(new ArrayAdapter(getContext(), + R.layout.spinner_text, new String[]{ "Not supported." })); + spinner.setSelection(0, false); + } else { + spinner.setEnabled(true); + spinner.setAlpha(1f); + spinner.setAdapter(new ArrayAdapter(getContext(), + R.layout.spinner_text, valuesStrings)); + spinner.setSelection(values.indexOf(value), false); + spinner.setOnItemSelectedListener(this); + } + } + + @Override + public void onItemSelected(AdapterView adapterView, View view, int i, long l) { + if (values.get(i) != value) { + Log.e("ControlView", "curr: " + value + " new: " + values.get(i)); + callback.onValueChanged(control, values.get(i), valuesStrings.get(i)); + } + } + + @Override + public void onNothingSelected(AdapterView adapterView) {} + + private String stringify(Value value) { + if (value instanceof Integer) { + if ((Integer) value == ViewGroup.LayoutParams.MATCH_PARENT) return "match parent"; + if ((Integer) value == ViewGroup.LayoutParams.WRAP_CONTENT) return "wrap content"; + } + return String.valueOf(value).replace("_", " ").toLowerCase(); + } +} diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/MainActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/MainActivity.java deleted file mode 100644 index 8c3cee08..00000000 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/MainActivity.java +++ /dev/null @@ -1,375 +0,0 @@ -package com.otaliastudios.cameraview.demo; - -import android.content.Intent; -import android.location.Location; -import android.net.Uri; -import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.widget.Button; -import android.widget.EditText; -import android.widget.RadioGroup; -import android.widget.TextView; -import android.widget.Toast; - -import com.otaliastudios.cameraview.CameraListener; -import com.otaliastudios.cameraview.CameraLogger; -import com.otaliastudios.cameraview.CameraView; -import com.otaliastudios.cameraview.Grid; -import com.otaliastudios.cameraview.SessionType; -import com.otaliastudios.cameraview.Size; -import com.otaliastudios.cameraview.VideoQuality; - -import java.io.File; - -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; - -public class MainActivity extends AppCompatActivity implements View.OnLayoutChangeListener { - - @BindView(R.id.activity_main) - ViewGroup parent; - - @BindView(R.id.camera) - CameraView camera; - - // Capture Mode: - @BindView(R.id.sessionTypeRadioGroup) - RadioGroup sessionTypeRadioGroup; - - // Crop Mode: - @BindView(R.id.cropModeRadioGroup) - RadioGroup cropModeRadioGroup; - - // Video Quality: - @BindView(R.id.videoQualityRadioGroup) - RadioGroup videoQualityRadioGroup; - - // Grid mode: - @BindView(R.id.gridModeRadioGroup) - RadioGroup gridModeRadioGroup; - - // Width: - @BindView(R.id.screenWidth) - TextView screenWidth; - @BindView(R.id.width) - EditText width; - @BindView(R.id.widthUpdate) - Button widthUpdate; - @BindView(R.id.widthModeRadioGroup) - RadioGroup widthModeRadioGroup; - - // Height: - @BindView(R.id.screenHeight) - TextView screenHeight; - @BindView(R.id.height) - EditText height; - @BindView(R.id.heightUpdate) - Button heightUpdate; - @BindView(R.id.heightModeRadioGroup) - RadioGroup heightModeRadioGroup; - - private boolean mCapturingPicture; - private boolean mCapturingVideo; - - // To show stuff in the callback - private Size mCaptureNativeSize; - private long mCaptureTime; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main); - ButterKnife.bind(this); - CameraLogger.setLogLevel(CameraLogger.LEVEL_VERBOSE); - - parent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - screenWidth.setText("screen: " + parent.getWidth() + "px"); - screenHeight.setText("screen: " + parent.getHeight() + "px"); - } - }); - - sessionTypeRadioGroup.setOnCheckedChangeListener(sessionTypeChangedListener); - cropModeRadioGroup.setOnCheckedChangeListener(cropModeChangedListener); - widthModeRadioGroup.setOnCheckedChangeListener(widthModeChangedListener); - heightModeRadioGroup.setOnCheckedChangeListener(heightModeChangedListener); - videoQualityRadioGroup.setOnCheckedChangeListener(videoQualityChangedListener); - gridModeRadioGroup.setOnCheckedChangeListener(gridModeChangedListener); - - camera.addOnLayoutChangeListener(this); - camera.addCameraListener(new CameraListener() { - @Override - public void onPictureTaken(byte[] jpeg) { - super.onPictureTaken(jpeg); - mCapturingPicture = false; - long callbackTime = System.currentTimeMillis(); - if (mCapturingVideo) { - message("Captured while taking video. Size="+mCaptureNativeSize, false); - return; - } - PicturePreviewActivity.setImage(jpeg); - Intent intent = new Intent(MainActivity.this, PicturePreviewActivity.class); - intent.putExtra("delay", callbackTime - mCaptureTime); - intent.putExtra("nativeWidth", mCaptureNativeSize.getWidth()); - intent.putExtra("nativeHeight", mCaptureNativeSize.getHeight()); - startActivity(intent); - } - - @Override - public void onVideoTaken(File video) { - super.onVideoTaken(video); - mCapturingVideo = false; - Intent intent = new Intent(MainActivity.this, VideoPreviewActivity.class); - intent.putExtra("video", Uri.fromFile(video)); - startActivity(intent); - } - }); - - // Debug location. - /* camera.postDelayed(new Runnable() { - @Override - public void run() { - message("set Location", false); - camera.setLocation(-20, 40.12345); - } - }, 4500); */ - } - - private void message(String content, boolean important) { - int length = important ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT; - Toast.makeText(this, content, length).show(); - } - - @Override - protected void onResume() { - super.onResume(); - camera.start(); - } - - @Override - protected void onPause() { - super.onPause(); - camera.stop(); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - camera.destroy(); - } - - - @OnClick(R.id.capturePhoto) - void capturePhoto() { - if (mCapturingPicture) return; - mCapturingPicture = true; - mCaptureTime = System.currentTimeMillis(); - mCaptureNativeSize = camera.getCaptureSize(); - message("Capturing picture...", false); - camera.capturePicture(); - } - - @OnClick(R.id.captureVideo) - void captureVideo() { - if (camera.getSessionType() != SessionType.VIDEO) { - message("Can't record video while session type is 'picture'.", false); - return; - } - if (mCapturingPicture || mCapturingVideo) return; - mCapturingVideo = true; - message("Recording for 8 seconds...", true); - camera.startCapturingVideo(null, 8000); - } - - @OnClick(R.id.toggleCamera) - void toggleCamera() { - if (mCapturingPicture) return; - switch (camera.toggleFacing()) { - case BACK: - message("Switched to back camera!", false); - break; - - case FRONT: - message("Switched to front camera!", false); - break; - } - } - - @OnClick(R.id.toggleFlash) - void toggleFlash() { - if (mCapturingPicture) return; - switch (camera.toggleFlash()) { - case ON: - message("Flash on!", false); - break; - - case OFF: - message("Flash off!", false); - break; - - case AUTO: - message("Flash auto!", false); - break; - } - } - - RadioGroup.OnCheckedChangeListener sessionTypeChangedListener = new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturingPicture) return; - boolean pic = checkedId == R.id.sessionTypePicture; - camera.setSessionType(pic ? SessionType.PICTURE : SessionType.VIDEO); - message("Session type set to" + (pic ? " picture!" : " video!"), true); - } - }; - - RadioGroup.OnCheckedChangeListener cropModeChangedListener = new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturingPicture) return; - camera.setCropOutput(checkedId == R.id.modeCropVisible); - message("Picture cropping is" + (checkedId == R.id.modeCropVisible ? " on!" : " off!"), false); - } - }; - - RadioGroup.OnCheckedChangeListener videoQualityChangedListener = new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturingVideo) return; - VideoQuality videoQuality = VideoQuality.HIGHEST; - switch (checkedId) { - case R.id.videoQualityLowest: videoQuality = VideoQuality.LOWEST; break; - case R.id.videoQualityQvga: videoQuality = VideoQuality.MAX_QVGA; break; - case R.id.videoQuality480p: videoQuality = VideoQuality.MAX_480P; break; - case R.id.videoQuality720p: videoQuality = VideoQuality.MAX_720P; break; - case R.id.videoQuality1080p: videoQuality = VideoQuality.MAX_1080P; break; - case R.id.videoQuality2160p: videoQuality = VideoQuality.MAX_2160P; break; - case R.id.videoQualityHighest: videoQuality = VideoQuality.HIGHEST; break; - } - camera.setVideoQuality(videoQuality); - message("Video quality changed!", false); - } - }; - - - RadioGroup.OnCheckedChangeListener gridModeChangedListener = new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - Grid grid = Grid.OFF; - switch (checkedId) { - case R.id.gridModeOff: grid = Grid.OFF; break; - case R.id.gridMode3x3: grid = Grid.DRAW_3X3; break; - case R.id.gridMode4x4: grid = Grid.DRAW_4X4; break; - case R.id.gridModeGolden: grid = Grid.DRAW_PHI; break; - } - camera.setGrid(grid); - message("Grid mode changed!", false); - } - }; - - - @OnClick(R.id.widthUpdate) - void widthUpdateClicked() { - if (mCapturingPicture) return; - if (widthUpdate.getAlpha() >= 1) { - updateCamera(true, false); - } - } - - RadioGroup.OnCheckedChangeListener widthModeChangedListener = new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturingPicture) return; - widthUpdate.setEnabled(checkedId == R.id.widthCustom); - widthUpdate.setAlpha(checkedId == R.id.widthCustom ? 1f : 0.3f); - width.clearFocus(); - width.setEnabled(checkedId == R.id.widthCustom); - width.setAlpha(checkedId == R.id.widthCustom ? 1f : 0.5f); - - updateCamera(true, false); - } - }; - - @OnClick(R.id.heightUpdate) - void heightUpdateClicked() { - if (mCapturingPicture) return; - if (heightUpdate.getAlpha() >= 1) { - updateCamera(false, true); - } - } - - RadioGroup.OnCheckedChangeListener heightModeChangedListener = new RadioGroup.OnCheckedChangeListener() { - @Override - public void onCheckedChanged(RadioGroup group, int checkedId) { - if (mCapturingPicture) return; - heightUpdate.setEnabled(checkedId == R.id.heightCustom); - heightUpdate.setAlpha(checkedId == R.id.heightCustom ? 1f : 0.3f); - height.clearFocus(); - height.setEnabled(checkedId == R.id.heightCustom); - height.setAlpha(checkedId == R.id.heightCustom ? 1f : 0.5f); - - updateCamera(false, true); - } - }; - - private void updateCamera(boolean updateWidth, boolean updateHeight) { - if (mCapturingPicture) return; - ViewGroup.LayoutParams cameraLayoutParams = camera.getLayoutParams(); - int width = cameraLayoutParams.width; - int height = cameraLayoutParams.height; - - if (updateWidth) { - switch (widthModeRadioGroup.getCheckedRadioButtonId()) { - case R.id.widthCustom: - String widthInput = this.width.getText().toString(); - try { width = Integer.valueOf(widthInput); } catch (Exception e) {} - break; - case R.id.widthWrapContent: - width = ViewGroup.LayoutParams.WRAP_CONTENT; - break; - case R.id.widthMatchParent: - width = ViewGroup.LayoutParams.MATCH_PARENT; - break; - } - } - - if (updateHeight) { - switch (heightModeRadioGroup.getCheckedRadioButtonId()) { - case R.id.heightCustom: - String heightInput = this.height.getText().toString(); - try { height = Integer.valueOf(heightInput); } catch (Exception e) {} - break; - case R.id.heightWrapContent: - height = ViewGroup.LayoutParams.WRAP_CONTENT; - break; - case R.id.heightMatchParent: - // We are in a vertically scrolling container, match parent would not work at all. - height = parent.getHeight(); - break; - } - } - - cameraLayoutParams.width = width; - cameraLayoutParams.height = height; - camera.addOnLayoutChangeListener(this); - camera.setLayoutParams(cameraLayoutParams); - - String what = (updateWidth && updateHeight ? "Width and height" : updateWidth ? "Width" : "Height"); - message(what + " updated! Internal preview size: " + camera.getPreviewSize(), false); - } - - @Override - public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) { - int mCameraWidth = right - left; - int mCameraHeight = bottom - top; - width.setText(String.valueOf(mCameraWidth)); - height.setText(String.valueOf(mCameraHeight)); - camera.removeOnLayoutChangeListener(this); - } - -} diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/MessageView.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/MessageView.java new file mode 100644 index 00000000..d123ceea --- /dev/null +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/MessageView.java @@ -0,0 +1,50 @@ +package com.otaliastudios.cameraview.demo; + + +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.Spinner; +import android.widget.TextView; + +import com.otaliastudios.cameraview.CameraView; + +import java.util.ArrayList; + +public class MessageView extends LinearLayout { + + private TextView message; + private TextView title; + + public MessageView(Context context) { + this(context, null); + } + + public MessageView(Context context, @Nullable AttributeSet attrs) { + this(context, attrs, 0); + } + + public MessageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setOrientation(VERTICAL); + inflate(context, R.layout.control_view, this); + ViewGroup content = findViewById(R.id.content); + inflate(context, R.layout.spinner_text, content); + title = findViewById(R.id.title); + message = (TextView) content.getChildAt(0); + } + + public void setTitle(String title) { + this.title.setText(title); + } + + public void setMessage(String message) { + this.message.setText(message); + } +} diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java index 0426be27..4770fdca 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java @@ -12,26 +12,9 @@ import com.otaliastudios.cameraview.CameraUtils; import java.lang.ref.WeakReference; -import butterknife.BindView; -import butterknife.ButterKnife; public class PicturePreviewActivity extends Activity { - @BindView(R.id.image) - ImageView imageView; - - @BindView(R.id.nativeCaptureResolution) - TextView nativeCaptureResolution; - - @BindView(R.id.actualResolution) - TextView actualResolution; - - @BindView(R.id.approxUncompressedSize) - TextView approxUncompressedSize; - - @BindView(R.id.captureLatency) - TextView captureLatency; - private static WeakReference image; public static void setImage(@Nullable byte[] im) { @@ -42,7 +25,11 @@ public class PicturePreviewActivity extends Activity { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_picture_preview); - ButterKnife.bind(this); + final ImageView imageView = findViewById(R.id.image); + final MessageView nativeCaptureResolution = findViewById(R.id.nativeCaptureResolution); + final MessageView actualResolution = findViewById(R.id.actualResolution); + final MessageView approxUncompressedSize = findViewById(R.id.approxUncompressedSize); + final MessageView captureLatency = findViewById(R.id.captureLatency); final long delay = getIntent().getLongExtra("delay", 0); final int nativeWidth = getIntent().getIntExtra("nativeWidth", 0); @@ -57,14 +44,21 @@ public class PicturePreviewActivity extends Activity { @Override public void onBitmapReady(Bitmap bitmap) { imageView.setImageBitmap(bitmap); - approxUncompressedSize.setText(getApproximateFileMegabytes(bitmap) + "MB"); - captureLatency.setText(delay + " milliseconds"); + + approxUncompressedSize.setTitle("Approx. uncompressed size"); + approxUncompressedSize.setMessage(getApproximateFileMegabytes(bitmap) + "MB"); + + captureLatency.setTitle("Capture latency"); + captureLatency.setMessage(delay + " milliseconds"); // ncr and ar might be different when cropOutput is true. AspectRatio nativeRatio = AspectRatio.of(nativeWidth, nativeHeight); AspectRatio finalRatio = AspectRatio.of(bitmap.getWidth(), bitmap.getHeight()); - nativeCaptureResolution.setText(nativeWidth + "x" + nativeHeight + " (" + nativeRatio + ")"); - actualResolution.setText(bitmap.getWidth() + "x" + bitmap.getHeight() + " (" + finalRatio + ")"); + nativeCaptureResolution.setTitle("Native capture resolution"); + nativeCaptureResolution.setMessage(nativeWidth + "x" + nativeHeight + " (" + nativeRatio + ")"); + + actualResolution.setTitle("Actual resolution"); + actualResolution.setMessage(bitmap.getWidth() + "x" + bitmap.getHeight() + " (" + finalRatio + ")"); } }); diff --git a/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java b/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java index 23bfced9..59a870d9 100644 --- a/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java +++ b/demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java @@ -5,28 +5,29 @@ import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.support.annotation.Nullable; +import android.view.View; import android.view.ViewGroup; import android.widget.MediaController; import android.widget.TextView; import android.widget.VideoView; -import butterknife.BindView; -import butterknife.ButterKnife; -import butterknife.OnClick; public class VideoPreviewActivity extends Activity { - @BindView(R.id.video) - VideoView videoView; - - @BindView(R.id.actualResolution) - TextView actualResolution; + private VideoView videoView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_preview); - ButterKnife.bind(this); + videoView = findViewById(R.id.video); + videoView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + playVideo(); + } + }); + final MessageView actualResolution = findViewById(R.id.actualResolution); Uri videoUri = getIntent().getParcelableExtra("video"); MediaController controller = new MediaController(this); @@ -38,7 +39,8 @@ public class VideoPreviewActivity extends Activity { videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { - actualResolution.setText(mp.getVideoWidth() + " x " + mp.getVideoHeight()); + actualResolution.setTitle("Actual resolution"); + actualResolution.setMessage(mp.getVideoWidth() + " x " + mp.getVideoHeight()); ViewGroup.LayoutParams lp = videoView.getLayoutParams(); float videoWidth = mp.getVideoWidth(); float videoHeight = mp.getVideoHeight(); @@ -50,8 +52,6 @@ public class VideoPreviewActivity extends Activity { }); } - - @OnClick(R.id.video) void playVideo() { if (videoView.isPlaying()) return; videoView.start(); diff --git a/demo/src/main/res/drawable/background.xml b/demo/src/main/res/drawable/background.xml index 548d368c..19bc1308 100644 --- a/demo/src/main/res/drawable/background.xml +++ b/demo/src/main/res/drawable/background.xml @@ -1,6 +1,6 @@ - - - - + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/ic_flash.xml b/demo/src/main/res/drawable/ic_edit.xml similarity index 57% rename from demo/src/main/res/drawable/ic_flash.xml rename to demo/src/main/res/drawable/ic_edit.xml index e9364686..35a774a5 100644 --- a/demo/src/main/res/drawable/ic_flash.xml +++ b/demo/src/main/res/drawable/ic_edit.xml @@ -5,5 +5,5 @@ android:viewportHeight="24.0"> + android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> diff --git a/demo/src/main/res/layout/activity_camera.xml b/demo/src/main/res/layout/activity_camera.xml new file mode 100644 index 00000000..fdc520ff --- /dev/null +++ b/demo/src/main/res/layout/activity_camera.xml @@ -0,0 +1,100 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/src/main/res/layout/activity_main.xml b/demo/src/main/res/layout/activity_main.xml deleted file mode 100644 index e6588043..00000000 --- a/demo/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,555 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -