summaryrefslogtreecommitdiffstats
path: root/src/com/android/swe/browser/AutofillHandler.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/swe/browser/AutofillHandler.java')
-rw-r--r--src/com/android/swe/browser/AutofillHandler.java283
1 files changed, 283 insertions, 0 deletions
diff --git a/src/com/android/swe/browser/AutofillHandler.java b/src/com/android/swe/browser/AutofillHandler.java
new file mode 100644
index 00000000..5b0320da
--- /dev/null
+++ b/src/com/android/swe/browser/AutofillHandler.java
@@ -0,0 +1,283 @@
+
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Message;
+import android.preference.PreferenceManager;
+import android.provider.ContactsContract;
+import android.util.Log;
+import android.webkit.WebSettingsClassic.AutoFillProfile;
+
+import java.util.concurrent.CountDownLatch;
+
+public class AutofillHandler {
+
+ private AutoFillProfile mAutoFillProfile;
+ // Default to zero. In the case no profile is set up, the initial
+ // value will come from the AutoFillSettingsFragment when the user
+ // creates a profile. Otherwise, we'll read the ID of the last used
+ // profile from the prefs db.
+ private int mAutoFillActiveProfileId;
+ private static final int NO_AUTOFILL_PROFILE_SET = 0;
+
+ private CountDownLatch mLoaded = new CountDownLatch(1);
+ private Context mContext;
+
+ private static final String LOGTAG = "AutofillHandler";
+
+ public AutofillHandler(Context context) {
+ mContext = context.getApplicationContext();
+ }
+
+ /**
+ * Load settings from the browser app's database. It is performed in
+ * an AsyncTask as it involves plenty of slow disk IO.
+ * NOTE: Strings used for the preferences must match those specified
+ * in the various preference XML files.
+ */
+ public void asyncLoadFromDb() {
+ // Run the initial settings load in an AsyncTask as it hits the
+ // disk multiple times through SharedPreferences and SQLite. We
+ // need to be certain though that this has completed before we start
+ // to load pages though, so in the worst case we will block waiting
+ // for it to finish in BrowserActivity.onCreate().
+ new LoadFromDb().start();
+ }
+
+ private void waitForLoad() {
+ try {
+ mLoaded.await();
+ } catch (InterruptedException e) {
+ Log.w(LOGTAG, "Caught exception while waiting for AutofillProfile to load.");
+ }
+ }
+
+ private class LoadFromDb extends Thread {
+
+ @Override
+ public void run() {
+ // Note the lack of synchronization over mAutoFillActiveProfileId and
+ // mAutoFillProfile here. This is because we control all other access
+ // to these members through the public functions of this class, and they
+ // all wait for this thread via the mLoaded CountDownLatch.
+
+ SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(mContext);
+
+ // Read the last active AutoFill profile id.
+ mAutoFillActiveProfileId = p.getInt(
+ PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID,
+ mAutoFillActiveProfileId);
+
+ // Load the autofill profile data from the database. We use a database separate
+ // to the browser preference DB to make it easier to support multiple profiles
+ // and switching between them. Note that this may block startup if this DB lookup
+ // is extremely slow. We do this to ensure that if there's a profile set, the
+ // user never sees the "setup Autofill" option.
+ AutoFillProfileDatabase autoFillDb = AutoFillProfileDatabase.getInstance(mContext);
+ Cursor c = autoFillDb.getProfile(mAutoFillActiveProfileId);
+
+ if (c.getCount() > 0) {
+ c.moveToFirst();
+
+ String fullName = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.FULL_NAME));
+ String email = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.EMAIL_ADDRESS));
+ String company = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.COMPANY_NAME));
+ String addressLine1 = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.ADDRESS_LINE_1));
+ String addressLine2 = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.ADDRESS_LINE_2));
+ String city = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.CITY));
+ String state = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.STATE));
+ String zip = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.ZIP_CODE));
+ String country = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.COUNTRY));
+ String phone = c.getString(c.getColumnIndex(
+ AutoFillProfileDatabase.Profiles.PHONE_NUMBER));
+ mAutoFillProfile = new AutoFillProfile(mAutoFillActiveProfileId,
+ fullName, email, company, addressLine1, addressLine2, city,
+ state, zip, country, phone);
+ }
+ c.close();
+ autoFillDb.close();
+
+ // At this point we've loaded the profile if there was one, so let any thread
+ // waiting on initialization continue.
+ mLoaded.countDown();
+
+ // Synchronization note: strictly speaking, it's possible that mAutoFillProfile
+ // may get a value after we check below, but that's OK. This check is only an
+ // optimisation, and we do a proper synchronized check further down when it comes
+ // to actually setting the inferred profile.
+ if (mAutoFillProfile == null) {
+ // We did not load a profile from disk. Try to infer one from the user's
+ // "me" contact.
+ final Uri profileUri = Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
+ ContactsContract.Contacts.Data.CONTENT_DIRECTORY);
+ String name = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+ // Only attempt to read other data and set a profile if we could successfully
+ // get a name.
+ if (name != null) {
+ String email = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.Email.ADDRESS,
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE);
+ String phone = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.Phone.NUMBER,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE);
+ String company = getContactField(profileUri,
+ ContactsContract.CommonDataKinds.Organization.COMPANY,
+ ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE);
+
+ // Can't easily get structured postal address information (even using
+ // CommonDataKinds.StructuredPostal) so omit prepopulating that for now.
+ // When querying structured postal data, it often all comes back as a string
+ // inside the "street" field.
+
+ synchronized(AutofillHandler.this) {
+ // Only use this profile if one hasn't been set inbetween the
+ // inital import and this thread getting to this point.
+ if (mAutoFillProfile == null) {
+ setAutoFillProfile(new AutoFillProfile(1, name, email, company,
+ null, null, null, null, null, null, phone), null);
+ }
+ }
+ }
+ }
+ }
+
+ private String getContactField(Uri uri, String field, String itemType) {
+ String result = null;
+
+ Cursor c = mContext.getContentResolver().query(uri, new String[] { field },
+ ContactsContract.Data.MIMETYPE + "=?", new String[] { itemType }, null);
+
+ if (c == null) {
+ return null;
+ }
+
+ try {
+ // Just use the first returned value if we get more than one.
+ if (c.moveToFirst()) {
+ result = c.getString(0);
+ }
+ } finally {
+ c.close();
+ }
+ return result;
+ }
+ }
+
+ public synchronized void setAutoFillProfile(AutoFillProfile profile, Message msg) {
+ waitForLoad();
+ int profileId = NO_AUTOFILL_PROFILE_SET;
+ if (profile != null) {
+ profileId = profile.getUniqueId();
+ // Update the AutoFill DB with the new profile.
+ new SaveProfileToDbTask(msg).execute(profile);
+ } else {
+ // Delete the current profile.
+ if (mAutoFillProfile != null) {
+ new DeleteProfileFromDbTask(msg).execute(mAutoFillProfile.getUniqueId());
+ }
+ }
+ // Make sure we set mAutoFillProfile before calling setActiveAutoFillProfileId
+ // Calling setActiveAutoFillProfileId will trigger an update of WebViews
+ // which will expect a new profile to be set
+ mAutoFillProfile = profile;
+ setActiveAutoFillProfileId(profileId);
+ }
+
+ public synchronized AutoFillProfile getAutoFillProfile() {
+ waitForLoad();
+ return mAutoFillProfile;
+ }
+
+ private synchronized void setActiveAutoFillProfileId(int activeProfileId) {
+ mAutoFillActiveProfileId = activeProfileId;
+ Editor ed = PreferenceManager.
+ getDefaultSharedPreferences(mContext).edit();
+ ed.putInt(PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID, activeProfileId);
+ ed.apply();
+ }
+
+ private abstract class AutoFillProfileDbTask<T> extends AsyncTask<T, Void, Void> {
+ AutoFillProfileDatabase mAutoFillProfileDb;
+ Message mCompleteMessage;
+
+ public AutoFillProfileDbTask(Message msg) {
+ mCompleteMessage = msg;
+ }
+
+ @Override
+ protected void onPostExecute(Void result) {
+ if (mCompleteMessage != null) {
+ mCompleteMessage.sendToTarget();
+ }
+ mAutoFillProfileDb.close();
+ }
+
+ @Override
+ abstract protected Void doInBackground(T... values);
+ }
+
+
+ private class SaveProfileToDbTask extends AutoFillProfileDbTask<AutoFillProfile> {
+ public SaveProfileToDbTask(Message msg) {
+ super(msg);
+ }
+
+ @Override
+ protected Void doInBackground(AutoFillProfile... values) {
+ mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext);
+ synchronized (AutofillHandler.this) {
+ assert mAutoFillActiveProfileId != NO_AUTOFILL_PROFILE_SET;
+ AutoFillProfile newProfile = values[0];
+ mAutoFillProfileDb.addOrUpdateProfile(mAutoFillActiveProfileId, newProfile);
+ }
+ return null;
+ }
+ }
+
+ private class DeleteProfileFromDbTask extends AutoFillProfileDbTask<Integer> {
+ public DeleteProfileFromDbTask(Message msg) {
+ super(msg);
+ }
+
+ @Override
+ protected Void doInBackground(Integer... values) {
+ mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext);
+ int id = values[0];
+ assert id > 0;
+ mAutoFillProfileDb.dropProfile(id);
+ return null;
+ }
+ }
+}