1
2/*
3 * Copyright (C) 2011 The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package com.android.browser;
19
20import android.content.Context;
21import android.content.SharedPreferences;
22import android.content.SharedPreferences.Editor;
23import android.database.Cursor;
24import android.os.AsyncTask;
25import android.os.Message;
26import android.preference.PreferenceManager;
27import android.webkit.WebSettings.AutoFillProfile;
28
29import java.util.concurrent.CountDownLatch;
30
31public class AutofillHandler {
32
33    private AutoFillProfile mAutoFillProfile;
34    // Default to zero. In the case no profile is set up, the initial
35    // value will come from the AutoFillSettingsFragment when the user
36    // creates a profile. Otherwise, we'll read the ID of the last used
37    // profile from the prefs db.
38    private int mAutoFillActiveProfileId;
39    private static final int NO_AUTOFILL_PROFILE_SET = 0;
40
41    private CountDownLatch mLoaded = new CountDownLatch(1);
42    private Context mContext;
43
44    public AutofillHandler(Context context) {
45        mContext = context.getApplicationContext();
46    }
47
48    /**
49     * Load settings from the browser app's database. It is performed in
50     * an AsyncTask as it involves plenty of slow disk IO.
51     * NOTE: Strings used for the preferences must match those specified
52     * in the various preference XML files.
53     */
54    public void asyncLoadFromDb() {
55        // Run the initial settings load in an AsyncTask as it hits the
56        // disk multiple times through SharedPreferences and SQLite. We
57        // need to be certain though that this has completed before we start
58        // to load pages though, so in the worst case we will block waiting
59        // for it to finish in BrowserActivity.onCreate().
60         new LoadFromDb().start();
61    }
62
63    public void waitForLoad() {
64        try {
65            mLoaded.await();
66        } catch (InterruptedException e) {}
67    }
68
69    private class LoadFromDb extends Thread {
70
71        @Override
72        public void run() {
73            SharedPreferences p =
74                    PreferenceManager.getDefaultSharedPreferences(mContext);
75
76            // Read the last active AutoFill profile id.
77            mAutoFillActiveProfileId = p.getInt(
78                    PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID,
79                    mAutoFillActiveProfileId);
80
81            // Load the autofill profile data from the database. We use a database separate
82            // to the browser preference DB to make it easier to support multiple profiles
83            // and switching between them.
84            AutoFillProfileDatabase autoFillDb = AutoFillProfileDatabase.getInstance(mContext);
85            Cursor c = autoFillDb.getProfile(mAutoFillActiveProfileId);
86
87            if (c.getCount() > 0) {
88                c.moveToFirst();
89
90                String fullName = c.getString(c.getColumnIndex(
91                        AutoFillProfileDatabase.Profiles.FULL_NAME));
92                String email = c.getString(c.getColumnIndex(
93                        AutoFillProfileDatabase.Profiles.EMAIL_ADDRESS));
94                String company = c.getString(c.getColumnIndex(
95                        AutoFillProfileDatabase.Profiles.COMPANY_NAME));
96                String addressLine1 = c.getString(c.getColumnIndex(
97                        AutoFillProfileDatabase.Profiles.ADDRESS_LINE_1));
98                String addressLine2 = c.getString(c.getColumnIndex(
99                        AutoFillProfileDatabase.Profiles.ADDRESS_LINE_2));
100                String city = c.getString(c.getColumnIndex(
101                        AutoFillProfileDatabase.Profiles.CITY));
102                String state = c.getString(c.getColumnIndex(
103                        AutoFillProfileDatabase.Profiles.STATE));
104                String zip = c.getString(c.getColumnIndex(
105                        AutoFillProfileDatabase.Profiles.ZIP_CODE));
106                String country = c.getString(c.getColumnIndex(
107                        AutoFillProfileDatabase.Profiles.COUNTRY));
108                String phone = c.getString(c.getColumnIndex(
109                        AutoFillProfileDatabase.Profiles.PHONE_NUMBER));
110                mAutoFillProfile = new AutoFillProfile(mAutoFillActiveProfileId,
111                        fullName, email, company, addressLine1, addressLine2, city,
112                        state, zip, country, phone);
113            }
114            c.close();
115            autoFillDb.close();
116
117            mLoaded.countDown();
118        }
119    }
120
121    public void setAutoFillProfile(AutoFillProfile profile, Message msg) {
122        int profileId = NO_AUTOFILL_PROFILE_SET;
123        if (profile != null) {
124            profileId = profile.getUniqueId();
125            // Update the AutoFill DB with the new profile.
126            new SaveProfileToDbTask(msg).execute(profile);
127        } else {
128            // Delete the current profile.
129            if (mAutoFillProfile != null) {
130                new DeleteProfileFromDbTask(msg).execute(mAutoFillProfile.getUniqueId());
131            }
132        }
133        // Make sure we set mAutoFillProfile before calling setActiveAutoFillProfileId
134        // Calling setActiveAutoFillProfileId will trigger an update of WebViews
135        // which will expect a new profile to be set
136        mAutoFillProfile = profile;
137        setActiveAutoFillProfileId(profileId);
138    }
139
140    public AutoFillProfile getAutoFillProfile() {
141        return mAutoFillProfile;
142    }
143
144    private void setActiveAutoFillProfileId(int activeProfileId) {
145        mAutoFillActiveProfileId = activeProfileId;
146        Editor ed = PreferenceManager.
147            getDefaultSharedPreferences(mContext).edit();
148        ed.putInt(PreferenceKeys.PREF_AUTOFILL_ACTIVE_PROFILE_ID, activeProfileId);
149        ed.apply();
150    }
151
152    private abstract class AutoFillProfileDbTask<T> extends AsyncTask<T, Void, Void> {
153        AutoFillProfileDatabase mAutoFillProfileDb;
154        Message mCompleteMessage;
155
156        public AutoFillProfileDbTask(Message msg) {
157            mCompleteMessage = msg;
158        }
159
160        @Override
161        protected void onPostExecute(Void result) {
162            if (mCompleteMessage != null) {
163                mCompleteMessage.sendToTarget();
164            }
165            mAutoFillProfileDb.close();
166        }
167
168        @Override
169        abstract protected Void doInBackground(T... values);
170    }
171
172
173    private class SaveProfileToDbTask extends AutoFillProfileDbTask<AutoFillProfile> {
174        public SaveProfileToDbTask(Message msg) {
175            super(msg);
176        }
177
178        @Override
179        protected Void doInBackground(AutoFillProfile... values) {
180            mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext);
181            assert mAutoFillActiveProfileId != NO_AUTOFILL_PROFILE_SET;
182            AutoFillProfile newProfile = values[0];
183            mAutoFillProfileDb.addOrUpdateProfile(mAutoFillActiveProfileId, newProfile);
184            return null;
185        }
186    }
187
188    private class DeleteProfileFromDbTask extends AutoFillProfileDbTask<Integer> {
189        public DeleteProfileFromDbTask(Message msg) {
190            super(msg);
191        }
192
193        @Override
194        protected Void doInBackground(Integer... values) {
195            mAutoFillProfileDb = AutoFillProfileDatabase.getInstance(mContext);
196            int id = values[0];
197            assert  id > 0;
198            mAutoFillProfileDb.dropProfile(id);
199            return null;
200        }
201    }
202}
203