mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 05:09:53 +01:00
Don't crash if camera is reopened or surface is recreated.
This commit is contained in:
@@ -6,7 +6,6 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.hardware.Camera;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
@@ -58,7 +57,6 @@ import static android.view.View.INVISIBLE;
|
|||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
import static java.util.logging.Level.WARNING;
|
|
||||||
|
|
||||||
public class ShowQrCodeFragment extends BaseEventFragment
|
public class ShowQrCodeFragment extends BaseEventFragment
|
||||||
implements QrCodeDecoder.ResultCallback {
|
implements QrCodeDecoder.ResultCallback {
|
||||||
@@ -86,7 +84,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
private ViewGroup mainProgressContainer;
|
private ViewGroup mainProgressContainer;
|
||||||
|
|
||||||
private BluetoothStateReceiver receiver;
|
private BluetoothStateReceiver receiver;
|
||||||
private QrCodeDecoder decoder;
|
|
||||||
private boolean gotRemotePayload, waitingForBluetooth;
|
private boolean gotRemotePayload, waitingForBluetooth;
|
||||||
private KeyAgreementTask task;
|
private KeyAgreementTask task;
|
||||||
|
|
||||||
@@ -136,8 +133,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
super.onActivityCreated(savedInstanceState);
|
super.onActivityCreated(savedInstanceState);
|
||||||
|
|
||||||
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
|
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
|
||||||
|
cameraView.setPreviewConsumer(new QrCodeDecoder(this));
|
||||||
decoder = new QrCodeDecoder(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -163,8 +159,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
} else {
|
} else {
|
||||||
startListening();
|
startListening();
|
||||||
}
|
}
|
||||||
|
cameraView.start();
|
||||||
openCamera();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -172,7 +167,7 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
super.onStop();
|
super.onStop();
|
||||||
stopListening();
|
stopListening();
|
||||||
if (receiver != null) getActivity().unregisterReceiver(receiver);
|
if (receiver != null) getActivity().unregisterReceiver(receiver);
|
||||||
releaseCamera();
|
cameraView.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -200,45 +195,11 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
|
||||||
@UiThread
|
|
||||||
private void openCamera() {
|
|
||||||
LOG.info("Opening camera");
|
|
||||||
Camera camera;
|
|
||||||
try {
|
|
||||||
camera = Camera.open();
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
LOG.log(WARNING, e.toString(), e);
|
|
||||||
camera = null;
|
|
||||||
}
|
|
||||||
if (camera == null) {
|
|
||||||
LOG.log(WARNING, "Error opening camera");
|
|
||||||
Toast.makeText(getActivity(), R.string.could_not_open_camera,
|
|
||||||
LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cameraView.start(camera, decoder, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
private void releaseCamera() {
|
|
||||||
LOG.info("Releasing camera");
|
|
||||||
try {
|
|
||||||
cameraView.stop();
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
LOG.log(WARNING, "Error releasing camera", e);
|
|
||||||
// TODO better solution
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void reset() {
|
private void reset() {
|
||||||
statusView.setVisibility(INVISIBLE);
|
statusView.setVisibility(INVISIBLE);
|
||||||
cameraView.setVisibility(VISIBLE);
|
cameraView.setVisibility(VISIBLE);
|
||||||
gotRemotePayload = false;
|
gotRemotePayload = false;
|
||||||
cameraView.startConsumer();
|
|
||||||
startListening();
|
startListening();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +341,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
|
|||||||
LOG.info("Got result from decoder");
|
LOG.info("Got result from decoder");
|
||||||
if (!gotRemotePayload) {
|
if (!gotRemotePayload) {
|
||||||
gotRemotePayload = true;
|
gotRemotePayload = true;
|
||||||
cameraView.stopConsumer();
|
|
||||||
qrCodeScanned(result.getText());
|
qrCodeScanned(result.getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
private final Reader reader = new QRCodeReader();
|
private final Reader reader = new QRCodeReader();
|
||||||
private final ResultCallback callback;
|
private final ResultCallback callback;
|
||||||
|
|
||||||
private boolean stopped = false;
|
private Camera camera = null;
|
||||||
|
|
||||||
public QrCodeDecoder(ResultCallback callback) {
|
public QrCodeDecoder(ResultCallback callback) {
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
@@ -36,37 +36,33 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start(Camera camera) {
|
public void start(Camera camera) {
|
||||||
stopped = false;
|
this.camera = camera;
|
||||||
askForPreviewFrame(camera);
|
askForPreviewFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
stopped = true;
|
camera = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void askForPreviewFrame(Camera camera) {
|
private void askForPreviewFrame() {
|
||||||
if (!stopped) camera.setOneShotPreviewCallback(this);
|
if (camera != null) camera.setOneShotPreviewCallback(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@Override
|
@Override
|
||||||
public void onPreviewFrame(byte[] data, Camera camera) {
|
public void onPreviewFrame(byte[] data, Camera camera) {
|
||||||
if (!stopped) {
|
Size size = camera.getParameters().getPreviewSize();
|
||||||
Size size = camera.getParameters().getPreviewSize();
|
new DecoderTask(data, size.width, size.height).execute();
|
||||||
new DecoderTask(camera, data, size.width, size.height).execute();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class DecoderTask extends AsyncTask<Void, Void, Void> {
|
private class DecoderTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
|
||||||
private final Camera camera;
|
|
||||||
private final byte[] data;
|
private final byte[] data;
|
||||||
private final int width, height;
|
private final int width, height;
|
||||||
|
|
||||||
DecoderTask(Camera camera, byte[] data, int width, int height) {
|
DecoderTask(byte[] data, int width, int height) {
|
||||||
this.camera = camera;
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
@@ -84,7 +80,7 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
} catch (ReaderException e) {
|
} catch (ReaderException e) {
|
||||||
return null; // No barcode found
|
return null; // No barcode found
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
return null; // Decoding failed due to bug in decoder
|
return null; // Preview data did not match width and height
|
||||||
} finally {
|
} finally {
|
||||||
reader.reset();
|
reader.reset();
|
||||||
}
|
}
|
||||||
@@ -97,7 +93,7 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void result) {
|
protected void onPostExecute(Void result) {
|
||||||
askForPreviewFrame(camera);
|
askForPreviewFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ import android.view.SurfaceHolder;
|
|||||||
import android.view.SurfaceView;
|
import android.view.SurfaceView;
|
||||||
|
|
||||||
import org.briarproject.android.util.PreviewConsumer;
|
import org.briarproject.android.util.PreviewConsumer;
|
||||||
|
import org.briarproject.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -33,6 +35,8 @@ import static java.util.logging.Level.INFO;
|
|||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
||||||
AutoFocusCallback {
|
AutoFocusCallback {
|
||||||
|
|
||||||
@@ -44,7 +48,7 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
private PreviewConsumer previewConsumer = null;
|
private PreviewConsumer previewConsumer = null;
|
||||||
private Surface surface = null;
|
private Surface surface = null;
|
||||||
private int displayOrientation = 0, surfaceWidth = 0, surfaceHeight = 0;
|
private int displayOrientation = 0, surfaceWidth = 0, surfaceHeight = 0;
|
||||||
private boolean autoFocus = false;
|
private boolean previewStarted = false, autoFocus = false;
|
||||||
|
|
||||||
public CameraView(Context context) {
|
public CameraView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
@@ -58,6 +62,12 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
public void setPreviewConsumer(PreviewConsumer previewConsumer) {
|
||||||
|
LOG.info("Setting preview consumer");
|
||||||
|
this.previewConsumer = previewConsumer;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow() {
|
protected void onAttachedToWindow() {
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
@@ -70,15 +80,18 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
setKeepScreenOn(false);
|
setKeepScreenOn(false);
|
||||||
getHolder().removeCallback(this);
|
getHolder().removeCallback(this);
|
||||||
if (surface != null) surface.release();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public void start(Camera camera, PreviewConsumer previewConsumer,
|
public void start() {
|
||||||
int rotationDegrees) {
|
try {
|
||||||
this.camera = camera;
|
LOG.info("Opening camera");
|
||||||
this.previewConsumer = previewConsumer;
|
camera = Camera.open();
|
||||||
setDisplayOrientation(rotationDegrees);
|
} catch (RuntimeException e) {
|
||||||
|
LOG.log(WARNING, "Error opening camera", e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDisplayOrientation(0);
|
||||||
// Use barcode scene mode if it's available
|
// Use barcode scene mode if it's available
|
||||||
Parameters params = camera.getParameters();
|
Parameters params = camera.getParameters();
|
||||||
params = setSceneMode(camera, params);
|
params = setSceneMode(camera, params);
|
||||||
@@ -96,14 +109,17 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
enableAutoFocus(params.getFocusMode());
|
enableAutoFocus(params.getFocusMode());
|
||||||
// Log the parameters that are being used (maybe not what we asked for)
|
// Log the parameters that are being used (maybe not what we asked for)
|
||||||
logCameraParameters();
|
logCameraParameters();
|
||||||
if (surface != null) startPreview(getHolder());
|
// Start the preview when the camera and the surface are both ready
|
||||||
|
if (surface != null && !previewStarted) startPreview(getHolder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public void stop() {
|
public void stop() {
|
||||||
|
if (camera == null) return;
|
||||||
stopPreview();
|
stopPreview();
|
||||||
try {
|
try {
|
||||||
if (camera != null) camera.release();
|
LOG.info("Releasing camera");
|
||||||
|
camera.release();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
LOG.log(WARNING, "Error releasing camera", e);
|
LOG.log(WARNING, "Error releasing camera", e);
|
||||||
}
|
}
|
||||||
@@ -112,9 +128,11 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void startPreview(SurfaceHolder holder) {
|
private void startPreview(SurfaceHolder holder) {
|
||||||
|
LOG.info("Starting preview");
|
||||||
try {
|
try {
|
||||||
camera.setPreviewDisplay(holder);
|
camera.setPreviewDisplay(holder);
|
||||||
camera.startPreview();
|
camera.startPreview();
|
||||||
|
previewStarted = true;
|
||||||
startConsumer();
|
startConsumer();
|
||||||
} catch (IOException | RuntimeException e) {
|
} catch (IOException | RuntimeException e) {
|
||||||
LOG.log(WARNING, "Error starting camera preview", e);
|
LOG.log(WARNING, "Error starting camera preview", e);
|
||||||
@@ -123,24 +141,26 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void stopPreview() {
|
private void stopPreview() {
|
||||||
|
LOG.info("Stopping preview");
|
||||||
try {
|
try {
|
||||||
stopConsumer();
|
stopConsumer();
|
||||||
if (camera != null) camera.stopPreview();
|
camera.stopPreview();
|
||||||
} catch (RuntimeException e) {
|
} catch (RuntimeException e) {
|
||||||
LOG.log(WARNING, "Error stopping camera preview", e);
|
LOG.log(WARNING, "Error stopping camera preview", e);
|
||||||
}
|
}
|
||||||
|
previewStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public void startConsumer() {
|
private void startConsumer() {
|
||||||
if (autoFocus) camera.autoFocus(this);
|
if (autoFocus) camera.autoFocus(this);
|
||||||
previewConsumer.start(camera);
|
previewConsumer.start(camera);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public void stopConsumer() {
|
private void stopConsumer() {
|
||||||
if (previewConsumer != null) previewConsumer.stop();
|
|
||||||
if (autoFocus) camera.cancelAutoFocus();
|
if (autoFocus) camera.cancelAutoFocus();
|
||||||
|
previewConsumer.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -295,9 +315,13 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
@UiThread
|
@UiThread
|
||||||
private void surfaceCreatedUi(SurfaceHolder holder) {
|
private void surfaceCreatedUi(SurfaceHolder holder) {
|
||||||
LOG.info("Surface created");
|
LOG.info("Surface created");
|
||||||
if (surface != null) throw new IllegalStateException();
|
if (surface != null && surface != holder.getSurface()) {
|
||||||
|
LOG.info("Releasing old surface");
|
||||||
|
surface.release();
|
||||||
|
}
|
||||||
surface = holder.getSurface();
|
surface = holder.getSurface();
|
||||||
if (camera != null) startPreview(holder);
|
// Start the preview when the camera and the surface are both ready
|
||||||
|
if (camera != null && !previewStarted) startPreview(holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -314,9 +338,10 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
@UiThread
|
@UiThread
|
||||||
private void surfaceChangedUi(SurfaceHolder holder, int w, int h) {
|
private void surfaceChangedUi(SurfaceHolder holder, int w, int h) {
|
||||||
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
|
if (LOG.isLoggable(INFO)) LOG.info("Surface changed: " + w + "x" + h);
|
||||||
// Release the previous surface if necessary
|
if (surface != null && surface != holder.getSurface()) {
|
||||||
if (surface != null && surface != holder.getSurface())
|
LOG.info("Releasing old surface");
|
||||||
surface.release();
|
surface.release();
|
||||||
|
}
|
||||||
surface = holder.getSurface();
|
surface = holder.getSurface();
|
||||||
surfaceWidth = w;
|
surfaceWidth = w;
|
||||||
surfaceHeight = h;
|
surfaceHeight = h;
|
||||||
@@ -346,8 +371,12 @@ public class CameraView extends SurfaceView implements SurfaceHolder.Callback,
|
|||||||
@UiThread
|
@UiThread
|
||||||
private void surfaceDestroyedUi(SurfaceHolder holder) {
|
private void surfaceDestroyedUi(SurfaceHolder holder) {
|
||||||
LOG.info("Surface destroyed");
|
LOG.info("Surface destroyed");
|
||||||
if (holder.getSurface() != surface) throw new IllegalStateException();
|
if (surface != null && surface != holder.getSurface()) {
|
||||||
if (surface != null) surface.release();
|
LOG.info("Releasing old surface");
|
||||||
|
surface.release();
|
||||||
|
}
|
||||||
|
surface = null;
|
||||||
|
holder.getSurface().release();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user