-
-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create Micro Image widget - 1/3 #2954
base: master
Are you sure you want to change the base?
Changes from all commits
acc55a4
0f19af3
03b4a7b
f9fb076
6733d7f
d069b69
70ebf1a
2c0674e
6438e94
9e99342
6dde542
4861275
4bc607a
56ecdaf
ad415aa
0ae7cb5
4df598c
b74b2b2
0b48bb2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,6 +66,11 @@ dependencies { | |
|
||
// this syntax doesn't work for compiling .aar files, so those have to be loaded manually | ||
implementation fileTree(include: '*.jar', exclude: 'regexp-me.jar', dir: 'libs') | ||
implementation "androidx.camera:camera-view:$cameraX_version" | ||
implementation "androidx.camera:camera-core:$cameraX_version" | ||
implementation "androidx.camera:camera-camera2:$cameraX_version" | ||
implementation "androidx.camera:camera-lifecycle:$cameraX_version" | ||
implementation 'com.google.android.gms:play-services-mlkit-face-detection:17.1.0' | ||
Comment on lines
+69
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the impact on apk size due to these additions ? |
||
implementation(name: 'htmlspanner-custom', ext: 'aar') | ||
implementation 'com.github.dimagi:zebra-print-android:v1.3' | ||
implementation (name: 'LibSimprints-1.0.12', ext: 'aar') | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,30 @@ | ||||||||||||||||||||
<?xml version="1.0" encoding="utf-8"?> | ||||||||||||||||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||||||||||||||||||
xmlns:app="http://schemas.android.com/apk/res-auto" | ||||||||||||||||||||
xmlns:tools="http://schemas.android.com/tools" | ||||||||||||||||||||
android:layout_width="match_parent" | ||||||||||||||||||||
android:layout_height="match_parent" | ||||||||||||||||||||
tools:viewBindingIgnore="true"> | ||||||||||||||||||||
|
||||||||||||||||||||
<androidx.camera.view.PreviewView | ||||||||||||||||||||
android:id="@+id/view_finder" | ||||||||||||||||||||
android:layout_width="match_parent" | ||||||||||||||||||||
android:layout_height="match_parent" /> | ||||||||||||||||||||
Comment on lines
+9
to
+12
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add content description for accessibility. The PreviewView should have a content description for screen readers. <androidx.camera.view.PreviewView
android:id="@+id/view_finder"
android:layout_width="match_parent"
- android:layout_height="match_parent" />
+ android:layout_height="match_parent"
+ android:contentDescription="@string/camera_preview_content_description" /> 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||
|
||||||||||||||||||||
<LinearLayout | ||||||||||||||||||||
android:layout_width="match_parent" | ||||||||||||||||||||
android:layout_height="match_parent" | ||||||||||||||||||||
android:orientation="vertical" | ||||||||||||||||||||
android:layout_gravity="center"> | ||||||||||||||||||||
|
||||||||||||||||||||
<org.commcare.views.FaceCaptureView | ||||||||||||||||||||
android:id="@+id/face_overlay" | ||||||||||||||||||||
android:layout_width="match_parent" | ||||||||||||||||||||
android:layout_height="0dp" | ||||||||||||||||||||
app:background_color="@color/cc_neutral_bg_tr" | ||||||||||||||||||||
app:face_capture_area_delimiter_color="@color/white" | ||||||||||||||||||||
app:face_marker_color="@color/cc_attention_positive_color" | ||||||||||||||||||||
app:countdown_text_size="@dimen/font_size_large" | ||||||||||||||||||||
android:layout_weight="1"/> | ||||||||||||||||||||
</LinearLayout> | ||||||||||||||||||||
</FrameLayout> |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,190 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
package org.commcare.fragments; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. package seems incorrect given it's an activity |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.annotation.SuppressLint; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.graphics.Bitmap; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.graphics.Rect; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.media.Image; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.os.Bundle; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.util.Size; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import android.widget.Toast; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.common.util.concurrent.ListenableFuture; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.common.MlKitException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.vision.common.InputImage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.vision.common.internal.ImageConvertUtils; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.vision.face.Face; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.vision.face.FaceDetection; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.vision.face.FaceDetector; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import com.google.mlkit.vision.face.FaceDetectorOptions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.commcare.dalvik.R; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.commcare.util.LogTypes; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.commcare.utils.MediaUtil; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.commcare.views.FaceCaptureView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.commcare.views.widgets.ImageWidget; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.javarosa.core.services.Logger; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import org.javarosa.core.services.locale.Localization; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import java.util.concurrent.ExecutionException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import androidx.annotation.NonNull; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import androidx.annotation.Nullable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import androidx.appcompat.app.ActionBar; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
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.core.UseCase; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import androidx.camera.lifecycle.ProcessCameraProvider; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import androidx.camera.view.PreviewView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import androidx.core.content.ContextCompat; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public class MicroImageActivity extends AppCompatActivity implements ImageAnalysis.Analyzer, FaceCaptureView.ImageStabilizedListener { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we add a class level javadoc here. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private static final String TAG = MicroImageActivity.class.toString(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private PreviewView cameraView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private FaceCaptureView faceCaptureView; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private Bitmap inputImage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
super.onCreate(savedInstanceState); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setContentView(R.layout.micro_image_widget); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
faceCaptureView = findViewById(R.id.face_overlay); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cameraView = findViewById(R.id.view_finder); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ActionBar actionBar = getSupportActionBar(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (actionBar != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
actionBar.setTitle(R.string.micro_image_activity_title); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
faceCaptureView.setImageStabilizedListener(this); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
startCamera(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (ExecutionException | InterruptedException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logErrorAndExit("Error starting camera", "microimage.camera.start.failed", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private void startCamera() throws ExecutionException, InterruptedException { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we are both throwing and catching the exceptions in this method which seems un-necessary |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cameraProviderFuture.addListener(() -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ProcessCameraProvider cameraProvider; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cameraProvider = cameraProviderFuture.get(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (ExecutionException | InterruptedException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logErrorAndExit("Error acquiring camera provider", "microimage.camera.start.failed", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+79
to
+81
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is there a practical scenario where we anticipate these exceptions to happen ? If not, we should just let the application hard crash. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
bindUseCases(cameraProvider); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}, ContextCompat.getMainExecutor(this)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private void bindUseCases(ProcessCameraProvider cameraProvider) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. did you mean |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
int targetRotation = getWindowManager().getDefaultDisplay().getRotation(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Size targetResolution = new Size(faceCaptureView.getImageWidth(), faceCaptureView.getImageHeight()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Preview use case | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Preview preview = new Preview.Builder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setTargetResolution(targetResolution) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setTargetRotation(targetRotation) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
preview.setSurfaceProvider(cameraView.getSurfaceProvider()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
UseCase imageAnalyzer = buildImageAnalysisUseCase(targetResolution, targetRotation); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Unbind any previous use cases before binding new ones | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cameraProvider.unbindAll(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Bind the use cases to the camera | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalyzer); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private UseCase buildImageAnalysisUseCase(Size targetResolution, int targetRotation) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
ImageAnalysis imageAnalyzer = new ImageAnalysis.Builder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setTargetResolution(targetResolution) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setTargetRotation(targetRotation) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imageAnalyzer.setAnalyzer(ContextCompat.getMainExecutor(getApplicationContext()), this); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return imageAnalyzer; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private void logErrorAndExit(String logMessage, String userMessageKey, Throwable e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (e == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logger.log(LogTypes.TYPE_EXCEPTION, logMessage); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logger.exception(logMessage, e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Toast.makeText(this, Localization.get(userMessageKey), Toast.LENGTH_LONG).show(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setResult(AppCompatActivity.RESULT_CANCELED); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
finish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public void analyze(@NonNull ImageProxy imageProxy) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@SuppressLint("UnsafeOptInUsageError") Image mediaImage = imageProxy.getImage(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we should not suppress lint here but add |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (mediaImage != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FaceDetectorOptions realTimeOpts = new FaceDetectorOptions.Builder() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_ACCURATE) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.build(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
FaceDetector faceDetector = FaceDetection.getClient(realTimeOpts); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// process image with the face detector | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
faceDetector.process(image) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.addOnSuccessListener(faces -> processFaceDetectionResult(faces, image)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.addOnFailureListener(e -> handleErrorDuringDetection(e)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
.addOnCompleteListener(task -> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imageProxy.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
imageProxy.close(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+131
to
+152
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error recovery mechanism in analyze method. The image analysis could fail silently if there's an exception during processing. Consider adding retry logic or graceful degradation. Add error recovery: @Override
public void analyze(@NonNull ImageProxy imageProxy) {
+ int retryCount = 0;
+ final int MAX_RETRIES = 3;
+
+ while (retryCount < MAX_RETRIES) {
@SuppressLint("UnsafeOptInUsageError") Image mediaImage = imageProxy.getImage();
if (mediaImage != null) {
try {
InputImage image = InputImage.fromMediaImage(mediaImage,
imageProxy.getImageInfo().getRotationDegrees());
processImage(image, imageProxy);
+ return;
+ } catch (Exception e) {
+ retryCount++;
+ if (retryCount == MAX_RETRIES) {
+ handleErrorDuringDetection(e);
+ }
+ }
}
+ }
imageProxy.close();
}
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private void handleErrorDuringDetection(Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logger.exception("Error during face detection ", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Toast.makeText(this, "microimage.face.detection.mode.failed", Toast.LENGTH_LONG).show(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// TODO: decide whether to switch to manual mode or close activity | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pending todo ? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
private void processFaceDetectionResult(List<Face> faces, InputImage image) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (faces.size() > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Only one face is processed, this can be increased if needed | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Face newFace = faces.get(0); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// this will draw a bounding circle around the first detected face | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
faceCaptureView.updateFace(newFace); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
inputImage = ImageConvertUtils.getInstance().convertToUpRightBitmap(image); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (MlKitException e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Logger.exception("Error during face detection ", e); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Toast.makeText(this, "microimage.face.detection.mode.failed", Toast.LENGTH_LONG).show(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// TODO: decide whether to switch to manual mode or close activity? | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pending ? |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
faceCaptureView.updateFace(null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public void onImageStabilizedListener(Rect faceArea) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
MediaUtil.cropAndSaveImage(inputImage, faceArea, ImageWidget.getTempFileForImageCapture()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
setResult(AppCompatActivity.RESULT_OK); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
finish(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
logErrorAndExit(e.getMessage(), "microimage.cropping.failed", e.getCause()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+185
to
+187
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we must not catch generic exceptions as it will potentially hide coding bugs under the rug. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+179
to
+189
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add cleanup in onImageStabilizedListener. The method should clean up resources after saving the image. Add cleanup: @Override
public void onImageStabilizedListener(Rect faceArea) {
try {
MediaUtil.cropAndSaveImage(inputImage, faceArea, ImageWidget.getTempFileForImageCapture());
setResult(AppCompatActivity.RESULT_OK);
+ cleanup();
finish();
} catch (Exception e) {
+ cleanup();
logErrorAndExit(e.getMessage(), "microimage.cropping.failed", e.getCause());
}
}
+
+ private void cleanup() {
+ if (inputImage != null) {
+ inputImage.recycle();
+ inputImage = null;
+ }
+ } 📝 Committable suggestion
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @avazirna is this not necessary ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this needed ?