Add settings to enable bookmarks sync.
Change-Id: I86bc4ce9d21ec0a040322bd704cef46eaa17dc9c
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 @@
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 @@
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 @@
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 @@
} 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.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 @@
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 @@
BookmarksHistoryCallbacks mCallbacks;
GridView mGrid;
- Spinner mAccountSelector;
BrowserBookmarksAdapter mAdapter;
boolean mDisableNewWindow;
BookmarkItem mContextHeader;
@@ -113,7 +108,6 @@
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 @@
break;
}
- case LOADER_ACCOUNTS:
case LOADER_ACCOUNTS_THEN_BOOKMARKS: {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
getActivity());
@@ -172,7 +165,6 @@
// 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 @@
}
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 @@
.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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
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 @@
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 @@
break;
}
+ case SETTINGS: {
+ id = 0;
+ insertSettingsInTransaction(db, values);
+ break;
+ }
+
default: {
throw new UnsupportedOperationException("Unknown insert URI " + uri);
}
@@ -900,6 +873,31 @@
}
}
+ /**
+ * 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) {