diff options
-rw-r--r-- | AndroidManifest.xml | 6 | ||||
-rw-r--r-- | res/layout/bookmarks.xml | 26 | ||||
-rw-r--r-- | res/layout/import_bookmarks_dialog.xml | 47 | ||||
-rw-r--r-- | res/layout/import_bookmarks_dialog_button.xml | 22 | ||||
-rw-r--r-- | res/values/strings.xml | 30 | ||||
-rw-r--r-- | res/xml/personal_preferences.xml | 24 | ||||
-rw-r--r-- | res/xml/preference_headers.xml | 10 | ||||
-rw-r--r-- | src/com/android/browser/AddBookmarkPage.java | 8 | ||||
-rw-r--r-- | src/com/android/browser/BrowserBookmarksPage.java | 50 | ||||
-rw-r--r-- | src/com/android/browser/BrowserSettings.java | 1 | ||||
-rw-r--r-- | src/com/android/browser/preferences/PersonalPreferencesFragment.java | 347 | ||||
-rw-r--r-- | src/com/android/browser/provider/BrowserProvider2.java | 150 |
12 files changed, 571 insertions, 150 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml index ee77294..58ce133 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -25,15 +25,17 @@ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> - <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> + <uses-permission android:name="android.permission.GET_ACCOUNTS"/> <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" /> <uses-permission android:name="android.permission.SET_WALLPAPER" /> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> + <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/> <uses-permission android:name="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"/> - <uses-permission android:name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" /> + <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> <application android:name="Browser" android:label="@string/application_name" diff --git a/res/layout/bookmarks.xml b/res/layout/bookmarks.xml index f5c9331..d90620c 100644 --- a/res/layout/bookmarks.xml +++ b/res/layout/bookmarks.xml @@ -25,29 +25,11 @@ android:orientation="vertical" > - <LinearLayout - android:layout_width="match_parent" + <Button android:id="@+id/up" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:orientation="horizontal" - > - <Button android:id="@+id/up" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:text="@string/defaultBookmarksUpButton" - /> - - <!-- space --> - <View - android:layout_width="0dip" - android:layout_height="0dip" - android:layout_weight="1" - /> - - <Spinner android:id="@+id/accounts" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - /> - </LinearLayout> + android:text="@string/defaultBookmarksUpButton" + /> <GridView android:id="@+id/grid" android:layout_width="match_parent" diff --git a/res/layout/import_bookmarks_dialog.xml b/res/layout/import_bookmarks_dialog.xml new file mode 100644 index 0000000..f10f35d --- /dev/null +++ b/res/layout/import_bookmarks_dialog.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="8dip" +> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/import_bookmarks_dialog_description" + /> + + <LinearLayout android:id="@+id/accountList" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + /> + + <Button android:id="@+id/remove" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/import_bookmarks_dialog_remove" + android:maxLines="2" + /> + + <Button android:id="@+id/cancel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@android:string/cancel" + /> +</LinearLayout>
\ No newline at end of file diff --git a/res/layout/import_bookmarks_dialog_button.xml b/res/layout/import_bookmarks_dialog_button.xml new file mode 100644 index 0000000..cf8f628 --- /dev/null +++ b/res/layout/import_bookmarks_dialog_button.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<Button xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/import_bookmarks_dialog_remove" + android:maxLines="2" +/> diff --git a/res/values/strings.xml b/res/values/strings.xml index 65f555c..33e30b0 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -322,6 +322,25 @@ <string name="pref_content_landscape_only">Landscape-only display</string> <!-- Settings summary --> <string name="pref_content_landscape_only_summary">Display pages only in the wider, landscape screen orientation</string> + + <!-- Settings screen & section title for "Personal settings". These include things like + configuring bookmark syncing to Google servers and form auto fill settings. [CHAR-LIMIT=32] --> + <string name="pref_personal_title">Personal settings</string> + <!-- Checkbox setting to enable or disable syncing bookmarks and other data with Google Chrome. [CHAR-LIMIT=48] --> + <string name="pref_personal_sync_with_chrome">Sync with Google Chrome</string> + <!-- Checkbox setting to enable or disable syncing bookmarks and other data with Google Chrome. [CHAR-LIMIT=none] --> + <string name="pref_personal_sync_with_chrome_summary">Share bookmarks & other data between Android Browser and Google Chrome</string> + <!-- Label indicating which Google account is being used to sync bookmarks between Android and Chrome [CHAR-LIMIT=20] --> + <string name="pref_personal_google_account">Google account</string> + <!-- Checkbox setting to enable or disable syncing bookmarks with Google Chrome. [CHAR-LIMIT=32] --> + <string name="pref_personal_sync_bookmarks">Sync bookmarks</string> + <!-- Summary for a checkbox setting to enable or disable syncing bookmarks with Google Chrome. [CHAR-LIMIT=none] --> + <string name="pref_personal_sync_bookmarks_summary">Sync bookmarks between Android Browser and Google Chrome</string> + <!-- Button to start a sync of bookmarks and other data between the Android Browser and Google Chrome [CHAR-LIMIT=20] --> + <string name="pref_personal_start_syncing">Start syncing</string> + <!-- Dialog title used when asking the user which Google account they want to use to sync data between Android Browser and Google Chrome [CHAR-LIMIT=20] --> + <string name="pref_personal_account_dialog_title">Select Google account to share with</string> + <!-- Settings screen, section title --> <string name="pref_privacy_title">Privacy settings</string> <!-- Settings label --> @@ -763,4 +782,15 @@ <!-- Access point for RLZ tracking. --> <string name="rlz_access_point">Y1</string> + <!-- Title for a dialog asking the user what they want to do with their bookmarks when adding a sync account [CHAR-LIMIT=32] --> + <string name="import_bookmarks_dialog_title">Sync with Google account</string> + + <!-- Description for a dialog asking the user what they want to do with their bookmarks when adding a sync account [CHAR-LIMIT=none] --> + <string name="import_bookmarks_dialog_description">Your Android bookmarks are not associated with a Google account</string> + + <!-- Button allowing users to remove all of their existing bookmarks when setting up syncing with their bookmarks stored in Google Chrome [CHAR-LIMIT=64] --> + <string name="import_bookmarks_dialog_remove">Remove your Android bookmarks</string> + + <!-- Button allowing users to import all of their existing bookmarks into an account when setting up syncing with their bookmarks stored in Google Chrome [CHAR-LIMIT=64] --> + <string name="import_bookmarks_dialog_import">Add your Android bookmarks to bookmarks for <xliff:g id="Google account" example="account@example.com">%s</xliff:g></string> </resources> diff --git a/res/xml/personal_preferences.xml b/res/xml/personal_preferences.xml new file mode 100644 index 0000000..14b057c --- /dev/null +++ b/res/xml/personal_preferences.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> + + <Preference android:key="sync_with_chrome" + android:title="@string/pref_personal_sync_with_chrome" + android:summary="@string/pref_personal_sync_with_chrome_summary" + /> + +</PreferenceScreen> diff --git a/res/xml/preference_headers.xml b/res/xml/preference_headers.xml index 54660b4..1a54990 100644 --- a/res/xml/preference_headers.xml +++ b/res/xml/preference_headers.xml @@ -19,11 +19,11 @@ <header android:fragment="com.android.browser.preferences.PageContentPreferencesFragment" android:title="@string/pref_content_title" /> -<!-- - <header android:fragment="com.example.android.apis.preference.PreferenceWithHeaders$Prefs2Fragment" - android:title="Personal settings" - </header> ---> + + <header android:fragment="com.android.browser.preferences.PersonalPreferencesFragment" + android:title="@string/pref_personal_title" + /> + <header android:fragment="com.android.browser.preferences.PrivacyPreferencesFragment" android:title="@string/pref_privacy_title" /> diff --git a/src/com/android/browser/AddBookmarkPage.java b/src/com/android/browser/AddBookmarkPage.java index 77f8ea8..5da4d2a 100644 --- a/src/com/android/browser/AddBookmarkPage.java +++ b/src/com/android/browser/AddBookmarkPage.java @@ -213,7 +213,7 @@ public class AddBookmarkPage extends Activity BrowserContract.Bookmarks.TITLE); int parentIndex = cursor.getColumnIndexOrThrow( BrowserContract.Bookmarks.PARENT); - while (parent != BrowserProvider2.FIXED_ID_BOOKMARKS_BAR) { + while (parent != BrowserProvider2.FIXED_ID_ROOT) { // First, find the folder corresponding to the current // folder if (!cursor.moveToFirst()) { @@ -350,7 +350,7 @@ public class AddBookmarkPage extends Activity list.setAdapter(mAdapter); list.setOnItemClickListener(this); LoaderManager manager = getLoaderManager(); - if (mCurrentFolder != BrowserProvider2.FIXED_ID_BOOKMARKS_BAR) { + if (mCurrentFolder != BrowserProvider2.FIXED_ID_ROOT) { // Find all the folders manager.initLoader(LOADER_ID_ALL_FOLDERS, null, this); } @@ -371,7 +371,7 @@ public class AddBookmarkPage extends Activity String accountType = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, null); if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { - return BrowserProvider2.FIXED_ID_BOOKMARKS_BAR; + return BrowserProvider2.FIXED_ID_ROOT; } Cursor cursor = null; try { @@ -393,7 +393,7 @@ public class AddBookmarkPage extends Activity } finally { if (cursor != null) cursor.close(); } - return BrowserProvider2.FIXED_ID_BOOKMARKS_BAR; + return BrowserProvider2.FIXED_ID_ROOT; } @Override diff --git a/src/com/android/browser/BrowserBookmarksPage.java b/src/com/android/browser/BrowserBookmarksPage.java index 4a089fb..4630d4e 100644 --- a/src/com/android/browser/BrowserBookmarksPage.java +++ b/src/com/android/browser/BrowserBookmarksPage.java @@ -54,13 +54,10 @@ import android.widget.Adapter; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.GridView; -import android.widget.Spinner; import android.widget.Toast; -import java.util.ArrayList; import java.util.Stack; /** @@ -74,8 +71,7 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte static final String LOGTAG = "browser"; static final int LOADER_BOOKMARKS = 1; - static final int LOADER_ACCOUNTS = 2; - static final int LOADER_ACCOUNTS_THEN_BOOKMARKS = 3; + static final int LOADER_ACCOUNTS_THEN_BOOKMARKS = 2; static final String EXTRA_SHORTCUT = "create_shortcut"; static final String EXTRA_DISABLE_WINDOW = "disable_new_window"; @@ -89,7 +85,6 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte BookmarksHistoryCallbacks mCallbacks; GridView mGrid; - Spinner mAccountSelector; BrowserBookmarksAdapter mAdapter; boolean mDisableNewWindow; BookmarkItem mContextHeader; @@ -113,7 +108,6 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte return new BookmarksLoader(getActivity(), accountType, accountName); } - case LOADER_ACCOUNTS: case LOADER_ACCOUNTS_THEN_BOOKMARKS: { return new CursorLoader(getActivity(), Accounts.CONTENT_URI, new String[] { Accounts.ACCOUNT_TYPE, Accounts.ACCOUNT_NAME }, null, null, @@ -156,7 +150,6 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte break; } - case LOADER_ACCOUNTS: case LOADER_ACCOUNTS_THEN_BOOKMARKS: { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences( getActivity()); @@ -172,7 +165,6 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte // No accounts, set the prefs to the default accountType = DEFAULT_ACCOUNT; accountName = DEFAULT_ACCOUNT; - mAccountSelector.setVisibility(View.GONE); } else { int accountPosition = -1; @@ -190,40 +182,22 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte } if (accountPosition == -1) { - if ((DEFAULT_ACCOUNT.equals(accountType) + if (!(DEFAULT_ACCOUNT.equals(accountType) && DEFAULT_ACCOUNT.equals(accountName))) { - // The "unsynced" account is selected - accountPosition = cursor.getCount(); - } else { // No account is set in prefs and there is at least one, // so pick the first one as the default cursor.moveToFirst(); accountType = cursor.getString(0); accountName = cursor.getString(1); - accountPosition = 0; } } args = new Bundle(); args.putString(BookmarksLoader.ARG_ACCOUNT_TYPE, accountType); args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName); - - // Add in the sync accounts - ArrayList<String> accounts = new ArrayList<String>(); - cursor.moveToFirst(); - do { - accounts.add(cursor.getString(1)); - } while (cursor.moveToNext()); - - // STOPSHIP: Add in the "unsynced" account temporarily until we - // have support for migrated unsynced bookmarks into sync accounts. - accounts.add(ACCOUNT_NAME_UNSYNCED); - - mAccountSelector.setAdapter(new ArrayAdapter<String>(getActivity(), - android.R.layout.simple_list_item_1, android.R.id.text1, accounts)); - mAccountSelector.setVisibility(View.VISIBLE); - mAccountSelector.setSelection(accountPosition); } + + // The stored account name wasn't found, update the stored account with a valid one if (!accountType.equals(storedAccountType) || !accountName.equals(storedAccountName)) { prefs.edit() @@ -231,9 +205,7 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte .putString(PREF_ACCOUNT_NAME, accountName) .apply(); } - if (loader.getId() == LOADER_ACCOUNTS_THEN_BOOKMARKS) { - getLoaderManager().initLoader(LOADER_BOOKMARKS, args, this); - } + getLoaderManager().initLoader(LOADER_BOOKMARKS, args, this); break; } @@ -395,10 +367,6 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte mGrid.setOnCreateContextMenuListener(this); } - mAccountSelector = (Spinner) root.findViewById(R.id.accounts); - mAccountSelector.setOnItemSelectedListener(this); - mAccountSelector.setVisibility(View.INVISIBLE); - mUpButton = (Button) root.findViewById(R.id.up); mUpButton.setEnabled(false); mUpButton.setOnClickListener(this); @@ -410,8 +378,8 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte // Start the loaders LoaderManager lm = getLoaderManager(); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String accountType = prefs.getString(PREF_ACCOUNT_TYPE, null); - String accountName = prefs.getString(PREF_ACCOUNT_NAME, null); + String accountType = prefs.getString(PREF_ACCOUNT_TYPE, DEFAULT_ACCOUNT); + String accountName = prefs.getString(PREF_ACCOUNT_NAME, DEFAULT_ACCOUNT); if (!TextUtils.isEmpty(accountType) && !TextUtils.isEmpty(accountName)) { // There is an account set, load up that one Bundle args = null; @@ -421,9 +389,8 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte args.putString(BookmarksLoader.ARG_ACCOUNT_NAME, accountName); } lm.restartLoader(LOADER_BOOKMARKS, args, this); - lm.restartLoader(LOADER_ACCOUNTS, null, this); } else { - // No account set, load them first + // No account set, load the account list first lm.restartLoader(LOADER_ACCOUNTS_THEN_BOOKMARKS, null, this); } @@ -624,6 +591,7 @@ public class BrowserBookmarksPage extends Fragment implements View.OnCreateConte cursor.getString(BookmarksLoader.COLUMN_INDEX_TITLE))) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { resolver.delete(uri, null, null); } diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java index 8860bbd..8cd001e 100644 --- a/src/com/android/browser/BrowserSettings.java +++ b/src/com/android/browser/BrowserSettings.java @@ -592,6 +592,7 @@ public class BrowserSettings extends Observable { SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(ctx); p.edit().clear().apply(); PreferenceManager.setDefaultValues(ctx, R.xml.page_content_preferences, true); + PreferenceManager.setDefaultValues(ctx, R.xml.personal_preferences, true); PreferenceManager.setDefaultValues(ctx, R.xml.privacy_preferences, true); PreferenceManager.setDefaultValues(ctx, R.xml.security_preferences, true); PreferenceManager.setDefaultValues(ctx, R.xml.advanced_preferences, true); diff --git a/src/com/android/browser/preferences/PersonalPreferencesFragment.java b/src/com/android/browser/preferences/PersonalPreferencesFragment.java new file mode 100644 index 0000000..12751c5 --- /dev/null +++ b/src/com/android/browser/preferences/PersonalPreferencesFragment.java @@ -0,0 +1,347 @@ +/* + * Copyright (C) 2010 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.preferences; + +import com.android.browser.BrowserBookmarksPage; +import com.android.browser.R; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.app.Fragment; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.DialogInterface; +import android.content.OperationApplicationException; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.os.Bundle; +import android.os.RemoteException; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.provider.BrowserContract; +import android.provider.BrowserContract.Bookmarks; +import android.provider.BrowserContract.ChromeSyncColumns; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.LinearLayout; + +import java.util.ArrayList; + +public class PersonalPreferencesFragment extends PreferenceFragment + implements OnPreferenceClickListener { + static final String TAG = "PersonalPreferencesFragment"; + + static final String PREF_CHROME_SYNC = "sync_with_chrome"; + + Preference mChromeSync; + boolean mEnabled; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the XML preferences file + addPreferencesFromResource(R.xml.personal_preferences); + } + + @Override + public void onResume() { + super.onResume(); + + // Setup the proper state for the sync with chrome item + Context context = getActivity(); + mChromeSync = findPreference(PREF_CHROME_SYNC); + refreshUi(context); + } + + void refreshUi(Context context) { + AccountManager am = (AccountManager) context.getSystemService(Context.ACCOUNT_SERVICE); + Account[] accounts = am.getAccountsByType("com.google"); + if (accounts == null || accounts.length == 0) { + // No Google accounts setup, don't offer Chrome sync + getPreferenceScreen().removePreference(mChromeSync); + } else { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); + Bundle args = mChromeSync.getExtras(); + args.putParcelableArray("accounts", accounts); + mEnabled = BrowserContract.Settings.isSyncEnabled(context); + if (!mEnabled) { + // Google accounts are present, but Chrome sync isn't enabled yet. + // Setup a link to the enable wizard + mChromeSync.setSummary(R.string.pref_personal_sync_with_chrome_summary); + } else { + // Chrome sync is enabled, setup a link to account switcher + String accountName = prefs.getString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, null); + mChromeSync.setSummary(accountName); + args.putString("curAccount", accountName); + } + mChromeSync.setOnPreferenceClickListener(this); + } + } + + @Override + public boolean onPreferenceClick(Preference preference) { + Fragment frag; + if (mEnabled) { + frag = new AccountChooserDialog(); + } else { + frag = new ImportWizardDialog(); + } + frag.setArguments(preference.getExtras()); + getFragmentManager().openTransaction() + .add(frag, null) + .commit(); + return true; + } + + final class AccountChooserDialog extends DialogFragment + implements DialogInterface.OnClickListener { + + AlertDialog mDialog; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle args = getArguments(); + Account[] accounts = (Account[]) args.getParcelableArray("accounts"); + String curAccount = args.getString("curAccount"); + int length = accounts.length; + int curAccountOffset = 0; + CharSequence[] accountNames = new CharSequence[length]; + for (int i = 0; i < length; i++) { + String name = accounts[i].name; + if (name.equals(curAccount)) { + curAccountOffset = i; + } + accountNames[i] = name; + } + + mDialog = new AlertDialog.Builder(getActivity()) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle("Choose account") // STOPSHIP localize + .setSingleChoiceItems(accountNames, curAccountOffset, this) + .create(); + return mDialog; + } + + @Override + public void onClick(DialogInterface dialog, int which) { + String accountName = mDialog.getListView().getAdapter().getItem(which).toString(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + prefs.edit().putString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, accountName).apply(); + refreshUi(getActivity()); + dismiss(); + } + } + + final class ImportWizardDialog extends DialogFragment implements OnClickListener { + View mRemoveButton; + View mCancelButton; + String mDefaultAccount; + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Context context = getActivity(); + Dialog dialog = new Dialog(context); + dialog.setTitle(R.string.import_bookmarks_dialog_title); + dialog.setContentView(R.layout.import_bookmarks_dialog); + mRemoveButton = dialog.findViewById(R.id.remove); + mRemoveButton.setOnClickListener(this); + mCancelButton = dialog.findViewById(R.id.cancel); + mCancelButton.setOnClickListener(this); + + LayoutInflater inflater = dialog.getLayoutInflater(); + LinearLayout accountList = (LinearLayout) dialog.findViewById(R.id.accountList); + Account[] accounts = (Account[]) getArguments().getParcelableArray("accounts"); + mDefaultAccount = accounts[0].name; + int length = accounts.length; + for (int i = 0; i < length; i++) { + Button button = (Button) inflater.inflate(R.layout.import_bookmarks_dialog_button, + null); + button.setText(context.getString(R.string.import_bookmarks_dialog_import, + accounts[i].name)); + button.setTag(accounts[i].name); + button.setOnClickListener(this); + accountList.addView(button); + } + + return dialog; + } + + @Override + public void onClick(View view) { + if (view == mCancelButton) { + dismiss(); + return; + } + + ContentResolver resolver = getActivity().getContentResolver(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + String accountName; + if (view == mRemoveButton) { + // The user chose to remove their old bookmarks, delete them now + resolver.delete(Bookmarks.CONTENT_URI, + Bookmarks.PARENT + "=1 AND " + Bookmarks.ACCOUNT_NAME + " IS NULL", null); + accountName = mDefaultAccount; + } else { + // The user chose to migrate their old bookmarks to the account they're syncing + accountName = view.getTag().toString(); + migrateBookmarks(resolver, accountName); + } + + // Record the fact that we turned on sync + BrowserContract.Settings.setSyncEnabled(getActivity(), true); + prefs.edit() + .putString(BrowserBookmarksPage.PREF_ACCOUNT_TYPE, "com.google") + .putString(BrowserBookmarksPage.PREF_ACCOUNT_NAME, accountName) + .apply(); + + // Enable bookmark sync on all accounts + Account[] accounts = (Account[]) getArguments().getParcelableArray("accounts"); + for (Account account : accounts) { + ContentResolver.setIsSyncable(account, BrowserContract.AUTHORITY, 1); + } + + refreshUi(getActivity()); + dismiss(); + } + + /** + * Migrates bookmarks to the given account + */ + void migrateBookmarks(ContentResolver resolver, String accountName) { + Cursor cursor = null; + try { + // Re-parent the bookmarks in the default root folder + cursor = resolver.query(Bookmarks.CONTENT_URI, new String[] { Bookmarks._ID }, + Bookmarks.ACCOUNT_NAME + " =? AND " + + ChromeSyncColumns.SERVER_UNIQUE + " =?", + new String[] { accountName, + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR }, + null); + ContentValues values = new ContentValues(); + if (cursor == null || !cursor.moveToFirst()) { + // The root folders don't exist for the account, create them now + ArrayList<ContentProviderOperation> ops = + new ArrayList<ContentProviderOperation>(); + + // Chrome sync root folder + values.clear(); + values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_ROOT); + values.put(Bookmarks.TITLE, "Google Chrome"); + values.put(Bookmarks.POSITION, 0); + values.put(Bookmarks.IS_FOLDER, true); + values.put(Bookmarks.DIRTY, true); + ops.add(ContentProviderOperation.newInsert( + Bookmarks.CONTENT_URI.buildUpon().appendQueryParameter( + BrowserContract.CALLER_IS_SYNCADAPTER, "true").build()) + .withValues(values) + .build()); + + // Bookmarks folder + values.clear(); + values.put(ChromeSyncColumns.SERVER_UNIQUE, + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS); + values.put(Bookmarks.TITLE, "Bookmarks"); + values.put(Bookmarks.POSITION, 0); + values.put(Bookmarks.IS_FOLDER, true); + values.put(Bookmarks.DIRTY, true); + ops.add(ContentProviderOperation.newInsert(Bookmarks.CONTENT_URI) + .withValues(values) + .withValueBackReference(Bookmarks.PARENT, 0) + .build()); + + // Bookmarks Bar folder + values.clear(); + values.put(ChromeSyncColumns.SERVER_UNIQUE, + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR); + values.put(Bookmarks.TITLE, "Bookmarks Bar"); + values.put(Bookmarks.POSITION, 0); + values.put(Bookmarks.IS_FOLDER, true); + values.put(Bookmarks.DIRTY, true); + ops.add(ContentProviderOperation.newInsert(Bookmarks.CONTENT_URI) + .withValues(values) + .withValueBackReference(Bookmarks.PARENT, 1) + .build()); + + // Other Bookmarks folder + values.clear(); + values.put(ChromeSyncColumns.SERVER_UNIQUE, + ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS); + values.put(Bookmarks.TITLE, "Other Bookmarks"); + values.put(Bookmarks.POSITION, 1000); + values.put(Bookmarks.IS_FOLDER, true); + values.put(Bookmarks.DIRTY, true); + ops.add(ContentProviderOperation.newInsert(Bookmarks.CONTENT_URI) + .withValues(values) + .withValueBackReference(Bookmarks.PARENT, 1) + .build()); + + // Re-parent the existing bookmarks to the newly create bookmarks bar folder + ops.add(ContentProviderOperation.newUpdate(Bookmarks.CONTENT_URI) + .withValueBackReference(Bookmarks.PARENT, 2) + .withSelection(Bookmarks.PARENT + "=?", + new String[] { Integer.toString(1) }) + .build()); + + // Mark all non-root folder items as belonging to the new account + values.clear(); + values.put(Bookmarks.ACCOUNT_TYPE, "com.google"); + values.put(Bookmarks.ACCOUNT_NAME, accountName); + ops.add(ContentProviderOperation.newUpdate(Bookmarks.CONTENT_URI) + .withValues(values) + .withSelection(Bookmarks.ACCOUNT_NAME + " IS NULL AND " + + Bookmarks._ID + "<>1", null) + .build()); + + try { + resolver.applyBatch(BrowserContract.AUTHORITY, ops); + } catch (RemoteException e) { + Log.e(TAG, "failed to create root folder for account " + accountName, e); + return; + } catch (OperationApplicationException e) { + Log.e(TAG, "failed to create root folder for account " + accountName, e); + return; + } + } else { + values.put(Bookmarks.PARENT, cursor.getLong(0)); + resolver.update(Bookmarks.CONTENT_URI, values, Bookmarks.PARENT + "=?", + new String[] { Integer.toString(1) }); + + // Mark all bookmarks at all levels as part of the new account + values.clear(); + values.put(Bookmarks.ACCOUNT_TYPE, "com.google"); + values.put(Bookmarks.ACCOUNT_NAME, accountName); + resolver.update(Bookmarks.CONTENT_URI, values, + Bookmarks.ACCOUNT_NAME + " IS NULL AND " + Bookmarks._ID + "<>1", + null); + } + } finally { + if (cursor != null) cursor.close(); + } + } + } +} diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java index e0520eb..b73927a 100644 --- a/src/com/android/browser/provider/BrowserProvider2.java +++ b/src/com/android/browser/provider/BrowserProvider2.java @@ -19,6 +19,7 @@ package com.android.browser.provider; import com.android.browser.R; import com.android.internal.content.SyncStateContentProviderHelper; +import android.accounts.Account; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; @@ -38,6 +39,7 @@ import android.provider.BrowserContract.Combined; import android.provider.BrowserContract.History; import android.provider.BrowserContract.Images; import android.provider.BrowserContract.Searches; +import android.provider.BrowserContract.Settings; import android.provider.BrowserContract.SyncState; import android.provider.ContactsContract.RawContacts; import android.provider.SyncStateContract; @@ -55,6 +57,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { static final String TABLE_IMAGES = "images"; static final String TABLE_SEARCHES = "searches"; static final String TABLE_SYNC_STATE = "syncstate"; + static final String TABLE_SETTINGS = "settings"; static final String VIEW_COMBINED = "combined"; static final String TABLE_BOOKMARKS_JOIN_IMAGES = "bookmarks LEFT OUTER JOIN images " + @@ -87,10 +90,9 @@ public class BrowserProvider2 extends SQLiteContentProvider { static final int ACCOUNTS = 7000; - static final long FIXED_ID_CHROME_ROOT = 1; - static final long FIXED_ID_BOOKMARKS = 2; - public static final long FIXED_ID_BOOKMARKS_BAR = 3; - static final long FIXED_ID_OTHER_BOOKMARKS = 4; + static final int SETTINGS = 8000; + + public static final long FIXED_ID_ROOT = 1; static final String DEFAULT_BOOKMARKS_SORT_ORDER = "position ASC, _id ASC"; @@ -105,6 +107,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { static final HashMap<String, String> IMAGES_PROJECTION_MAP = new HashMap<String, String>(); static final HashMap<String, String> COMBINED_PROJECTION_MAP = new HashMap<String, String>(); static final HashMap<String, String> SEARCHES_PROJECTION_MAP = new HashMap<String, String>(); + static final HashMap<String, String> SETTINGS_PROJECTION_MAP = new HashMap<String, String>(); static { final UriMatcher matcher = URI_MATCHER; @@ -123,6 +126,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { matcher.addURI(authority, "images", IMAGES); matcher.addURI(authority, "combined", COMBINED); matcher.addURI(authority, "combined/#", COMBINED_ID); + matcher.addURI(authority, "settings", SETTINGS); // Projection maps HashMap<String, String> map; @@ -217,7 +221,12 @@ public class BrowserProvider2 extends SQLiteContentProvider { map.put(Searches._ID, Searches._ID); map.put(Searches.SEARCH, Searches.SEARCH); map.put(Searches.DATE, Searches.DATE); -} + + // Settings + map = SETTINGS_PROJECTION_MAP; + map.put(Settings.KEY, Settings.KEY); + map.put(Settings.VALUE, Settings.VALUE); + } static final String bookmarkOrHistoryColumn(String column) { return "CASE WHEN bookmarks." + column + " IS NOT NULL THEN " + @@ -233,7 +242,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { final class DatabaseHelper extends SQLiteOpenHelper { static final String DATABASE_NAME = "browser2.db"; - static final int DATABASE_VERSION = 22; + static final int DATABASE_VERSION = 25; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @@ -292,6 +301,11 @@ public class BrowserProvider2 extends SQLiteContentProvider { Searches.DATE + " LONG" + ");"); + db.execSQL("CREATE TABLE " + TABLE_SETTINGS + " (" + + Settings.KEY + " TEXT PRIMARY KEY," + + Settings.VALUE + " TEXT NOT NULL" + + ");"); + db.execSQL("CREATE VIEW " + VIEW_COMBINED + " AS " + "SELECT " + bookmarkOrHistoryColumn(Combined._ID) + ", " + @@ -333,7 +347,9 @@ public class BrowserProvider2 extends SQLiteContentProvider { db.execSQL("DROP TABLE IF EXISTS " + TABLE_HISTORY); db.execSQL("DROP TABLE IF EXISTS " + TABLE_SEARCHES); db.execSQL("DROP TABLE IF EXISTS " + TABLE_IMAGES); + db.execSQL("DROP TABLE IF EXISTS " + TABLE_SETTINGS); db.execSQL("DROP VIEW IF EXISTS " + VIEW_COMBINED); + mSyncHelper.onAccountsChanged(db, new Account[] {}); // remove all sync info onCreate(db); } @@ -342,71 +358,21 @@ public class BrowserProvider2 extends SQLiteContentProvider { mSyncHelper.onDatabaseOpened(db); } - private void createDefaultBookmarks(SQLiteDatabase db) { ContentValues values = new ContentValues(); // TODO figure out how to deal with localization for the defaults - // Chrome sync root folder - values.put(Bookmarks._ID, FIXED_ID_CHROME_ROOT); - values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_ROOT); - values.put(Bookmarks.TITLE, "Google Chrome"); - values.put(Bookmarks.PARENT, 0); - values.put(Bookmarks.POSITION, 0); - values.put(Bookmarks.IS_FOLDER, true); - values.put(Bookmarks.DIRTY, true); - db.insertOrThrow(TABLE_BOOKMARKS, null, values); - // Bookmarks folder - values.put(Bookmarks._ID, FIXED_ID_BOOKMARKS); + values.put(Bookmarks._ID, FIXED_ID_ROOT); values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_BOOKMARKS); values.put(Bookmarks.TITLE, "Bookmarks"); - values.put(Bookmarks.PARENT, FIXED_ID_CHROME_ROOT); + values.putNull(Bookmarks.PARENT); values.put(Bookmarks.POSITION, 0); values.put(Bookmarks.IS_FOLDER, true); values.put(Bookmarks.DIRTY, true); db.insertOrThrow(TABLE_BOOKMARKS, null, values); - // Bookmarks Bar folder - values.clear(); - values.put(Bookmarks._ID, FIXED_ID_BOOKMARKS_BAR); - values.put(ChromeSyncColumns.SERVER_UNIQUE, - ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR); - values.put(Bookmarks.TITLE, "Bookmarks Bar"); - values.put(Bookmarks.PARENT, FIXED_ID_BOOKMARKS); - values.put(Bookmarks.POSITION, 0); - values.put(Bookmarks.IS_FOLDER, true); - values.put(Bookmarks.DIRTY, true); - db.insertOrThrow(TABLE_BOOKMARKS, null, values); - - // Other Bookmarks folder - values.clear(); - values.put(Bookmarks._ID, FIXED_ID_OTHER_BOOKMARKS); - values.put(ChromeSyncColumns.SERVER_UNIQUE, - ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS); - values.put(Bookmarks.TITLE, "Other Bookmarks"); - values.put(Bookmarks.PARENT, FIXED_ID_BOOKMARKS); - values.put(Bookmarks.POSITION, 1000); - values.put(Bookmarks.IS_FOLDER, true); - values.put(Bookmarks.DIRTY, true); - db.insertOrThrow(TABLE_BOOKMARKS, null, values); - - addDefaultBookmarks(db, FIXED_ID_BOOKMARKS_BAR); - - // TODO remove this testing code - db.execSQL("INSERT INTO bookmarks (" + - Bookmarks.TITLE + ", " + - Bookmarks.URL + ", " + - Bookmarks.IS_FOLDER + "," + - Bookmarks.PARENT + "," + - Bookmarks.POSITION + - ") VALUES (" + - "'Google Reader', " + - "'http://reader.google.com', " + - "0," + - Long.toString(FIXED_ID_OTHER_BOOKMARKS) + "," + - 0 + - ");"); + addDefaultBookmarks(db, FIXED_ID_ROOT); } private void addDefaultBookmarks(SQLiteDatabase db, long parentId) { @@ -615,25 +581,18 @@ public class BrowserProvider2 extends SQLiteContentProvider { } qb.setTables(TABLE_BOOKMARKS_JOIN_IMAGES); - String bookmarksBarQuery; - String otherBookmarksQuery; String[] args; + String query; if (!useAccount) { qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); - bookmarksBarQuery = qb.buildQuery(projection, + query = qb.buildQuery(projection, Bookmarks.PARENT + "=? AND " + Bookmarks.IS_DELETED + "=0", null, null, null, null, null); - qb.setProjectionMap(OTHER_BOOKMARKS_PROJECTION_MAP); - otherBookmarksQuery = qb.buildQuery(projection, - Bookmarks._ID + "=?", - null, null, null, null, null); - - args = new String[] { Long.toString(FIXED_ID_BOOKMARKS_BAR), - Long.toString(FIXED_ID_OTHER_BOOKMARKS) }; + args = new String[] { Long.toString(FIXED_ID_ROOT) }; } else { qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); - bookmarksBarQuery = qb.buildQuery(projection, + String bookmarksBarQuery = qb.buildQuery(projection, Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=? " + "AND parent = " + "(SELECT _id FROM " + TABLE_BOOKMARKS + " WHERE " + @@ -644,20 +603,21 @@ public class BrowserProvider2 extends SQLiteContentProvider { null, null, null, null, null); qb.setProjectionMap(OTHER_BOOKMARKS_PROJECTION_MAP); - otherBookmarksQuery = qb.buildQuery(projection, + String otherBookmarksQuery = qb.buildQuery(projection, Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=?" + " AND " + ChromeSyncColumns.SERVER_UNIQUE + "=?", null, null, null, null, null); + query = qb.buildUnionQuery( + new String[] { bookmarksBarQuery, otherBookmarksQuery }, + DEFAULT_BOOKMARKS_SORT_ORDER, limit); + args = new String[] { accountType, accountName, accountType, accountName, accountType, accountName, ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS, }; } - String query = qb.buildUnionQuery( - new String[] { bookmarksBarQuery, otherBookmarksQuery }, - DEFAULT_BOOKMARKS_SORT_ORDER, limit); return db.rawQuery(query, args); } @@ -721,6 +681,12 @@ public class BrowserProvider2 extends SQLiteContentProvider { break; } + case SETTINGS: { + qb.setTables(TABLE_SETTINGS); + qb.setProjectionMap(SETTINGS_PROJECTION_MAP); + break; + } + default: { throw new UnsupportedOperationException("Unknown URL " + uri.toString()); } @@ -801,7 +767,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { long id = -1; switch (match) { case BOOKMARKS: { - // Mark rows dirty if they're not coming from a sync adapater + // Mark rows dirty if they're not coming from a sync adapter if (!callerIsSyncAdapter) { long now = System.currentTimeMillis(); values.put(Bookmarks.DATE_CREATED, now); @@ -809,8 +775,9 @@ public class BrowserProvider2 extends SQLiteContentProvider { values.put(Bookmarks.DIRTY, 1); // If no parent is set default to the "Bookmarks Bar" folder + // TODO set the parent based on the account info if (!values.containsKey(Bookmarks.PARENT)) { - values.put(Bookmarks.PARENT, FIXED_ID_BOOKMARKS_BAR); + values.put(Bookmarks.PARENT, FIXED_ID_ROOT); } } @@ -863,6 +830,12 @@ public class BrowserProvider2 extends SQLiteContentProvider { break; } + case SETTINGS: { + id = 0; + insertSettingsInTransaction(db, values); + break; + } + default: { throw new UnsupportedOperationException("Unknown insert URI " + uri); } @@ -900,6 +873,31 @@ public class BrowserProvider2 extends SQLiteContentProvider { } } + /** + * Settings are unique, so perform an UPSERT manually since SQLite doesn't support them. + */ + private long insertSettingsInTransaction(SQLiteDatabase db, ContentValues values) { + String key = values.getAsString(Settings.KEY); + if (TextUtils.isEmpty(key)) { + throw new IllegalArgumentException("Must include the KEY field"); + } + String[] keyArray = new String[] { key }; + Cursor cursor = null; + try { + cursor = db.query(TABLE_SETTINGS, new String[] { Settings.KEY }, + Settings.KEY + "=?", keyArray, null, null, null); + if (cursor.moveToNext()) { + long id = cursor.getLong(0); + db.update(TABLE_SETTINGS, values, Settings.KEY + "=?", keyArray); + return id; + } else { + return db.insertOrThrow(TABLE_SETTINGS, Settings.VALUE, values); + } + } finally { + if (cursor != null) cursor.close(); + } + } + @Override public int updateInTransaction(Uri uri, ContentValues values, String selection, String[] selectionArgs, boolean callerIsSyncAdapter) { |