diff --git a/app/AndroidManifest.xml b/app/AndroidManifest.xml index 96b54a39a2..4fac7c8ddd 100644 --- a/app/AndroidManifest.xml +++ b/app/AndroidManifest.xml @@ -1,9 +1,9 @@ - + @@ -20,74 +20,64 @@ - - - - - - + android:maxSdkVersion="32" /> + + + + + + android:maxSdkVersion="32" /> - + - + + android:required="false" /> + android:required="false" /> + android:required="false" /> + android:required="false" /> - - + android:required="false" /> - + android:label="@string/permission_content_provider_label" /> + android:label="@string/permission_external_action_label" /> - - + android:protectionLevel="dangerous"> - + android:protectionLevel="dangerous"> @@ -95,12 +85,17 @@ + - + + + @@ -108,16 +103,16 @@ + android:usesCleartextTraffic="true" + tools:replace="android:label,android:icon,android:theme, android:allowBackup"> + android:label="@string/application_name"> - + - + - + - + - + - + android:label="@string/application_name" + android:windowSoftInputMode="adjustResize"> @@ -188,24 +182,24 @@ + android:authorities="${odkProvider}.forms" + android:exported="false" /> + android:authorities="${odkProvider}.instances" + android:exported="false" /> + android:readPermission="${applicationId}.provider.cases.read" /> + android:readPermission="${applicationId}.provider.cases.read" /> + android:resource="@xml/provider_paths" /> - + android:theme="@style/PreferenceTheme"> - + android:theme="@style/PreferenceTheme"> - + android:theme="@style/FullscreenTheme"> + android:name="org.commcare.activities.DotsEntryActivity" + android:exported="false"> - + - + - - + - + - - + + - - - - + + + + - + - - + + - - - + + + - + - - + + - - - + + + - + - + - - - + + + - + - - + + + android:scheme="file" /> - + - + - - - + + + - + android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize"> - - - + android:windowSoftInputMode="stateUnchanged|adjustResize"> + - + android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize"> - + android:name="org.commcare.services.CommCareSessionService" + android:enabled="true"> - + android:launchMode="singleTop" + android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize"> - + android:windowSoftInputMode="adjustResize"> - - - - - - - - + android:windowSoftInputMode="adjustResize"> + + - + - + - - - - - - - - - - + + + + + + android:required="false" /> - - + - - + + android:name="org.commcare.activities.ReportProblemActivity" + android:label="@string/title_activity_report_problem"> + android:value="org.commcare.activities.CommCareSetupActivity" /> - - - + android:windowSoftInputMode="adjustResize"> + + android:name="org.commcare.activities.KeyAccessRequestActivity" + android:exported="true"> - + - + + android:name="org.commcare.android.nfc.NfcWriteActivity" + android:exported="false"> - + - + + android:name="org.commcare.android.nfc.NfcReadActivity" + android:exported="false"> - + - + @@ -443,156 +408,144 @@ android:name="org.commcare.provider.ExternalApiReceiver" android:exported="true"> - - + + + - - - + + + - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - + - + android:windowSoftInputMode="adjustPan"> - + android:windowSoftInputMode="adjustResize"> - - - + android:screenOrientation="portrait"> + - + - + - - - - + + + - + android:windowSoftInputMode="stateVisible|adjustResize"> - - - + android:windowSoftInputMode="stateVisible|adjustResize"> + + + android:label="@string/title_data_change_logs_activity"> - + - - - - - + + + + android:value="${googlePlayMapsApiKey}" /> + android:value="@integer/google_play_services_version" /> - - - + android:resource="@xml/app_restrictions" /> - + - - - - - + + + android:exported="true" + tools:node="merge" /> - - + \ No newline at end of file diff --git a/app/res/drawable/arow_icon.png b/app/res/drawable/arow_icon.png new file mode 100644 index 0000000000..7255d45c1e Binary files /dev/null and b/app/res/drawable/arow_icon.png differ diff --git a/app/res/drawable/commcare_diamgi_logo.png b/app/res/drawable/commcare_diamgi_logo.png new file mode 100644 index 0000000000..73205b4c90 Binary files /dev/null and b/app/res/drawable/commcare_diamgi_logo.png differ diff --git a/app/res/drawable/dialpad.png b/app/res/drawable/dialpad.png new file mode 100644 index 0000000000..cae8bacdbe Binary files /dev/null and b/app/res/drawable/dialpad.png differ diff --git a/app/res/drawable/grey_small_button.xml b/app/res/drawable/grey_small_button.xml new file mode 100644 index 0000000000..0de12b63d4 --- /dev/null +++ b/app/res/drawable/grey_small_button.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/app/res/drawable/lock.png b/app/res/drawable/lock.png new file mode 100644 index 0000000000..dd2e8b48f6 Binary files /dev/null and b/app/res/drawable/lock.png differ diff --git a/app/res/drawable/rounded_rect.xml b/app/res/drawable/rounded_rect.xml new file mode 100644 index 0000000000..5bc899c501 --- /dev/null +++ b/app/res/drawable/rounded_rect.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/res/values-fr/strings.xml b/app/res/values-fr/strings.xml index 39db1c83c9..aaf4450a44 100644 --- a/app/res/values-fr/strings.xml +++ b/app/res/values-fr/strings.xml @@ -203,13 +203,10 @@ License. Statut de vérification Vérification en attente: %d\nNot Approved: %d\Approved: %d Statut de paiement - Montant gagné: %s\nMontant transféré: %s Payé %s Confirmé Non confirmé Émis %s - - Mis à jour: %s Vous ne suivez aucune formation pour un emploi pour le moment Vous n\'avez aucun emploi actif pour le moment diff --git a/app/res/values/colors.xml b/app/res/values/colors.xml index 5f9214660f..dde7ec7d5d 100644 --- a/app/res/values/colors.xml +++ b/app/res/values/colors.xml @@ -146,4 +146,6 @@ #005ab2 #d0e3ff + + #1c1b1f diff --git a/app/res/values/strings.xml b/app/res/values/strings.xml index 215ec6e8c0..7e9d845a25 100644 --- a/app/res/values/strings.xml +++ b/app/res/values/strings.xml @@ -91,7 +91,7 @@ CommCare Login Expired Submitting Data Submitting CommCare Logs - + 200 250 @@ -467,7 +467,7 @@ %1s sq m Could not parse input coordinates Turn on your location to receive location updates - CommCare is still trying to get your location. Please wait or click Cancel to abort. + CommCare is still trying to get your location. Please wait or click Cancel to abort. Invalid %1s, value must be 255 characters or less This form introduces an invalid case relationship Rooted Device Detected @@ -786,7 +786,7 @@ Sign up for ConnectID Sign out of ConnectID Forget ConnectID user - + App install failed due to an unknown error App installed Required CommCare App is not installed on device @@ -797,4 +797,8 @@ User is Suspended. Please contact admin. Select Phone Number An error occurred while connecting to the server. + App Lock + Unlock with biometric + Setup 6 Digit PIN + When enabled, you’ll need to use fingerprint, face or other unique identifiers to open the CommCare App diff --git a/app/src/org/commcare/activities/LoginActivity.java b/app/src/org/commcare/activities/LoginActivity.java index e677c21bbb..9a0d821152 100644 --- a/app/src/org/commcare/activities/LoginActivity.java +++ b/app/src/org/commcare/activities/LoginActivity.java @@ -17,6 +17,8 @@ import android.widget.Toast; import androidx.annotation.NonNull; +import androidx.biometric.BiometricManager; +import androidx.biometric.BiometricPrompt; import androidx.core.app.ActivityCompat; import androidx.core.util.Pair; import androidx.preference.PreferenceManager; @@ -51,6 +53,7 @@ import org.commcare.tasks.ManageKeyRecordTask; import org.commcare.tasks.PullTaskResultReceiver; import org.commcare.tasks.ResultAndError; +import org.commcare.utils.BiometricsHelper; import org.commcare.utils.ConsumerAppsUtil; import org.commcare.utils.CrashUtil; import org.commcare.utils.Permissions; @@ -112,6 +115,10 @@ public class LoginActivity extends CommCareActivity private String presetAppId; private boolean appLaunchedFromConnect; private boolean connectLaunchPerformed; + private BiometricPrompt.AuthenticationCallback biometricPromptCallbacks; + private BiometricManager biometricManager; + + @Override protected void onCreate(Bundle savedInstanceState) { @@ -134,6 +141,8 @@ protected void onCreate(Bundle savedInstanceState) { presetAppId = getIntent().getStringExtra(EXTRA_APP_ID); appLaunchedFromConnect = ConnectManager.wasAppLaunchedFromConnect(presetAppId); connectLaunchPerformed = false; + biometricManager = BiometricManager.from(this); + biometricPromptCallbacks = preparePromptCallbacks(); if (savedInstanceState == null) { // Only restore last user on the initial creation @@ -197,17 +206,40 @@ protected void onSaveInstanceState(Bundle savedInstanceState) { } } + + + private BiometricPrompt.AuthenticationCallback preparePromptCallbacks() { + return new BiometricPrompt.AuthenticationCallback() { + + + @Override + public void onAuthenticationSucceeded( + @NonNull BiometricPrompt.AuthenticationResult result) { + super.onAuthenticationSucceeded(result); + ConnectManager.goToConnectJobsList(); + setResult(RESULT_OK); + finish(); + + } + + @Override + public void onAuthenticationFailed() { + super.onAuthenticationFailed(); + Toast.makeText(getApplicationContext(), "Authentication failed", + Toast.LENGTH_SHORT) + .show(); + } + }; + } + /** * @param restoreSession Indicates if CommCare should attempt to restore the saved session * upon successful login */ protected void initiateLoginAttempt(boolean restoreSession) { if(isConnectJobsSelected()) { - ConnectManager.unlockConnect(this, success -> { - if(success) { - ConnectManager.goToConnectJobsList(); - } - }); + boolean allowOtherOptions = BiometricsHelper.isPinConfigured(this, biometricManager); + BiometricsHelper.authenticateFingerprint(this, biometricManager, allowOtherOptions, biometricPromptCallbacks); } else { LoginMode loginMode = uiController.getLoginMode(); @@ -469,13 +501,15 @@ private void setResultAndFinish(boolean goToJobInfo) { public void handleConnectButtonPress() { selectedAppIndex = -1; - ConnectManager.unlockConnect(this, success -> { - if(success) { - ConnectManager.goToConnectJobsList(); - setResult(RESULT_OK); - finish(); - } - }); + boolean allowOtherOptions = BiometricsHelper.isPinConfigured(this, biometricManager); + BiometricsHelper.authenticateFingerprint(this, biometricManager, allowOtherOptions, biometricPromptCallbacks); +// ConnectManager.unlockConnect(this, success -> { +// if(success) { +// ConnectManager.goToConnectJobsList(); +// setResult(RESULT_OK); +// finish(); +// } +// }); } public boolean handleConnectSignIn() { diff --git a/app/src/org/commcare/activities/connect/ConnectIdBiometricConfigActivity.java b/app/src/org/commcare/activities/connect/ConnectIdBiometricConfigActivity.java index d04ef0a201..244c906c96 100644 --- a/app/src/org/commcare/activities/connect/ConnectIdBiometricConfigActivity.java +++ b/app/src/org/commcare/activities/connect/ConnectIdBiometricConfigActivity.java @@ -1,15 +1,24 @@ package org.commcare.activities.connect; +import android.content.Context; import android.content.Intent; import android.os.Bundle; +import android.widget.Toast; + +import androidx.annotation.NonNull; +import androidx.biometric.BiometricManager; +import androidx.biometric.BiometricPrompt; import org.commcare.activities.CommCareActivity; import org.commcare.connect.ConnectConstants; import org.commcare.dalvik.R; +import org.commcare.google.services.analytics.AnalyticsParamValue; +import org.commcare.google.services.analytics.FirebaseAnalyticsUtil; import org.commcare.interfaces.CommCareActivityUIController; import org.commcare.interfaces.WithUIController; import org.commcare.utils.BiometricsHelper; import org.commcare.views.dialogs.CustomProgressDialog; +import org.javarosa.core.services.Logger; import androidx.biometric.BiometricManager; @@ -24,22 +33,26 @@ public class ConnectIdBiometricConfigActivity extends CommCareActivity parent, ConnectManager. }else if (user.shouldForcePassword()) { requestCode = ConnectTask.CONNECT_UNLOCK_PASSWORD; } else { - requestCode = ConnectTask.CONNECT_UNLOCK_BIOMETRIC; +// requestCode = ConnectTask.CONNECT_UNLOCK_BIOMETRIC; } } default -> { @@ -91,7 +91,7 @@ public static void unlockConnect(CommCareActivity parent, ConnectManager.Conn ConnectUserRecord user = ConnectDatabaseHelper.getUser(parentActivity); - phase = ConnectTask.CONNECT_UNLOCK_BIOMETRIC; +// phase = ConnectTask.CONNECT_UNLOCK_BIOMETRIC; if (user.shouldForcePin()) { phase = ConnectTask.CONNECT_UNLOCK_PIN; } else if (user.shouldForcePassword()) { @@ -218,8 +218,8 @@ private static void continueWorkflow() { params.put(ConnectConstants.MESSAGE, R.string.connect_recovery_success_message); params.put(ConnectConstants.BUTTON, R.string.connect_recovery_success_button); } - case CONNECT_UNLOCK_BIOMETRIC -> params.put(ConnectConstants.ALLOW_PASSWORD, "true"); - case CONNECT_REGISTRATION_UNLOCK_BIOMETRIC, CONNECT_RECOVERY_UNLOCK_BIOMETRIC -> params.put(ConnectConstants.ALLOW_PASSWORD, "false"); +// case CONNECT_UNLOCK_BIOMETRIC -> params.put(ConnectConstants.ALLOW_PASSWORD, "true"); +// case CONNECT_REGISTRATION_UNLOCK_BIOMETRIC, CONNECT_RECOVERY_UNLOCK_BIOMETRIC -> params.put(ConnectConstants.ALLOW_PASSWORD, "false"); case CONNECT_BIOMETRIC_ENROLL_FAIL -> { params.put(ConnectConstants.TITLE, R.string.connect_biometric_enroll_fail_title); params.put(ConnectConstants.MESSAGE, R.string.connect_biometric_enroll_fail_message); @@ -297,8 +297,12 @@ public static boolean handleFinishedActivity(int requestCode, int resultCode, In if (success) { boolean failedEnrollment = intent.getBooleanExtra(ConnectConstants.ENROLL_FAIL, false); nextRequestCode = failedEnrollment ? ConnectTask.CONNECT_BIOMETRIC_ENROLL_FAIL : - ConnectTask.CONNECT_REGISTRATION_UNLOCK_BIOMETRIC; + ConnectTask.CONNECT_REGISTRATION_VERIFY_PRIMARY_PHONE; + }else{ + nextRequestCode = + ConnectTask.CONNECT_REGISTRATION_CONFIGURE_BIOMETRICS; } + rememberPhase = success; } case CONNECT_BIOMETRIC_ENROLL_FAIL -> { nextRequestCode = ConnectTask.CONNECT_REGISTRATION_CONFIGURE_BIOMETRICS; @@ -307,11 +311,11 @@ public static boolean handleFinishedActivity(int requestCode, int resultCode, In launchSecuritySettings = true; } } - case CONNECT_REGISTRATION_UNLOCK_BIOMETRIC -> { - nextRequestCode = success ? ConnectTask.CONNECT_REGISTRATION_VERIFY_PRIMARY_PHONE : - ConnectTask.CONNECT_REGISTRATION_CONFIGURE_BIOMETRICS; - rememberPhase = success; - } +// case CONNECT_REGISTRATION_UNLOCK_BIOMETRIC -> { +// nextRequestCode = success ? ConnectTask.CONNECT_REGISTRATION_VERIFY_PRIMARY_PHONE : +// ConnectTask.CONNECT_REGISTRATION_CONFIGURE_BIOMETRICS; +// rememberPhase = success; +// } case CONNECT_REGISTRATION_VERIFY_PRIMARY_PHONE -> { nextRequestCode = ConnectTask.CONNECT_REGISTRATION_CONFIGURE_BIOMETRICS; if (success) { @@ -388,15 +392,15 @@ public static boolean handleFinishedActivity(int requestCode, int resultCode, In } } case CONNECT_RECOVERY_CONFIGURE_BIOMETRICS -> { - if (success) { - nextRequestCode = ConnectTask.CONNECT_RECOVERY_UNLOCK_BIOMETRIC; - } - } - case CONNECT_RECOVERY_UNLOCK_BIOMETRIC -> { if (success) { nextRequestCode = ConnectTask.CONNECT_RECOVERY_VERIFY_PRIMARY_PHONE; } } +// case CONNECT_RECOVERY_UNLOCK_BIOMETRIC -> { +// if (success) { +// nextRequestCode = ConnectTask.CONNECT_RECOVERY_VERIFY_PRIMARY_PHONE; +// } +// } case CONNECT_RECOVERY_VERIFY_PRIMARY_PHONE -> { if (success) { if(intent.hasExtra(ConnectConstants.CONNECT_KEY_SECONDARY_PHONE)) { @@ -484,15 +488,15 @@ public static boolean handleFinishedActivity(int requestCode, int resultCode, In rememberPhase = true; completeSignIn(); } - case CONNECT_UNLOCK_BIOMETRIC -> { - if (success) { - nextRequestCode = completeUnlock(); - } else if (intent != null && intent.getBooleanExtra(ConnectConstants.PASSWORD, false)) { - nextRequestCode = ConnectTask.CONNECT_UNLOCK_PASSWORD; - } else if (intent != null && intent.getBooleanExtra(ConnectConstants.RECOVER, false)) { - nextRequestCode = ConnectTask.CONNECT_RECOVERY_PRIMARY_PHONE; - } - } +// case CONNECT_UNLOCK_BIOMETRIC -> { +// if (success) { +// nextRequestCode = completeUnlock(); +// } else if (intent != null && intent.getBooleanExtra(ConnectConstants.PASSWORD, false)) { +// nextRequestCode = ConnectTask.CONNECT_UNLOCK_PASSWORD; +// } else if (intent != null && intent.getBooleanExtra(ConnectConstants.RECOVER, false)) { +// nextRequestCode = ConnectTask.CONNECT_RECOVERY_PRIMARY_PHONE; +// } +// } case CONNECT_UNLOCK_PIN -> { nextRequestCode = ConnectTask.CONNECT_RECOVERY_VERIFY_PRIMARY_PHONE; if (success) { diff --git a/app/src/org/commcare/connect/ConnectTask.java b/app/src/org/commcare/connect/ConnectTask.java index 87b4bad680..fb4a694699 100644 --- a/app/src/org/commcare/connect/ConnectTask.java +++ b/app/src/org/commcare/connect/ConnectTask.java @@ -30,8 +30,7 @@ public enum ConnectTask { ConnectIdRegistrationActivity.class), CONNECT_REGISTRATION_CONFIGURE_BIOMETRICS(ConnectConstants.ConnectIdTaskIdOffset + 5, ConnectIdBiometricConfigActivity.class), - CONNECT_REGISTRATION_UNLOCK_BIOMETRIC(ConnectConstants.ConnectIdTaskIdOffset + 6, - ConnectIdBiometricUnlockActivity.class), + CONNECT_REGISTRATION_VERIFY_PRIMARY_PHONE(ConnectConstants.ConnectIdTaskIdOffset + 7, ConnectIdPhoneVerificationActivity.class), CONNECT_REGISTRATION_CHANGE_PRIMARY_PHONE(ConnectConstants.ConnectIdTaskIdOffset + 8, @@ -52,8 +51,8 @@ public enum ConnectTask { ConnectIdPhoneVerificationActivity.class), CONNECT_RECOVERY_SUCCESS(ConnectConstants.ConnectIdTaskIdOffset + 18, ConnectIdMessageActivity.class), - CONNECT_UNLOCK_BIOMETRIC(ConnectConstants.ConnectIdTaskIdOffset + 19, - ConnectIdBiometricUnlockActivity.class), +// CONNECT_UNLOCK_BIOMETRIC(ConnectConstants.ConnectIdTaskIdOffset + 19, +// ConnectIdBiometricUnlockActivity.class), CONNECT_UNLOCK_PASSWORD(ConnectConstants.ConnectIdTaskIdOffset + 20, ConnectIdPasswordVerificationActivity.class), CONNECT_UNLOCK_PIN(ConnectConstants.ConnectIdTaskIdOffset + 21, @@ -72,8 +71,6 @@ public enum ConnectTask { ConnectIdPinActivity.class), CONNECT_RECOVERY_CONFIGURE_BIOMETRICS(ConnectConstants.ConnectIdTaskIdOffset + 28, ConnectIdBiometricConfigActivity.class), - CONNECT_RECOVERY_UNLOCK_BIOMETRIC(ConnectConstants.ConnectIdTaskIdOffset + 29, - ConnectIdBiometricUnlockActivity.class), CONNECT_VERIFY_ALT_PHONE_MESSAGE(ConnectConstants.ConnectIdTaskIdOffset + 30, ConnectIdMessageActivity.class), CONNECT_VERIFY_ALT_PHONE(ConnectConstants.ConnectIdTaskIdOffset + 31,