Skip to content

Instantly share code, notes, and snippets.

@saket
Forked from imminent/AccountUtils.java
Created February 14, 2016 07:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saket/6c5456bb426761a2e0b2 to your computer and use it in GitHub Desktop.
Save saket/6c5456bb426761a2e0b2 to your computer and use it in GitHub Desktop.
Utility to retrieve user profile on Android device
/**
* A collection of authentication and account connection utilities. With strong inspiration from the Google IO session
* app.
* @author Dandré Allison
*/
public class AccountUtils {
/**
* Interface for interacting with the result of {@link AccountUtils#getUserProfile}.
*/
public static class UserProfile {
/**
* Adds an email address to the list of possible email addresses for the user
* @param email the possible email address
*/
public void addPossibleEmail(String email) {
addPossibleEmail(email, false);
}
/**
* Adds an email address to the list of possible email addresses for the user. Retains information about whether this
* email address is the primary email address of the user.
* @param email the possible email address
* @param is_primary whether the email address is the primary email address
*/
public void addPossibleEmail(String email, boolean is_primary) {
if (email == null) return;
if (is_primary) {
_primary_email = email;
_possible_emails.add(email);
} else
_possible_emails.add(email);
}
/**
* Adds a name to the list of possible names for the user.
* @param name the possible name
*/
public void addPossibleName(String name) {
if (name != null) _possible_names.add(name);
}
/**
* Adds a phone number to the list of possible phone numbers for the user.
* @param phone_number the possible phone number
*/
public void addPossiblePhoneNumber(String phone_number) {
if (phone_number != null) _possible_phone_numbers.add(phone_number);
}
/**
* Adds a phone number to the list of possible phone numbers for the user. Retains information about whether this
* phone number is the primary phone number of the user.
* @param phone_number the possible phone number
* @param is_primary whether the phone number is teh primary phone number
*/
public void addPossiblePhoneNumber(String phone_number, boolean is_primary) {
if (phone_number == null) return;
if (is_primary) {
_primary_phone_number = phone_number;
_possible_phone_numbers.add(phone_number);
} else
_possible_phone_numbers.add(phone_number);
}
/**
* Sets the possible photo for the user.
* @param photo the possible photo
*/
public void addPossiblePhoto(Uri photo) {
if (photo != null) _possible_photo = photo;
}
/**
* Retrieves the list of possible email addresses.
* @return the list of possible email addresses
*/
public List<String> possibleEmails() {
return _possible_emails;
}
/**
* Retrieves the list of possible names.
* @return the list of possible names
*/
public List<String> possibleNames() {
return _possible_names;
}
/**
* Retrieves the list of possible phone numbers
* @return the list of possible phone numbers
*/
public List<String> possiblePhoneNumbers() {
return _possible_phone_numbers;
}
/**
* Retrieves the possible photo.
* @return the possible photo
*/
public Uri possiblePhoto() {
return _possible_photo;
}
/**
* Retrieves the primary email address.
* @return the primary email address
*/
public String primaryEmail() {
return _primary_email;
}
/**
* Retrieves the primary phone number
* @return the primary phone number
*/
public String primaryPhoneNumber() {
return _primary_phone_number;
}
/** The primary email address */
private String _primary_email;
/** The primary name */
private String _primary_name;
/** The primary phone number */
private String _primary_phone_number;
/** A list of possible email addresses for the user */
private List<String> _possible_emails = Lists.newArrayList();
/** A list of possible names for the user */
private List<String> _possible_names = Lists.newArrayList();
/** A list of possible phone numbers for the user */
private List<String> _possible_phone_numbers = Lists.newArrayList();
/** A possible photo for the user */
private Uri _possible_photo;
}
/**
* Retrieves the user profile information.
* @param context the context from which to retrieve the user profile
* @return the user profile
*/
public static UserProfile getUserProfile(Context context) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH
? getUserProfileOnIcsDevice(context)
: getUserProfileOnGingerbreadDevice(context);
}
/**
* Retrieves the user profile information in a manner supported by Gingerbread devices.
* @param context the context from which to retrieve the user's email address and name
* @return a list of the possible user's email address and name
*/
private static UserProfile getUserProfileOnGingerbreadDevice(Context context) {
// Other that using Patterns (API level 8) this works on devices down to API level 5
final Matcher valid_email_address = Patterns.EMAIL_ADDRESS.matcher("");
final Account[] accounts = AccountManager.get(context).getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
final UserProfile user_profile = new UserProfile();
// As far as I can tell, there is no way to get the real name or phone number from the Google account
for (Account account : accounts) {
if (valid_email_address.reset(account.name).matches())
user_profile.addPossibleEmail(account.name);
}
// Gets the phone number of the device is the device has one
if (context.getPackageManager().hasSystemFeature(TELEPHONY_SERVICE)) {
final TelephonyManager telephony = (TelephonyManager) context.getSystemService(TELEPHONY_SERVICE);
user_profile.addPossiblePhoneNumber(telephony.getLine1Number());
}
return user_profile;
}
/**
* Retrieves the user profile information in a manner supported by Ice Cream Sandwich devices.
* @param context the context from which to retrieve the user's email address and name
* @return a list of the possible user's email address and name
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private static UserProfile getUserProfileOnIcsDevice(Context context) {
final ContentResolver content = context.getContentResolver();
final Cursor cursor = content.query(
// Retrieves data rows for the device user's 'profile' contact
Uri.withAppendedPath(
ContactsContract.Profile.CONTENT_URI,
ContactsContract.Contacts.Data.CONTENT_DIRECTORY),
ProfileQuery.PROJECTION,
// Selects only email addresses or names
ContactsContract.Contacts.Data.MIMETYPE + "=? OR "
+ ContactsContract.Contacts.Data.MIMETYPE + "=? OR "
+ ContactsContract.Contacts.Data.MIMETYPE + "=? OR "
+ ContactsContract.Contacts.Data.MIMETYPE + "=?",
new String[]{
ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE
},
// Show primary rows first. Note that there won't be a primary email address if the
// user hasn't specified one.
ContactsContract.Contacts.Data.IS_PRIMARY + " DESC"
);
final UserProfile user_profile = new UserProfile();
String mime_type;
while (cursor.moveToNext()) {
mime_type = cursor.getString(ProfileQuery.MIME_TYPE);
if (mime_type.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE))
user_profile.addPossibleEmail(cursor.getString(ProfileQuery.EMAIL),
cursor.getInt(ProfileQuery.IS_PRIMARY_EMAIL) > 0);
else if (mime_type.equals(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE))
user_profile.addPossibleName(cursor.getString(ProfileQuery.GIVEN_NAME) + " " + cursor.getString(ProfileQuery.FAMILY_NAME));
else if (mime_type.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE))
user_profile.addPossiblePhoneNumber(cursor.getString(ProfileQuery.PHONE_NUMBER),
cursor.getInt(ProfileQuery.IS_PRIMARY_PHONE_NUMBER) > 0);
else if (mime_type.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE))
user_profile.addPossiblePhoto(Uri.parse(cursor.getString(ProfileQuery.PHOTO)));
}
cursor.close();
return user_profile;
}
/**
* Contacts user profile query interface.
*/
private interface ProfileQuery {
/** The set of columns to extract from the profile query results */
String[] PROJECTION = {
ContactsContract.CommonDataKinds.Email.ADDRESS,
ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER,
ContactsContract.CommonDataKinds.Phone.IS_PRIMARY,
ContactsContract.CommonDataKinds.Photo.PHOTO_URI,
ContactsContract.Contacts.Data.MIMETYPE
};
/** Column index for the email address in the profile query results */
int EMAIL = 0;
/** Column index for the primary email address indicator in the profile query results */
int IS_PRIMARY_EMAIL = 1;
/** Column index for the family name in the profile query results */
int FAMILY_NAME = 2;
/** Column index for the given name in the profile query results */
int GIVEN_NAME = 3;
/** Column index for the phone number in the profile query results */
int PHONE_NUMBER = 4;
/** Column index for the primary phone number in the profile query results */
int IS_PRIMARY_PHONE_NUMBER = 5;
/** Column index for the photo in the profile query results */
int PHOTO = 6;
/** Column index for the MIME type in the profile query results */
int MIME_TYPE = 7;
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="8dp"
android:paddingLeft="@dimen/page_padding"
android:paddingRight="@dimen/page_padding"
android:orientation="vertical"
android:gravity="center">
<TextView style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:singleLine="true"
android:paddingLeft="13dp"
android:text="account" />
<AutoCompleteTextView
android:id="@+id/email_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/view_margin"
android:layout_marginRight="@dimen/view_margin"
android:singleLine="true"
android:inputType="textEmailAddress"
android:imeOptions="actionNext"
android:hint="email address" />
<EditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/view_margin"
android:layout_marginBottom="@dimen/view_margin"
android:singleLine="true"
android:inputType="textPassword"
android:hint="password" />
<TextView style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:singleLine="true"
android:paddingLeft="13dp"
android:text="information" />
<AutoCompleteTextView
android:id="@+id/full_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/view_margin"
android:singleLine="true"
android:inputType="textPersonName"
android:imeOptions="actionNext"
android:capitalize="words"
android:hint="full name" />
<AutoCompleteTextView
android:id="@+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/view_margin"
android:layout_marginBottom="@dimen/view_margin"
android:singleLine="true"
android:phoneNumber="true"
android:inputType="phone"
android:imeOptions="actionNext"
android:hint="phone number" />
<ImageButton style="?android:attr/borderlessButtonStyle"
android:id="@+id/photo_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/view_margin"
android:layout_marginBottom="@dimen/view_margin"
android:maxHeight="128dp"
android:src="@drawable/anonymous_image_2"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:cropToPadding="true"
android:contentDescription="choose an avatar picture, this is the picture set for user by default."
android:onClick="startPhotoPicker" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/view_margin"
android:text="create account"
android:onClick="startHomeScreen" />
</LinearLayout>
public class CreateAccountActivity extends FragmentActivity
implements LoaderManager.LoaderCallbacks<AccountUtils.UserProfile> {
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
// ...
getSupportLoaderManager().initLoader(0, Bundle.EMPTY, this);
}
@Override
public Loader<AccountUtils.UserProfile> onCreateLoader(int _, Bundle __) {
return new UserProfileLoader(this);
}
@Override
public void onLoadFinished(Loader<AccountUtils.UserProfile> loader, AccountUtils.UserProfile user_profile) {
final List<String> possible_emails = user_profile.possibleEmails();
final List<String> possible_names = user_profile.possibleNames();
final List<String> possible_phone_numbers = user_profile.possiblePhoneNumbers();
final String primary_email = user_profile.primaryEmail();
final String primary_phone_number = user_profile.primaryPhoneNumber();
// Sets the text to the likely possibility, this is the user defined primary possibility or the first possibility if there
// isn't a primary possibility
if (possible_emails.size() > 0) _email_address.setText(primary_email == null ? possible_emails.get(0) :
primary_email);
if (possible_names.size() > 0) _full_name.setText(possible_names.get(0));
if (possible_phone_numbers.size() > 0) _phone_number.setText(primary_phone_number == null? possible_phone_numbers.get(0) : primary_phone_number);
_photo_picker.setImageURI(user_profile.possiblePhoto());
_email_address.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
possible_emails));
_full_name.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
possible_names));
_phone_number.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_dropdown_item_1line,
possible_phone_numbers));
}
@Override
public void onLoaderReset(Loader<AccountUtils.UserProfile> loader) { }
}
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
/**
* A custom {@link android.support.v4.content.Loader} that retrieves the {@link AccountUtils.UserProfile} asynchronously.
* @author Dandré Allison
*/
public class UserProfileLoader extends AsyncTaskLoader<AccountUtils.UserProfile> {
public UserProfileLoader(Context context) {
super(context);
}
@Override
public AccountUtils.UserProfile loadInBackground() {
return AccountUtils.getUserProfile(getContext());
}
@Override
public void deliverResult(AccountUtils.UserProfile user_profile) {
_user_profile = user_profile;
if (isStarted())
// If the Loader is currently started, we can immediately
// deliver its results.
super.deliverResult(user_profile);
}
@Override
protected void onStartLoading() {
if (_user_profile != null)
// Delivers the result immediately when it's already available
deliverResult(_user_profile);
if (takeContentChanged() || _user_profile == null) {
// If the data has changed since the last time it was loaded
// or is not currently available, start a load.
forceLoad();
}
}
@Override
protected void onStopLoading() {
// Attempts to cancel the current load task if possible.
cancelLoad();
}
/**
* Handles a request to completely reset the Loader.
*/
@Override protected void onReset() {
super.onReset();
// Ensures the loader is stopped
onStopLoading();
// Clears the stored list
if (_user_profile != null)
_user_profile = null;
}
/** The list of the user's possible email address and name */
private AccountUtils.UserProfile _user_profile;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment