package nodomain.freeyourgadget.gadgetbridge.womp; import android.Manifest; import android.annotation.SuppressLint; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Size; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.camera.core.CameraSelector; import androidx.camera.core.ImageAnalysis; import androidx.camera.core.ImageProxy; import androidx.camera.core.Preview; import androidx.camera.lifecycle.ProcessCameraProvider; import androidx.camera.view.PreviewView; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import com.google.common.util.concurrent.ListenableFuture; import com.google.zxing.BinaryBitmap; import com.google.zxing.MultiFormatReader; import com.google.zxing.NotFoundException; import com.google.zxing.PlanarYUVLuminanceSource; import com.google.zxing.Result; import com.google.zxing.common.HybridBinarizer; import java.nio.ByteBuffer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import nodomain.freeyourgadget.gadgetbridge.R; public class WompScanActivity extends AppCompatActivity { public static final String EXTRA_CODE = "code"; private PreviewView previewView; private ExecutorService cameraExecutor; private volatile boolean found; private final MultiFormatReader reader = new MultiFormatReader(); @Override protected void onCreate(@Nullable Bundle savedInstanceState) { setTheme(R.style.GadgetbridgeTheme_NoActionBar); super.onCreate(savedInstanceState); setContentView(R.layout.activity_womp_scan); previewView = findViewById(R.id.womp_preview); TextView hint = findViewById(R.id.womp_scan_hint); hint.setText("对准二维码扫描"); cameraExecutor = Executors.newSingleThreadExecutor(); findViewById(R.id.womp_scan_cancel).setOnClickListener(v -> { setResult(RESULT_CANCELED); finish(); }); if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 1); } else { startCamera(); } } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startCamera(); } else { Toast.makeText(this, "需要摄像头权限", Toast.LENGTH_SHORT).show(); finish(); } } private void startCamera() { ListenableFuture future = ProcessCameraProvider.getInstance(this); future.addListener(() -> { try { ProcessCameraProvider provider = future.get(); Preview preview = new Preview.Builder().build(); preview.setSurfaceProvider(previewView.getSurfaceProvider()); ImageAnalysis analysis = new ImageAnalysis.Builder() .setTargetResolution(new Size(1280, 720)) .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build(); analysis.setAnalyzer(cameraExecutor, this::analyze); provider.unbindAll(); provider.bindToLifecycle(this, CameraSelector.DEFAULT_BACK_CAMERA, preview, analysis); } catch (Exception e) { Toast.makeText(this, "无法打开摄像头", Toast.LENGTH_SHORT).show(); finish(); } }, ContextCompat.getMainExecutor(this)); } @SuppressLint("UnsafeOptInUsageError") private void analyze(ImageProxy image) { if (found) { image.close(); return; } try { ImageProxy.PlaneProxy plane = image.getPlanes()[0]; ByteBuffer buffer = plane.getBuffer(); int width = image.getWidth(); int height = image.getHeight(); int rowStride = plane.getRowStride(); byte[] data = new byte[width * height]; if (rowStride == width) { buffer.get(data, 0, Math.min(data.length, buffer.remaining())); } else { byte[] row = new byte[rowStride]; int pos = 0; for (int i = 0; i < height; i++) { int toRead = Math.min(rowStride, buffer.remaining()); if (toRead <= 0) break; buffer.get(row, 0, toRead); System.arraycopy(row, 0, data, pos, width); pos += width; } } PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource( data, width, height, 0, 0, width, height, false); BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source)); Result result = reader.decodeWithState(bitmap); if (result != null && result.getText() != null) { found = true; Intent dataIntent = new Intent(); dataIntent.putExtra(EXTRA_CODE, result.getText().trim()); setResult(RESULT_OK, dataIntent); finish(); } } catch (NotFoundException ignored) { } catch (Exception ignored) { } finally { reader.reset(); image.close(); } } @Override protected void onDestroy() { super.onDestroy(); if (cameraExecutor != null) { cameraExecutor.shutdown(); } } }