Home | History | Annotate | Download | only in autofill
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // Populates data fields from Android contacts profile API (i.e. "me" contact).
      6 
      7 package org.chromium.components.browser.autofill;
      8 
      9 import android.content.ContentResolver;
     10 import android.content.Context;
     11 import android.content.pm.PackageManager;
     12 import android.database.Cursor;
     13 import android.net.Uri;
     14 import android.provider.ContactsContract;
     15 
     16 import org.chromium.base.CalledByNative;
     17 import org.chromium.base.JNINamespace;
     18 
     19 /**
     20  * Loads user profile information stored under the "Me" contact.
     21  * Requires permissions: READ_CONTACTS and READ_PROFILE.
     22  */
     23 @JNINamespace("autofill")
     24 public class PersonalAutofillPopulator {
     25     /**
     26      * SQL query definitions for obtaining specific profile information.
     27      */
     28     private abstract static class ProfileQuery {
     29         Uri profileDataUri = Uri.withAppendedPath(
     30                 ContactsContract.Profile.CONTENT_URI,
     31                 ContactsContract.Contacts.Data.CONTENT_DIRECTORY
     32                 );
     33         public abstract String[] projection();
     34         public abstract String mimeType();
     35     }
     36 
     37     private static class EmailProfileQuery extends ProfileQuery {
     38         private static final int EMAIL_ADDRESS = 0;
     39 
     40         @Override
     41         public String[] projection() {
     42             return new String[] {
     43                 ContactsContract.CommonDataKinds.Email.ADDRESS,
     44             };
     45         }
     46 
     47         @Override
     48         public String mimeType() {
     49             return ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE;
     50         }
     51     }
     52 
     53     private static class PhoneProfileQuery extends ProfileQuery {
     54         private static final int NUMBER = 0;
     55 
     56         @Override
     57         public String[] projection() {
     58             return new String[] {
     59                 ContactsContract.CommonDataKinds.Phone.NUMBER,
     60             };
     61         }
     62 
     63         @Override
     64         public String mimeType() {
     65             return ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE;
     66         }
     67     }
     68 
     69     private static class AddressProfileQuery extends ProfileQuery {
     70         private static final int STREET = 0;
     71         private static final int POBOX = 1;
     72         private static final int NEIGHBORHOOD = 2;
     73         private static final int CITY = 3;
     74         private static final int REGION = 4;
     75         private static final int POSTALCODE = 5;
     76         private static final int COUNTRY = 6;
     77 
     78         @Override
     79         public String[] projection() {
     80             return new String[] {
     81                 ContactsContract.CommonDataKinds.StructuredPostal.STREET,
     82                     ContactsContract.CommonDataKinds.StructuredPostal.POBOX,
     83                     ContactsContract.CommonDataKinds.StructuredPostal.NEIGHBORHOOD,
     84                     ContactsContract.CommonDataKinds.StructuredPostal.CITY,
     85                     ContactsContract.CommonDataKinds.StructuredPostal.REGION,
     86                     ContactsContract.CommonDataKinds.StructuredPostal.POSTCODE,
     87                     ContactsContract.CommonDataKinds.StructuredPostal.COUNTRY,
     88             };
     89         }
     90 
     91         @Override
     92         public String mimeType() {
     93             return ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE;
     94         }
     95     }
     96 
     97     private static class NameProfileQuery extends ProfileQuery {
     98         private static final int GIVEN_NAME = 0;
     99         private static final int MIDDLE_NAME = 1;
    100         private static final int FAMILY_NAME = 2;
    101         private static final int SUFFIX = 3;
    102 
    103         @Override
    104         public String[] projection() {
    105             return new String[] {
    106                 ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME,
    107                     ContactsContract.CommonDataKinds.StructuredName.MIDDLE_NAME,
    108                     ContactsContract.CommonDataKinds.StructuredName.FAMILY_NAME,
    109                     ContactsContract.CommonDataKinds.StructuredName.SUFFIX
    110             };
    111         }
    112 
    113         @Override
    114         public String mimeType() {
    115             return ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE;
    116         }
    117     }
    118 
    119     /**
    120      * Takes a query object, transforms into actual query and returns cursor.
    121      * Primary contact values will be first.
    122      */
    123     private Cursor cursorFromProfileQuery(ProfileQuery query, ContentResolver contentResolver) {
    124         String sortDescriptor = ContactsContract.Contacts.Data.IS_PRIMARY + " DESC";
    125         return contentResolver.query(
    126                 query.profileDataUri,
    127                 query.projection(),
    128                 ContactsContract.Contacts.Data.MIMETYPE + " = ?",
    129                 new String[]{query.mimeType()},
    130                 sortDescriptor
    131                 );
    132     }
    133     // Extracted data variables.
    134     private String[] mEmailAddresses;
    135     private String mGivenName;
    136     private String mMiddleName;
    137     private String mFamilyName;
    138     private String mSuffix;
    139     private String mPobox;
    140     private String mStreet;
    141     private String mNeighborhood;
    142     private String mCity;
    143     private String mRegion;
    144     private String mCountry;
    145     private String mPostalCode;
    146     private String[] mPhoneNumbers;
    147     private boolean mHasPermissions;
    148 
    149     /**
    150      * Constructor
    151      * @param context a valid android context reference
    152      */
    153     PersonalAutofillPopulator(Context context) {
    154         mHasPermissions = hasPermissions(context);
    155         if (mHasPermissions) {
    156             ContentResolver contentResolver = context.getContentResolver();
    157             populateName(contentResolver);
    158             populateEmail(contentResolver);
    159             populateAddress(contentResolver);
    160             populatePhone(contentResolver);
    161         }
    162     }
    163 
    164     // Check if the user has granted permissions.
    165     private boolean hasPermissions(Context context) {
    166         String [] permissions = {
    167             "android.permission.READ_CONTACTS",
    168             "android.permission.READ_PROFILE"
    169         };
    170         for (String permission : permissions) {
    171             int res = context.checkCallingOrSelfPermission(permission);
    172             if (res != PackageManager.PERMISSION_GRANTED) return false;
    173         }
    174         return true;
    175     }
    176 
    177     // Populating data fields.
    178     private void populateName(ContentResolver contentResolver) {
    179         NameProfileQuery nameProfileQuery = new NameProfileQuery();
    180         Cursor nameCursor = cursorFromProfileQuery(nameProfileQuery, contentResolver);
    181         if (nameCursor.moveToNext()) {
    182             mGivenName = nameCursor.getString(nameProfileQuery.GIVEN_NAME);
    183             mMiddleName = nameCursor.getString(nameProfileQuery.MIDDLE_NAME);
    184             mFamilyName = nameCursor.getString(nameProfileQuery.FAMILY_NAME);
    185             mSuffix = nameCursor.getString(nameProfileQuery.SUFFIX);
    186         }
    187         nameCursor.close();
    188     }
    189 
    190     private void populateEmail(ContentResolver contentResolver) {
    191         EmailProfileQuery emailProfileQuery = new EmailProfileQuery();
    192         Cursor emailCursor = cursorFromProfileQuery(emailProfileQuery, contentResolver);
    193         mEmailAddresses = new String[emailCursor.getCount()];
    194         for (int i = 0; emailCursor.moveToNext(); i++) {
    195             mEmailAddresses[i] = emailCursor.getString(emailProfileQuery.EMAIL_ADDRESS);
    196         }
    197         emailCursor.close();
    198     }
    199 
    200     private void populateAddress(ContentResolver contentResolver) {
    201         AddressProfileQuery addressProfileQuery = new AddressProfileQuery();
    202         Cursor addressCursor = cursorFromProfileQuery(addressProfileQuery, contentResolver);
    203         if(addressCursor.moveToNext()) {
    204             mPobox = addressCursor.getString(addressProfileQuery.POBOX);
    205             mStreet = addressCursor.getString(addressProfileQuery.STREET);
    206             mNeighborhood = addressCursor.getString(addressProfileQuery.NEIGHBORHOOD);
    207             mCity = addressCursor.getString(addressProfileQuery.CITY);
    208             mRegion = addressCursor.getString(addressProfileQuery.REGION);
    209             mPostalCode = addressCursor.getString(addressProfileQuery.POSTALCODE);
    210             mCountry = addressCursor.getString(addressProfileQuery.COUNTRY);
    211         }
    212         addressCursor.close();
    213     }
    214 
    215     private void populatePhone(ContentResolver contentResolver) {
    216         PhoneProfileQuery phoneProfileQuery = new PhoneProfileQuery();
    217         Cursor phoneCursor = cursorFromProfileQuery(phoneProfileQuery, contentResolver);
    218         mPhoneNumbers = new String[phoneCursor.getCount()];
    219         for (int i = 0; phoneCursor.moveToNext(); i++) {
    220             mPhoneNumbers[i] = phoneCursor.getString(phoneProfileQuery.NUMBER);
    221         }
    222         phoneCursor.close();
    223     }
    224 
    225     /**
    226      * Static factory method for instance creation.
    227      * @param context valid Android context.
    228      * @return PersonalAutofillPopulator new instance of PersonalAutofillPopulator.
    229      */
    230     @CalledByNative
    231     static PersonalAutofillPopulator create(Context context) {
    232         return new PersonalAutofillPopulator(context);
    233     }
    234 
    235     @CalledByNative
    236     private String getFirstName() {
    237         return mGivenName;
    238     }
    239 
    240     @CalledByNative
    241     private String getLastName() {
    242         return mFamilyName;
    243     }
    244 
    245     @CalledByNative
    246     private String getMiddleName() {
    247         return mMiddleName;
    248     }
    249 
    250     @CalledByNative
    251     private String getSuffix() {
    252         return mSuffix;
    253     }
    254 
    255     @CalledByNative
    256     private String[] getEmailAddresses() {
    257         return mEmailAddresses;
    258     }
    259 
    260     @CalledByNative
    261     private String getStreet() {
    262         return mStreet;
    263     }
    264 
    265     @CalledByNative
    266     private String getPobox() {
    267         return mPobox;
    268     }
    269 
    270     @CalledByNative
    271     private String getNeighborhood() {
    272         return mNeighborhood;
    273     }
    274 
    275     @CalledByNative
    276     private String getCity() {
    277         return mCity;
    278     }
    279 
    280     @CalledByNative
    281     private String getRegion() {
    282         return mRegion;
    283     }
    284 
    285     @CalledByNative
    286     private String getPostalCode() {
    287         return mPostalCode;
    288     }
    289 
    290     @CalledByNative
    291     private String getCountry() {
    292         return mCountry;
    293     }
    294 
    295     @CalledByNative
    296     private String[] getPhoneNumbers() {
    297         return mPhoneNumbers;
    298     }
    299 
    300     @CalledByNative
    301     private boolean getHasPermissions() {
    302         return mHasPermissions;
    303     }
    304 }
    305