Changes in the demo app

v2
Mattia Iavarone 6 years ago
parent 8d54e149f8
commit 44e600b283
  1. 5
      MIGRATION.md
  2. 3
      cameraview/src/androidTest/java/com/otaliastudios/cameraview/VideoResultTest.java
  3. 9
      cameraview/src/main/java/com/otaliastudios/cameraview/AspectRatio.java
  4. 8
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraController.java
  5. 22
      cameraview/src/main/java/com/otaliastudios/cameraview/CameraView.java
  6. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/FullVideoRecorder.java
  7. 11
      cameraview/src/main/java/com/otaliastudios/cameraview/PictureResult.java
  8. 2
      cameraview/src/main/java/com/otaliastudios/cameraview/VideoResult.java
  9. 24
      cameraview/src/main/views/com/otaliastudios/cameraview/GridLinesLayout.java
  10. 10
      demo/src/main/java/com/otaliastudios/cameraview/demo/CameraActivity.java
  11. 58
      demo/src/main/java/com/otaliastudios/cameraview/demo/Control.java
  12. 2
      demo/src/main/java/com/otaliastudios/cameraview/demo/ControlView.java
  13. 5
      demo/src/main/java/com/otaliastudios/cameraview/demo/MessageView.java
  14. 51
      demo/src/main/java/com/otaliastudios/cameraview/demo/PicturePreviewActivity.java
  15. 45
      demo/src/main/java/com/otaliastudios/cameraview/demo/VideoPreviewActivity.java
  16. 13
      demo/src/main/res/layout/activity_picture_preview.xml
  17. 35
      demo/src/main/res/layout/activity_video_preview.xml
  18. 40
      demo/src/main/res/layout/control_view.xml

@ -56,6 +56,5 @@
If device has no BACK cameras, defaults to FRONT.
- Added CameraView.setGridColor() and app:cameraGridColor.
TODO: document this
TODO: revisit the demo app, change the controls appearance, add missing controls,
add all information from the VideoResult in the VideoPreviewActivity, same for pictures
- UI Changes in the demo app, changed controls appearance, added some missing controls.
added all information from the VideoResult in the VideoPreviewActivity, same for pictures

@ -1,7 +1,6 @@
package com.otaliastudios.cameraview;
import android.hardware.Camera;
import android.location.Location;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@ -54,7 +53,7 @@ public class VideoResultTest extends BaseTest {
assertEquals(result.getFile(), file);
assertEquals(result.getRotation(), rotation);
assertEquals(result.getSize(), size);
assertEquals(result.getCodec(), codec);
assertEquals(result.getVideoCodec(), codec);
assertEquals(result.getLocation(), location);
assertEquals(result.isSnapshot(), isSnapshot);
assertEquals(result.getMaxSize(), maxFileSize);

@ -15,6 +15,15 @@ public class AspectRatio implements Comparable<AspectRatio> {
final static HashMap<String, AspectRatio> sCache = new HashMap<>(16);
/**
* Creates an aspect ratio for the given size.
* @param size the size
* @return a (possibly cached) aspect ratio
*/
public static AspectRatio of(Size size) {
return AspectRatio.of(size.getWidth(), size.getHeight());
}
/**
* Creates an aspect ratio with the given values.
* @param x the width

@ -371,6 +371,10 @@ abstract class CameraController implements
return mVideoCodec;
}
final int getVideoBitRate() {
return mVideoBitRate;
}
final long getVideoMaxSize() {
return mVideoMaxSize;
}
@ -395,6 +399,10 @@ abstract class CameraController implements
return mAudio;
}
final int getAudioBitRate() {
return mAudioBitRate;
}
/* for tests */ final SizeSelector getPictureSizeSelector() {
return mPictureSizeSelector;
}

@ -826,6 +826,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mGridLinesLayout.setGridColor(color);
}
/**
* Returns the current grid color.
* @return the current grid color
*/
public int getGridColor() {
return mGridLinesLayout.getGridColor();
}
/**
* Controls the grids to be drawn over the current layout.
@ -1119,6 +1126,14 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraController.setVideoBitRate(bitRate);
}
/**
* Returns the current video bit rate.
* @return current bit rate
*/
public int getVideoBitRate() {
return mCameraController.getVideoBitRate();
}
/**
* Sets the bit rate in bits per second for audio capturing.
* Will be used by both {@link #takeVideo(File)} and {@link #takeVideoSnapshot(File)}.
@ -1129,6 +1144,13 @@ public class CameraView extends FrameLayout implements LifecycleObserver {
mCameraController.setAudioBitRate(bitRate);
}
/**
* Returns the current audio bit rate.
* @return current bit rate
*/
public int getAudioBitRate() {
return mCameraController.getAudioBitRate();
}
/**
* Adds a {@link CameraListener} instance to be notified of all

@ -45,7 +45,7 @@ class FullVideoRecorder extends VideoRecorder {
mMediaRecorder.setVideoFrameRate(mResult.videoFrameRate);
}
mMediaRecorder.setVideoSize(size.getWidth(), size.getHeight());
switch (mResult.getCodec()) {
switch (mResult.getVideoCodec()) {
case H_263: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); break;
case H_264: mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); break;
case DEVICE_DEFAULT: mMediaRecorder.setVideoEncoder(mProfile.videoCodec); break;

@ -82,4 +82,15 @@ public class PictureResult {
public void asBitmap(int maxWidth, int maxHeight, BitmapCallback callback) {
CameraUtils.decodeBitmap(getJpeg(), maxWidth, maxHeight, callback);
}
/**
* Shorthand for {@link CameraUtils#decodeBitmap(byte[], BitmapCallback)}.
* Decodes this picture on a background thread and posts the result in the UI thread using
* the given callback.
*
* @param callback a callback to be notified of image decoding
*/
public void asBitmap(BitmapCallback callback) {
CameraUtils.decodeBitmap(getJpeg(), callback);
}
}

