Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -624,6 +624,10 @@
<string name="personalid_otp_missing_activity">Something went wrong</string>
<string name="personalid_otp_verification_failed_generic">OTP verification failed:</string>
<string name="personalid_otp_verification_failed">Verification failed and please try again</string>
<string name="personalid_location_permission_error">Location permission denied</string>
<string name="personalid_location_service_error">Location service is not available. Please enable it</string>
<string name="personalid_grant_location_permission">Grant Location Permission</string>
<string name="personalid_grant_location_service">Enable Location Service</string>
<string name="my_credentials">My Credentials</string>
<string name="credential">Credential</string>
</resources>
2 changes: 2 additions & 0 deletions app/src/org/commcare/connect/ConnectConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ public class ConnectConstants {
public final static int PERSONALID_DEVICE_CONFIGURATION_FAILED = ConnectConstants.PERSONAL_ID_TASK_ID_OFFSET + 8;
public final static int PERSONALID_RECOVERY_ACCOUNT_ORPHANED = ConnectConstants.PERSONAL_ID_TASK_ID_OFFSET + 9;
public final static int PERSONALID_RECOVERY_ACCOUNT_LOCKED = ConnectConstants.PERSONAL_ID_TASK_ID_OFFSET + 10;
public final static int PERSONALID_LOCATION_PERMISSION_FAILURE = ConnectConstants.PERSONAL_ID_TASK_ID_OFFSET + 11;

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import org.commcare.connect.PersonalIdManager;
import org.commcare.connect.database.ConnectDatabaseHelper;
import org.commcare.dalvik.databinding.ScreenPersonalidMessageBinding;
import org.commcare.utils.GeoUtils;

import androidx.annotation.NonNull;
import androidx.lifecycle.ViewModelProvider;
Expand Down Expand Up @@ -143,6 +144,12 @@ private void finish() {
personalIdSessionData.setAccountExists(false);
directions = navigateToBackupCode();
break;
case ConnectConstants.PERSONALID_LOCATION_PERMISSION_FAILURE:
NavHostFragment.findNavController(this).navigateUp();
GeoUtils.goToProperLocationSettingsScreen(activity);
activity.finish();
break;

}
if (directions != null) {
NavHostFragment.findNavController(this).navigate(directions);
Expand Down
172 changes: 163 additions & 9 deletions app/src/org/commcare/fragments/personalId/PersonalIdPhoneFragment.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package org.commcare.fragments.personalId;

import android.Manifest;
import android.app.Activity;
import android.content.DialogInterface;
import android.location.Location;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
Expand All @@ -16,15 +19,16 @@
import androidx.activity.result.IntentSenderRequest;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.navigation.NavDirections;
import androidx.navigation.Navigation;

import com.google.android.gms.auth.api.identity.Identity;
import com.google.android.gms.common.api.ApiException;
import com.google.android.play.core.integrity.StandardIntegrityException;
import com.google.android.play.core.integrity.model.StandardIntegrityErrorCode;
import com.google.android.gms.common.api.ResolvableApiException;

import org.commcare.activities.connect.viewmodel.PersonalIdSessionDataViewModel;
import org.commcare.android.database.connect.models.PersonalIdSessionData;
Expand All @@ -37,14 +41,22 @@
import org.commcare.dalvik.databinding.ScreenPersonalidPhonenoBinding;
import org.commcare.google.services.analytics.AnalyticsParamValue;
import org.commcare.google.services.analytics.FirebaseAnalyticsUtil;
import org.commcare.location.CommCareLocationController;
import org.commcare.location.CommCareLocationControllerFactory;
import org.commcare.location.CommCareLocationListener;
import org.commcare.location.LocationRequestFailureHandler;
import org.commcare.util.LogTypes;
import org.commcare.utils.GeoUtils;
import org.commcare.utils.Permissions;
import org.commcare.utils.PhoneNumberHelper;
import org.javarosa.core.services.Logger;
import org.jetbrains.annotations.NotNull;

import java.util.HashMap;

public class PersonalIdPhoneFragment extends Fragment {
import static org.commcare.utils.Permissions.shouldShowPermissionRationale;

public class PersonalIdPhoneFragment extends Fragment implements CommCareLocationListener {

private ScreenPersonalidPhonenoBinding binding;
private boolean shouldShowPhoneHintDialog = true;
Expand All @@ -53,6 +65,16 @@ public class PersonalIdPhoneFragment extends Fragment {
private PersonalIdSessionDataViewModel personalIdSessionDataViewModel;
private IntegrityTokenApiRequestHelper integrityTokenApiRequestHelper;
private String phone;
private Location location;
private CommCareLocationController locationController;
private ActivityResultLauncher<String[]> locationPermissionLauncher;
private ActivityResultLauncher<IntentSenderRequest> resolutionLauncher;


private static final String[] REQUIRED_PERMISSIONS = new String[]{
Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION
};


@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Expand All @@ -63,14 +85,29 @@ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
activity.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
personalIdSessionDataViewModel = new ViewModelProvider(requireActivity()).get(
PersonalIdSessionDataViewModel.class);
locationController = CommCareLocationControllerFactory.getLocationController(requireActivity(), this);
integrityTokenApiRequestHelper = new IntegrityTokenApiRequestHelper(getViewLifecycleOwner());
initializeUi();
registerLauncher();
return binding.getRoot();
}

@Override
public void onResume() {
super.onResume();
locationController.start();
}

@Override
public void onPause() {
super.onPause();
locationController.stop();
}

@Override
public void onDestroyView() {
super.onDestroyView();
locationController.destroy();
binding = null;
}

Expand Down Expand Up @@ -147,7 +184,7 @@ private void updateContinueButtonState() {
boolean isValidPhone = phoneNumberHelper.isValidPhoneNumber(phone);
boolean isConsentChecked = binding.connectConsentCheck.isChecked();

enableContinueButton(isValidPhone && isConsentChecked);
enableContinueButton(isValidPhone && isConsentChecked && location != null);
}

private void displayPhoneNumber(String fullPhoneNumber) {
Expand Down Expand Up @@ -183,6 +220,7 @@ private void startConfigurationRequest() {
HashMap<String, String> body = new HashMap<>();
body.put("phone_number", phone);
body.put("application_id", requireContext().getPackageName());
body.put("gps_location", GeoUtils.locationToString(location));

integrityTokenApiRequestHelper.withIntegrityToken(body,
new IntegrityTokenViewModel.IntegrityTokenCallback() {
Expand All @@ -199,8 +237,108 @@ public void onTokenFailure(@NotNull Exception exception) {
});
}

private void makeStartConfigurationCall(String integrityToken, String requestHash,
HashMap<String, String> body) {
@Override
public void onLocationResult(@NonNull Location result) {
location = result;
updateContinueButtonState();
}

@Override
public void onLocationRequestFailure(@NonNull Failure failure) {
LocationRequestFailureHandler.INSTANCE.handleFailure(failure,
new LocationRequestFailureHandler.LocationResolutionCallback() {
@Override
public void onResolvableException(ResolvableApiException exception) {
try {
IntentSenderRequest request = new IntentSenderRequest.Builder(
exception.getResolution()).build();
resolutionLauncher.launch(request);
} catch (Exception e) {
navigateToPermissionErrorMessageDisplay(
R.string.personalid_location_service_error,
R.string.personalid_grant_location_service
);
}
}

@Override
public void onNonResolvableFailure() {
handleNoLocationServiceProviders();
}
});
}

private void handleNoLocationServiceProviders() {
DialogInterface.OnCancelListener onCancelListener = dialog -> {
location = null;
navigateToPermissionErrorMessageDisplay(R.string.personalid_location_service_error,
R.string.personalid_grant_location_service);
};

DialogInterface.OnClickListener onChangeListener = (dialog, i) -> {
switch (i) {
case DialogInterface.BUTTON_POSITIVE:
GeoUtils.goToProperLocationSettingsScreen((AppCompatActivity)requireActivity());
break;
case DialogInterface.BUTTON_NEGATIVE:
location = null;
navigateToPermissionErrorMessageDisplay(R.string.personalid_location_service_error,
R.string.personalid_grant_location_service);
break;
}
dialog.dismiss();
};

GeoUtils.showNoGpsDialog((AppCompatActivity)requireActivity(), onChangeListener, onCancelListener);
}

@Override
public void onLocationRequestStart() {
}

private boolean isOnPermissionErrorScreen() {
return Navigation.findNavController(requireView())
.getCurrentDestination()
.getId() == R.id.personalid_message_display;
}

private void registerLauncher() {
locationPermissionLauncher = registerForActivityResult(
new ActivityResultContracts.RequestMultiplePermissions(),
result -> {
boolean allPermissionsGranted = !Permissions.missingAppPermission(requireActivity(),
REQUIRED_PERMISSIONS);

if (allPermissionsGranted) {
locationController.start();
} else {
if (!isOnPermissionErrorScreen()) {
navigateToPermissionErrorMessageDisplay(R.string.personalid_location_permission_error,
R.string.personalid_grant_location_permission);
}
}
}
);

resolutionLauncher = registerForActivityResult(
new ActivityResultContracts.StartIntentSenderForResult(),
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// User enabled location settings
} else {
// User cancelled or failed
navigateToPermissionErrorMessageDisplay(
R.string.personalid_location_service_error,
R.string.personalid_grant_location_service
);
}
}
);
}


private void makeStartConfigurationCall(@Nullable String integrityToken, String requestHash,
HashMap<String, String> body) {
new PersonalIdApiHandler<PersonalIdSessionData>() {
@Override
public void onSuccess(PersonalIdSessionData sessionData) {
Expand Down Expand Up @@ -240,7 +378,8 @@ private void onConfigurationSuccess() {
private void onConfigurationFailure(String failureCause, String failureMessage) {
FirebaseAnalyticsUtil.reportPersonalIdConfigurationFailure(failureCause);
Navigation.findNavController(binding.personalidPhoneContinueButton).navigate(
navigateToMessageDisplay(failureMessage, false));
navigateToMessageDisplay(failureMessage, false,
ConnectConstants.PERSONALID_DEVICE_CONFIGURATION_FAILED, R.string.ok));
}

private void navigateFailure(PersonalIdApiHandler.PersonalIdOrConnectApiErrorCodes failureCode, Throwable t) {
Expand All @@ -264,11 +403,26 @@ private NavDirections navigateToBiometricSetup() {
return PersonalIdPhoneFragmentDirections.actionPersonalidPhoneFragmentToPersonalidBiometricConfig();
}

private NavDirections navigateToMessageDisplay(String errorMessage, boolean isCancellable) {
private NavDirections navigateToMessageDisplay(String errorMessage, boolean isCancellable, int phase,
int buttonText) {
return PersonalIdPhoneFragmentDirections.actionPersonalidPhoneFragmentToPersonalidMessageDisplay(
getString(R.string.personalid_configuration_process_failed_title),
errorMessage,
ConnectConstants.PERSONALID_DEVICE_CONFIGURATION_FAILED, getString(R.string.ok),
phase, getString(buttonText),
null).setIsCancellable(isCancellable);
}

private void navigateToPermissionErrorMessageDisplay(int errorMeesage, int buttonText) {
Navigation.findNavController(binding.personalidPhoneContinueButton).navigate(
navigateToMessageDisplay(
requireActivity().getString(errorMeesage), true,
ConnectConstants.PERSONALID_LOCATION_PERMISSION_FAILURE, buttonText));
}

@Override
public void missingPermissions() {
if (!shouldShowPermissionRationale(requireActivity(), REQUIRED_PERMISSIONS)) {
locationPermissionLauncher.launch(REQUIRED_PERMISSIONS);
}
}
}
27 changes: 27 additions & 0 deletions app/src/org/commcare/location/LocationRequestFailureHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.commcare.location
import com.google.android.gms.common.api.ResolvableApiException

object LocationRequestFailureHandler {

interface LocationResolutionCallback {
fun onResolvableException(exception: ResolvableApiException)
fun onNonResolvableFailure()
}

fun handleFailure(
failure: CommCareLocationListener.Failure,
callback: LocationResolutionCallback
) {
when (failure) {
is CommCareLocationListener.Failure.ApiException -> {
val exception = failure.exception
if (exception is ResolvableApiException) {
callback.onResolvableException(exception)
} else {
callback.onNonResolvableFailure()
}
}
else -> callback.onNonResolvableFailure()
}
}
}
9 changes: 5 additions & 4 deletions app/src/org/commcare/utils/Permissions.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.commcare.utils;

import android.Manifest;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;

Expand Down Expand Up @@ -60,7 +61,7 @@ public static boolean acquireAllAppPermissions(AppCompatActivity activity,
}
}

public static boolean missingAppPermission(AppCompatActivity activity,
public static boolean missingAppPermission(Activity activity,
String[] permissions) {
for (String perm : permissions) {
if (missingAppPermission(activity, perm)) {
Expand All @@ -71,12 +72,12 @@ public static boolean missingAppPermission(AppCompatActivity activity,
}


public static boolean missingAppPermission(AppCompatActivity activity,
public static boolean missingAppPermission(Activity activity,
String permission) {
return ContextCompat.checkSelfPermission(activity, permission) == PackageManager.PERMISSION_DENIED;
}

public static boolean shouldShowPermissionRationale(AppCompatActivity activity,
public static boolean shouldShowPermissionRationale(Activity activity,
String[] permissions) {
for (String perm : permissions) {
if (shouldShowPermissionRationale(activity, perm)) {
Expand All @@ -86,7 +87,7 @@ public static boolean shouldShowPermissionRationale(AppCompatActivity activity,
return false;
}

public static boolean shouldShowPermissionRationale(AppCompatActivity activity,
public static boolean shouldShowPermissionRationale(Activity activity,
String permission) {
return ActivityCompat.shouldShowRequestPermissionRationale(activity, permission);
}
Expand Down
Loading