Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
ead3b87
baseline code for micro image capture pulled from micro image widget …
shubham1g5 May 13, 2025
b18d901
adds a new fragment and UI to capture photo
shubham1g5 May 14, 2025
665b451
Default to front camera
shubham1g5 May 14, 2025
2d4cc9b
Crop, Scale, compress and return image as base64 from the microImageA…
shubham1g5 May 14, 2025
f90c39b
minor lint and renaming
shubham1g5 May 14, 2025
bcd205e
Adds a api call to upload photo
shubham1g5 May 14, 2025
ff8c268
Display and upload Photo on server
shubham1g5 May 14, 2025
227c114
Adds a save button to upload, error handling
shubham1g5 May 15, 2025
59bedc6
set contextual title
shubham1g5 May 15, 2025
e82b7af
UI tweaks
shubham1g5 May 16, 2025
b56bc79
Implement padding for crop, take image size as intent extra
shubham1g5 May 16, 2025
91c298f
Save Photo to db
shubham1g5 May 16, 2025
519c33e
Adds user name to update photo call
shubham1g5 May 16, 2025
9616daf
rename to personal id
shubham1g5 May 16, 2025
2cec482
wire in photo capture fragment in the workflow
shubham1g5 May 19, 2025
2421137
error handling, validation
shubham1g5 May 19, 2025
aa45aa7
connectId -> personalId
shubham1g5 May 19, 2025
498fa80
remove stray quotes
shubham1g5 May 19, 2025
943db9d
reuse error handler
shubham1g5 May 19, 2025
10bd50a
Minor linty corrections
shubham1g5 May 20, 2025
0d7db9b
remove exceptions that are not thrown anymore
shubham1g5 May 20, 2025
efefbd0
call set_profile endpoint instead on photo capture page
shubham1g5 May 20, 2025
98d20c2
Adds a prefix to denote the format and compression type to image string
shubham1g5 May 20, 2025
1587119
use vector drawable
shubham1g5 May 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions app/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,9 @@
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/fcm_default_notification_channel" />
<meta-data
android:name="com.google.mlkit.vision.DEPENDENCIES"
android:value="face" />
<receiver
android:name="org.commcare.connect.SMSBroadcastReceiver"
android:exported="true"
Expand Down Expand Up @@ -577,6 +580,7 @@
<activity android:name="org.commcare.activities.TargetMismatchErrorActivity"/>
<activity android:name="org.commcare.recovery.measures.ExecuteRecoveryMeasuresActivity"/>
<activity android:name="org.commcare.activities.PromptCCReinstallActivity"/>
<activity android:name="org.commcare.fragments.MicroImageActivity" />
<!-- Start Collect Manifest -->

<meta-data
Expand Down
7 changes: 7 additions & 0 deletions app/assets/locales/android_translatable_strings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1014,3 +1014,10 @@ android.package.name.callout.commcare.org.sendussd=Commcare USSD
android.package.name.org.commcare.dalvik.abha=CommCare ABHA

location.capture.cancelled=Location capture cancelled

microimage.camera.start.failed=Camera failed to start
microimage.face.detection.mode.failed=Face detection mode failed, switching to manual mode
microimage.cropping.failed=Image cropping failed, make sure the target is inside the capture area
microimage.decoding.no.image=Image processing failed. Please try again
microimage.scalingdown.compression.error=Image compression failed. Please try again
microimage.manual.face.capture.failed=Manual capture failed, contact CommCare Support if the issue persists
5 changes: 5 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,11 @@ dependencies {
implementation "androidx.navigation:navigation-compose:$nav_version"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
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'
}

