18

In order to manage user preferences, at present I'm grabbing the google user name (effectively the email address they've registered to the device) and using (a hash of) that as a "user ID" to distinguish between different users. Something along the lines of what is described here.

That all works fine, but some users are (understandably) scared off by any permission that seems to give the app some evil power to trawl through their account info or contacts.

EDIT: this issue is even more acute with the way in which Google has now implemented Permission Groups in the Android 6 runtime permission schema. They have put GET_ACCOUNTS into the CONTACTS permission group. So now, in order to generate a unique anonymous User ID, the user has to be presented with a dialog that says:

Allow this app to access your contacts? | Deny | Allow |

How misleading is that?? My app has no wish to access contacts, but the user is being asked to grant permission for the app to access contacts! Yes, there is an opportunity to explain to the user with a separate dialog that, in fact, access to contacts is NOT required, but then they still get the stock system dialog that asks them to grant permission to access contacts... naturally they will think "why am I being asked to grant this permission if access to contacts is not required?" and "do I really trust the developer? they say access to contacts is not required, but is that really true?"... very very confusing and a very poor user experience IMHO (and the opinion of others too).

I'd rather just avoid all of this hassle and confusion and obtain a unique user ID without special permissions being required.

So is there a stock function or method in the SDK that will return a unique user ID for the user, without requiring any additional permission? I'm kinda surprised if there isn't, because it would seem to be quite a common thing to want to do... to have a user ID for managing app users.

Two points:

(a) I only need an anonymous user ID... I don't actually need the user's email address, nor would I ever want the user's email address. So I can't see why google can't provide a getUserID() method that returns a unique anonymised ID, without having to grant special permissions to the app.

(b) It has to be a user ID, not a device ID, so that it will work across all of the user's devices registered to the same google account.

Thanks.

Community
  • 1
  • 1
drmrbrewer
  • 11,491
  • 21
  • 85
  • 181
  • maybe you can use gcmid ? – Randyka Yudhistira Oct 09 '15 at 08:33
  • 1
    The GCM ID can change if the user clears the data of the Google Play Services app, which happens when they read on the internet that doing so will speed up android software update or fix some bugs... – Stephane Mathis Oct 09 '15 at 09:00
  • 1
    You can get a unique device id: http://stackoverflow.com/a/17625641/1048340 Of course, this won't account for users with more than one device. This might be a good option to get the email address without permissions: http://stackoverflow.com/a/19444640/1048340 – Jared Rummler Jan 05 '16 at 18:42
  • 1
    Thanks, Jared. The second link (relating to `AccountPicker`) should suit me just fine... big bonus is that I can finally ditch `GET_ACCOUNTS` and all the hassle trying to explain to users that I'm not trying to steal their contacts, despite the Android 6 runtime permission request suggesting that I might! Would you be able to post your comment as an Answer, so that I can accept it and award you the BOUNTY?! – drmrbrewer Jan 06 '16 at 21:57
  • @drmrbrewer I posted as an answer with some more details. I wrote a test app and it worked great. Glad it was helpful. – Jared Rummler Jan 07 '16 at 13:36
  • What did you mean by ~"**anonymous user ID**"? That seems like a contradiction, as the user ID clearly identifies the user as NON-ANONYMOUS. – IgorGanapolsky Sep 12 '16 at 13:24
  • I mean a string that is unique to a particular user and yet allows them to remain anonymous (i.e. so that I cannot know who is actually behind the ID). Something like `af880f15a` and `89e0f480` rather than `joebloggs@gmail.com` and `iansmith@gmail.com`. – drmrbrewer Sep 12 '16 at 14:46

4 Answers4

8

If you use Google Play Services you can get the account type and account name without any extra permissions.

First, add the following dependency to build.gradle:

compile 'com.google.android.gms:play-services-auth:8.4.0'

Next, launch the account chooser intent:

Intent intent = AccountPicker.newChooseAccountIntent(null, null,
    new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE},
    false, null, null, null, null);

try {
  startActivityForResult(intent, REQUEST_CODE_EMAIL);
} catch (ActivityNotFoundException e) {
  // This device may not have Google Play Services installed.
  // TODO: do something else
}

Finally, override onActivityResult to get the account type and account name:

