diff options
author | Christian Schneppe <christian@pix-art.de> | 2018-03-31 22:09:22 +0200 |
---|---|---|
committer | Christian Schneppe <christian@pix-art.de> | 2018-03-31 22:12:10 +0200 |
commit | a7af64cbeb43214351bbf9557e7e78f2dd295f51 (patch) | |
tree | 832f8e2212432934c68b094aea1673f5aa88b5f4 /src/main/java/de/pixart/messenger/ui/ScanActivity.java | |
parent | 6cfb99ae03a5ea8c992db099caa28ab79b0fbfce (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.java | 292 |
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 |