@ -88,7 +88,7 @@ public class VideoResult {
* @return the video codec
*/
@NonNull
public VideoCodec getCodec() {
public VideoCodec getVideoCodec() {
return codec;
}

@ -14,17 +14,18 @@ import android.view.View;
class GridLinesLayout extends View {
private final static float GOLDEN_RATIO_INV = 0.61803398874989f;
public final static int DEFAULT_COLOR = Color.argb(160, 255, 255, 255);
private Grid gridMode;
private int gridColor = DEFAULT_COLOR;
private Drawable horiz;
private Drawable vert;
private ColorDrawable horiz;
private ColorDrawable vert;
private final float width;
Task<Integer> drawTask = new Task<>();
private final static float GOLDEN_RATIO_INV = 0.61803398874989f;
public final static int DEFAULT_COLOR = Color.argb(160, 255, 255, 255);
public GridLinesLayout(@NonNull Context context) {
this(context, null);
@ -32,8 +33,8 @@ class GridLinesLayout extends View {
public GridLinesLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
horiz = new ColorDrawable(DEFAULT_COLOR);
vert = new ColorDrawable(DEFAULT_COLOR);
horiz = new ColorDrawable(gridColor);
vert = new ColorDrawable(gridColor);
width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0.9f, context.getResources().getDisplayMetrics());
}
@ -54,11 +55,16 @@ class GridLinesLayout extends View {
}
public void setGridColor(@ColorInt int gridColor) {
horiz = new ColorDrawable(gridColor);
vert = new ColorDrawable(gridColor);
this.gridColor = gridColor;
horiz.setColor(gridColor);
vert.setColor(gridColor);
postInvalidate();
}
public int getGridColor() {
return gridColor;
}
private int getLineCount() {
switch (gridMode) {
case OFF: return 0;

@ -44,7 +44,7 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
camera.addCameraListener(new CameraListener() {
public void onCameraOpened(CameraOptions options) { onOpened(); }
public void onPictureTaken(PictureResult result) { onPicture(result); }
public void onVideoTaken(VideoResult result) { onVideo(result.getFile()); }
public void onVideoTaken(VideoResult result) { onVideo(result); }
public void onCameraError(@NonNull CameraException exception) {
onError(exception);
}
@ -102,18 +102,16 @@ public class CameraActivity extends AppCompatActivity implements View.OnClickLis
// This can happen if picture was taken with a gesture.
long callbackTime = System.currentTimeMillis();
if (mCaptureTime == 0) mCaptureTime = callbackTime - 300;
PicturePreviewActivity.setImage(result.getJpeg());
PicturePreviewActivity.setPictureResult(result);
Intent intent = new Intent(CameraActivity.this, PicturePreviewActivity.class);
intent.putExtra("delay", callbackTime - mCaptureTime);
intent.putExtra("nativeWidth", result.getSize().getWidth());
intent.putExtra("nativeHeight", result.getSize().getHeight());
startActivity(intent);
mCaptureTime = 0;
}
private void onVideo(File video) {
private void onVideo(VideoResult video) {
VideoPreviewActivity.setVideoResult(video);
Intent intent = new Intent(CameraActivity.this, VideoPreviewActivity.class);
intent.putExtra("video", Uri.fromFile(video));
startActivity(intent);
}

@ -1,5 +1,6 @@
package com.otaliastudios.cameraview.demo;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
@ -12,6 +13,7 @@ import com.otaliastudios.cameraview.GestureAction;
import com.otaliastudios.cameraview.Grid;
import com.otaliastudios.cameraview.Hdr;
import com.otaliastudios.cameraview.Mode;
import com.otaliastudios.cameraview.VideoCodec;
import com.otaliastudios.cameraview.WhiteBalance;
import java.util.ArrayList;
@ -25,17 +27,26 @@ public enum Control {
WIDTH("Width", false),
HEIGHT("Height", true),
MODE("Mode", false),
FLASH("Flash", false),
WHITE_BALANCE("White balance", false),
GRID("Grid", true),
HDR("Hdr", false),
HDR("Hdr", true),
GRID("Grid lines", false),
GRID_COLOR("Grid color", true),
// TODO audio bitRate
// TODO video bitRate
// THey are a bit annoying because it's not clear what the default should be.
VIDEO_CODEC("Video codec", 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);
PINCH("Pinch", false),
HSCROLL("Horizontal scroll", false),
VSCROLL("Vertical scroll", false),
TAP("Single tap", false),
LONG_TAP("Long tap", true);
private String name;
private boolean last;
@ -76,6 +87,7 @@ public enum Control {
case HDR: return options.getSupportedControls(Hdr.class);
case GRID: return options.getSupportedControls(Grid.class);
case AUDIO: return options.getSupportedControls(Audio.class);
case VIDEO_CODEC: return options.getSupportedControls(VideoCodec.class);
case PINCH:
case HSCROLL:
case VSCROLL:
@ -92,7 +104,13 @@ public enum Control {
addIfSupported(options, list2, GestureAction.FOCUS);
addIfSupported(options, list2, GestureAction.FOCUS_WITH_MARKER);
return list2;
case GRID_COLOR:
ArrayList<GridColor> list3 = new ArrayList<>();
list3.add(new GridColor(Color.argb(160, 255, 255, 255), "default"));
list3.add(new GridColor(Color.WHITE, "white"));
list3.add(new GridColor(Color.BLACK, "black"));
list3.add(new GridColor(Color.YELLOW, "yellow"));
return list3;
}
return null;
}
@ -109,7 +127,9 @@ public enum Control {
case FLASH: return view.getFlash();
case WHITE_BALANCE: return view.getWhiteBalance();
case GRID: return view.getGrid();
case GRID_COLOR: return new GridColor(view.getGridColor(), "color");
case AUDIO: return view.getAudio();
case VIDEO_CODEC: return view.getVideoCodec();
case HDR: return view.getHdr();
case PINCH: return view.getGestureAction(Gesture.PINCH);
case HSCROLL: return view.getGestureAction(Gesture.SCROLL_HORIZONTAL);
@ -135,6 +155,7 @@ public enum Control {
case WHITE_BALANCE:
case GRID:
case AUDIO:
case VIDEO_CODEC:
case HDR:
camera.set((com.otaliastudios.cameraview.Control) value);
break;
@ -153,8 +174,29 @@ public enum Control {
case LONG_TAP:
camera.mapGesture(Gesture.LONG_TAP, (GestureAction) value);
break;
case GRID_COLOR:
camera.setGridColor(((GridColor) value).color);
}
}
static class GridColor {
int color;
String name;
GridColor(int color, String name) {
this.color = color;
this.name = name;
}
@Override
public String toString() {
return name;
}
@Override
public boolean equals(Object obj) {
return obj instanceof GridColor && color == ((GridColor) obj).color;
}
}
}

@ -84,7 +84,7 @@ public class ControlView<Value> extends LinearLayout implements Spinner.OnItemSe
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (values.get(i) != value) {
if (!values.get(i).equals(value)) {
Log.e("ControlView", "curr: " + value + " new: " + values.get(i));
if (!callback.onValueChanged(control, values.get(i), valuesStrings.get(i))) {
spinner.setSelection(values.indexOf(value)); // Go back.

@ -40,6 +40,11 @@ public class MessageView extends LinearLayout {
message = (TextView) content.getChildAt(0);
}
public void setTitleAndMessage(String title, String message) {
setTitle(title);
setMessage(message);
}
public void setTitle(String title) {
this.title.setText(title);
}

@ -9,15 +9,16 @@ import android.widget.ImageView;
import com.otaliastudios.cameraview.AspectRatio;
import com.otaliastudios.cameraview.BitmapCallback;
import com.otaliastudios.cameraview.CameraUtils;
import com.otaliastudios.cameraview.PictureResult;
import java.lang.ref.WeakReference;
public class PicturePreviewActivity extends Activity {
private static WeakReference<byte[]> image;
private static WeakReference<PictureResult> image;
public static void setImage(@Nullable byte[] im) {
public static void setPictureResult(@Nullable PictureResult im) {
image = im != null ? new WeakReference<>(im) : null;
}
@ -26,46 +27,32 @@ public class PicturePreviewActivity extends Activity {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_preview);
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 captureResolution = findViewById(R.id.nativeCaptureResolution);
final MessageView captureLatency = findViewById(R.id.captureLatency);
final long delay = getIntent().getLongExtra("delay", 0);
final int nativeWidth = getIntent().getIntExtra("nativeWidth", 0);
final int nativeHeight = getIntent().getIntExtra("nativeHeight", 0);
byte[] b = image == null ? null : image.get();
if (b == null) {
final MessageView exifRotation = findViewById(R.id.exifRotation);
PictureResult result = image == null ? null : image.get();
if (result == null) {
finish();
return;
}
CameraUtils.decodeBitmap(b, 1000, 1000, new BitmapCallback() {
final long delay = getIntent().getLongExtra("delay", 0);
AspectRatio ratio = AspectRatio.of(result.getSize());
captureLatency.setTitleAndMessage("Approx. latency", delay + " milliseconds");
captureResolution.setTitleAndMessage("Resolution", result.getSize() + " (" + ratio + ")");
exifRotation.setTitleAndMessage("EXIF rotation", result.getRotation() + "");
result.asBitmap(1000, 1000, new BitmapCallback() {
@Override
public void onBitmapReady(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
// approxUncompressedSize.setTitle("Approx. uncompressed size");
// approxUncompressedSize.setMessage(getApproximateFileMegabytes(bitmap) + "MB");
captureLatency.setTitle("Approx. capture latency");
captureLatency.setMessage(delay + " milliseconds");
// ncr and ar might be different when cropOutput is true.
AspectRatio nativeRatio = AspectRatio.of(nativeWidth, nativeHeight);
nativeCaptureResolution.setTitle("Native capture resolution");
nativeCaptureResolution.setMessage(nativeWidth + "x" + nativeHeight + " (" + nativeRatio + ")");
// AspectRatio finalRatio = AspectRatio.of(bitmap.getWidth(), bitmap.getHeight());
// actualResolution.setTitle("Actual resolution");
// actualResolution.setMessage(bitmap.getWidth() + "x" + bitmap.getHeight() + " (" + finalRatio + ")");
}
});
}
private static float getApproximateFileMegabytes(Bitmap bitmap) {
return (bitmap.getRowBytes() * bitmap.getHeight()) / 1024 / 1024;
@Override
protected void onDestroy() {
super.onDestroy();
if (!isChangingConfigurations()) {
setPictureResult(null);
}
}
}

@ -8,14 +8,22 @@ 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 com.otaliastudios.cameraview.VideoResult;
import java.lang.ref.WeakReference;
public class VideoPreviewActivity extends Activity {
private VideoView videoView;
private static WeakReference<VideoResult> videoResult;
public static void setVideoResult(@Nullable VideoResult result) {
videoResult = result != null ? new WeakReference<>(result) : null;
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -28,19 +36,36 @@ public class VideoPreviewActivity extends Activity {
}
});
final MessageView actualResolution = findViewById(R.id.actualResolution);
final MessageView isSnapshot = findViewById(R.id.isSnapshot);
final MessageView rotation = findViewById(R.id.rotation);
final MessageView audio = findViewById(R.id.audio);
final MessageView audioBitRate = findViewById(R.id.audioBitRate);
final MessageView videoCodec = findViewById(R.id.videoCodec);
final MessageView videoBitRate = findViewById(R.id.videoBitRate);
final MessageView videoFrameRate = findViewById(R.id.videoFrameRate);
final VideoResult result = videoResult == null ? null : videoResult.get();
if (result == null) {
finish();
return;
}
Uri videoUri = getIntent().getParcelableExtra("video");
actualResolution.setTitleAndMessage("Size", result.getSize() + "");
isSnapshot.setTitleAndMessage("Snapshot", result.isSnapshot() + "");
rotation.setTitleAndMessage("Rotation", result.getRotation() + "");
audio.setTitleAndMessage("Audio", result.getAudio().name());
audioBitRate.setTitleAndMessage("Audio bit rate", result.getAudioBitRate() + " bits per sec.");
videoCodec.setTitleAndMessage("VideoCodec", result.getVideoCodec().name());
videoBitRate.setTitleAndMessage("Video bit rate", result.getVideoBitRate() + " bits per sec.");
videoFrameRate.setTitleAndMessage("Video frame rate", result.getVideoFrameRate() + " fps");
MediaController controller = new MediaController(this);
controller.setAnchorView(videoView);
controller.setMediaPlayer(videoView);
videoView.setMediaController(controller);
videoView.setVideoURI(videoUri);
videoView.setVideoURI(Uri.fromFile(result.getFile()));
videoView.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
actualResolution.setTitle("Actual resolution");
actualResolution.setMessage(mp.getVideoWidth() + " x " + mp.getVideoHeight());
ViewGroup.LayoutParams lp = videoView.getLayoutParams();
float videoWidth = mp.getVideoWidth();
float videoHeight = mp.getVideoHeight();
@ -56,4 +81,12 @@ public class VideoPreviewActivity extends Activity {
if (videoView.isPlaying()) return;
videoView.start();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (!isChangingConfigurations()) {
setVideoResult(null);
}
}
}

@ -20,18 +20,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<!-- com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/actualResolution"
android:layout_width="match_parent"
android:layout_height="wrap_content"/-->
<!-- com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/approxUncompressedSize"
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/captureLatency"
android:layout_width="match_parent"
android:layout_height="wrap_content"/-->
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/captureLatency"
android:id="@+id/exifRotation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

@ -19,5 +19,40 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/isSnapshot"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/rotation"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/audio"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/audioBitRate"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/videoCodec"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/videoBitRate"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<com.otaliastudios.cameraview.demo.MessageView
android:id="@+id/videoFrameRate"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</ScrollView>

@ -2,27 +2,35 @@
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
style="@style/TextAppearance.AppCompat.Subhead"
android:id="@+id/title"
android:layout_marginTop="16dp"
<LinearLayout
android:orientation="horizontal"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
android:paddingRight="16dp">
<FrameLayout
android:id="@+id/content"
android:paddingTop="4dp"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
style="@style/TextAppearance.AppCompat.Subhead"
android:id="@+id/title"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/content"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"/>
</LinearLayout>
<View
android:id="@+id/divider"
android:layout_marginTop="8dp"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#44AAAAAA" />

Loading…
Cancel
Save