ext {
Expand Down
5 changes: 5 additions & 0 deletions app/res/drawable/baseline_person_24.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="120dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="120dp">

<path android:fillColor="@android:color/white" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>

</vector>
5 changes: 5 additions & 0 deletions app/res/drawable/ic_camera_shutter.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">

<path android:fillColor="@android:color/white" android:pathData="M9.4,10.5l4.77,-8.26C13.47,2.09 12.75,2 12,2c-2.4,0 -4.6,0.85 -6.32,2.25l3.66,6.35 0.06,-0.1zM21.54,9c-0.92,-2.92 -3.15,-5.26 -6,-6.34L11.88,9h9.66zM21.8,10h-7.49l0.29,0.5 4.76,8.25C21,16.97 22,14.61 22,12c0,-0.69 -0.07,-1.35 -0.2,-2zM8.54,12l-3.9,-6.75C3.01,7.03 2,9.39 2,12c0,0.69 0.07,1.35 0.2,2h7.49l-1.15,-2zM2.46,15c0.92,2.92 3.15,5.26 6,6.34L12.12,15L2.46,15zM13.73,15l-3.9,6.76c0.7,0.15 1.42,0.24 2.17,0.24 2.4,0 4.6,-0.85 6.32,-2.25l-3.66,-6.35 -0.93,1.6z"/>

</vector>
39 changes: 39 additions & 0 deletions app/res/layout/micro_image_widget.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?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" />

<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"/>

<ImageView
android:id="@+id/camera_shutter_button"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:src="@drawable/ic_camera_shutter"
android:background="@color/cc_neutral_bg_tr"
android:visibility="gone"
android:paddingBottom="15dp"/>
</LinearLayout>
</FrameLayout>
88 changes: 88 additions & 0 deletions app/res/layout/screen_personalid_photo_capture.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white"
android:orientation="vertical">

<ImageView
android:id="@+id/company_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="40dp"
android:contentDescription="@null"
android:src="@drawable/ic_dimagi_logo" />

<View
android:id="@+id/divider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="20dp"
android:background="@color/connect_light_grey" />

<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:layout_margin="@dimen/activity_horizontal_margin"
android:text="@string/connectid_photo_capture_title"
android:textColor="@color/connect_blue_color"
android:textSize="16sp"
android:textStyle="bold"/>

<TextView
android:id="@+id/subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:layout_margin="@dimen/activity_horizontal_margin"
android:text="@string/connectid_photo_capture_subtitle"
android:textColor="@color/connect_subtext"
android:textSize="14sp"
android:textStyle="bold"/>

<ImageView
android:id="@+id/photo_image_view"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_gravity="center"
android:layout_margin="@dimen/activity_horizontal_margin"
android:contentDescription="@null"
android:src="@drawable/baseline_person_24" />

<com.google.android.material.button.MaterialButton
android:id="@+id/take_photo_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/activity_horizontal_margin"
android:text="@string/connectid_photo_capture_take_photo_button" />

<com.google.android.material.button.MaterialButton
android:id="@+id/save_photo_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:enabled="false"
android:text="@string/connectid_photo_capture_save_photo_button" />

<TextView
android:id="@+id/errorTextView"
tools:text="Error uploading photo"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove hard coded text, also visibility should be gone by default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch on visibility.
tools:text is stripped down in the final apk and is there only for debugging layouts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_horizontal_margin"
android:layout_gravity="center_horizontal"
android:textColor="@android:color/holo_red_light"
android:visibility="gone"
android:textSize="14sp" />
</LinearLayout>

</layout>
22 changes: 21 additions & 1 deletion app/res/navigation/nav_graph_personalid.xml
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@
android:id="@+id/action_personalid_pin_self"
app:destination="@id/personalid_backup_code"
app:popUpTo="@id/personalid_backup_code" />
<action
android:id="@+id/action_personalid_pin_to_personalid_photo_capture"
app:destination="@id/personalid_photo_capture"
app:popUpTo="@id/personalid_backup_code" />
<argument
android:name="isRecovery"
app:argType="boolean"
Expand Down Expand Up @@ -147,4 +151,20 @@
android:id="@+id/action_personalid_name_to_personalid_backup_code"
app:destination="@id/personalid_backup_code" />
</fragment>
</navigation>
<fragment
android:id="@+id/personalid_photo_capture"
android:name="org.commcare.fragments.personalId.PersonalIdPhotoCaptureFragment"
android:label="fragment_personalid_photo_capture">
<argument
android:name="userName"
android:defaultValue=""
app:argType="string" />
<action
android:id="@+id/action_personalid_signup_fragment_self"
app:destination="@id/personalid_photo_capture" />
<action
android:id="@+id/action_personalid_photo_capture_to_personalid_message"
app:destination="@id/personalid_message_display"
app:popUpTo="@id/personalid_photo_capture" />
</fragment>
</navigation>
7 changes: 7 additions & 0 deletions app/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,11 @@
<attr name="roundButtonTextSize" format="dimension" />
<attr name="roundButtonIconPadding" format="dimension"/>
</declare-styleable>

<declare-styleable name="FaceCaptureView">
<attr name="background_color" format="color" />
<attr name="face_capture_area_delimiter_color" format="color" />
<attr name="face_marker_color" format="color" />
<attr name="countdown_text_size" format="dimension" />
</declare-styleable>
</resources>
1 change: 1 addition & 0 deletions app/res/values/colors.xml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
<color name="cc_neutral_color">#685C53</color>
<color name="cc_neutral_bg">#D6D6D4</color>
<color name="cc_neutral_text">#373534</color>
<color name="cc_neutral_bg_tr">#80D6D6D4</color>

<!-- Attention Positive -->
<color name="cc_attention_positive_color">#47B700</color>
Expand Down
7 changes: 7 additions & 0 deletions app/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,8 @@
<item>save</item>
</string-array>

<string name="micro_image_activity_title">Capture Face Picture</string>

<string name="connect_job_info_title">Opportunity Details</string>
<string name="connect_job_info_delivery_detail">Delivery Details</string>
<string name="connect_job_info_review_delivery">Review the delivery details</string>
Expand Down Expand Up @@ -909,4 +911,9 @@
<string name="failed_to_login_with_connectid">We are facing an unrecoverable error when tying to login using your Connect ID. Please login with your password or restore your Connect ID login</string>
<string name="name_fragment_subtitle">Please enter your name as it appears on your government-issued identification</string>
<string name="name_empty_error">Name field cannot be empty</string>
<string name="connectid_photo_capture_title">Welcome %1$s</string>
<string name="connectid_photo_capture_subtitle">Please take a photo of yourself to complete your account setup</string>
<string name="connectid_photo_capture_take_photo_button">Take Photo</string>
<string name="connectid_photo_capture_save_photo_button">Save Photo</string>
<string name="connectid_photo_upload_failure">"Failed to save photo. Please check your internet and try again. "</string>
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,8 @@ public static ConnectUserRecord fromV13(ConnectUserRecordV13 oldRecord) {
newRecord.isDemo = false;
return newRecord;
}

public void setPhoto(String photo) {
this.photo = photo;
}
}
27 changes: 22 additions & 5 deletions app/src/org/commcare/connect/network/ApiPersonalId.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;

