mirror of
https://github.com/oxen-io/session-android.git
synced 2025-02-20 05:48:26 +00:00
parent
534421eb57
commit
b0137c08cb
@ -32,6 +32,9 @@ repositories {
|
|||||||
maven { // material-dialogs
|
maven { // material-dialogs
|
||||||
url "https://jitpack.io"
|
url "https://jitpack.io"
|
||||||
}
|
}
|
||||||
|
maven { // cwac-camera
|
||||||
|
url 'https://repo.commonsware.com.s3.amazonaws.com'
|
||||||
|
}
|
||||||
jcenter()
|
jcenter()
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
}
|
}
|
||||||
@ -72,6 +75,7 @@ dependencies {
|
|||||||
exclude group: 'com.android.support', module: 'support-v4'
|
exclude group: 'com.android.support', module: 'support-v4'
|
||||||
}
|
}
|
||||||
compile 'com.madgag.spongycastle:prov:1.51.0.0'
|
compile 'com.madgag.spongycastle:prov:1.51.0.0'
|
||||||
|
compile 'com.commonsware.cwac:camera:0.6.12'
|
||||||
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
|
provided 'com.squareup.dagger:dagger-compiler:1.2.2'
|
||||||
|
|
||||||
compile 'org.whispersystems:jobmanager:1.0.2'
|
compile 'org.whispersystems:jobmanager:1.0.2'
|
||||||
@ -124,6 +128,7 @@ dependencyVerification {
|
|||||||
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
|
'com.squareup.dagger:dagger:789aca24537022e49f91fc6444078d9de8f1dd99e1bfb090f18491b186967883',
|
||||||
'com.doomonafireball.betterpickers:library:132ecd685c95a99e7377c4e27bfadbb2d7ed0bea995944060cd62d4369fdaf3d',
|
'com.doomonafireball.betterpickers:library:132ecd685c95a99e7377c4e27bfadbb2d7ed0bea995944060cd62d4369fdaf3d',
|
||||||
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
|
'com.madgag.spongycastle:prov:b8c3fec3a59aac1aa04ccf4dad7179351e54ef7672f53f508151b614c131398a',
|
||||||
|
'com.commonsware.cwac:camera:dcc93ddbb2f0393114fa1f31a13fe9e6edfcf5dbe96b22bc4b66c7b15e179054',
|
||||||
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
|
'org.whispersystems:jobmanager:506f679fc2fcf7bb6d10f00f41d6f6ea0abf75c70dc95b913398661ad538a181',
|
||||||
'org.whispersystems:libpastelog:550d33c565380d90f4c671e7b8ed5f3a6da55a9fda468373177106b2eb5220b2',
|
'org.whispersystems:libpastelog:550d33c565380d90f4c671e7b8ed5f3a6da55a9fda468373177106b2eb5220b2',
|
||||||
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',
|
'com.amulyakhare:com.amulyakhare.textdrawable:54c92b5fba38cfd316a07e5a30528068f45ce8515a6890f1297df4c401af5dcb',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.camera.CameraView
|
<org.thoughtcrime.securesms.components.camera.QuickCamera
|
||||||
android:id="@+id/quick_camera"
|
android:id="@+id/quick_camera"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
@ -54,6 +54,7 @@ import android.widget.TextView;
|
|||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.afollestad.materialdialogs.AlertDialogWrapper;
|
import com.afollestad.materialdialogs.AlertDialogWrapper;
|
||||||
|
import com.commonsware.cwac.camera.CameraHost.FailureReason;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import org.thoughtcrime.redphone.RedPhone;
|
import org.thoughtcrime.redphone.RedPhone;
|
||||||
@ -1340,7 +1341,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCameraFail() {
|
public void onCameraFail(FailureReason reason) {
|
||||||
Toast.makeText(this, R.string.ConversationActivity_quick_camera_unavailable, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.ConversationActivity_quick_camera_unavailable, Toast.LENGTH_SHORT).show();
|
||||||
quickAttachmentDrawer.hide(false);
|
quickAttachmentDrawer.hide(false);
|
||||||
quickAttachmentToggle.disable();
|
quickAttachmentToggle.disable();
|
||||||
|
@ -1,56 +0,0 @@
|
|||||||
package org.thoughtcrime.securesms.components.camera;
|
|
||||||
|
|
||||||
import android.hardware.Camera;
|
|
||||||
import android.hardware.Camera.Size;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
public class CameraUtils {
|
|
||||||
/*
|
|
||||||
* modified from: https://github.com/commonsguy/cwac-camera/blob/master/camera/src/com/commonsware/cwac/camera/CameraUtils.java
|
|
||||||
*/
|
|
||||||
public static @Nullable Size getPreferredPreviewSize(int displayOrientation,
|
|
||||||
int width,
|
|
||||||
int height,
|
|
||||||
@NonNull Camera camera) {
|
|
||||||
double targetRatio = (double)width / height;
|
|
||||||
Size optimalSize = null;
|
|
||||||
double minDiff = Double.MAX_VALUE;
|
|
||||||
|
|
||||||
if (displayOrientation == 90 || displayOrientation == 270) {
|
|
||||||
targetRatio = (double)height / width;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Size> sizes = camera.getParameters().getSupportedPreviewSizes();
|
|
||||||
|
|
||||||
Collections.sort(sizes, Collections.reverseOrder(new SizeComparator()));
|
|
||||||
|
|
||||||
for (Size size : sizes) {
|
|
||||||
double ratio = (double)size.width / size.height;
|
|
||||||
|
|
||||||
if (Math.abs(ratio - targetRatio) < minDiff) {
|
|
||||||
optimalSize = size;
|
|
||||||
minDiff = Math.abs(ratio - targetRatio);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return optimalSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SizeComparator implements Comparator<Size> {
|
|
||||||
@Override
|
|
||||||
public int compare(Size lhs, Size rhs) {
|
|
||||||
int left = lhs.width * lhs.height;
|
|
||||||
int right = rhs.width * rhs.height;
|
|
||||||
|
|
||||||
if (left < right) return -1;
|
|
||||||
if (left > right) return 1;
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,47 +20,44 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.hardware.Camera;
|
import android.hardware.Camera;
|
||||||
import android.hardware.Camera.CameraInfo;
|
import android.hardware.Camera.PreviewCallback;
|
||||||
import android.hardware.Camera.Parameters;
|
|
||||||
import android.hardware.Camera.Size;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Build.VERSION;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.OrientationEventListener;
|
import android.view.OrientationEventListener;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import com.commonsware.cwac.camera.CameraHost;
|
||||||
|
import com.commonsware.cwac.camera.CameraHost.FailureReason;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.ApplicationContext;
|
import org.thoughtcrime.securesms.ApplicationContext;
|
||||||
import org.thoughtcrime.securesms.util.BitmapUtil;
|
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.jobqueue.Job;
|
import org.whispersystems.jobqueue.Job;
|
||||||
import org.whispersystems.jobqueue.JobParameters;
|
import org.whispersystems.jobqueue.JobParameters;
|
||||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
public class CameraView extends FrameLayout {
|
public class CameraView extends FrameLayout {
|
||||||
private static final String TAG = CameraView.class.getSimpleName();
|
private static final String TAG = CameraView.class.getSimpleName();
|
||||||
|
|
||||||
private final CameraSurfaceView surface;
|
private PreviewStrategy previewStrategy = null;
|
||||||
private final OnOrientationChange onOrientationChange;
|
private Camera.Size previewSize = null;
|
||||||
|
private volatile Camera camera = null;
|
||||||
private @NonNull volatile Optional<Camera> camera = Optional.absent();
|
private boolean inPreview = false;
|
||||||
private volatile int cameraId = CameraInfo.CAMERA_FACING_BACK;
|
private boolean cameraReady = false;
|
||||||
|
private CameraHost host = null;
|
||||||
private boolean started;
|
private OnOrientationChange onOrientationChange = null;
|
||||||
private @Nullable CameraViewListener listener;
|
private int displayOrientation = -1;
|
||||||
private int displayOrientation = -1;
|
private int outputOrientation = -1;
|
||||||
private int outputOrientation = -1;
|
private int cameraId = -1;
|
||||||
|
private int lastPictureOrientation = -1;
|
||||||
|
|
||||||
public CameraView(Context context) {
|
public CameraView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@ -74,39 +71,51 @@ public class CameraView extends FrameLayout {
|
|||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
setBackgroundColor(Color.BLACK);
|
setBackgroundColor(Color.BLACK);
|
||||||
|
|
||||||
if (isMultiCamera()) cameraId = CameraInfo.CAMERA_FACING_FRONT;
|
|
||||||
|
|
||||||
surface = new CameraSurfaceView(getContext());
|
|
||||||
onOrientationChange = new OnOrientationChange(context.getApplicationContext());
|
onOrientationChange = new OnOrientationChange(context.getApplicationContext());
|
||||||
addView(surface);
|
}
|
||||||
|
|
||||||
|
public CameraHost getHost() {
|
||||||
|
return host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHost(CameraHost host) {
|
||||||
|
this.host = host;
|
||||||
|
|
||||||
|
if (host.getDeviceProfile().useTextureView()) {
|
||||||
|
previewStrategy = new TexturePreviewStrategy(this);
|
||||||
|
} else {
|
||||||
|
previewStrategy = new SurfacePreviewStrategy(this);
|
||||||
|
}
|
||||||
|
addView(previewStrategy.getWidget());
|
||||||
}
|
}
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
if (started) return;
|
|
||||||
started = true;
|
|
||||||
Log.w(TAG, "onResume() queued");
|
Log.w(TAG, "onResume() queued");
|
||||||
enqueueTask(new SerialAsyncTask<Camera>() {
|
final CameraHost host = getHost();
|
||||||
@Override
|
submitTask(new SerializedAsyncTask<FailureReason>() {
|
||||||
protected @Nullable Camera onRunBackground() {
|
@Override protected FailureReason onRunBackground() {
|
||||||
try {
|
try {
|
||||||
return Camera.open(cameraId);
|
cameraId = host.getCameraId();
|
||||||
|
if (cameraId >= 0) {
|
||||||
|
camera = Camera.open(cameraId);
|
||||||
|
} else {
|
||||||
|
return FailureReason.NO_CAMERAS_REPORTED;
|
||||||
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, e);
|
return FailureReason.UNKNOWN;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override protected void onPostMain(FailureReason result) {
|
||||||
protected void onPostMain(@Nullable Camera camera) {
|
if (result != null) {
|
||||||
if (camera == null) {
|
host.onCameraFail(result);
|
||||||
Log.w(TAG, "tried to open camera but got null");
|
|
||||||
if (listener != null) listener.onCameraFail();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CameraView.this.camera = Optional.of(camera);
|
|
||||||
try {
|
try {
|
||||||
|
cameraReady = true;
|
||||||
if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
|
if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
|
||||||
onOrientationChange.enable();
|
onOrientationChange.enable();
|
||||||
}
|
}
|
||||||
@ -114,196 +123,227 @@ public class CameraView extends FrameLayout {
|
|||||||
synchronized (CameraView.this) {
|
synchronized (CameraView.this) {
|
||||||
CameraView.this.notifyAll();
|
CameraView.this.notifyAll();
|
||||||
}
|
}
|
||||||
onCameraReady();
|
previewCreated();
|
||||||
|
initPreview();
|
||||||
requestLayout();
|
requestLayout();
|
||||||
invalidate();
|
invalidate();
|
||||||
Log.w(TAG, "onResume() completed");
|
Log.w(TAG, "onResume() completed");
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException re) {
|
||||||
Log.w(TAG, "exception when starting camera preview", e);
|
Log.w(TAG, "exception when starting camera preview", re);
|
||||||
onPause();
|
try {
|
||||||
|
previewDestroyed();
|
||||||
|
} catch (RuntimeException re2) {
|
||||||
|
Log.w(TAG, "also failed to release camera", re2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
if (!started) return;
|
|
||||||
started = false;
|
|
||||||
Log.w(TAG, "onPause() queued");
|
Log.w(TAG, "onPause() queued");
|
||||||
|
submitTask(new SerializedAsyncTask<Void>() {
|
||||||
enqueueTask(new SerialAsyncTask<Void>() {
|
|
||||||
private Optional<Camera> cameraToDestroy;
|
|
||||||
@Override protected void onPreMain() {
|
@Override protected void onPreMain() {
|
||||||
cameraToDestroy = camera;
|
cameraReady = false;
|
||||||
camera = Optional.absent();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Void onRunBackground() {
|
@Override protected Void onRunBackground() {
|
||||||
if (cameraToDestroy.isPresent()) {
|
previewDestroyed();
|
||||||
try {
|
|
||||||
stopPreview();
|
|
||||||
cameraToDestroy.get().release();
|
|
||||||
Log.w(TAG, "released old camera instance");
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onPostMain(Void avoid) {
|
@Override protected void onPostMain(Void avoid) {
|
||||||
onOrientationChange.disable();
|
onOrientationChange.disable();
|
||||||
|
previewSize = null;
|
||||||
displayOrientation = -1;
|
displayOrientation = -1;
|
||||||
outputOrientation = -1;
|
outputOrientation = -1;
|
||||||
|
cameraId = -1;
|
||||||
|
lastPictureOrientation = -1;
|
||||||
Log.w(TAG, "onPause() completed");
|
Log.w(TAG, "onPause() completed");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStarted() {
|
// based on CameraPreview.java from ApiDemos
|
||||||
return started;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
|
|
||||||
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0 && camera.isPresent()) {
|
if (getMeasuredWidth() > 0 && getMeasuredHeight() > 0 && camera != null && cameraReady) {
|
||||||
final Size preferredPreviewSize = CameraUtils.getPreferredPreviewSize(displayOrientation,
|
Camera.Size newSize = null;
|
||||||
getMeasuredWidth(),
|
|
||||||
getMeasuredHeight(),
|
try {
|
||||||
camera.get());
|
if (getHost().getRecordingHint() != CameraHost.RecordingHint.STILL_ONLY) {
|
||||||
final Parameters parameters = camera.get().getParameters();
|
newSize = getHost().getPreferredPreviewSizeForVideo(getDisplayOrientation(),
|
||||||
if (preferredPreviewSize != null && !parameters.getPreviewSize().equals(preferredPreviewSize)) {
|
getMeasuredWidth(),
|
||||||
Log.w(TAG, "setting preview size to " + preferredPreviewSize.width + "x" + preferredPreviewSize.height);
|
getMeasuredHeight(),
|
||||||
stopPreview();
|
camera.getParameters(),
|
||||||
parameters.setPreviewSize(preferredPreviewSize.width, preferredPreviewSize.height);
|
null);
|
||||||
camera.get().setParameters(parameters);
|
}
|
||||||
requestLayout();
|
if (newSize == null || newSize.width * newSize.height < 65536) {
|
||||||
startPreview();
|
newSize = getHost().getPreviewSize(getDisplayOrientation(),
|
||||||
|
getMeasuredWidth(),
|
||||||
|
getMeasuredHeight(),
|
||||||
|
camera.getParameters());
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Could not work with camera parameters?", e);
|
||||||
|
// TODO get this out to library clients
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newSize != null) {
|
||||||
|
if (previewSize == null) {
|
||||||
|
previewSize = newSize;
|
||||||
|
synchronized (this) { notifyAll(); }
|
||||||
|
} else if (previewSize.width != newSize.width || previewSize.height != newSize.height) {
|
||||||
|
if (inPreview) {
|
||||||
|
stopPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
previewSize = newSize;
|
||||||
|
synchronized (this) { notifyAll(); }
|
||||||
|
initPreview();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("SuspiciousNameCombination")
|
// based on CameraPreview.java from ApiDemos
|
||||||
@Override
|
|
||||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
|
||||||
final int width = r - l;
|
|
||||||
final int height = b - t;
|
|
||||||
final int previewWidth;
|
|
||||||
final int previewHeight;
|
|
||||||
|
|
||||||
if (camera.isPresent()) {
|
@SuppressWarnings("SuspiciousNameCombination") @Override
|
||||||
final Size previewSize = camera.get().getParameters().getPreviewSize();
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
if (displayOrientation == 90 || displayOrientation == 270) {
|
if (getChildCount() > 0) {
|
||||||
|
final View child = getChildAt(0);
|
||||||
|
final int width = r - l;
|
||||||
|
final int height = b - t;
|
||||||
|
final int previewWidth;
|
||||||
|
final int previewHeight;
|
||||||
|
|
||||||
|
// handle orientation
|
||||||
|
|
||||||
|
if (previewSize != null && (getDisplayOrientation() == 90 || getDisplayOrientation() == 270)) {
|
||||||
previewWidth = previewSize.height;
|
previewWidth = previewSize.height;
|
||||||
previewHeight = previewSize.width;
|
previewHeight = previewSize.width;
|
||||||
} else {
|
} else if (previewSize != null) {
|
||||||
previewWidth = previewSize.width;
|
previewWidth = previewSize.width;
|
||||||
previewHeight = previewSize.height;
|
previewHeight = previewSize.height;
|
||||||
|
} else {
|
||||||
|
previewWidth = width;
|
||||||
|
previewHeight = height;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
previewWidth = width;
|
|
||||||
previewHeight = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (previewHeight == 0 || previewWidth == 0) {
|
if (previewHeight == 0 || previewWidth == 0) {
|
||||||
Log.w(TAG, "skipping layout due to zero-width/height preview size");
|
Log.w(TAG, "skipping layout due to zero-width/height preview size");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.w(TAG, "layout " + width + "x" + height + ", target " + previewWidth + "x" + previewHeight);
|
|
||||||
|
|
||||||
if (width * previewHeight > height * previewWidth) {
|
boolean useFirstStrategy = (width * previewHeight > height * previewWidth);
|
||||||
final int scaledChildHeight = previewHeight * width / previewWidth;
|
boolean useFullBleed = getHost().useFullBleedPreview();
|
||||||
surface.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2);
|
|
||||||
} else {
|
if ((useFirstStrategy && !useFullBleed) || (!useFirstStrategy && useFullBleed)) {
|
||||||
final int scaledChildWidth = previewWidth * height / previewHeight;
|
final int scaledChildWidth = previewWidth * height / previewHeight;
|
||||||
surface.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height);
|
child.layout((width - scaledChildWidth) / 2, 0, (width + scaledChildWidth) / 2, height);
|
||||||
|
} else {
|
||||||
|
final int scaledChildHeight = previewHeight * width / previewWidth;
|
||||||
|
child.layout(0, (height - scaledChildHeight) / 2, width, (height + scaledChildHeight) / 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setListener(@Nullable CameraViewListener listener) {
|
public int getDisplayOrientation() {
|
||||||
this.listener = listener;
|
return displayOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMultiCamera() {
|
public void setOneShotPreviewCallback(PreviewCallback callback) {
|
||||||
return Camera.getNumberOfCameras() > 1;
|
if (camera != null) camera.setOneShotPreviewCallback(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRearCamera() {
|
public @Nullable Camera.Parameters getCameraParameters() {
|
||||||
return cameraId == CameraInfo.CAMERA_FACING_BACK;
|
return camera == null || !cameraReady ? null : camera.getParameters();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flipCamera() {
|
void previewCreated() {
|
||||||
if (Camera.getNumberOfCameras() > 1) {
|
Log.w(TAG, "previewCreated() queued");
|
||||||
cameraId = cameraId == CameraInfo.CAMERA_FACING_BACK
|
final CameraHost host = getHost();
|
||||||
? CameraInfo.CAMERA_FACING_FRONT
|
submitTask(new PostInitializationTask<Void>() {
|
||||||
: CameraInfo.CAMERA_FACING_BACK;
|
|
||||||
onPause();
|
|
||||||
onResume();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@TargetApi(14)
|
|
||||||
private void onCameraReady() {
|
|
||||||
if (!camera.isPresent()) return;
|
|
||||||
|
|
||||||
final Parameters parameters = camera.get().getParameters();
|
|
||||||
final List<String> focusModes = parameters.getSupportedFocusModes();
|
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= 14) parameters.setRecordingHint(true);
|
|
||||||
|
|
||||||
if (VERSION.SDK_INT >= 14 && focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
|
||||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
|
||||||
} else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
|
||||||
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
|
||||||
}
|
|
||||||
|
|
||||||
camera.get().setParameters(parameters);
|
|
||||||
|
|
||||||
enqueueTask(new PostInitializationTask<Void>() {
|
|
||||||
@Override protected void onPostMain(Void avoid) {
|
@Override protected void onPostMain(Void avoid) {
|
||||||
if (camera.isPresent()) {
|
try {
|
||||||
try {
|
if (camera != null) {
|
||||||
camera.get().setPreviewDisplay(surface.getHolder());
|
previewStrategy.attach(camera);
|
||||||
requestLayout();
|
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
host.handleException(e);
|
||||||
|
}
|
||||||
|
Log.w(TAG, "previewCreated() completed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void previewDestroyed() {
|
||||||
|
try {
|
||||||
|
if (camera != null) {
|
||||||
|
previewStopped();
|
||||||
|
camera.release();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
camera = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void previewStopped() {
|
||||||
|
if (inPreview) {
|
||||||
|
stopPreview();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
public void initPreview() {
|
||||||
|
Log.w(TAG, "initPreview() queued");
|
||||||
|
submitTask(new PostInitializationTask<Void>() {
|
||||||
|
@Override protected void onPostMain(Void avoid) {
|
||||||
|
if (camera != null && cameraReady) {
|
||||||
|
Camera.Parameters parameters = camera.getParameters();
|
||||||
|
|
||||||
|
parameters.setPreviewSize(previewSize.width, previewSize.height);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
|
parameters.setRecordingHint(getHost().getRecordingHint() != CameraHost.RecordingHint.STILL_ONLY);
|
||||||
|
}
|
||||||
|
|
||||||
|
camera.setParameters(getHost().adjustPreviewParameters(parameters));
|
||||||
|
startPreview();
|
||||||
|
requestLayout();
|
||||||
|
invalidate();
|
||||||
|
Log.w(TAG, "initPreview() completed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startPreview() {
|
private void startPreview() {
|
||||||
if (camera.isPresent()) {
|
camera.startPreview();
|
||||||
try {
|
inPreview = true;
|
||||||
camera.get().startPreview();
|
getHost().autoFocusAvailable();
|
||||||
} catch (Exception e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopPreview() {
|
private void stopPreview() {
|
||||||
if (camera.isPresent()) {
|
camera.startPreview();
|
||||||
try {
|
inPreview = false;
|
||||||
camera.get().stopPreview();
|
getHost().autoFocusUnavailable();
|
||||||
} catch (Exception e) {
|
camera.stopPreview();
|
||||||
Log.w(TAG, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// based on
|
// based on
|
||||||
// http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
|
// http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
|
||||||
// and http://stackoverflow.com/a/10383164/115145
|
// and http://stackoverflow.com/a/10383164/115145
|
||||||
private void setCameraDisplayOrientation() {
|
private void setCameraDisplayOrientation() {
|
||||||
Camera.CameraInfo info = getCameraInfo();
|
Camera.CameraInfo info = new Camera.CameraInfo();
|
||||||
int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
|
int rotation = getActivity().getWindowManager().getDefaultDisplay().getRotation();
|
||||||
int degrees = 0;
|
int degrees = 0;
|
||||||
DisplayMetrics dm = new DisplayMetrics();
|
DisplayMetrics dm = new DisplayMetrics();
|
||||||
|
|
||||||
|
Camera.getCameraInfo(cameraId, info);
|
||||||
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
|
getActivity().getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||||
|
|
||||||
switch (rotation) {
|
switch (rotation) {
|
||||||
@ -316,55 +356,69 @@ public class CameraView extends FrameLayout {
|
|||||||
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||||
displayOrientation = (info.orientation + degrees ) % 360;
|
displayOrientation = (info.orientation + degrees ) % 360;
|
||||||
displayOrientation = (360 - displayOrientation) % 360;
|
displayOrientation = (360 - displayOrientation) % 360;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
displayOrientation = (info.orientation - degrees + 360) % 360;
|
displayOrientation = (info.orientation - degrees + 360) % 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
stopPreview();
|
boolean wasInPreview = inPreview;
|
||||||
camera.get().setDisplayOrientation(displayOrientation);
|
|
||||||
startPreview();
|
if (inPreview) {
|
||||||
|
stopPreview();
|
||||||
|
}
|
||||||
|
|
||||||
|
camera.setDisplayOrientation(displayOrientation);
|
||||||
|
|
||||||
|
if (wasInPreview) {
|
||||||
|
startPreview();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCameraPictureOrientation() {
|
public int getCameraPictureOrientation() {
|
||||||
|
Camera.CameraInfo info = new Camera.CameraInfo();
|
||||||
|
|
||||||
|
Camera.getCameraInfo(cameraId, info);
|
||||||
|
|
||||||
if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
|
if (getActivity().getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
|
||||||
outputOrientation = getCameraPictureRotation(getActivity().getWindowManager()
|
outputOrientation = getCameraPictureRotation(getActivity().getWindowManager()
|
||||||
.getDefaultDisplay()
|
.getDefaultDisplay()
|
||||||
.getOrientation());
|
.getOrientation());
|
||||||
} else if (getCameraInfo().facing == CameraInfo.CAMERA_FACING_FRONT) {
|
} else if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||||
outputOrientation = (360 - displayOrientation) % 360;
|
outputOrientation = (360 - displayOrientation) % 360;
|
||||||
} else {
|
} else {
|
||||||
outputOrientation = displayOrientation;
|
outputOrientation = displayOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastPictureOrientation != outputOrientation) {
|
||||||
|
lastPictureOrientation = outputOrientation;
|
||||||
|
}
|
||||||
return outputOrientation;
|
return outputOrientation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private @NonNull CameraInfo getCameraInfo() {
|
// based on:
|
||||||
final CameraInfo info = new Camera.CameraInfo();
|
// http://developer.android.com/reference/android/hardware/Camera.Parameters.html#setRotation(int)
|
||||||
Camera.getCameraInfo(cameraId, info);
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX this sucks
|
|
||||||
private Activity getActivity() {
|
|
||||||
return (Activity)getContext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCameraPictureRotation(int orientation) {
|
public int getCameraPictureRotation(int orientation) {
|
||||||
final CameraInfo info = getCameraInfo();
|
Camera.CameraInfo info = new Camera.CameraInfo();
|
||||||
final int rotation;
|
Camera.getCameraInfo(cameraId, info);
|
||||||
|
int rotation;
|
||||||
|
|
||||||
orientation = (orientation + 45) / 90 * 90;
|
orientation = (orientation + 45) / 90 * 90;
|
||||||
|
|
||||||
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
|
||||||
rotation = (info.orientation - orientation + 360) % 360;
|
rotation = (info.orientation - orientation + 360) % 360;
|
||||||
} else {
|
}
|
||||||
|
else { // back-facing camera
|
||||||
rotation = (info.orientation + orientation) % 360;
|
rotation = (info.orientation + orientation) % 360;
|
||||||
}
|
}
|
||||||
|
|
||||||
return rotation;
|
return rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Activity getActivity() {
|
||||||
|
return (Activity)getContext();
|
||||||
|
}
|
||||||
|
|
||||||
private class OnOrientationChange extends OrientationEventListener {
|
private class OnOrientationChange extends OrientationEventListener {
|
||||||
public OnOrientationChange(Context context) {
|
public OnOrientationChange(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@ -373,18 +427,19 @@ public class CameraView extends FrameLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOrientationChanged(int orientation) {
|
public void onOrientationChanged(int orientation) {
|
||||||
if (camera.isPresent() && orientation != ORIENTATION_UNKNOWN) {
|
if (camera != null && orientation != ORIENTATION_UNKNOWN) {
|
||||||
int newOutputOrientation = getCameraPictureRotation(orientation);
|
int newOutputOrientation = getCameraPictureRotation(orientation);
|
||||||
|
|
||||||
if (newOutputOrientation != outputOrientation) {
|
if (newOutputOrientation != outputOrientation) {
|
||||||
outputOrientation = newOutputOrientation;
|
outputOrientation = newOutputOrientation;
|
||||||
|
|
||||||
Camera.Parameters params = camera.get().getParameters();
|
Camera.Parameters params = camera.getParameters();
|
||||||
|
|
||||||
params.setRotation(outputOrientation);
|
params.setRotation(outputOrientation);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
camera.get().setParameters(params);
|
camera.setParameters(params);
|
||||||
|
lastPictureOrientation = outputOrientation;
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
Log.e(TAG, "Exception updating camera parameters in orientation change", e);
|
Log.e(TAG, "Exception updating camera parameters in orientation change", e);
|
||||||
@ -394,65 +449,13 @@ public class CameraView extends FrameLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void takePicture(final Rect previewRect) {
|
private void submitTask(SerializedAsyncTask job) {
|
||||||
if (!camera.isPresent() || camera.get().getParameters() == null) {
|
|
||||||
Log.w(TAG, "camera not in capture-ready state");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
camera.get().setOneShotPreviewCallback(new Camera.PreviewCallback() {
|
|
||||||
@Override
|
|
||||||
public void onPreviewFrame(byte[] data, final Camera camera) {
|
|
||||||
final int rotation = getCameraPictureOrientation();
|
|
||||||
final Size previewSize = camera.getParameters().getPreviewSize();
|
|
||||||
final Rect croppingRect = getCroppedRect(previewSize, previewRect, rotation);
|
|
||||||
|
|
||||||
Log.w(TAG, "previewSize: " + previewSize.width + "x" + previewSize.height);
|
|
||||||
Log.w(TAG, "data bytes: " + data.length);
|
|
||||||
Log.w(TAG, "previewFormat: " + camera.getParameters().getPreviewFormat());
|
|
||||||
Log.w(TAG, "croppingRect: " + croppingRect.toString());
|
|
||||||
Log.w(TAG, "rotation: " + rotation);
|
|
||||||
new CaptureTask(previewSize, rotation, croppingRect).execute(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Rect getCroppedRect(Size cameraPreviewSize, Rect visibleRect, int rotation) {
|
|
||||||
final int previewWidth = cameraPreviewSize.width;
|
|
||||||
final int previewHeight = cameraPreviewSize.height;
|
|
||||||
|
|
||||||
if (rotation % 180 > 0) rotateRect(visibleRect);
|
|
||||||
|
|
||||||
float scale = (float) previewWidth / visibleRect.width();
|
|
||||||
if (visibleRect.height() * scale > previewHeight) {
|
|
||||||
scale = (float) previewHeight / visibleRect.height();
|
|
||||||
}
|
|
||||||
final float newWidth = visibleRect.width() * scale;
|
|
||||||
final float newHeight = visibleRect.height() * scale;
|
|
||||||
final float centerX = (VERSION.SDK_INT < 14) ? previewWidth - newWidth / 2 : previewWidth / 2;
|
|
||||||
final float centerY = previewHeight / 2;
|
|
||||||
|
|
||||||
visibleRect.set((int) (centerX - newWidth / 2),
|
|
||||||
(int) (centerY - newHeight / 2),
|
|
||||||
(int) (centerX + newWidth / 2),
|
|
||||||
(int) (centerY + newHeight / 2));
|
|
||||||
|
|
||||||
if (rotation % 180 > 0) rotateRect(visibleRect);
|
|
||||||
return visibleRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SuspiciousNameCombination")
|
|
||||||
private void rotateRect(Rect rect) {
|
|
||||||
rect.set(rect.top, rect.left, rect.bottom, rect.right);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enqueueTask(SerialAsyncTask job) {
|
|
||||||
ApplicationContext.getInstance(getContext()).getJobManager().add(job);
|
ApplicationContext.getInstance(getContext()).getJobManager().add(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static abstract class SerialAsyncTask<Result> extends Job {
|
private static abstract class SerializedAsyncTask<Result> extends Job {
|
||||||
|
|
||||||
public SerialAsyncTask() {
|
public SerializedAsyncTask() {
|
||||||
super(JobParameters.newBuilder().withGroupId(CameraView.class.getSimpleName()).create());
|
super(JobParameters.newBuilder().withGroupId(CameraView.class.getSimpleName()).create());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -461,7 +464,7 @@ public class CameraView extends FrameLayout {
|
|||||||
@Override public final void onRun() {
|
@Override public final void onRun() {
|
||||||
try {
|
try {
|
||||||
onWait();
|
onWait();
|
||||||
Util.runOnMainSync(new Runnable() {
|
runOnMainSync(new Runnable() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
onPreMain();
|
onPreMain();
|
||||||
}
|
}
|
||||||
@ -469,7 +472,7 @@ public class CameraView extends FrameLayout {
|
|||||||
|
|
||||||
final Result result = onRunBackground();
|
final Result result = onRunBackground();
|
||||||
|
|
||||||
Util.runOnMainSync(new Runnable() {
|
runOnMainSync(new Runnable() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
onPostMain(result);
|
onPostMain(result);
|
||||||
}
|
}
|
||||||
@ -485,62 +488,44 @@ public class CameraView extends FrameLayout {
|
|||||||
|
|
||||||
@Override public void onCanceled() { }
|
@Override public void onCanceled() { }
|
||||||
|
|
||||||
|
private void runOnMainSync(final Runnable runnable) {
|
||||||
|
final CountDownLatch sync = new CountDownLatch(1);
|
||||||
|
Util.runOnMain(new Runnable() {
|
||||||
|
@Override public void run() {
|
||||||
|
try {
|
||||||
|
runnable.run();
|
||||||
|
} finally {
|
||||||
|
sync.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
sync.await();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
throw new AssertionError(ie);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void onWait() throws PreconditionsNotMetException {}
|
protected void onWait() throws PreconditionsNotMetException {}
|
||||||
protected void onPreMain() {}
|
protected void onPreMain() {}
|
||||||
protected Result onRunBackground() { return null; }
|
protected Result onRunBackground() { return null; }
|
||||||
protected void onPostMain(Result result) {}
|
protected void onPostMain(Result result) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
private abstract class PostInitializationTask<Result> extends SerialAsyncTask<Result> {
|
private abstract class PostInitializationTask<Result> extends SerializedAsyncTask<Result> {
|
||||||
@Override protected void onWait() throws PreconditionsNotMetException {
|
@Override protected void onWait() throws PreconditionsNotMetException {
|
||||||
synchronized (CameraView.this) {
|
synchronized (CameraView.this) {
|
||||||
if (!camera.isPresent()) {
|
if (!cameraReady) {
|
||||||
throw new PreconditionsNotMetException();
|
throw new PreconditionsNotMetException();
|
||||||
}
|
}
|
||||||
while (getMeasuredHeight() <= 0 || getMeasuredWidth() <= 0 || !surface.isReady()) {
|
while (camera == null || previewSize == null || !previewStrategy.isReady()) {
|
||||||
Log.w(TAG, String.format("waiting. surface ready? %s", surface.isReady()));
|
Log.w(TAG, String.format("waiting. camera? %s previewSize? %s prevewStrategy? %s",
|
||||||
|
camera != null, previewSize != null, previewStrategy.isReady()));
|
||||||
Util.wait(CameraView.this, 0);
|
Util.wait(CameraView.this, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CaptureTask extends AsyncTask<byte[], Void, byte[]> {
|
|
||||||
private final Size previewSize;
|
|
||||||
private final int rotation;
|
|
||||||
private final Rect croppingRect;
|
|
||||||
|
|
||||||
public CaptureTask(Size previewSize, int rotation, Rect croppingRect) {
|
|
||||||
this.previewSize = previewSize;
|
|
||||||
this.rotation = rotation;
|
|
||||||
this.croppingRect = croppingRect;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected byte[] doInBackground(byte[]... params) {
|
|
||||||
final byte[] data = params[0];
|
|
||||||
try {
|
|
||||||
return BitmapUtil.createFromNV21(data,
|
|
||||||
previewSize.width,
|
|
||||||
previewSize.height,
|
|
||||||
rotation,
|
|
||||||
croppingRect);
|
|
||||||
} catch (IOException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(byte[] imageBytes) {
|
|
||||||
if (imageBytes != null && listener != null) listener.onImageCapture(imageBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PreconditionsNotMetException extends Exception {}
|
private static class PreconditionsNotMetException extends Exception {}
|
||||||
|
|
||||||
public interface CameraViewListener {
|
|
||||||
void onImageCapture(@NonNull final byte[] imageBytes);
|
|
||||||
void onCameraFail();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.camera;
|
||||||
|
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public interface PreviewStrategy extends com.commonsware.cwac.camera.PreviewStrategy {
|
||||||
|
boolean isReady();
|
||||||
|
}
|
@ -26,7 +26,7 @@ import com.nineoldandroids.animation.ObjectAnimator;
|
|||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.components.InputAwareLayout.InputView;
|
import org.thoughtcrime.securesms.components.InputAwareLayout.InputView;
|
||||||
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||||
import org.thoughtcrime.securesms.components.camera.CameraView.CameraViewListener;
|
import org.thoughtcrime.securesms.components.camera.QuickCamera.QuickCameraListener;
|
||||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.thoughtcrime.securesms.util.ViewUtil;
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
@ -36,7 +36,7 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
|
|
||||||
private final ViewDragHelper dragHelper;
|
private final ViewDragHelper dragHelper;
|
||||||
|
|
||||||
private CameraView cameraView;
|
private QuickCamera quickCamera;
|
||||||
private int coverViewPosition;
|
private int coverViewPosition;
|
||||||
private KeyboardAwareLinearLayout container;
|
private KeyboardAwareLinearLayout container;
|
||||||
private View coverView;
|
private View coverView;
|
||||||
@ -74,12 +74,12 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
|
|
||||||
private void initializeView() {
|
private void initializeView() {
|
||||||
inflate(getContext(), R.layout.quick_attachment_drawer, this);
|
inflate(getContext(), R.layout.quick_attachment_drawer, this);
|
||||||
cameraView = ViewUtil.findById(this, R.id.quick_camera);
|
quickCamera = (QuickCamera) findViewById(R.id.quick_camera);
|
||||||
updateControlsView();
|
updateControlsView();
|
||||||
|
|
||||||
coverViewPosition = getChildCount();
|
coverViewPosition = getChildCount();
|
||||||
controls.setVisibility(GONE);
|
controls.setVisibility(GONE);
|
||||||
cameraView.setVisibility(GONE);
|
quickCamera.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isDeviceSupported(Context context) {
|
public static boolean isDeviceSupported(Context context) {
|
||||||
@ -108,7 +108,7 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
if (rotationChanged) {
|
if (rotationChanged) {
|
||||||
if (isShowing()) {
|
if (isShowing()) {
|
||||||
cameraView.onPause();
|
quickCamera.onPause();
|
||||||
}
|
}
|
||||||
updateControlsView();
|
updateControlsView();
|
||||||
setDrawerStateAndUpdate(drawerState, true);
|
setDrawerStateAndUpdate(drawerState, true);
|
||||||
@ -123,13 +123,13 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
shutterButton = (ImageButton) controls.findViewById(R.id.shutter_button);
|
shutterButton = (ImageButton) controls.findViewById(R.id.shutter_button);
|
||||||
swapCameraButton = (ImageButton) controls.findViewById(R.id.swap_camera_button);
|
swapCameraButton = (ImageButton) controls.findViewById(R.id.swap_camera_button);
|
||||||
fullScreenButton = (ImageButton) controls.findViewById(R.id.fullscreen_button);
|
fullScreenButton = (ImageButton) controls.findViewById(R.id.fullscreen_button);
|
||||||
if (cameraView.isMultiCamera()) {
|
if (quickCamera.isMultipleCameras()) {
|
||||||
swapCameraButton.setVisibility(View.VISIBLE);
|
swapCameraButton.setVisibility(View.VISIBLE);
|
||||||
swapCameraButton.setOnClickListener(new CameraFlipClickListener());
|
swapCameraButton.setOnClickListener(new CameraFlipClickListener());
|
||||||
}
|
}
|
||||||
shutterButton.setOnClickListener(new ShutterClickListener());
|
shutterButton.setOnClickListener(new ShutterClickListener());
|
||||||
fullScreenButton.setOnClickListener(new FullscreenClickListener());
|
fullScreenButton.setOnClickListener(new FullscreenClickListener());
|
||||||
ViewUtil.swapChildInPlace(this, this.controls, controls, indexOfChild(cameraView) + 1);
|
ViewUtil.swapChildInPlace(this, this.controls, controls, indexOfChild(quickCamera) + 1);
|
||||||
this.controls = controls;
|
this.controls = controls;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,11 +170,11 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
int childLeft = paddingLeft;
|
int childLeft = paddingLeft;
|
||||||
int childBottom;
|
int childBottom;
|
||||||
|
|
||||||
if (child == cameraView) {
|
if (child == quickCamera) {
|
||||||
childTop = computeCameraTopPosition(slideOffset);
|
childTop = computeCameraTopPosition(slideOffset);
|
||||||
childBottom = childTop + childHeight;
|
childBottom = childTop + childHeight;
|
||||||
if (cameraView.getMeasuredWidth() < getMeasuredWidth())
|
if (quickCamera.getMeasuredWidth() < getMeasuredWidth())
|
||||||
childLeft = (getMeasuredWidth() - cameraView.getMeasuredWidth()) / 2 + paddingLeft;
|
childLeft = (getMeasuredWidth() - quickCamera.getMeasuredWidth()) / 2 + paddingLeft;
|
||||||
} else if (child == controls) {
|
} else if (child == controls) {
|
||||||
childBottom = getMeasuredHeight();
|
childBottom = getMeasuredHeight();
|
||||||
} else {
|
} else {
|
||||||
@ -271,14 +271,14 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
ViewCompat.postInvalidateOnAnimation(this);
|
ViewCompat.postInvalidateOnAnimation(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slideOffset == 0 && cameraView.isStarted()) {
|
if (slideOffset == 0 && quickCamera.isStarted()) {
|
||||||
cameraView.onPause();
|
quickCamera.onPause();
|
||||||
controls.setVisibility(GONE);
|
controls.setVisibility(GONE);
|
||||||
cameraView.setVisibility(GONE);
|
quickCamera.setVisibility(GONE);
|
||||||
} else if (slideOffset != 0 && !cameraView.isStarted() & !paused) {
|
} else if (slideOffset != 0 && !quickCamera.isStarted() & !paused) {
|
||||||
controls.setVisibility(VISIBLE);
|
controls.setVisibility(VISIBLE);
|
||||||
cameraView.setVisibility(VISIBLE);
|
quickCamera.setVisibility(VISIBLE);
|
||||||
cameraView.onResume();
|
quickCamera.onResume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,10 +335,10 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
|
|
||||||
public void setListener(AttachmentDrawerListener listener) {
|
public void setListener(AttachmentDrawerListener listener) {
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
if (cameraView != null) cameraView.setListener(listener);
|
if (quickCamera != null) quickCamera.setQuickCameraListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface AttachmentDrawerListener extends CameraViewListener {
|
public interface AttachmentDrawerListener extends QuickCameraListener {
|
||||||
void onAttachmentDrawerStateChanged(DrawerState drawerState);
|
void onAttachmentDrawerStateChanged(DrawerState drawerState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,8 +391,8 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
int slideOffset = getTargetSlideOffset();
|
int slideOffset = getTargetSlideOffset();
|
||||||
dragHelper.captureChildView(coverView, 0);
|
dragHelper.captureChildView(coverView, 0);
|
||||||
dragHelper.settleCapturedViewAt(coverView.getLeft(), computeCoverTopPosition(slideOffset));
|
dragHelper.settleCapturedViewAt(coverView.getLeft(), computeCoverTopPosition(slideOffset));
|
||||||
dragHelper.captureChildView(cameraView, 0);
|
dragHelper.captureChildView(quickCamera, 0);
|
||||||
dragHelper.settleCapturedViewAt(cameraView.getLeft(), computeCameraTopPosition(slideOffset));
|
dragHelper.settleCapturedViewAt(quickCamera.getLeft(), computeCameraTopPosition(slideOffset));
|
||||||
ViewCompat.postInvalidateOnAnimation(QuickAttachmentDrawer.this);
|
ViewCompat.postInvalidateOnAnimation(QuickAttachmentDrawer.this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -455,13 +455,13 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
@SuppressWarnings("ResourceType")
|
@SuppressWarnings("ResourceType")
|
||||||
private boolean isDragViewUnder(int x, int y) {
|
private boolean isDragViewUnder(int x, int y) {
|
||||||
int[] viewLocation = new int[2];
|
int[] viewLocation = new int[2];
|
||||||
cameraView.getLocationOnScreen(viewLocation);
|
quickCamera.getLocationOnScreen(viewLocation);
|
||||||
int[] parentLocation = new int[2];
|
int[] parentLocation = new int[2];
|
||||||
this.getLocationOnScreen(parentLocation);
|
this.getLocationOnScreen(parentLocation);
|
||||||
int screenX = parentLocation[0] + x;
|
int screenX = parentLocation[0] + x;
|
||||||
int screenY = parentLocation[1] + y;
|
int screenY = parentLocation[1] + y;
|
||||||
return screenX >= viewLocation[0] && screenX < viewLocation[0] + cameraView.getWidth() &&
|
return screenX >= viewLocation[0] && screenX < viewLocation[0] + quickCamera.getWidth() &&
|
||||||
screenY >= viewLocation[1] && screenY < viewLocation[1] + cameraView.getHeight();
|
screenY >= viewLocation[1] && screenY < viewLocation[1] + quickCamera.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int computeCameraTopPosition(int slideOffset) {
|
private int computeCameraTopPosition(int slideOffset) {
|
||||||
@ -469,7 +469,7 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
return getPaddingTop();
|
return getPaddingTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
final int baseCameraTop = (cameraView.getMeasuredHeight() - halfExpandedHeight) / 2;
|
final int baseCameraTop = (quickCamera.getMeasuredHeight() - halfExpandedHeight) / 2;
|
||||||
final int baseOffset = getMeasuredHeight() - slideOffset - baseCameraTop;
|
final int baseOffset = getMeasuredHeight() - slideOffset - baseCameraTop;
|
||||||
final float slop = Util.clamp((float)(slideOffset - halfExpandedHeight) / (getMeasuredHeight() - halfExpandedHeight),
|
final float slop = Util.clamp((float)(slideOffset - halfExpandedHeight) / (getMeasuredHeight() - halfExpandedHeight),
|
||||||
0f,
|
0f,
|
||||||
@ -502,12 +502,12 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
|
|
||||||
public void onPause() {
|
public void onPause() {
|
||||||
paused = true;
|
paused = true;
|
||||||
cameraView.onPause();
|
quickCamera.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onResume() {
|
public void onResume() {
|
||||||
paused = false;
|
paused = false;
|
||||||
if (drawerState.isVisible()) cameraView.onResume();
|
if (drawerState.isVisible()) quickCamera.onResume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum DrawerState {
|
public enum DrawerState {
|
||||||
@ -522,18 +522,18 @@ public class QuickAttachmentDrawer extends ViewGroup implements InputView {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
boolean crop = drawerState != DrawerState.FULL_EXPANDED;
|
boolean crop = drawerState != DrawerState.FULL_EXPANDED;
|
||||||
int imageHeight = crop ? getContainer().getKeyboardHeight() : cameraView.getMeasuredHeight();
|
int imageHeight = crop ? getContainer().getKeyboardHeight() : quickCamera.getMeasuredHeight();
|
||||||
Rect previewRect = new Rect(0, 0, cameraView.getMeasuredWidth(), imageHeight);
|
Rect previewRect = new Rect(0, 0, quickCamera.getMeasuredWidth(), imageHeight);
|
||||||
cameraView.takePicture(previewRect);
|
quickCamera.takePicture(previewRect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CameraFlipClickListener implements OnClickListener {
|
private class CameraFlipClickListener implements OnClickListener {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
cameraView.flipCamera();
|
quickCamera.swapCamera();
|
||||||
swapCameraButton.setImageResource(cameraView.isRearCamera() ? R.drawable.quick_camera_front
|
swapCameraButton.setImageResource(quickCamera.isRearCamera() ? R.drawable.quick_camera_front
|
||||||
: R.drawable.quick_camera_rear);
|
: R.drawable.quick_camera_rear);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,207 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.camera;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.hardware.Camera.CameraInfo;
|
||||||
|
import android.hardware.Camera.Parameters;
|
||||||
|
import android.hardware.Camera.Size;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build.VERSION;
|
||||||
|
import android.os.Build.VERSION_CODES;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.commonsware.cwac.camera.CameraHost.FailureReason;
|
||||||
|
import com.commonsware.cwac.camera.SimpleCameraHost;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation") public class QuickCamera extends CameraView {
|
||||||
|
private static final String TAG = QuickCamera.class.getSimpleName();
|
||||||
|
|
||||||
|
private QuickCameraListener listener;
|
||||||
|
private boolean capturing;
|
||||||
|
private boolean started;
|
||||||
|
private QuickCameraHost cameraHost;
|
||||||
|
|
||||||
|
public QuickCamera(Context context) {
|
||||||
|
this(context, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuickCamera(Context context, AttributeSet attrs) {
|
||||||
|
this(context, attrs, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public QuickCamera(Context context, AttributeSet attrs, int defStyle) {
|
||||||
|
super(context, attrs, defStyle);
|
||||||
|
cameraHost = new QuickCameraHost(context);
|
||||||
|
setHost(cameraHost);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
if (started) return;
|
||||||
|
super.onResume();
|
||||||
|
started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
if (!started) return;
|
||||||
|
super.onPause();
|
||||||
|
started = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isStarted() {
|
||||||
|
return started;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void takePicture(final Rect previewRect) {
|
||||||
|
if (capturing) {
|
||||||
|
Log.w(TAG, "takePicture() called while previous capture pending.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Parameters cameraParameters = getCameraParameters();
|
||||||
|
if (cameraParameters == null) {
|
||||||
|
Log.w(TAG, "camera not in capture-ready state");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setOneShotPreviewCallback(new Camera.PreviewCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPreviewFrame(byte[] data, final Camera camera) {
|
||||||
|
final int rotation = getCameraPictureOrientation();
|
||||||
|
final Size previewSize = cameraParameters.getPreviewSize();
|
||||||
|
final Rect croppingRect = getCroppedRect(previewSize, previewRect, rotation);
|
||||||
|
|
||||||
|
Log.w(TAG, "previewSize: " + previewSize.width + "x" + previewSize.height);
|
||||||
|
Log.w(TAG, "previewFormat: " + cameraParameters.getPreviewFormat());
|
||||||
|
Log.w(TAG, "croppingRect: " + croppingRect.toString());
|
||||||
|
Log.w(TAG, "rotation: " + rotation);
|
||||||
|
new AsyncTask<byte[], Void, byte[]>() {
|
||||||
|
@Override
|
||||||
|
protected byte[] doInBackground(byte[]... params) {
|
||||||
|
byte[] data = params[0];
|
||||||
|
try {
|
||||||
|
|
||||||
|
return BitmapUtil.createFromNV21(data,
|
||||||
|
previewSize.width,
|
||||||
|
previewSize.height,
|
||||||
|
rotation,
|
||||||
|
croppingRect);
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(byte[] imageBytes) {
|
||||||
|
capturing = false;
|
||||||
|
if (imageBytes != null && listener != null) listener.onImageCapture(imageBytes);
|
||||||
|
}
|
||||||
|
}.execute(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rect getCroppedRect(Size cameraPreviewSize, Rect visibleRect, int rotation) {
|
||||||
|
final int previewWidth = cameraPreviewSize.width;
|
||||||
|
final int previewHeight = cameraPreviewSize.height;
|
||||||
|
|
||||||
|
if (rotation % 180 > 0) rotateRect(visibleRect);
|
||||||
|
|
||||||
|
float scale = (float) previewWidth / visibleRect.width();
|
||||||
|
if (visibleRect.height() * scale > previewHeight) {
|
||||||
|
scale = (float) previewHeight / visibleRect.height();
|
||||||
|
}
|
||||||
|
final float newWidth = visibleRect.width() * scale;
|
||||||
|
final float newHeight = visibleRect.height() * scale;
|
||||||
|
final float centerX;
|
||||||
|
final float centerY = previewHeight / 2;
|
||||||
|
if (VERSION.SDK_INT < VERSION_CODES.ICE_CREAM_SANDWICH) {
|
||||||
|
centerX = previewWidth - newWidth / 2;
|
||||||
|
} else {
|
||||||
|
centerX = previewWidth / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
visibleRect.set((int) (centerX - newWidth / 2),
|
||||||
|
(int) (centerY - newHeight / 2),
|
||||||
|
(int) (centerX + newWidth / 2),
|
||||||
|
(int) (centerY + newHeight / 2));
|
||||||
|
|
||||||
|
if (rotation % 180 > 0) rotateRect(visibleRect);
|
||||||
|
return visibleRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("SuspiciousNameCombination")
|
||||||
|
private void rotateRect(Rect rect) {
|
||||||
|
rect.set(rect.top, rect.left, rect.bottom, rect.right);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setQuickCameraListener(QuickCameraListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMultipleCameras() {
|
||||||
|
return Camera.getNumberOfCameras() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRearCamera() {
|
||||||
|
return cameraHost.getCameraId() == Camera.CameraInfo.CAMERA_FACING_BACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapCamera() {
|
||||||
|
cameraHost.swapCameraId();
|
||||||
|
onPause();
|
||||||
|
onResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface QuickCameraListener {
|
||||||
|
void onImageCapture(@NonNull final byte[] imageBytes);
|
||||||
|
void onCameraFail(FailureReason reason);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class QuickCameraHost extends SimpleCameraHost {
|
||||||
|
int cameraId = CameraInfo.CAMERA_FACING_BACK;
|
||||||
|
|
||||||
|
public QuickCameraHost(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(VERSION_CODES.ICE_CREAM_SANDWICH) @Override
|
||||||
|
public Parameters adjustPreviewParameters(Parameters parameters) {
|
||||||
|
List<String> focusModes = parameters.getSupportedFocusModes();
|
||||||
|
if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
|
||||||
|
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
|
||||||
|
} else if (focusModes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {
|
||||||
|
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
|
||||||
|
}
|
||||||
|
return parameters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCameraId() {
|
||||||
|
return cameraId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapCameraId() {
|
||||||
|
if (isMultipleCameras()) {
|
||||||
|
if (cameraId == CameraInfo.CAMERA_FACING_BACK) cameraId = CameraInfo.CAMERA_FACING_FRONT;
|
||||||
|
else cameraId = CameraInfo.CAMERA_FACING_BACK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCameraFail(FailureReason reason) {
|
||||||
|
super.onCameraFail(reason);
|
||||||
|
if (listener != null) listener.onCameraFail(reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,82 @@
|
|||||||
|
/***
|
||||||
|
Copyright (c) 2013 CommonsWare, LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.thoughtcrime.securesms.components.camera;
|
||||||
|
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.SurfaceHolder;
|
||||||
|
import android.view.SurfaceView;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
class SurfacePreviewStrategy implements PreviewStrategy,
|
||||||
|
SurfaceHolder.Callback {
|
||||||
|
private final static String TAG = SurfacePreviewStrategy.class.getSimpleName();
|
||||||
|
private final CameraView cameraView;
|
||||||
|
private SurfaceView preview=null;
|
||||||
|
private SurfaceHolder previewHolder=null;
|
||||||
|
private boolean ready = false;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
SurfacePreviewStrategy(CameraView cameraView) {
|
||||||
|
this.cameraView=cameraView;
|
||||||
|
preview=new SurfaceView(cameraView.getContext());
|
||||||
|
previewHolder=preview.getHolder();
|
||||||
|
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
|
||||||
|
previewHolder.addCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceCreated(SurfaceHolder holder) {
|
||||||
|
Log.w(TAG, "surfaceCreated()");
|
||||||
|
ready = true;
|
||||||
|
synchronized (cameraView) { cameraView.notifyAll(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceChanged(SurfaceHolder holder, int format,
|
||||||
|
int width, int height) {
|
||||||
|
Log.w(TAG, "surfaceChanged()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||||
|
Log.w(TAG, "surfaceDestroyed()");
|
||||||
|
cameraView.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach(Camera camera) throws IOException {
|
||||||
|
Log.w(TAG, "attach(Camera)");
|
||||||
|
camera.setPreviewDisplay(previewHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach(MediaRecorder recorder) {
|
||||||
|
recorder.setPreviewDisplay(previewHolder.getSurface());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getWidget() {
|
||||||
|
return(preview);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return ready;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,93 @@
|
|||||||
|
package org.thoughtcrime.securesms.components.camera;
|
||||||
|
/***
|
||||||
|
Copyright (c) 2013 CommonsWare, LLC
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
not use this file except in compliance with the License. You may obtain
|
||||||
|
a copy of the License at
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.graphics.SurfaceTexture;
|
||||||
|
import android.hardware.Camera;
|
||||||
|
import android.media.MediaRecorder;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.TextureView;
|
||||||
|
import android.view.View;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
|
||||||
|
class TexturePreviewStrategy implements PreviewStrategy,
|
||||||
|
TextureView.SurfaceTextureListener {
|
||||||
|
private final static String TAG = TexturePreviewStrategy.class.getSimpleName();
|
||||||
|
private final CameraView cameraView;
|
||||||
|
private TextureView widget=null;
|
||||||
|
private SurfaceTexture surface=null;
|
||||||
|
|
||||||
|
TexturePreviewStrategy(CameraView cameraView) {
|
||||||
|
this.cameraView=cameraView;
|
||||||
|
widget=new TextureView(cameraView.getContext());
|
||||||
|
widget.setSurfaceTextureListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureAvailable(SurfaceTexture surface,
|
||||||
|
int width, int height) {
|
||||||
|
Log.w(TAG, "onSurfaceTextureAvailable()");
|
||||||
|
this.surface=surface;
|
||||||
|
synchronized (cameraView) { cameraView.notifyAll(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureSizeChanged(SurfaceTexture surface,
|
||||||
|
int width, int height) {
|
||||||
|
Log.w(TAG, "onSurfaceTextureChanged()");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
|
||||||
|
Log.w(TAG, "onSurfaceTextureDestroyed()");
|
||||||
|
cameraView.onPause();
|
||||||
|
|
||||||
|
return(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach(Camera camera) throws IOException {
|
||||||
|
camera.setPreviewTexture(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach(MediaRecorder recorder) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Cannot use TextureView with MediaRecorder");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isReady() {
|
||||||
|
return widget.isAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getWidget() {
|
||||||
|
return(widget);
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user