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 7package org.chromium.components.browser.autofill; 8 9import android.content.ContentResolver; 10import android.content.Context; 11import android.content.pm.PackageManager; 12import android.database.Cursor; 13import android.net.Uri; 14import android.provider.ContactsContract; 15 16import org.chromium.base.CalledByNative; 17import 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") 24public 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