Why do hasResolution() and getStatusCode==RESOLUTION_REQUIRED behave differently in SmartLock?

560 Views Asked by At

We have too apps where we're implementing Google Smart Lock. We had apparently the same implementation, but they were behaving differently.

The first one would only show the dialog to pick account when the user had some credentials saved already, and only showed the account or accounts he had saved.

The second one would always show a dialog to pick account even if the user never saved a credential, and would show all the accounts in the device. This results in the user seen too many accounts, and picking one only autocompletes the email without password.

It took us some time to figure out what was going on. It happens that we were checking the resolution for an unsuccessful credential request with different conditions. The first apps was using result.getStatus().getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED, while the second app was doing result.getStatus().hasResolution(), even though the status code wasn't RESOLUTION_REQUIRED but SIGN_IN_REQUIRED.

By the naming it appears that they should have similar behavior, but they don't. Why this difference?

1

There are 1 best solutions below

2
On BEST ANSWER

Depending whether user has credentials stored for the calling app, the result status code of a CredentialsApi.request() call will vary. Check out the API overview for details, but a quick summary:

When a user has multiple stored credentials (possibly stored in one or more Google accounts on the device), the RESOLUTION_REQUIRED result is returned and can be resolved with a dialog showing the multiple saved credentials, allowing the user to pick one. Details

When no stored credentials are available, the SIGN_IN_REQUIRED result is returned and can be resolved with a dialog showing a list of email addresses (for email address auto-fill, along with name or picture if available). This does not require a device permission (very useful on Android M, which would otherwise require a runtime GET_ACCOUNTS prompt), and helps the user fill a sign-in or sign-up form easily. Details

Auth.CredentialsApi.request(apiClient, request).setResultCallback(
        new ResultCallback<CredentialRequestResult>() {
            public void onResult(CredentialRequestResult result) {
                Status status = result.getStatus();
                if (status.isSuccess()) {
                    // Successfully read credential without any user interaction, this
                    // means there was only a single credential and user has auto
                    // sign-in enabled.
                    processRetrievedCredential(result.getCredential(), false);
                } else if (status.getStatusCode() == CommonStatusCodes.RESOLUTION_REQUIRED) {
                    // This is the case where the user has multiple saved
                    // credentials and needs to pick one
                    resolveResult(status, RC_READ);
                } else if (status.getStatusCode() == CommonStatusCodes.SIGN_IN_REQUIRED) {
                    // User has no saved credentials, but a dialog to select email
                    // address (a "hint") is available (optional)
                    resolveResult(status, RC_HINT);
                }
            }
        });

For both results hasResolution() returns true, because both can be resolved, though the result is different, which is unfortunately confusing. We'll update documentation to better explain.

In either scenario, the user's selection will be returned in onActivityResult(), but the "hints" will only have the identifier (email address) set and no password.

public void onActivityResult(int requestCode, int resultCode, Intent data) {
...
    if (resultCode == RESULT_OK) {
        Credential result = data.getParcelableExtra(Credential.EXTRA_KEY);
        if (requestCode == RC_HINT) {
            String email = result.getId(); // for auto-fill
        } else if (requestCode == RC_READ) {
            String email = result.getId(); // for auto sign-in
            String password = result.getPassword(); // only for saved credentials

The reason the email-only dialog is returned in a CredentialsApi.request() is to save an additional API call to fetch the email "hint" selector dialog described in the details above, in the case that an app would otherwise need to make the API calls sequentially. However, this is optional since for many app's UX it makes more sense to retrieve credentials at app start for auto sign-in, then request the email selector dialog separately later when user starts sign in or sign up, as illustrated in the sample code.