@Override 
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  if (requestCode == REQUEST_CODE_EMAIL && resultCode == RESULT_OK) {
    String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
    String accountType = data.getStringExtra(AccountManager.KEY_ACCOUNT_TYPE);
    // TODO: do something with the accountName
    return;
  }
  super.onActivityResult(requestCode, resultCode, data);
}

source: https://stackoverflow.com/a/19444640/1048340

Community
  • 1
  • 1
Jared Rummler
  • 37,824
  • 19
  • 133
  • 148
  • Hmm, can I clarify something please? The [API docs](https://developers.google.com/android/reference/com/google/android/gms/common/AccountPicker.html) say that if the `alwaysPromptForAccount` is `false`, as you have it above, then "the account chooser screen is ...only shown when there is more than one account from which to choose or the calling app doesn't have the GET_ACCOUNTS permission." And we don't want to request the GET_ACCOUNTS permission. So this suggests the account chooser screen will ALWAYS be shown, even when there's only one account. Is this true? – snark Jun 07 '16 at 20:11
  • The reason I ask is because I associate user's in-app purchases with their user account. Currently I request the GET_ACCOUNTS permission but I'd love to remove that requirement. However, it sounds like I'd end up having to ask for the user's account *every* time they use the app even when they only have one account. This is an even worse user experience than having to ask them for the Contacts permission only once up-front. – snark Jun 07 '16 at 20:15
  • I can answer my own question now after putting the above code in a simple test app. Yes, the app will show the account picker every single time if you only have one account when the app doesn't have the GET_ACCOUNTS permission. Of course, you could store the returned account name but then you'd have to assume it wouldn't change, which isn't a safe assumption because [apps remain installed if a user changes their one account on a device](http://android.stackexchange.com/a/24484/155892). So using the account picker like this isn't really a viable solution in the case of in-app purchases. – snark Jun 09 '16 at 19:20
  • @snark How often are you asking the user for his account anyway? I mean, are you starting In-App Purchases multiple times in your app's session? – IgorGanapolsky Sep 12 '16 at 13:49
  • 1
    @Igor Ganapolsky My app used to use the user's account in the developer's payload when making and querying in-app purchases (and the latter happened often per session). But after http://stackoverflow.com/q/38817547/1843329 I know now I don't need to do that. So I don't need the GET_ACCOUNTS permission or the AccountPicker any more. – snark Sep 12 '16 at 20:02
2

You can get the exact gmail email address using AccountPicker without requiring to add the permissions.

Your app needs to include the Google Play Services but it doesn't need any permissions.

This whole process will fail on older versions of Android (2.2+ is required), or if Google Play is not available so you should consider that case.

Sample code from the source:

private static final int REQUEST_CODE_EMAIL = 1;
    private TextView email = (TextView) findViewById(R.id.email);

    // ...

    try {
        Intent intent = AccountPicker.newChooseAccountIntent(null, null,
                new String[] { GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE }, false, null, null, null, null);
        startActivityForResult(intent, REQUEST_CODE_EMAIL);
    } catch (ActivityNotFoundException e) {
        // TODO
    }

    // ...

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_EMAIL && resultCode == RESULT_OK) {
            String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
            email.setText(accountName);
        }
    }

Source of the above information is : This Answer on SO - to a similar but slightly different question asked in the past.

This Post - also has some nice approaches that would serve your purpose.

Community
  • 1
  • 1
Viral Patel
  • 32,418
  • 18
  • 82
  • 110
0

You can use a Google advertising ID as a UUID, but it's not cross-device(yet?):

https://support.google.com/googleplay/android-developer/answer/6048248?hl=en

That'll at least get rid of the scary permission request.

I don't believe Android/Google has a cross-device UUID that doesn't require permission, though Doubleclick by Google supposedly was developing one.

Morifen
  • 126
  • 1
  • 7
  • ~"**cross-device UUID that doesn't require permission**". AccountPicker doesn't require any permissions, and it shows the same user IDs across multiple devices. – IgorGanapolsky Sep 12 '16 at 13:51
-1

Unfortunately there is no solution to do that, even the most basic account access requires user permission.

Bakeneko
  • 1
  • 1
  • **AccountPicker** does not require permissions. Did you see this: https://developers.google.com/android/reference/com/google/android/gms/common/AccountPicker – IgorGanapolsky Sep 06 '16 at 21:29