GeneralPreferencesFragment.java revision 267b6af7ee9f731f0da6ae0b27f015308671b835
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.browser.preferences; 18 19import com.android.browser.BrowserBookmarksPage; 20import com.android.browser.BrowserHomepagePreference; 21import com.android.browser.BrowserPreferencesPage; 22import com.android.browser.BrowserSettings; 23import com.android.browser.R; 24 25import android.accounts.Account; 26import android.accounts.AccountManager; 27import android.app.AlertDialog; 28import android.app.Dialog; 29import android.app.DialogFragment; 30import android.app.Fragment; 31import android.content.ContentProviderOperation; 32import android.content.ContentResolver; 33import android.content.ContentValues; 34import android.content.Context; 35import android.content.DialogInterface; 36import android.content.OperationApplicationException; 37import android.content.SharedPreferences; 38import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 39import android.database.Cursor; 40import android.os.AsyncTask; 41import android.os.Bundle; 42import android.os.RemoteException; 43import android.preference.Preference; 44import android.preference.Preference.OnPreferenceClickListener; 45import android.preference.PreferenceFragment; 46import android.preference.PreferenceManager; 47import android.preference.PreferenceScreen; 48import android.provider.BrowserContract; 49import android.provider.BrowserContract.Bookmarks; 50import android.provider.BrowserContract.ChromeSyncColumns; 51import android.util.Log; 52import android.view.LayoutInflater; 53import android.view.View; 54import android.view.View.OnClickListener; 55import android.widget.Button; 56import android.widget.LinearLayout; 57 58import java.util.ArrayList; 59 60public class GeneralPreferencesFragment extends PreferenceFragment 61 implements OnPreferenceClickListener, Preference.OnPreferenceChangeListener { 62 static final String TAG = "PersonalPreferencesFragment"; 63 64 static final String PREF_CHROME_SYNC = "sync_with_chrome"; 65 66 Preference mChromeSync; 67 boolean mEnabled; 68 SharedPreferences mSharedPrefs; 69 70 @Override 71 public void onCreate(Bundle savedInstanceState) { 72 super.onCreate(savedInstanceState); 73 74 // Load the XML preferences file 75 addPreferencesFromResource(R.xml.general_preferences); 76 77 Preference e = findPreference(BrowserSettings.PREF_HOMEPAGE); 78 e.setOnPreferenceChangeListener(this); 79 e.setSummary(getPreferenceScreen().getSharedPreferences() 80 .getString(BrowserSettings.PREF_HOMEPAGE, null)); 81 ((BrowserHomepagePreference) e).setCurrentPage( 82 getActivity().getIntent().getStringExtra(BrowserPreferencesPage.CURRENT_PAGE)); 83 } 84 85 @Override 86 public boolean onPreferenceChange(Preference pref, Object objValue) { 87 if (getActivity() == null) { 88 // We aren't attached, so don't accept preferences changes from the 89 // invisible UI. 90 Log.w("PageContentPreferencesFragment", "onPreferenceChange called from detached fragment!"); 91 return false; 92 } 93 94 if (pref.getKey().equals(BrowserSettings.PREF_HOMEPAGE)) { 95 pref.setSummary((String) objValue); 96 return true; 97 } 98 99 return false; 100 } 101 102 @Override 103 public void onResume() { 104 super.onResume(); 105 106 // Setup the proper state for the sync with chrome item 107 mChromeSync = findPreference(PREF_CHROME_SYNC); 108 refreshUi(); 109 mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 110 mSharedPrefs.registerOnSharedPreferenceChangeListener(mListener); 111 } 112 113 @Override 114 public void onPause() { 115 super.onPause(); 116 117 mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mListener); 118 } 119 120 OnSharedPreferenceChangeListener mListener 121 = new OnSharedPreferenceChangeListener() { 122 @Override 123 public void onSharedPreferenceChanged( 124 SharedPreferences sharedPreferences, String key) { 125 if (BrowserBookmarksPage.PREF_ACCOUNT_NAME.equals(key) 126 || BrowserBookmarksPage.PREF_ACCOUNT_TYPE.equals(key)) { 127 refreshUi(); 128 } 129 } 130 131 }; 132 133 private class GetAccountsTask extends AsyncTask<Void, Void, String> { 134 private Context mContext; 135 136 GetAccountsTask(Context ctx) { 137 mContext = ctx; 138 } 139 140 @Override 141 protected String doInBackground(Void... unused) { 142 AccountManager am = (AccountManager) mContext.getSystemService(Context.ACCOUNT_SERVICE); 143 Account[] accounts = am.getAccountsByType("com.google"); 144 if (accounts == null || accounts.length == 0) { 145 // No Google accounts setup, don't offer Chrome sync 146 if (mChromeSync != null) { 147 getPreferenceScreen().removePreference(mChromeSync); 148 } 149 } else { 150 // Google accounts are present. 151 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); 152 Bundle args = mChromeSync.getExtras(); 153 args.putParcelableArray("accounts", accounts); 154 mEnabled = BrowserContract.Settings.isSyncEnabled(mContext); 155 mChromeSync.setOnPreferenceClickListener(GeneralPreferencesFragment.this); 156 157 if (!mEnabled) { 158 // Setup a link to the enable wizard 159 return mContext.getResources().getString( 160 R.string.pref_personal_sync_with_chrome_summary); 161 } else { 162 // Chrome sync is enabled, setup a link to account switcher 163 String accountName = prefs.getString( 164 BrowserBookmarksPage.PREF_ACCOUNT_NAME, null); 165 args.putString("curAccount", accountName); 166 return accountName; 167 } 168 } 169 170 return null; 171 } 172 173 @Override 174 protected void onPostExecute(String summary) { 175 if (summary != null) { 176 mChromeSync.setSummary(summary); 177 } 178 } 179 } 180 181 void refreshUi() { 182 new GetAccountsTask(getActivity()).execute(); 183 184 PreferenceScreen autoFillSettings = 185 (PreferenceScreen)findPreference(BrowserSettings.PREF_AUTOFILL_PROFILE); 186 autoFillSettings.setDependency(BrowserSettings.PREF_AUTOFILL_ENABLED); 187 } 188 189 @Override 190 public boolean onPreferenceClick(Preference preference) { 191 Fragment frag; 192 if (mEnabled) { 193 frag = new AccountChooserDialog(); 194 } else { 195 frag = new ImportWizardDialog(); 196 } 197 frag.setArguments(preference.getExtras()); 198 getFragmentManager().beginTransaction() 199 .add(frag, null) 200 .commit(); 201 return true; 202 } 203 204 public static class AccountChooserDialog extends DialogFragment 205 implements DialogInterface.OnClickListener { 206 207 AlertDialog mDialog; 208 209 @Override 210 public Dialog onCreateDialog(Bundle savedInstanceState) { 211 Bundle args = getArguments(); 212 Account[] accounts = (Account[]) args.getParcelableArray("accounts"); 213 String curAccount = args.getString("curAccount"); 214 int length = accounts.length; 215 int curAccountOffset = 0; 216 CharSequence[] accountNames = new CharSequence[length]; 217 for (int i = 0; i < length; i++) { 218 String name = accounts[i].name; 219 if (name.equals(curAccount)) { 220 curAccountOffset = i; 221 } 222 accountNames[i] = name; 223 } 224 225 mDialog = new AlertDialog.Builder(getActivity()) 226 .setIcon(android.R.drawable.ic_dialog_alert) 227 .setTitle("Choose account") // STOPSHIP localize 228 .setSingleChoiceItems(accountNames, curAccountOffset, this) 229 .create(); 230 return mDialog; 231 } 232 233 @Override 234 public void onClick(DialogInterface dialog, int which) { 235 String accountName = mDialog.getListView().getAdapter().getItem(which).toString(); 236 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 237 prefs.edit().putString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, accountName).apply(); 238 dismiss(); 239 } 240 } 241 242 public static class ImportWizardDialog extends DialogFragment implements OnClickListener { 243 View mRemoveButton; 244 View mCancelButton; 245 String mDefaultAccount; 246 247 @Override 248 public Dialog onCreateDialog(Bundle savedInstanceState) { 249 Context context = getActivity(); 250 Dialog dialog = new Dialog(context); 251 dialog.setTitle(R.string.import_bookmarks_dialog_title); 252 dialog.setContentView(R.layout.import_bookmarks_dialog); 253 mRemoveButton = dialog.findViewById(R.id.remove); 254 mRemoveButton.setOnClickListener(this); 255 mCancelButton = dialog.findViewById(R.id.cancel); 256 mCancelButton.setOnClickListener(this); 257 258 LayoutInflater inflater = dialog.getLayoutInflater(); 259 LinearLayout accountList = (LinearLayout) dialog.findViewById(R.id.accountList); 260 Account[] accounts = (Account[]) getArguments().getParcelableArray("accounts"); 261 mDefaultAccount = accounts[0].name; 262 int length = accounts.length; 263 for (int i = 0; i < length; i++) { 264 Button button = (Button) inflater.inflate(R.layout.import_bookmarks_dialog_button, 265 null); 266 button.setText(context.getString(R.string.import_bookmarks_dialog_import, 267 accounts[i].name)); 268 button.setTag(accounts[i].name); 269 button.setOnClickListener(this); 270 accountList.addView(button); 271 } 272 273 return dialog; 274 } 275 276 @Override 277 public void onClick(View view) { 278 if (view == mCancelButton) { 279 dismiss(); 280 return; 281 } 282 283 ContentResolver resolver = getActivity().getContentResolver(); 284 SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); 285 String accountName; 286 if (view == mRemoveButton) { 287 // The user chose to remove their old bookmarks, delete them now 288 resolver.delete(Bookmarks.CONTENT_URI, 289 Bookmarks.PARENT + "=1 AND " + Bookmarks.ACCOUNT_NAME + " IS NULL", null); 290 accountName = mDefaultAccount; 291 } else { 292 // The user chose to migrate their old bookmarks to the account they're syncing 293 accountName = view.getTag().toString(); 294 migrateBookmarks(resolver, accountName); 295 } 296 297 // Record the fact that we turned on sync 298 BrowserContract.Settings.setSyncEnabled(getActivity(), true); 299 prefs.edit() 300 .putString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, "com.google") 301 .putString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, accountName) 302 .apply(); 303 304 // Enable bookmark sync on all accounts 305 Account[] accounts = (Account[]) getArguments().getParcelableArray("accounts"); 306 for (Account account : accounts) { 307 if (ContentResolver.getIsSyncable(account, BrowserContract.AUTHORITY) == 0) { 308 // Account wasn't syncable, enable it 309 ContentResolver.setIsSyncable(account, BrowserContract.AUTHORITY, 1); 310 ContentResolver.setSyncAutomatically(account, BrowserContract.AUTHORITY, true); 311 } 312 } 313 314 dismiss(); 315 } 316 317 /** 318 * Migrates bookmarks to the given account 319 */ 320 void migrateBookmarks(ContentResolver resolver, String accountName) { 321 Cursor cursor = null; 322 try { 323 // Re-parent the bookmarks in the default root folder 324 cursor = resolver.query(Bookmarks.CONTENT_URI, new String[] { Bookmarks._ID }, 325 Bookmarks.ACCOUNT_NAME + " =? AND " + 326 ChromeSyncColumns.SERVER_UNIQUE + " =?", 327 new String[] { accountName, 328 ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR }, 329 null); 330 ContentValues values = new ContentValues(); 331 if (cursor == null || !cursor.moveToFirst()) { 332 // The root folders don't exist for the account, create them now 333 ArrayList<ContentProviderOperation> ops = 334 new ArrayList<ContentProviderOperation>(); 335 336 // Chrome sync root folder 337 values.clear(); 338 values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_ROOT); 339 values.put(Bookmarks.TITLE, "Google Chrome"); 340 values.put(Bookmarks.POSITION, 0); 341 values.put(Bookmarks.IS_FOLDER, true); 342 values.put(Bookmarks.DIRTY, true); 343 ops.add(ContentProviderOperation.newInsert( 344 Bookmarks.CONTENT_URI.buildUpon().appendQueryParameter( 345 BrowserContract.CALLER_IS_SYNCADAPTER, "true").build()) 346 .withValues(values) 347 .build()); 348 349 // Bookmarks folder 350 values.clear(); 351 values.put(ChromeSyncColumns.SERVER_UNIQUE, 352 ChromeSyncColumns.FOLDER_NAME_BOOKMARKS); 353 values.put(Bookmarks.TITLE, "Bookmarks"); 354 values.put(Bookmarks.POSITION, 0); 355 values.put(Bookmarks.IS_FOLDER, true); 356 values.put(Bookmarks.DIRTY, true); 357 ops.add(ContentProviderOperation.newInsert(Bookmarks.CONTENT_URI) 358 .withValues(values) 359 .withValueBackReference(Bookmarks.PARENT, 0) 360 .build()); 361 362 // Bookmarks Bar folder 363 values.clear(); 364 values.put(ChromeSyncColumns.SERVER_UNIQUE, 365 ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR); 366 values.put(Bookmarks.TITLE, "Bookmarks Bar"); 367 values.put(Bookmarks.POSITION, 0); 368 values.put(Bookmarks.IS_FOLDER, true); 369 values.put(Bookmarks.DIRTY, true); 370 ops.add(ContentProviderOperation.newInsert(Bookmarks.CONTENT_URI) 371 .withValues(values) 372 .withValueBackReference(Bookmarks.PARENT, 1) 373 .build()); 374 375 // Other Bookmarks folder 376 values.clear(); 377 values.put(ChromeSyncColumns.SERVER_UNIQUE, 378 ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS); 379 values.put(Bookmarks.TITLE, "Other Bookmarks"); 380 values.put(Bookmarks.POSITION, 1000); 381 values.put(Bookmarks.IS_FOLDER, true); 382 values.put(Bookmarks.DIRTY, true); 383 ops.add(ContentProviderOperation.newInsert(Bookmarks.CONTENT_URI) 384 .withValues(values) 385 .withValueBackReference(Bookmarks.PARENT, 1) 386 .build()); 387 388 // Re-parent the existing bookmarks to the newly create bookmarks bar folder 389 ops.add(ContentProviderOperation.newUpdate(Bookmarks.CONTENT_URI) 390 .withValueBackReference(Bookmarks.PARENT, 2) 391 .withSelection(Bookmarks.PARENT + "=?", 392 new String[] { Integer.toString(1) }) 393 .build()); 394 395 // Mark all non-root folder items as belonging to the new account 396 values.clear(); 397 values.put(Bookmarks.ACCOUNT_TYPE, "com.google"); 398 values.put(Bookmarks.ACCOUNT_NAME, accountName); 399 ops.add(ContentProviderOperation.newUpdate(Bookmarks.CONTENT_URI) 400 .withValues(values) 401 .withSelection(Bookmarks.ACCOUNT_NAME + " IS NULL AND " + 402 Bookmarks._ID + "<>1", null) 403 .build()); 404 405 try { 406 resolver.applyBatch(BrowserContract.AUTHORITY, ops); 407 } catch (RemoteException e) { 408 Log.e(TAG, "failed to create root folder for account " + accountName, e); 409 return; 410 } catch (OperationApplicationException e) { 411 Log.e(TAG, "failed to create root folder for account " + accountName, e); 412 return; 413 } 414 } else { 415 values.put(Bookmarks.PARENT, cursor.getLong(0)); 416 resolver.update(Bookmarks.CONTENT_URI, values, Bookmarks.PARENT + "=?", 417 new String[] { Integer.toString(1) }); 418 419 // Mark all bookmarks at all levels as part of the new account 420 values.clear(); 421 values.put(Bookmarks.ACCOUNT_TYPE, "com.google"); 422 values.put(Bookmarks.ACCOUNT_NAME, accountName); 423 resolver.update(Bookmarks.CONTENT_URI, values, 424 Bookmarks.ACCOUNT_NAME + " IS NULL AND " + Bookmarks._ID + "<>1", 425 null); 426 } 427 } finally { 428 if (cursor != null) cursor.close(); 429 } 430 } 431 } 432} 433