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