import androidx.annotation.NonNull;
import okhttp3.ResponseBody;
Expand Down Expand Up @@ -214,9 +215,9 @@ public static void dismissProgressDialog(Context context) {
}
}

static void callApi(Context context, Call<ResponseBody> call, IApiCallback callback) {
private static void callApi(Context context, Call<ResponseBody> call, IApiCallback callback) {
showProgressDialog(context);
call.enqueue(new Callback<ResponseBody>() {
call.enqueue(new Callback<>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
dismissProgressDialog(context);
Expand All @@ -230,7 +231,7 @@ public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<Respo
}
} else {
// Handle validation errors
handleApiError(response);
logNetworkError(response);
callback.processFailure(response.code());
}
}
Expand All @@ -241,7 +242,6 @@ public void onFailure(Call<ResponseBody> call, Throwable t) {
// Handle network errors, etc.
handleNetworkError(t);
callback.processNetworkFailure();

}
});
}
Expand Down Expand Up @@ -317,6 +317,23 @@ public static void updateUserProfile(Context context, String username,
callApi(context, call, callback);
}

public static void setPhotoAndName(Context context, String userId, String password, String userName,
String photoAsBase64, IApiCallback callback) {
Objects.requireNonNull(photoAsBase64);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requires Objects.requireNonNull(username)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Objects.requireNonNull(userName);
AuthInfo authInfo = new AuthInfo.ProvidedAuth(userId, password, false);
String token = HttpUtils.getCredential(authInfo);
Objects.requireNonNull(token);

HashMap<String, String> params = new HashMap<>();
params.put("photo", photoAsBase64);
params.put("name", userName);

ApiService apiService = ApiClient.getClient().create(ApiService.class);
Call<ResponseBody> call = apiService.setProfile(token, params);
callApi(context, call, callback);
}

public static void requestRegistrationOtpPrimary(Context context, String username, String password,
IApiCallback callback) {
AuthInfo authInfo = new AuthInfo.ProvidedAuth(username, password, false);
Expand Down Expand Up @@ -379,7 +396,7 @@ public static void confirmUserDeactivation(Context context, String phone, String
callApi(context, call, callback);
}

private static void handleApiError(Response<?> response) {
private static void logNetworkError(Response<?> response) {
String message = response.message();
if (response.code() == 400) {
// Bad request (e.g., validation failed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class ApiEndPoints {
public static final String phoneAvailable = "/users/phone_available";
public static final String changePhoneNo = "/users/change_phone";
public static final String updateProfile = "/users/update_profile";
public static final String setProfile = "/users/set_profile";
public static final String validatePhone = "/users/validate_phone";
public static final String recoverOTPPrimary = "/users/recover";
public static final String recoverOTPSecondary = "/users/validate_secondary_phone";
Expand All @@ -31,4 +32,4 @@ public class ApiEndPoints {
public static final String connectPaymentConfirmationURL = "https://%s/api/payment/%s/confirm";
public static final String connectInitiateUserAccountDeactivationURL = "https://connectid.dimagi.com/users/recover/initiate_deactivation";
public static final String connectConfirmUserAccountDeactivationURL = "https://connectid.dimagi.com/users/recover/confirm_deactivation";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ Call<ResponseBody> changePhoneNo(@Header("Authorization") String token,
Call<ResponseBody> updateProfile(@Header("Authorization") String token,
@Body Map<String, String> updateProfile);

@POST(ApiEndPoints.setProfile)
Call<ResponseBody> setProfile(@Header("Authorization") String token,
@Body Map<String, String> body);

@POST(ApiEndPoints.validatePhone)
Call<ResponseBody> validatePhone(@Header("Authorization") String token, @Body Map<String, String> requestOTP);

Expand Down Expand Up @@ -49,4 +53,4 @@ Call<ResponseBody> changePIN(@Header("Authorization") String token,
@POST(ApiEndPoints.resetPassword)
Call<ResponseBody> resetPassword(@Body Map<String, String> resetPasswordRequest);

}
}
Loading
Loading