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