2626import androidx .annotation .NonNull ;
2727import androidx .appcompat .app .AlertDialog ;
2828import androidx .appcompat .app .AppCompatActivity ;
29- import androidx .camera .core .AspectRatio ;
3029import androidx .camera .core .Camera ;
3130import androidx .camera .core .CameraSelector ;
3231import androidx .camera .core .ImageAnalysis ;
33- import androidx .camera .core .ImageProxy ;
3432import androidx .camera .core .Preview ;
3533import androidx .camera .lifecycle .ProcessCameraProvider ;
3634import androidx .camera .view .PreviewView ;
4038import androidx .lifecycle .LiveData ;
4139
4240import com .google .android .gms .common .api .CommonStatusCodes ;
43- import com .google .android .gms .tasks .OnCompleteListener ;
44- import com .google .android .gms .tasks .OnFailureListener ;
45- import com .google .android .gms .tasks .OnSuccessListener ;
4641import com .google .android .gms .tasks .Task ;
4742import com .google .android .material .snackbar .Snackbar ;
4843import com .google .common .util .concurrent .ListenableFuture ;
5550
5651import java .nio .charset .StandardCharsets ;
5752import java .util .List ;
53+ import java .util .Objects ;
5854import java .util .concurrent .ExecutionException ;
5955import java .util .concurrent .ExecutorService ;
6056import java .util .concurrent .Executors ;
@@ -69,12 +65,10 @@ public class CaptureActivity extends AppCompatActivity implements SurfaceHolder.
6965 public static final String BarcodeValue = "MLKitBarcodeValue" ;
7066
7167 private ListenableFuture <ProcessCameraProvider > cameraProviderFuture ;
72- private ExecutorService executor = Executors .newSingleThreadExecutor ();
68+ private final ExecutorService executor = Executors .newSingleThreadExecutor ();
7369 private PreviewView mCameraView ;
7470 private SurfaceHolder holder ;
7571 private SurfaceView surfaceView ;
76- private Canvas canvas ;
77- private Paint paint ;
7872
7973 private static final int RC_HANDLE_CAMERA_PERM = 2 ;
8074 private ImageButton _TorchButton ;
@@ -118,27 +112,24 @@ protected void onCreate(Bundle savedInstanceState) {
118112
119113 _TorchButton = findViewById (getResources ().getIdentifier ("torch_button" , "id" , this .getPackageName ()));
120114
121- _TorchButton .setOnClickListener (new View .OnClickListener () {
122- @ Override
123- public void onClick (View v ) {
124-
125- LiveData <Integer > flashState = camera .getCameraInfo ().getTorchState ();
126- if (flashState .getValue () != null ) {
127- boolean state = flashState .getValue () == 1 ;
128- _TorchButton .setBackgroundResource (getResources ().getIdentifier (!state ? "torch_active" : "torch_inactive" ,
129- "drawable" , CaptureActivity .this .getPackageName ()));
130- camera .getCameraControl ().enableTorch (!state );
131- }
115+ _TorchButton .setOnClickListener (v -> {
132116
117+ LiveData <Integer > flashState = camera .getCameraInfo ().getTorchState ();
118+ if (flashState .getValue () != null ) {
119+ boolean state = flashState .getValue () == 1 ;
120+ _TorchButton .setBackgroundResource (getResources ().getIdentifier (!state ? "torch_active" : "torch_inactive" ,
121+ "drawable" , CaptureActivity .this .getPackageName ()));
122+ camera .getCameraControl ().enableTorch (!state );
133123 }
124+
134125 });
135126
136127 }
137128
138129 // ----------------------------------------------------------------------------
139130 // | Helper classes
140131 // ----------------------------------------------------------------------------
141- private class CaptureGestureListener extends GestureDetector .SimpleOnGestureListener {
132+ private static class CaptureGestureListener extends GestureDetector .SimpleOnGestureListener {
142133 @ Override
143134 public boolean onSingleTapConfirmed (MotionEvent e ) {
144135 return super .onSingleTapConfirmed (e );
@@ -160,7 +151,8 @@ public boolean onScaleBegin(ScaleGestureDetector detector) {
160151 public void onScaleEnd (ScaleGestureDetector detector ) {
161152
162153 if (camera != null ) {
163- float scale = camera .getCameraInfo ().getZoomState ().getValue ().getZoomRatio () * detector .getScaleFactor ();
154+ float ratio = Objects .requireNonNull (camera .getCameraInfo ().getZoomState ().getValue ()).getZoomRatio ();
155+ float scale = ratio * detector .getScaleFactor ();
164156 camera .getCameraControl ().setZoomRatio (scale );
165157 }
166158 }
@@ -181,12 +173,8 @@ private void requestCameraPermission() {
181173 return ;
182174 }
183175
184- View .OnClickListener listener = new View .OnClickListener () {
185- @ Override
186- public void onClick (View view ) {
187- ActivityCompat .requestPermissions (CaptureActivity .this , permissions , RC_HANDLE_CAMERA_PERM );
188- }
189- };
176+ View .OnClickListener listener = view -> ActivityCompat .requestPermissions (CaptureActivity .this , permissions ,
177+ RC_HANDLE_CAMERA_PERM );
190178
191179 findViewById (getResources ().getIdentifier ("topLayout" , "id" , getPackageName ())).setOnClickListener (listener );
192180 Snackbar
@@ -209,11 +197,7 @@ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permis
209197 return ;
210198 }
211199
212- DialogInterface .OnClickListener listener = new DialogInterface .OnClickListener () {
213- public void onClick (DialogInterface dialog , int id ) {
214- finish ();
215- }
216- };
200+ DialogInterface .OnClickListener listener = (dialog , id ) -> finish ();
217201
218202 AlertDialog .Builder builder = new AlertDialog .Builder (this );
219203 builder .setTitle ("Camera permission required" )
@@ -260,7 +244,7 @@ void startCamera() {
260244 mCameraView = findViewById (getResources ().getIdentifier ("previewView" , "id" , getPackageName ()));
261245 mCameraView .setPreferredImplementationMode (PreviewView .ImplementationMode .TEXTURE_VIEW );
262246
263- Boolean rotateCamera = getIntent ().getBooleanExtra ("RotateCamera" , false );
247+ boolean rotateCamera = getIntent ().getBooleanExtra ("RotateCamera" , false );
264248 if (rotateCamera ) {
265249 mCameraView .setScaleX (-1F );
266250 mCameraView .setScaleY (-1F );
@@ -272,24 +256,22 @@ void startCamera() {
272256 // mCameraView.setScaleType(PreviewView.ScaleType.FIT_CENTER);
273257
274258 cameraProviderFuture = ProcessCameraProvider .getInstance (this );
275- cameraProviderFuture .addListener (new Runnable () {
276- @ Override
277- public void run () {
278- try {
279- ProcessCameraProvider cameraProvider = cameraProviderFuture .get ();
280- CaptureActivity .this .bindPreview (cameraProvider );
281-
282- } catch (ExecutionException | InterruptedException e ) {
283- // No errors need to be handled for this Future.
284- // This should never be reached.
285- }
259+ cameraProviderFuture .addListener (() -> {
260+ try {
261+ ProcessCameraProvider cameraProvider = cameraProviderFuture .get ();
262+ CaptureActivity .this .bindPreview (cameraProvider );
263+
264+ } catch (ExecutionException | InterruptedException e ) {
265+ // No errors need to be handled for this Future.
266+ // This should never be reached.
286267 }
287268 }, ContextCompat .getMainExecutor (this ));
288269 }
289270
290271 /**
291272 * Binding to camera
292273 */
274+ @ SuppressLint ("UnsafeOptInUsageError" )
293275 private void bindPreview (ProcessCameraProvider cameraProvider ) {
294276
295277 int barcodeFormat ;
@@ -315,88 +297,69 @@ private void bindPreview(ProcessCameraProvider cameraProvider) {
315297 BarcodeScanner scanner = BarcodeScanning
316298 .getClient (new BarcodeScannerOptions .Builder ().setBarcodeFormats (barcodeFormat ).build ());
317299
318- imageAnalysis .setAnalyzer (executor , new ImageAnalysis .Analyzer () {
319- @ SuppressLint ("UnsafeExperimentalUsageError" )
320- @ Override
321- public void analyze (@ NonNull ImageProxy image ) {
300+ imageAnalysis .setAnalyzer (executor , image -> {
301+ if (image .getImage () == null ) {
302+ return ;
303+ }
304+ Bitmap bmp = BitmapUtils .getBitmap (image );
305+ if (bmp == null ) {
306+ return ;
307+ }
322308
323- if (image == null || image .getImage () == null ) {
324- return ;
325- }
309+ int height = bmp .getHeight ();
310+ int width = bmp .getWidth ();
326311
327- Bitmap bmp = BitmapUtils .getBitmap (image );
312+ int left , right , top , bottom , boxHeight , boxWidth ;
313+ int diameter = Math .min (height , width );
328314
329- int height = bmp . getHeight ( );
330- int width = bmp . getWidth () ;
315+ int offset = ( int ) (( 1 - DetectorSize ) * diameter );
316+ diameter -= offset ;
331317
332- int left , right , top , bottom , diameter , boxHeight , boxWidth ;
318+ left = width / 2 - diameter / 2 ;
319+ top = height / 2 - diameter / 2 ;
320+ right = width / 2 + diameter / 2 ;
321+ bottom = height / 2 + diameter / 2 ;
333322
334- diameter = width ;
335- if (height < width ) {
336- diameter = height ;
323+ boxHeight = bottom - top ;
324+ boxWidth = right - left ;
325+
326+ Bitmap bitmap = Bitmap .createBitmap (bmp , left , top , boxWidth , boxHeight );
327+ Task <List <Barcode >> task = scanner
328+ .process (InputImage .fromBitmap (bitmap , image .getImageInfo ().getRotationDegrees ()));
329+ task .addOnSuccessListener (barCodes -> {
330+ // # Code to test image viewfinder
331+ /*
332+ * ImageView imageView = (ImageView)
333+ * findViewById(getResources().getIdentifier("imageView", "id",
334+ * getPackageName())); imageView.setImageBitmap(bitmap);
335+ */
336+ if (barCodes .size () > 0 ) {
337+ for (Barcode barcode : barCodes ) {
338+ // Toast.makeText(CaptureActivity.this, "FOUND: " + barcode.getDisplayValue(),
339+ // Toast.LENGTH_SHORT).show();
340+ Intent data = new Intent ();
341+ String value = barcode .getRawValue ();
342+
343+ // rawValue returns null if string is not UTF-8 encoded.
344+ // If that's the case, we will decode it as ASCII,
345+ // because it's the most common encoding for barcodes.
346+ // e.g. https://www.barcodefaq.com/1d/code-128/
347+ if (barcode .getRawValue () == null ) {
348+ value = new String (barcode .getRawBytes (), StandardCharsets .US_ASCII );
349+ }
350+
351+ data .putExtra (BarcodeFormat , barcode .getFormat ());
352+ data .putExtra (BarcodeType , barcode .getValueType ());
353+ data .putExtra (BarcodeValue , value );
354+ setResult (CommonStatusCodes .SUCCESS , data );
355+ finish ();
356+
357+ }
337358 }
338-
339- int offset = (int ) ((1 - DetectorSize ) * diameter );
340- diameter -= offset ;
341-
342- left = width / 2 - diameter / 2 ;
343- top = height / 2 - diameter / 2 ;
344- right = width / 2 + diameter / 2 ;
345- bottom = height / 2 + diameter / 2 ;
346-
347- boxHeight = bottom - top ;
348- boxWidth = right - left ;
349-
350- Bitmap bitmap = Bitmap .createBitmap (bmp , left , top , boxWidth , boxHeight );
351- scanner .process (InputImage .fromBitmap (bitmap , image .getImageInfo ().getRotationDegrees ()))
352- .addOnSuccessListener (new OnSuccessListener <List <Barcode >>() {
353- @ Override
354- public void onSuccess (List <Barcode > barCodes ) {
355-
356- // # Code to test image viewfinder
357- /*
358- * ImageView imageView = (ImageView)
359- * findViewById(getResources().getIdentifier("imageView", "id",
360- * getPackageName())); imageView.setImageBitmap(bitmap);
361- */
362-
363- if (barCodes .size () > 0 ) {
364- for (Barcode barcode : barCodes ) {
365- // Toast.makeText(CaptureActivity.this, "FOUND: " + barcode.getDisplayValue(),
366- // Toast.LENGTH_SHORT).show();
367- Intent data = new Intent ();
368- String value = barcode .getRawValue ();
369-
370- // rawValue returns null if string is not UTF-8 encoded.
371- // If that's the case, we will decode it as ASCII,
372- // because it's the most common encoding for barcodes.
373- // e.g. https://www.barcodefaq.com/1d/code-128/
374- if (barcode .getRawValue () == null ) {
375- value = new String (barcode .getRawBytes (), StandardCharsets .US_ASCII );
376- }
377-
378- data .putExtra (BarcodeFormat , barcode .getFormat ());
379- data .putExtra (BarcodeType , barcode .getValueType ());
380- data .putExtra (BarcodeValue , value );
381- setResult (CommonStatusCodes .SUCCESS , data );
382- finish ();
383-
384- }
385- }
386- }
387- }).addOnFailureListener (new OnFailureListener () {
388- @ Override
389- public void onFailure (@ NonNull Exception e ) {
390-
391- }
392- }).addOnCompleteListener (new OnCompleteListener <List <Barcode >>() {
393- @ Override
394- public void onComplete (@ NonNull Task <List <Barcode >> task ) {
395- image .close ();
396- }
397- });
398- }
399-
359+ });
360+ task .addOnFailureListener (e -> {
361+ });
362+ task .addOnCompleteListener (r -> image .close ());
400363 });
401364
402365 camera = cameraProvider .bindToLifecycle ((LifecycleOwner ) this , cameraSelector , imageAnalysis , preview );
@@ -411,20 +374,16 @@ private void DrawFocusRect(int color) {
411374 int height = mCameraView .getHeight ();
412375 int width = mCameraView .getWidth ();
413376
414- int left , right , top , bottom , diameter ;
415-
416- diameter = width ;
417- if (height < width ) {
418- diameter = height ;
419- }
377+ int left , right , top , bottom ;
378+ int diameter = Math .min (height , width );
420379
421380 int offset = (int ) ((1 - DetectorSize ) * diameter );
422381 diameter -= offset ;
423382
424- canvas = holder .lockCanvas ();
383+ Canvas canvas = holder .lockCanvas ();
425384 canvas .drawColor (0 , PorterDuff .Mode .CLEAR );
426385 // border's properties
427- paint = new Paint ();
386+ Paint paint = new Paint ();
428387 paint .setStyle (Paint .Style .STROKE );
429388 paint .setColor (color );
430389 paint .setStrokeWidth (5 );
0 commit comments