tons of demo updates and support cropping to visible preview

pull/1/head
Dylan McIntyre 8 years ago
parent 4567c66e9d
commit 7e3899bc2e
  1. 51
      camerakit/src/main/java/com/flurgle/camerakit/Camera1.java
  2. 8
      camerakit/src/main/java/com/flurgle/camerakit/CameraListener.java
  3. 31
      camerakit/src/main/java/com/flurgle/camerakit/CameraView.java
  4. 51
      camerakit/src/main/java/com/flurgle/camerakit/utils/CenterCrop.java
  5. 4
      demo/src/main/AndroidManifest.xml
  6. 1
      demo/src/main/java/com/flurgle/camerakit/demo/AutoUnfocusEditText.java
  7. 163
      demo/src/main/java/com/flurgle/camerakit/demo/MainActivity.java
  8. 37
      demo/src/main/java/com/flurgle/camerakit/demo/MediaHolder.java
  9. 31
      demo/src/main/java/com/flurgle/camerakit/demo/PreviewActivity.java
  10. 47
      demo/src/main/res/layout/activity_main.xml
  11. 0
      demo/src/main/res/layout/activity_preview.xml
  12. 2
      demo/src/main/res/values/strings.xml
  13. 4
      demo/src/main/res/values/styles.xml

@ -1,6 +1,5 @@
package com.flurgle.camerakit;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.hardware.Camera;
import android.media.CamcorderProfile;
@ -10,12 +9,13 @@ import android.support.annotation.NonNull;
import android.support.v4.util.SparseArrayCompat;
import android.view.SurfaceHolder;
import com.flurgle.camerakit.utils.AspectRatio;
import com.flurgle.camerakit.utils.Size;
import com.flurgle.camerakit.utils.YuvUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
@ -29,6 +29,12 @@ public class Camera1 extends CameraImpl {
FLASH_MODES.put(CameraKit.Constants.FLASH_AUTO, Camera.Parameters.FLASH_MODE_AUTO);
}
private static final SparseArrayCompat<Integer> FACING_MODES = new SparseArrayCompat<>();
static {
FACING_MODES.put(CameraKit.Constants.FACING_BACK, Camera.CameraInfo.CAMERA_FACING_BACK);
FACING_MODES.put(CameraKit.Constants.FACING_FRONT, Camera.CameraInfo.CAMERA_FACING_FRONT);
}
private File mVideoFile;
private int mCameraId;
@ -42,8 +48,8 @@ public class Camera1 extends CameraImpl {
private int mFlash;
private int mDisplayOrientation;
private SortedSet<Size> mPreviewSizes;
private SortedSet<Size> mCaptureSizes;
private TreeSet<Size> mPreviewSizes;
private TreeSet<Size> mCaptureSizes;
private MediaRecorder mMediaRecorder;
@ -105,9 +111,14 @@ public class Camera1 extends CameraImpl {
}
private boolean setFacingInternal(int facing) {
int trueValue = FACING_MODES.get(facing, -1);
if (trueValue == -1) {
return false;
}
for (int i = 0, count = Camera.getNumberOfCameras(); i < count; i++) {
Camera.getCameraInfo(i, mCameraInfo);
if (mCameraInfo.facing == facing) {
if (mCameraInfo.facing == trueValue) {
mCameraId = i;
mFacing = facing;
return true;
@ -224,11 +235,11 @@ public class Camera1 extends CameraImpl {
public void onPreviewFrame(byte[] data, Camera camera) {
new Thread(new ProcessStillTask(data, camera, mCameraInfo, new ProcessStillTask.OnStillProcessedListener() {
@Override
public void onStillProcessed(final byte[] data) {
public void onStillProcessed(final YuvImage yuv) {
getView().post(new Runnable() {
@Override
public void run() {
mCameraListener.onPictureTaken(data);
mCameraListener.onPictureTaken(yuv);
}
});
}
@ -279,14 +290,11 @@ public class Camera1 extends CameraImpl {
YuvImage yuv = new YuvImage(rotatedData, parameters.getPreviewFormat(), postWidth, postHeight, null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, postWidth, postHeight), 50, out);
onStillProcessedListener.onStillProcessed(out.toByteArray());
onStillProcessedListener.onStillProcessed(yuv);
}
interface OnStillProcessedListener {
void onStillProcessed(byte[] data);
void onStillProcessed(YuvImage yuv);
}
}
@ -390,20 +398,31 @@ public class Camera1 extends CameraImpl {
Size previewSize = chooseOptimalSize(sizes);
final Camera.Size currentSize = mCameraParameters.getPictureSize();
if (currentSize.width != previewSize.getWidth() || currentSize.height != previewSize.getHeight()) {
final Size pictureSize = mCaptureSizes.last();
Iterator<Size> iterator = mCaptureSizes.descendingIterator();
Size pictureSize;
while ((pictureSize = iterator.next()) != null) {
if (AspectRatio.of(previewSize.getWidth(), previewSize.getHeight()).matches(pictureSize)) {
break;
}
}
if (pictureSize == null) {
pictureSize = mCaptureSizes.last();
}
if (mShowingPreview) {
mCamera.stopPreview();
}
mCameraParameters.setPreviewSize(previewSize.getWidth(), previewSize.getHeight());
mPreview.setTruePreviewSize(previewSize.getWidth(), previewSize.getHeight());
mCameraParameters.setPictureSize(pictureSize.getWidth(), pictureSize.getHeight());
mCameraParameters.setRotation(calcCameraRotation(mDisplayOrientation) + (mFacing == CameraKit.Constants.FACING_FRONT ? 180 : 0));
setAutoFocusInternal(mAutoFocus);
setFlashInternal(mFlash);
//setFlashInternal(mFlash);
mCamera.setParameters(mCameraParameters);
if (mShowingPreview) {
mCamera.startPreview();

@ -1,5 +1,7 @@
package com.flurgle.camerakit;
import android.graphics.YuvImage;
import java.io.File;
public abstract class CameraListener {
@ -12,7 +14,11 @@ public abstract class CameraListener {
}
public void onPictureTaken(byte[] picture) {
public void onPictureTaken(byte[] jpeg) {
}
public void onPictureTaken(YuvImage yuv) {
}

@ -6,6 +6,8 @@ import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.annotation.NonNull;
@ -26,8 +28,11 @@ import com.flurgle.camerakit.types.Facing;
import com.flurgle.camerakit.types.Flash;
import com.flurgle.camerakit.types.PictureMode;
import com.flurgle.camerakit.types.TapToFocus;
import com.flurgle.camerakit.utils.AspectRatio;
import com.flurgle.camerakit.utils.CenterCrop;
import com.flurgle.camerakit.utils.DisplayOrientationDetector;
import java.io.ByteArrayOutputStream;
import java.io.File;
import static com.flurgle.camerakit.CameraKit.Constants.FACING_BACK;
@ -371,7 +376,7 @@ public class CameraView extends FrameLayout {
}
private static class CameraListenerMiddleWare extends CameraListener {
private class CameraListenerMiddleWare extends CameraListener {
private CameraListener mCameraListener;
@ -388,9 +393,27 @@ public class CameraView extends FrameLayout {
}
@Override
public void onPictureTaken(byte[] picture) {
super.onPictureTaken(picture);
getCameraListener().onPictureTaken(picture);
public void onPictureTaken(byte[] jpeg) {
super.onPictureTaken(jpeg);
if (mCropOutput) {
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
getCameraListener().onPictureTaken(new CenterCrop(jpeg, outputRatio).getJpeg());
} else {
getCameraListener().onPictureTaken(jpeg);
}
}
@Override
public void onPictureTaken(YuvImage yuv) {
super.onPictureTaken(yuv);
if (mCropOutput) {
AspectRatio outputRatio = AspectRatio.of(getWidth(), getHeight());
getCameraListener().onPictureTaken(new CenterCrop(yuv, outputRatio).getJpeg());
} else {
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0, 0, yuv.getWidth(), yuv.getHeight()), 50, out);
getCameraListener().onPictureTaken(out.toByteArray());
}
}
@Override

@ -0,0 +1,51 @@
package com.flurgle.camerakit.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.YuvImage;
import java.io.ByteArrayOutputStream;
public class CenterCrop {
private byte[] croppedJpeg;
public CenterCrop(YuvImage yuv, AspectRatio targetRatio) {
Rect crop = getCrop(yuv.getWidth(), yuv.getHeight(), targetRatio);
ByteArrayOutputStream out = new ByteArrayOutputStream();
yuv.compressToJpeg(crop, 100, out);
this.croppedJpeg = out.toByteArray();
}
public CenterCrop(byte[] jpeg, AspectRatio targetRatio) {
Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length);
Rect crop = getCrop(bitmap.getWidth(), bitmap.getHeight(), targetRatio);
bitmap = Bitmap.createBitmap(bitmap, crop.left, crop.top, crop.width(), crop.height());
ByteArrayOutputStream out = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
this.croppedJpeg = out.toByteArray();
}
public byte[] getJpeg() {
return croppedJpeg;
}
private static Rect getCrop(int currentWidth, int currentHeight, AspectRatio targetRatio) {
AspectRatio currentRatio = AspectRatio.of(currentWidth, currentHeight);
Rect crop;
if (currentRatio.toFloat() > targetRatio.toFloat()) {
int width = (int) (currentHeight * targetRatio.toFloat());
int widthOffset = (currentWidth - width) / 2;
crop = new Rect(widthOffset, 0, currentWidth - widthOffset, currentHeight);
} else {
int height = (int) (currentWidth * targetRatio.inverse().toFloat());
int heightOffset = (currentHeight - height) / 2;
crop = new Rect(0, heightOffset, currentWidth, currentHeight - heightOffset);
}
return crop;
}
}

@ -19,6 +19,10 @@
</intent-filter>
</activity>
<activity
android:name=".PreviewActivity"
android:screenOrientation="portrait" />
</application>
</manifest>

@ -27,6 +27,7 @@ public class AutoUnFocusEditText extends EditText {
clearFocus();
return true;
}
return super.dispatchKeyEvent(event);
}

@ -1,16 +1,18 @@
package com.flurgle.camerakit.demo;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
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.CompoundButton;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
@ -26,6 +28,8 @@ import butterknife.OnCheckedChanged;
import butterknife.OnClick;
import butterknife.OnTextChanged;
import static com.flurgle.camerakit.demo.R.id.widthCustom;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.activity_main)
@ -34,29 +38,37 @@ public class MainActivity extends AppCompatActivity {
@BindView(R.id.camera)
CameraView camera;
// Capture Mode:
@BindView(R.id.modeCaptureQuality)
RadioButton modeQuality;
@BindView(R.id.modeCaptureSpeed)
RadioButton modeSpeed;
// Width:
@BindView(R.id.screenWidth)
TextView screenWidth;
@BindView(R.id.width)
EditText width;
@BindView(R.id.widthUpdate)
Button widthUpdate;
@BindView(R.id.widthRadioGroup)
RadioGroup widthRadioGroup;
@BindView(R.id.widthWrapContent)
RadioButton widthWrapContent;
@BindView(R.id.widthMatchParent)
RadioButton widthMatchParent;
// Height:
@BindView(R.id.screenHeight)
TextView screenHeight;
@BindView(R.id.height)
EditText height;
@BindView(R.id.heightUpdate)
Button heightUpdate;
@BindView(R.id.heightRadioGroup)
RadioGroup heightRadioGroup;
private int mCameraWidth;
private int mCameraHeight;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -75,7 +87,13 @@ public class MainActivity extends AppCompatActivity {
camera.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
invalidateParameters();
mCameraWidth = right - left;
mCameraHeight = bottom - top;
width.setText(String.valueOf(mCameraWidth));
height.setText(String.valueOf(mCameraHeight));
camera.removeOnLayoutChangeListener(this);
}
});
}
@ -96,10 +114,13 @@ public class MainActivity extends AppCompatActivity {
void capturePhoto() {
camera.setCameraListener(new CameraListener() {
@Override
public void onPictureTaken(byte[] picture) {
super.onPictureTaken(picture);
Bitmap bitmap = BitmapFactory.decodeByteArray(picture, 0, picture.length);
new PreviewDialog(MainActivity.this, bitmap).show();
public void onPictureTaken(byte[] jpeg) {
super.onPictureTaken(jpeg);
Bitmap bitmap = BitmapFactory.decodeByteArray(jpeg, 0, jpeg.length);
MediaHolder.dispose();
MediaHolder.setImage(bitmap);
Intent intent = new Intent(MainActivity.this, PreviewActivity.class);
startActivity(intent);
}
});
camera.capturePicture();
@ -111,8 +132,10 @@ public class MainActivity extends AppCompatActivity {
@Override
public void onVideoTaken(File video) {
super.onVideoTaken(video);
PreviewDialog previewDialog = new PreviewDialog(MainActivity.this, video);
previewDialog.show();
MediaHolder.dispose();
MediaHolder.setVideo(video);
Intent intent = new Intent(MainActivity.this, PreviewActivity.class);
startActivity(intent);
}
});
@ -166,20 +189,38 @@ public class MainActivity extends AppCompatActivity {
@OnTextChanged(value = R.id.width, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void widthChanged() {
if (width.isFocused()) {
new Handler().postDelayed(new UpdateCameraRunnable(width), 2000);
if (String.valueOf(mCameraWidth).equals(width.getText().toString())) {
widthUpdate.setAlpha(.2f);
} else {
widthUpdate.setAlpha(1f);
}
}
@OnCheckedChanged({R.id.widthCustom, R.id.widthWrapContent, R.id.widthMatchParent})
@OnClick(R.id.widthUpdate)
void widthUpdateClicked() {
if (widthUpdate.getAlpha() >= 1) {
updateCamera(true, false);
}
}
@OnCheckedChanged({widthCustom, R.id.widthWrapContent, R.id.widthMatchParent})
void widthModeChanged(CompoundButton buttonCompat, boolean checked) {
}
@OnTextChanged(value = R.id.height)
void heightChanged() {
if (height.isFocused()) {
new Handler().postDelayed(new UpdateCameraRunnable(height), 2000);
if (String.valueOf(mCameraHeight).equals(height.getText().toString())) {
heightUpdate.setAlpha(.2f);
} else {
heightUpdate.setAlpha(1f);
}
}
@OnClick(R.id.heightUpdate)
void heightUpdateClicked() {
if (heightUpdate.getAlpha() >= 1) {
updateCamera(false, true);
}
}
@ -188,50 +229,72 @@ public class MainActivity extends AppCompatActivity {
}
private void updateCamera(boolean updateWidth, boolean updateHeight) {
ViewGroup.LayoutParams cameraLayoutParams = camera.getLayoutParams();
int width = cameraLayoutParams.width;
int height = cameraLayoutParams.height;
private void invalidateParameters() {
if (!widthMatchParent.isChecked() && !widthWrapContent.isChecked()) {
width.setText(String.valueOf(camera.getWidth()));
width.setHint("pixels");
} else if (widthMatchParent.isChecked()) {
width.setHint("match_parent");
width.setText("");
} else if (widthWrapContent.isChecked()) {
width.setHint("wrap_content");
width.setText("");
}
if (updateWidth) {
switch (widthRadioGroup.getCheckedRadioButtonId()) {
case R.id.widthCustom:
String widthInput = this.width.getText().toString();
if (widthInput.length() > 0) {
try {
width = Integer.valueOf(widthInput);
} catch (Exception e) {
height.setText(String.valueOf(camera.getHeight()));
}
}
private class UpdateCameraRunnable implements Runnable {
break;
private EditText editText;
private String startText;
case R.id.widthWrapContent:
width = ViewGroup.LayoutParams.WRAP_CONTENT;
break;
public UpdateCameraRunnable(EditText editText) {
this.startText = editText.getText().toString();
case R.id.widthMatchParent:
width = ViewGroup.LayoutParams.MATCH_PARENT;
break;
}
}
@Override
public void run() {
if (startText.equals(editText.getText().toString())) {
ViewGroup.LayoutParams layoutParams = camera.getLayoutParams();
switch (editText.getId()) {
case R.id.width:
layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;
break;
if (updateHeight) {
switch (heightRadioGroup.getCheckedRadioButtonId()) {
case R.id.heightCustom:
String heightInput = this.height.getText().toString();
if (heightInput.length() > 0) {
try {
height = Integer.valueOf(heightInput);
} catch (Exception e) {
case R.id.height:
layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT;
break;
}
}
break;
camera.setLayoutParams(layoutParams);
case R.id.heightWrapContent:
height = ViewGroup.LayoutParams.WRAP_CONTENT;
break;
case R.id.heightMatchParent:
height = parent.getHeight();
break;
}
}
cameraLayoutParams.width = width;
cameraLayoutParams.height = height;
camera.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
mCameraWidth = right - left;
mCameraHeight = bottom - top;
camera.removeOnLayoutChangeListener(this);
widthChanged();
heightChanged();
}
});
camera.setLayoutParams(cameraLayoutParams);
}
}

@ -0,0 +1,37 @@
package com.flurgle.camerakit.demo;
import android.graphics.Bitmap;
import android.support.annotation.Nullable;
import java.io.File;
import java.lang.ref.WeakReference;
public class MediaHolder {
private static WeakReference<File> video;
private static WeakReference<Bitmap> image;
public static void setVideo(@Nullable File video) {
MediaHolder.video = video != null ? new WeakReference<>(video) : null;
}
@Nullable
public static File getVideo() {
return video != null ? video.get() : null;
}
public static void setImage(@Nullable Bitmap image) {
MediaHolder.image = image != null ? new WeakReference<>(image) : null;
}
@Nullable
public static Bitmap getImage() {
return image != null ? image.get() : null;
}
public static void dispose() {
setVideo(null);
setImage(null);
}
}

@ -1,11 +1,10 @@
package com.flurgle.camerakit.demo;
import android.app.Dialog;
import android.content.Context;
import android.app.Activity;
import android.graphics.Bitmap;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;
import android.widget.VideoView;
@ -15,33 +14,25 @@ import java.io.File;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PreviewDialog extends Dialog {
import static com.flurgle.camerakit.demo.R.id.video;
public class PreviewActivity extends Activity {
@BindView(R.id.image)
ImageView imageView;
@BindView(R.id.video)
@BindView(video)
VideoView videoView;
private Bitmap bitmap;
private File video;
public PreviewDialog(@NonNull Context context, Bitmap bitmap) {
super(context);
this.bitmap = bitmap;
}
public PreviewDialog(@NonNull Context context, File video) {
super(context);
this.video = video;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_preview);
setContentView(R.layout.activity_preview);
ButterKnife.bind(this);
Bitmap bitmap = MediaHolder.getImage();
File video = MediaHolder.getVideo();
if (bitmap != null) {
imageView.setImageBitmap(bitmap);
} else if (video != null) {

@ -126,7 +126,7 @@
android:id="@+id/modeCaptureSpeed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginLeft="10dp"
android:text="Speed" />
</RadioGroup>
@ -177,7 +177,7 @@
android:id="@+id/modeCropFullSize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginLeft="10dp"
android:text="Full size" />
</RadioGroup>
@ -281,14 +281,35 @@
android:textColor="@android:color/black"
android:textSize="11dp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.flurgle.camerakit.demo.AutoUnFocusEditText
android:id="@+id/width"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="120dp"
android:hint="pixels"
android:inputType="number" />
<Button
android:id="@+id/widthUpdate"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="15dp"
android:backgroundTint="@color/colorPrimary"
android:gravity="center"
android:text="UPDATE"
android:textColor="@android:color/white"
android:textSize="14dp"
android:textStyle="bold" />
</FrameLayout>
<RadioGroup
android:id="@+id/widthRadioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
@ -358,14 +379,35 @@
android:textColor="@android:color/black"
android:textSize="11dp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.flurgle.camerakit.demo.AutoUnFocusEditText
android:id="@+id/height"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginRight="120dp"
android:hint="pixels"
android:inputType="number" />
<Button
android:id="@+id/heightUpdate"
android:layout_width="90dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:layout_marginRight="15dp"
android:backgroundTint="@color/colorPrimary"
android:gravity="center"
android:text="UPDATE"
android:textColor="@android:color/white"
android:textSize="14dp"
android:textStyle="bold" />
</FrameLayout>
<RadioGroup
android:id="@+id/heightRadioGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
@ -394,7 +436,6 @@
</RadioGroup>
</LinearLayout>
</FrameLayout>

@ -1,3 +1,3 @@
<resources>
<string name="app_name">CameraKit Demo</string>
<string name="app_name">CameraKit</string>
</resources>

@ -8,4 +8,8 @@
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="Theme.PreviewActivity" parent="Theme.AppCompat.NoActionBar">
</style>
</resources>

Loading…
Cancel
Save