aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/de/pixart/messenger/ui/ScanActivity.java
diff options
context:
space:
mode:
authorChristian Schneppe <christian@pix-art.de>2018-03-31 22:09:22 +0200
committerChristian Schneppe <christian@pix-art.de>2018-03-31 22:12:10 +0200
commita7af64cbeb43214351bbf9557e7e78f2dd295f51 (patch)
tree832f8e2212432934c68b094aea1673f5aa88b5f4 /src/main/java/de/pixart/messenger/ui/ScanActivity.java
parent6cfb99ae03a5ea8c992db099caa28ab79b0fbfce (diff)
integrate qr code scanner. temporarily break omemo activity scan
Diffstat (limited to 'src/main/java/de/pixart/messenger/ui/ScanActivity.java')
-rw-r--r--src/main/java/de/pixart/messenger/ui/ScanActivity.java292
1 files changed, 292 insertions, 0 deletions
diff --git a/src/main/java/de/pixart/messenger/ui/ScanActivity.java b/src/main/java/de/pixart/messenger/ui/ScanActivity.java
new file mode 100644
index 000000000..d44f50512
--- /dev/null
+++ b/src/main/java/de/pixart/messenger/ui/ScanActivity.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2012-2015 the original author or authors.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package de.pixart.messenger.ui;
+
+import android.Manifest;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.SurfaceTexture;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.Vibrator;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.TextureView.SurfaceTextureListener;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.google.zxing.BinaryBitmap;
+import com.google.zxing.DecodeHintType;
+import com.google.zxing.PlanarYUVLuminanceSource;
+import com.google.zxing.ReaderException;
+import com.google.zxing.Result;
+import com.google.zxing.ResultPointCallback;
+import com.google.zxing.common.HybridBinarizer;
+import com.google.zxing.qrcode.QRCodeReader;
+
+import java.util.EnumMap;
+import java.util.Map;
+
+import de.pixart.messenger.Config;
+import de.pixart.messenger.R;
+import de.pixart.messenger.ui.service.CameraManager;
+import de.pixart.messenger.ui.widget.ScannerView;
+
+/**
+ * @author Andreas Schildbach
+ */
+@SuppressWarnings("deprecation")
+public final class ScanActivity extends Activity implements SurfaceTextureListener, ActivityCompat.OnRequestPermissionsResultCallback {
+ public static final String INTENT_EXTRA_RESULT = "result";
+
+ private static final long VIBRATE_DURATION = 50L;
+ private static final long AUTO_FOCUS_INTERVAL_MS = 2500L;
+ private static boolean DISABLE_CONTINUOUS_AUTOFOCUS = Build.MODEL.equals("GT-I9100") // Galaxy S2
+ || Build.MODEL.equals("SGH-T989") // Galaxy S2
+ || Build.MODEL.equals("SGH-T989D") // Galaxy S2 X
+ || Build.MODEL.equals("SAMSUNG-SGH-I727") // Galaxy S2 Skyrocket
+ || Build.MODEL.equals("GT-I9300") // Galaxy S3
+ || Build.MODEL.equals("GT-N7000"); // Galaxy Note
+ private final CameraManager cameraManager = new CameraManager();
+ private ScannerView scannerView;
+ private TextureView previewView;
+ private volatile boolean surfaceCreated = false;
+ private Vibrator vibrator;
+ private HandlerThread cameraThread;
+ private volatile Handler cameraHandler;
+ private final Runnable closeRunnable = new Runnable() {
+ @Override
+ public void run() {
+ cameraHandler.removeCallbacksAndMessages(null);
+ cameraManager.close();
+ }
+ };
+ private final Runnable fetchAndDecodeRunnable = new Runnable() {
+ private final QRCodeReader reader = new QRCodeReader();
+ private final Map<DecodeHintType, Object> hints = new EnumMap<DecodeHintType, Object>(DecodeHintType.class);
+
+ @Override
+ public void run() {
+ cameraManager.requestPreviewFrame((data, camera) -> decode(data));
+ }
+
+ private void decode(final byte[] data) {
+ final PlanarYUVLuminanceSource source = cameraManager.buildLuminanceSource(data);
+ final BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+
+ try {
+ hints.put(DecodeHintType.NEED_RESULT_POINT_CALLBACK, (ResultPointCallback) dot -> runOnUiThread(() -> scannerView.addDot(dot)));
+ final Result scanResult = reader.decode(bitmap, hints);
+
+ runOnUiThread(() -> handleResult(scanResult));
+ } catch (final ReaderException x) {
+ // retry
+ cameraHandler.post(fetchAndDecodeRunnable);
+ } finally {
+ reader.reset();
+ }
+ }
+ };
+ private final Runnable openRunnable = new Runnable() {
+ @Override
+ public void run() {
+ try {
+ final Camera camera = cameraManager.open(previewView, displayRotation(), !DISABLE_CONTINUOUS_AUTOFOCUS);
+
+ final Rect framingRect = cameraManager.getFrame();
+ final RectF framingRectInPreview = new RectF(cameraManager.getFramePreview());
+ framingRectInPreview.offsetTo(0, 0);
+ final boolean cameraFlip = cameraManager.getFacing() == CameraInfo.CAMERA_FACING_FRONT;
+ final int cameraRotation = cameraManager.getOrientation();
+
+ runOnUiThread(() -> scannerView.setFraming(framingRect, framingRectInPreview, displayRotation(), cameraRotation, cameraFlip));
+
+ final String focusMode = camera.getParameters().getFocusMode();
+ final boolean nonContinuousAutoFocus = Camera.Parameters.FOCUS_MODE_AUTO.equals(focusMode)
+ || Camera.Parameters.FOCUS_MODE_MACRO.equals(focusMode);
+
+ if (nonContinuousAutoFocus)
+ cameraHandler.post(new AutoFocusRunnable(camera));
+
+ cameraHandler.post(fetchAndDecodeRunnable);
+ } catch (final Exception x) {
+ Log.d(Config.LOGTAG, "problem opening camera", x);
+ }
+ }
+
+ private int displayRotation() {
+ final int rotation = getWindowManager().getDefaultDisplay().getRotation();
+ if (rotation == Surface.ROTATION_0)
+ return 0;
+ else if (rotation == Surface.ROTATION_90)
+ return 90;
+ else if (rotation == Surface.ROTATION_180)
+ return 180;
+ else if (rotation == Surface.ROTATION_270)
+ return 270;
+ else
+ throw new IllegalStateException("rotation: " + rotation);
+ }
+ };
+
+ @Override
+ public void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+
+ setContentView(R.layout.activity_scan);
+ scannerView = findViewById(R.id.scan_activity_mask);
+ previewView = findViewById(R.id.scan_activity_preview);
+ previewView.setSurfaceTextureListener(this);
+
+ cameraThread = new HandlerThread("cameraThread", Process.THREAD_PRIORITY_BACKGROUND);
+ cameraThread.start();
+ cameraHandler = new Handler(cameraThread.getLooper());
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ maybeOpenCamera();
+ }
+
+ @Override
+ protected void onPause() {
+ cameraHandler.post(closeRunnable);
+
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ // cancel background thread
+ cameraHandler.removeCallbacksAndMessages(null);
+ cameraThread.quit();
+
+ previewView.setSurfaceTextureListener(null);
+
+ super.onDestroy();
+ }
+
+ private void maybeOpenCamera() {
+ if (surfaceCreated && ContextCompat.checkSelfPermission(this,
+ Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED)
+ cameraHandler.post(openRunnable);
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(final SurfaceTexture surface, final int width, final int height) {
+ surfaceCreated = true;
+ maybeOpenCamera();
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(final SurfaceTexture surface) {
+ surfaceCreated = false;
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(final SurfaceTexture surface, final int width, final int height) {
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(final SurfaceTexture surface) {
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ }
+
+ @Override
+ public void onBackPressed() {
+ scannerView.setVisibility(View.GONE);
+ setResult(RESULT_CANCELED);
+ postFinish();
+ }
+
+ @Override
+ public boolean onKeyDown(final int keyCode, final KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_FOCUS:
+ case KeyEvent.KEYCODE_CAMERA:
+ // don't launch camera app
+ return true;
+ case KeyEvent.KEYCODE_VOLUME_DOWN:
+ case KeyEvent.KEYCODE_VOLUME_UP:
+ cameraHandler.post(() -> cameraManager.setTorch(keyCode == KeyEvent.KEYCODE_VOLUME_UP));
+ return true;
+ }
+
+ return super.onKeyDown(keyCode, event);
+ }
+
+ public void handleResult(final Result scanResult) {
+ vibrator.vibrate(VIBRATE_DURATION);
+
+ scannerView.setIsResult(true);
+
+ final Intent result = new Intent();
+ result.putExtra(INTENT_EXTRA_RESULT, scanResult.getText());
+ setResult(RESULT_OK, result);
+ postFinish();
+ }
+
+ private void postFinish() {
+ new Handler().postDelayed(() -> finish(), 50);
+ }
+
+ private final class AutoFocusRunnable implements Runnable {
+ private final Camera camera;
+ private final Camera.AutoFocusCallback autoFocusCallback = new Camera.AutoFocusCallback() {
+ @Override
+ public void onAutoFocus(final boolean success, final Camera camera) {
+ // schedule again
+ cameraHandler.postDelayed(AutoFocusRunnable.this, AUTO_FOCUS_INTERVAL_MS);
+ }
+ };
+
+ public AutoFocusRunnable(final Camera camera) {
+ this.camera = camera;
+ }
+
+ @Override
+ public void run() {
+ try {
+ camera.autoFocus(autoFocusCallback);
+ } catch (final Exception x) {
+ Log.d(Config.LOGTAG, "problem with auto-focus, will not schedule again", x);
+ }
+ }
+ }
+} \ No newline at end of file