diff options
Diffstat (limited to 'src/com/android/browser/preferences')
7 files changed, 1804 insertions, 0 deletions
diff --git a/src/com/android/browser/preferences/AdvancedPreferencesFragment.java b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java new file mode 100644 index 000000000..e2e45f5d7 --- /dev/null +++ b/src/com/android/browser/preferences/AdvancedPreferencesFragment.java @@ -0,0 +1,181 @@ +/* + * 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.BrowserActivity; +import com.android.browser.BrowserSettings; +import com.android.browser.R; + +import android.content.Intent; +import android.content.res.Resources; +import android.os.Bundle; +import android.preference.ListPreference; +import android.preference.Preference; +import android.preference.PreferenceFragment; +import android.preference.PreferenceScreen; +import android.util.Log; +import android.webkit.GeolocationPermissions; +import android.webkit.ValueCallback; +import android.webkit.WebStorage; + +import java.util.Map; +import java.util.Set; + +public class AdvancedPreferencesFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the XML preferences file + addPreferencesFromResource(R.xml.advanced_preferences); + + PreferenceScreen websiteSettings = (PreferenceScreen) findPreference( + BrowserSettings.PREF_WEBSITE_SETTINGS); + websiteSettings.setFragment(WebsiteSettingsFragment.class.getName()); + + Preference e = findPreference(BrowserSettings.PREF_TEXT_SIZE); + e.setOnPreferenceChangeListener(this); + e.setSummary(getVisualTextSizeName( + getPreferenceScreen().getSharedPreferences() + .getString(BrowserSettings.PREF_TEXT_SIZE, null)) ); + + e = findPreference(BrowserSettings.PREF_DEFAULT_ZOOM); + e.setOnPreferenceChangeListener(this); + e.setSummary(getVisualDefaultZoomName( + getPreferenceScreen().getSharedPreferences() + .getString(BrowserSettings.PREF_DEFAULT_ZOOM, null)) ); + + e = findPreference(BrowserSettings.PREF_DEFAULT_TEXT_ENCODING); + e.setOnPreferenceChangeListener(this); + + e = findPreference(BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS); + e.setOnPreferenceChangeListener(this); + + e = findPreference(BrowserSettings.PREF_PLUGIN_STATE); + e.setOnPreferenceChangeListener(this); + updatePluginSummary((ListPreference) e); + } + + void updatePluginSummary(ListPreference e) { + e.setSummary(e.getEntry()); + } + + /* + * We need to set the PreferenceScreen state in onResume(), as the number of + * origins with active features (WebStorage, Geolocation etc) could have + * changed after calling the WebsiteSettingsActivity. + */ + @Override + public void onResume() { + super.onResume(); + final PreferenceScreen websiteSettings = (PreferenceScreen) findPreference( + BrowserSettings.PREF_WEBSITE_SETTINGS); + websiteSettings.setEnabled(false); + WebStorage.getInstance().getOrigins(new ValueCallback<Map>() { + @Override + public void onReceiveValue(Map webStorageOrigins) { + if ((webStorageOrigins != null) && !webStorageOrigins.isEmpty()) { + websiteSettings.setEnabled(true); + } + } + }); + GeolocationPermissions.getInstance().getOrigins(new ValueCallback<Set<String> >() { + @Override + public void onReceiveValue(Set<String> geolocationOrigins) { + if ((geolocationOrigins != null) && !geolocationOrigins.isEmpty()) { + websiteSettings.setEnabled(true); + } + } + }); + } + + @Override + public boolean onPreferenceChange(Preference pref, Object objValue) { + if (getActivity() == null) { + // We aren't attached, so don't accept preferences changes from the + // invisible UI. + Log.w("PageContentPreferencesFragment", "onPreferenceChange called from detached fragment!"); + return false; + } + + if (pref.getKey().equals(BrowserSettings.PREF_TEXT_SIZE)) { + pref.setSummary(getVisualTextSizeName((String) objValue)); + return true; + } else if (pref.getKey().equals(BrowserSettings.PREF_DEFAULT_ZOOM)) { + pref.setSummary(getVisualDefaultZoomName((String) objValue)); + return true; + } else if (pref.getKey().equals(BrowserSettings.PREF_DEFAULT_TEXT_ENCODING)) { + pref.setSummary((String) objValue); + return true; + } else if (pref.getKey().equals(BrowserSettings.PREF_EXTRAS_RESET_DEFAULTS)) { + Boolean value = (Boolean) objValue; + if (value.booleanValue() == true) { + startActivity(new Intent(BrowserActivity.ACTION_RESTART, null, + getActivity(), BrowserActivity.class)); + return true; + } + } else if (pref.getKey().equals(BrowserSettings.PREF_PLUGIN_STATE)) { + ListPreference lp = (ListPreference) pref; + lp.setValue((String) objValue); + updatePluginSummary(lp); + return false; + } + return false; + } + + private CharSequence getVisualTextSizeName(String enumName) { + Resources res = getActivity().getResources(); + CharSequence[] visualNames = res.getTextArray(R.array.pref_text_size_choices); + CharSequence[] enumNames = res.getTextArray(R.array.pref_text_size_values); + + // Sanity check + if (visualNames.length != enumNames.length) { + return ""; + } + + int length = enumNames.length; + for (int i = 0; i < length; i++) { + if (enumNames[i].equals(enumName)) { + return visualNames[i]; + } + } + + return ""; + } + + private CharSequence getVisualDefaultZoomName(String enumName) { + Resources res = getActivity().getResources(); + CharSequence[] visualNames = res.getTextArray(R.array.pref_default_zoom_choices); + CharSequence[] enumNames = res.getTextArray(R.array.pref_default_zoom_values); + + // Sanity check + if (visualNames.length != enumNames.length) { + return ""; + } + + int length = enumNames.length; + for (int i = 0; i < length; i++) { + if (enumNames[i].equals(enumName)) { + return visualNames[i]; + } + } + + return ""; + } +}
\ No newline at end of file diff --git a/src/com/android/browser/preferences/DebugPreferencesFragment.java b/src/com/android/browser/preferences/DebugPreferencesFragment.java new file mode 100644 index 000000000..0a823716e --- /dev/null +++ b/src/com/android/browser/preferences/DebugPreferencesFragment.java @@ -0,0 +1,61 @@ +/* + * 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.BrowserActivity; +import com.android.browser.BrowserSettings; +import com.android.browser.Controller; +import com.android.browser.R; + +import android.content.Context; +import android.content.Intent; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceActivity.Header; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager.OnActivityResultListener; + +import java.io.IOException; +import java.io.Serializable; + +public class DebugPreferencesFragment extends PreferenceFragment + implements OnPreferenceChangeListener { + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the XML preferences file + addPreferencesFromResource(R.xml.debug_preferences); + + if (BrowserSettings.getInstance().showDebugSettings()) { + addPreferencesFromResource(R.xml.hidden_debug_preferences); + } + + Preference e = findPreference(BrowserSettings.PREF_HARDWARE_ACCEL); + e.setOnPreferenceChangeListener(this); + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + // Attempt to restart + startActivity(new Intent(BrowserActivity.ACTION_RESTART, null, + getActivity(), BrowserActivity.class)); + return true; + } +} diff --git a/src/com/android/browser/preferences/GeneralPreferencesFragment.java b/src/com/android/browser/preferences/GeneralPreferencesFragment.java new file mode 100644 index 000000000..0c63ab5e1 --- /dev/null +++ b/src/com/android/browser/preferences/GeneralPreferencesFragment.java @@ -0,0 +1,263 @@ +/* + * 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.BrowserHomepagePreference; +import com.android.browser.BrowserPreferencesPage; +import com.android.browser.BrowserSettings; +import com.android.browser.R; +import com.android.browser.widget.BookmarkThumbnailWidgetProvider; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.accounts.AccountManagerCallback; +import android.accounts.AccountManagerFuture; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceClickListener; +import android.preference.PreferenceFragment; +import android.preference.PreferenceManager; +import android.preference.PreferenceScreen; +import android.provider.BrowserContract; +import android.util.Log; + +public class GeneralPreferencesFragment extends PreferenceFragment + implements OnPreferenceClickListener, Preference.OnPreferenceChangeListener { + static final String TAG = "PersonalPreferencesFragment"; + + static final String PREF_CHROME_SYNC = "sync_with_chrome"; + + Preference mChromeSync; + boolean mEnabled; + SharedPreferences mSharedPrefs; + Account[] mAccounts; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + // Load the XML preferences file + addPreferencesFromResource(R.xml.general_preferences); + + Preference e = findPreference(BrowserSettings.PREF_HOMEPAGE); + e.setOnPreferenceChangeListener(this); + e.setSummary(getPreferenceScreen().getSharedPreferences() + .getString(BrowserSettings.PREF_HOMEPAGE, null)); + ((BrowserHomepagePreference) e).setCurrentPage( + getActivity().getIntent().getStringExtra(BrowserPreferencesPage.CURRENT_PAGE)); + mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + mSharedPrefs.registerOnSharedPreferenceChangeListener(mListener); + } + + @Override + public boolean onPreferenceChange(Preference pref, Object objValue) { + if (getActivity() == null) { + // We aren't attached, so don't accept preferences changes from the + // invisible UI. + Log.w("PageContentPreferencesFragment", "onPreferenceChange called from detached fragment!"); + return false; + } + + if (pref.getKey().equals(BrowserSettings.PREF_HOMEPAGE)) { + pref.setSummary((String) objValue); + return true; + } + + return false; + } + + @Override + public void onResume() { + super.onResume(); + + // Setup the proper state for the sync with chrome item + mChromeSync = findPreference(PREF_CHROME_SYNC); + refreshUi(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + + mSharedPrefs.unregisterOnSharedPreferenceChangeListener(mListener); + } + + OnSharedPreferenceChangeListener mListener + = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + if (BrowserBookmarksPage.PREF_ACCOUNT_NAME.equals(key) + || BrowserBookmarksPage.PREF_ACCOUNT_TYPE.equals(key)) { + refreshUi(); + BookmarkThumbnailWidgetProvider.refreshWidgets(getActivity(), true); + } + } + + }; + + private AccountManagerCallback<Bundle> mCallback = + new AccountManagerCallback<Bundle>() { + + @Override + public void run(AccountManagerFuture<Bundle> future) { + try { + Bundle bundle = future.getResult(); + String name = bundle.getString(AccountManager.KEY_ACCOUNT_NAME); + String type = bundle.getString(AccountManager.KEY_ACCOUNT_TYPE); + Account account = new Account(name, type); + mAccounts = new Account[] { account }; + ImportWizard wizard = ImportWizard.newInstance(mAccounts); + wizard.show(getFragmentManager(), null); + } catch (Exception ex) { + // Canceled or failed to login, doesn't matter to us + } + } + }; + + OnPreferenceClickListener mAddAccount = new OnPreferenceClickListener() { + + @Override + public boolean onPreferenceClick(Preference preference) { + AccountManager am = AccountManager.get(getActivity()); + am.addAccount("com.google", null, null, null, getActivity(), + mCallback, null); + return true; + } + }; + + private class GetAccountsTask extends AsyncTask<Void, Void, String> { + private Context mContext; + + GetAccountsTask(Context ctx) { + mContext = ctx; + } + + @Override + protected String doInBackground(Void... unused) { + AccountManager am = (AccountManager) mContext.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 + if (mChromeSync != null) { + mChromeSync.setOnPreferenceClickListener(mAddAccount); + } + } else { + // Google accounts are present. + mAccounts = accounts; + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext); + Bundle args = mChromeSync.getExtras(); + args.putParcelableArray("accounts", accounts); + mEnabled = BrowserContract.Settings.isSyncEnabled(mContext); + mChromeSync.setOnPreferenceClickListener(GeneralPreferencesFragment.this); + + if (!mEnabled) { + // Setup a link to the enable wizard + return mContext.getResources().getString( + 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); + args.putString("curAccount", accountName); + return accountName; + } + } + + return null; + } + + @Override + protected void onPostExecute(String summary) { + if (summary != null) { + mChromeSync.setSummary(summary); + } + } + } + + void refreshUi() { + new GetAccountsTask(getActivity()).execute(); + + PreferenceScreen autoFillSettings = + (PreferenceScreen)findPreference(BrowserSettings.PREF_AUTOFILL_PROFILE); + autoFillSettings.setDependency(BrowserSettings.PREF_AUTOFILL_ENABLED); + } + + @Override + public boolean onPreferenceClick(Preference preference) { + if (mAccounts == null) { + Log.w(TAG, "NULL accounts!"); + return true; + } + DialogFragment frag; + if (mEnabled) { + frag = new AccountChooserDialog(); + frag.setArguments(preference.getExtras()); + } else { + frag = ImportWizard.newInstance(mAccounts); + } + frag.show(getFragmentManager(), null); + return true; + } + + public static 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(R.string.account_chooser_dialog_title) + .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(); + dismiss(); + } + } +} diff --git a/src/com/android/browser/preferences/ImportWizard.java b/src/com/android/browser/preferences/ImportWizard.java new file mode 100644 index 000000000..7105f4d56 --- /dev/null +++ b/src/com/android/browser/preferences/ImportWizard.java @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2011 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 com.android.browser.view.EventRedirectingFrameLayout; + +import android.accounts.Account; +import android.animation.LayoutTransition; +import android.animation.ObjectAnimator; +import android.app.AlertDialog; +import android.app.Dialog; +import android.app.DialogFragment; +import android.content.ContentProviderOperation; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.DialogInterface; +import android.content.DialogInterface.OnKeyListener; +import android.content.DialogInterface.OnShowListener; +import android.content.OperationApplicationException; +import android.content.SharedPreferences; +import android.content.res.Resources; +import android.database.Cursor; +import android.os.Bundle; +import android.os.RemoteException; +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.KeyEvent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.ListView; +import android.widget.TextView; + +import java.util.ArrayList; + +public class ImportWizard extends DialogFragment implements OnClickListener, + OnItemClickListener { + + static final String TAG = "BookmarkImportWizard"; + + static final int PAGE_IMPORT_OR_DELETE = 0; + static final int PAGE_SELECT_ACCOUNT = 1; + static final int PAGE_CONFIRMATION = 2; + + static final String STATE_CURRENT_PAGE = "wizard.current_page"; + static final String STATE_IMPORT_OR_DELETE = "wizard.import_or_delete"; + static final String STATE_SELECTED_ACCOUNT = "wizard.selected_account"; + + static final String ARG_ACCOUNTS = "accounts"; + + AlertDialog mDialog; + EventRedirectingFrameLayout mPages; + int mCurrentPage; + Button mPositiveButton, mNegativeButton; + ListView mImportOrDelete, mSelectAccount; + Account[] mAccounts; + TextView mSelectAccountDescription, mConfirmation; + + static ImportWizard newInstance(Account[] accounts) { + ImportWizard wizard = new ImportWizard(); + Bundle args = new Bundle(); + args.putParcelableArray(ARG_ACCOUNTS, accounts); + wizard.setArguments(args); + return wizard; + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mAccounts = (Account[]) getArguments().getParcelableArray(ARG_ACCOUNTS); + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + mDialog = new AlertDialog.Builder(getActivity()) + .setTitle(R.string.import_bookmarks_dialog_title) + .setView(createView(savedInstanceState)) + .setPositiveButton("?", null) // This is just a placeholder + .setNegativeButton("?", null) // Ditto + .setOnKeyListener(new OnKeyListener() { + @Override + public boolean onKey(DialogInterface arg0, int arg1, KeyEvent key) { + if (key.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (key.getAction() == KeyEvent.ACTION_UP + && !key.isCanceled()) { + mNegativeButton.performClick(); + } + return true; + } + return false; + } + }) + .create(); + mDialog.setOnShowListener(new OnShowListener() { + @Override + public void onShow(DialogInterface dialog) { + mPositiveButton = mDialog.getButton(AlertDialog.BUTTON_POSITIVE); + mNegativeButton = mDialog.getButton(AlertDialog.BUTTON_NEGATIVE); + mPositiveButton.setOnClickListener(ImportWizard.this); + mNegativeButton.setOnClickListener(ImportWizard.this); + setupAnimations(); + updateNavigation(); + } + }); + return mDialog; + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(STATE_CURRENT_PAGE, mCurrentPage); + outState.putInt(STATE_IMPORT_OR_DELETE, mImportOrDelete.getCheckedItemPosition()); + outState.putInt(STATE_SELECTED_ACCOUNT, mSelectAccount.getCheckedItemPosition()); + } + + public View createView(Bundle savedInstanceState) { + LayoutInflater inflater = LayoutInflater.from(getActivity()); + View root = inflater.inflate(R.layout.bookmark_sync_wizard, null); + mPages = (EventRedirectingFrameLayout) root.findViewById(R.id.pages); + if (mPages.getChildCount() < 1) { + throw new IllegalStateException("no pages in wizard!"); + } + if (savedInstanceState != null) { + mCurrentPage = savedInstanceState.getInt(STATE_CURRENT_PAGE); + } else { + mCurrentPage = 0; + } + setupPage1(savedInstanceState); + setupPage2(savedInstanceState); + setupPage3(savedInstanceState); + for (int i = 0; i < mPages.getChildCount(); i++) { + View v = mPages.getChildAt(i); + if (i <= mCurrentPage) { + preparePage(); + v.setVisibility(View.VISIBLE); + } else { + v.setVisibility(View.GONE); + } + } + mPages.setTargetChild(mCurrentPage); + return root; + } + + void setupPage1(Bundle savedInstanceState) { + mImportOrDelete = (ListView) mPages.findViewById(R.id.add_remove_bookmarks); + // Add an empty header so we get a divider above the list + mImportOrDelete.addHeaderView(new View(getActivity())); + Resources res = getActivity().getResources(); + String[] choices = new String[] { + res.getString(R.string.import_bookmarks_dialog_add), + res.getString(R.string.import_bookmarks_dialog_remove) + }; + ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), + R.layout.bookmark_sync_wizard_item, choices); + mImportOrDelete.setAdapter(adapter); + if (savedInstanceState != null) { + int position = savedInstanceState.getInt(STATE_IMPORT_OR_DELETE); + if (position == ListView.INVALID_POSITION) { + mImportOrDelete.clearChoices(); + } else { + mImportOrDelete.setItemChecked(position, true); + } + } + mImportOrDelete.setOnItemClickListener(this); + } + + void setupPage2(Bundle savedInstanceState) { + mSelectAccount = (ListView) mPages.findViewById(R.id.select_account); + mSelectAccountDescription = + (TextView) mPages.findViewById(R.id.select_account_description); + // Add an empty header so we get a divider above the list + mSelectAccount.addHeaderView(new View(getActivity())); + Resources res = getActivity().getResources(); + String[] accountNames = new String[mAccounts.length]; + for (int i = 0; i < mAccounts.length; i++) { + accountNames[i] = mAccounts[i].name; + } + ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), + R.layout.bookmark_sync_wizard_item, accountNames); + mSelectAccount.setAdapter(adapter); + mSelectAccount.setItemChecked(mSelectAccount.getHeaderViewsCount(), true); + if (savedInstanceState != null) { + int position = savedInstanceState.getInt(STATE_SELECTED_ACCOUNT); + if (position != ListView.INVALID_POSITION) { + mSelectAccount.setItemChecked(position, true); + } + } + mSelectAccount.setOnItemClickListener(this); + } + + void setupPage3(Bundle savedInstanceState) { + mConfirmation = (TextView) mPages.findViewById(R.id.confirm); + } + + void preparePage() { + switch (mCurrentPage) { + case PAGE_SELECT_ACCOUNT: + if (shouldDeleteBookmarks()) { + mSelectAccountDescription.setText( + R.string.import_bookmarks_dialog_delete_select_account); + } else { + mSelectAccountDescription.setText( + R.string.import_bookmarks_dialog_select_add_account); + } + break; + case PAGE_CONFIRMATION: + String account = getSelectedAccount().name; + String confirmationMessage; + if (shouldDeleteBookmarks()) { + confirmationMessage = getActivity().getString( + R.string.import_bookmarks_dialog_confirm_delete, account); + } else { + confirmationMessage = getActivity().getString( + R.string.import_bookmarks_dialog_confirm_add, account); + } + mConfirmation.setText(confirmationMessage); + break; + } + } + + int getAdjustedCheckedItemPosition(ListView list) { + int position = list.getCheckedItemPosition(); + if (position != ListView.INVALID_POSITION) { + position -= list.getHeaderViewsCount(); + } + return position; + } + + Account getSelectedAccount() { + return mAccounts[getAdjustedCheckedItemPosition(mSelectAccount)]; + } + + boolean shouldDeleteBookmarks() { + return getAdjustedCheckedItemPosition(mImportOrDelete) == 1; + } + + @Override + public void onItemClick( + AdapterView<?> parent, View view, int position, long id) { + validate(); + } + + void updateNavigation() { + if (mCurrentPage == 0) { + mNegativeButton.setText(R.string.import_bookmarks_wizard_cancel); + } else { + mNegativeButton.setText(R.string.import_bookmarks_wizard_previous); + } + if ((mCurrentPage + 1) == mPages.getChildCount()) { + mPositiveButton.setText(R.string.import_bookmarks_wizard_done); + } else { + mPositiveButton.setText(R.string.import_bookmarks_wizard_next); + } + validate(); + } + + void validate() { + switch (mCurrentPage) { + case PAGE_IMPORT_OR_DELETE: + mPositiveButton.setEnabled( + mImportOrDelete.getCheckedItemPosition() != ListView.INVALID_POSITION); + break; + case PAGE_SELECT_ACCOUNT: + mPositiveButton.setEnabled( + mSelectAccount.getCheckedItemPosition() != ListView.INVALID_POSITION); + break; + } + } + + void setupAnimations() { + float animX = mPages.getMeasuredWidth(); + final LayoutTransition transitioner = new LayoutTransition(); + ObjectAnimator appearing = ObjectAnimator.ofFloat(this, "translationX", + animX, 0); + ObjectAnimator disappearing = ObjectAnimator.ofFloat(this, "translationX", + 0, animX); + transitioner.setAnimator(LayoutTransition.APPEARING, appearing); + transitioner.setAnimator(LayoutTransition.DISAPPEARING, disappearing); + mPages.setLayoutTransition(transitioner); + } + + boolean next() { + if (mCurrentPage + 1 < mPages.getChildCount()) { + mCurrentPage++; + preparePage(); + mPages.getChildAt(mCurrentPage).setVisibility(View.VISIBLE); + mPages.setTargetChild(mCurrentPage); + return true; + } + return false; + } + + boolean prev() { + if (mCurrentPage > 0) { + mPages.getChildAt(mCurrentPage).setVisibility(View.GONE); + mCurrentPage--; + mPages.setTargetChild(mCurrentPage); + return true; + } + return false; + } + + void done() { + ContentResolver resolver = getActivity().getContentResolver(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); + String accountName = getSelectedAccount().name; + if (shouldDeleteBookmarks()) { + // 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); + } else { + // The user chose to migrate their old bookmarks to the account they're syncing + 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) { + if (ContentResolver.getIsSyncable(account, BrowserContract.AUTHORITY) == 0) { + // Account wasn't syncable, enable it + ContentResolver.setIsSyncable(account, BrowserContract.AUTHORITY, 1); + ContentResolver.setSyncAutomatically(account, BrowserContract.AUTHORITY, true); + } + } + + dismiss(); + } + + @Override + public void onClick(View v) { + if (v == mNegativeButton) { + if (prev()) { + updateNavigation(); + } else { + dismiss(); + } + } else if (v == mPositiveButton) { + if (next()) { + updateNavigation(); + } else { + done(); + } + } + } + + /** + * 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.ACCOUNT_NAME + " IS NULL AND " + + 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/preferences/LabPreferencesFragment.java b/src/com/android/browser/preferences/LabPreferencesFragment.java new file mode 100644 index 000000000..a06dc3e0e --- /dev/null +++ b/src/com/android/browser/preferences/LabPreferencesFragment.java @@ -0,0 +1,71 @@ +/* + * 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.BrowserActivity; +import com.android.browser.BrowserSettings; +import com.android.browser.R; +import com.android.browser.search.SearchEngine; + +import android.content.Intent; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.Preference.OnPreferenceChangeListener; +import android.preference.PreferenceFragment; + +public class LabPreferencesFragment extends PreferenceFragment + implements OnPreferenceChangeListener { + private BrowserSettings mBrowserSettings; + private Preference useInstantPref; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mBrowserSettings = BrowserSettings.getInstance(); + + // Load the XML preferences file + addPreferencesFromResource(R.xml.lab_preferences); + + Preference e = findPreference(BrowserSettings.PREF_QUICK_CONTROLS); + e.setOnPreferenceChangeListener(this); + useInstantPref = findPreference(BrowserSettings.PREF_USE_INSTANT); + } + + @Override + public void onResume() { + super.onResume(); + useInstantPref.setEnabled(false); + + // Enable the "use instant" preference only if the selected + // search engine is google. + if (mBrowserSettings.getSearchEngine() != null) { + final String currentName = mBrowserSettings.getSearchEngine().getName(); + if (SearchEngine.GOOGLE.equals(currentName)) { + useInstantPref.setEnabled(true); + } + } + } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + // Attempt to restart + startActivity(new Intent(BrowserActivity.ACTION_RESTART, null, + getActivity(), BrowserActivity.class)); + return true; + } +} diff --git a/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java b/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java new file mode 100644 index 000000000..22666088f --- /dev/null +++ b/src/com/android/browser/preferences/PrivacySecurityPreferencesFragment.java @@ -0,0 +1,65 @@ +/* + * 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.BrowserSettings; +import com.android.browser.R; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.preference.Preference; +import android.preference.PreferenceFragment; + +public class PrivacySecurityPreferencesFragment extends PreferenceFragment + implements Preference.OnPreferenceChangeListener { + + private BrowserSettings mSettings; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mSettings = BrowserSettings.getInstance(); + + // Load the preferences from an XML resource + addPreferencesFromResource(R.xml.privacy_security_preferences); + + Preference e = findPreference(BrowserSettings.PREF_CLEAR_HISTORY); + e.setOnPreferenceChangeListener(this); + } + + @Override + public void onResume() { + super.onResume(); + } + + @Override + public boolean onPreferenceChange(Preference pref, Object objValue) { + if (pref.getKey().equals(BrowserSettings.PREF_CLEAR_HISTORY) + && ((Boolean) objValue).booleanValue() == true) { + // Need to tell the browser to remove the parent/child relationship + // between tabs + getActivity().setResult(Activity.RESULT_OK, (new Intent()).putExtra(Intent.EXTRA_TEXT, + pref.getKey())); + return true; + } + + return false; + } + +} diff --git a/src/com/android/browser/preferences/WebsiteSettingsFragment.java b/src/com/android/browser/preferences/WebsiteSettingsFragment.java new file mode 100644 index 000000000..6339d726d --- /dev/null +++ b/src/com/android/browser/preferences/WebsiteSettingsFragment.java @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2009 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.R; +import com.android.browser.WebStorageSizeManager; + +import android.app.AlertDialog; +import android.app.FragmentTransaction; +import android.app.ListFragment; +import android.content.Context; +import android.content.DialogInterface; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.AsyncTask; +import android.os.Bundle; +import android.preference.PreferenceActivity; +import android.preference.PreferenceFragment; +import android.provider.BrowserContract.Bookmarks; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.webkit.GeolocationPermissions; +import android.webkit.ValueCallback; +import android.webkit.WebStorage; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemClickListener; +import android.widget.ArrayAdapter; +import android.widget.ImageView; +import android.widget.TextView; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * Manage the settings for an origin. + * We use it to keep track of the 'HTML5' settings, i.e. database (webstorage) + * and Geolocation. + */ +public class WebsiteSettingsFragment extends ListFragment implements OnClickListener { + + private static final String EXTRA_SITE = "site"; + private String LOGTAG = "WebsiteSettingsActivity"; + private static String sMBStored = null; + private SiteAdapter mAdapter = null; + private Site mSite = null; + + static class Site implements Serializable { + private String mOrigin; + private String mTitle; + private Bitmap mIcon; + private int mFeatures; + + // These constants provide the set of features that a site may support + // They must be consecutive. To add a new feature, add a new FEATURE_XXX + // variable with value equal to the current value of FEATURE_COUNT, then + // increment FEATURE_COUNT. + private final static int FEATURE_WEB_STORAGE = 0; + private final static int FEATURE_GEOLOCATION = 1; + // The number of features available. + private final static int FEATURE_COUNT = 2; + + public Site(String origin) { + mOrigin = origin; + mTitle = null; + mIcon = null; + mFeatures = 0; + } + + public void addFeature(int feature) { + mFeatures |= (1 << feature); + } + + public void removeFeature(int feature) { + mFeatures &= ~(1 << feature); + } + + public boolean hasFeature(int feature) { + return (mFeatures & (1 << feature)) != 0; + } + + /** + * Gets the number of features supported by this site. + */ + public int getFeatureCount() { + int count = 0; + for (int i = 0; i < FEATURE_COUNT; ++i) { + count += hasFeature(i) ? 1 : 0; + } + return count; + } + + /** + * Gets the ID of the nth (zero-based) feature supported by this site. + * The return value is a feature ID - one of the FEATURE_XXX values. + * This is required to determine which feature is displayed at a given + * position in the list of features for this site. This is used both + * when populating the view and when responding to clicks on the list. + */ + public int getFeatureByIndex(int n) { + int j = -1; + for (int i = 0; i < FEATURE_COUNT; ++i) { + j += hasFeature(i) ? 1 : 0; + if (j == n) { + return i; + } + } + return -1; + } + + public String getOrigin() { + return mOrigin; + } + + public void setTitle(String title) { + mTitle = title; + } + + public void setIcon(Bitmap icon) { + mIcon = icon; + } + + public Bitmap getIcon() { + return mIcon; + } + + public String getPrettyOrigin() { + return mTitle == null ? null : hideHttp(mOrigin); + } + + public String getPrettyTitle() { + return mTitle == null ? hideHttp(mOrigin) : mTitle; + } + + private String hideHttp(String str) { + Uri uri = Uri.parse(str); + return "http".equals(uri.getScheme()) ? str.substring(7) : str; + } + } + + class SiteAdapter extends ArrayAdapter<Site> + implements AdapterView.OnItemClickListener { + private int mResource; + private LayoutInflater mInflater; + private Bitmap mDefaultIcon; + private Bitmap mUsageEmptyIcon; + private Bitmap mUsageLowIcon; + private Bitmap mUsageHighIcon; + private Bitmap mLocationAllowedIcon; + private Bitmap mLocationDisallowedIcon; + private Site mCurrentSite; + + public SiteAdapter(Context context, int rsc) { + this(context, rsc, null); + } + + public SiteAdapter(Context context, int rsc, Site site) { + super(context, rsc); + mResource = rsc; + mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + mDefaultIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.app_web_browser_sm); + mUsageEmptyIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_list_data_off); + mUsageLowIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_list_data_small); + mUsageHighIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_list_data_large); + mLocationAllowedIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_gps_on_holo_dark); + mLocationDisallowedIcon = BitmapFactory.decodeResource(getResources(), + R.drawable.ic_gps_denied_holo_dark); + mCurrentSite = site; + if (mCurrentSite == null) { + askForOrigins(); + } + } + + /** + * Adds the specified feature to the site corresponding to supplied + * origin in the map. Creates the site if it does not already exist. + */ + private void addFeatureToSite(Map<String, Site> sites, String origin, int feature) { + Site site = null; + if (sites.containsKey(origin)) { + site = (Site) sites.get(origin); + } else { + site = new Site(origin); + sites.put(origin, site); + } + site.addFeature(feature); + } + + public void askForOrigins() { + // Get the list of origins we want to display. + // All 'HTML 5 modules' (Database, Geolocation etc) form these + // origin strings using WebCore::SecurityOrigin::toString(), so it's + // safe to group origins here. Note that WebCore::SecurityOrigin + // uses 0 (which is not printed) for the port if the port is the + // default for the protocol. Eg http://www.google.com and + // http://www.google.com:80 both record a port of 0 and hence + // toString() == 'http://www.google.com' for both. + + WebStorage.getInstance().getOrigins(new ValueCallback<Map>() { + public void onReceiveValue(Map origins) { + Map<String, Site> sites = new HashMap<String, Site>(); + if (origins != null) { + Iterator<String> iter = origins.keySet().iterator(); + while (iter.hasNext()) { + addFeatureToSite(sites, iter.next(), Site.FEATURE_WEB_STORAGE); + } + } + askForGeolocation(sites); + } + }); + } + + public void askForGeolocation(final Map<String, Site> sites) { + GeolocationPermissions.getInstance().getOrigins(new ValueCallback<Set<String> >() { + public void onReceiveValue(Set<String> origins) { + if (origins != null) { + Iterator<String> iter = origins.iterator(); + while (iter.hasNext()) { + addFeatureToSite(sites, iter.next(), Site.FEATURE_GEOLOCATION); + } + } + populateIcons(sites); + populateOrigins(sites); + } + }); + } + + public void populateIcons(Map<String, Site> sites) { + // Create a map from host to origin. This is used to add metadata + // (title, icon) for this origin from the bookmarks DB. We must do + // the DB access on a background thread. + new UpdateFromBookmarksDbTask(this.getContext(), sites).execute(); + } + + private class UpdateFromBookmarksDbTask extends AsyncTask<Void, Void, Void> { + + private Context mContext; + private boolean mDataSetChanged; + private Map<String, Site> mSites; + + public UpdateFromBookmarksDbTask(Context ctx, Map<String, Site> sites) { + mContext = ctx; + mSites = sites; + } + + protected Void doInBackground(Void... unused) { + HashMap<String, Set<Site>> hosts = new HashMap<String, Set<Site>>(); + Set<Map.Entry<String, Site>> elements = mSites.entrySet(); + Iterator<Map.Entry<String, Site>> originIter = elements.iterator(); + while (originIter.hasNext()) { + Map.Entry<String, Site> entry = originIter.next(); + Site site = entry.getValue(); + String host = Uri.parse(entry.getKey()).getHost(); + Set<Site> hostSites = null; + if (hosts.containsKey(host)) { + hostSites = (Set<Site>)hosts.get(host); + } else { + hostSites = new HashSet<Site>(); + hosts.put(host, hostSites); + } + hostSites.add(site); + } + + // Check the bookmark DB. If we have data for a host used by any of + // our origins, use it to set their title and favicon + Cursor c = mContext.getContentResolver().query(Bookmarks.CONTENT_URI, + new String[] { Bookmarks.URL, Bookmarks.TITLE, Bookmarks.FAVICON }, + Bookmarks.IS_FOLDER + " == 0", null, null); + + if (c != null) { + if (c.moveToFirst()) { + int urlIndex = c.getColumnIndex(Bookmarks.URL); + int titleIndex = c.getColumnIndex(Bookmarks.TITLE); + int faviconIndex = c.getColumnIndex(Bookmarks.FAVICON); + do { + String url = c.getString(urlIndex); + String host = Uri.parse(url).getHost(); + if (hosts.containsKey(host)) { + String title = c.getString(titleIndex); + Bitmap bmp = null; + byte[] data = c.getBlob(faviconIndex); + if (data != null) { + bmp = BitmapFactory.decodeByteArray(data, 0, data.length); + } + Set matchingSites = (Set) hosts.get(host); + Iterator<Site> sitesIter = matchingSites.iterator(); + while (sitesIter.hasNext()) { + Site site = sitesIter.next(); + // We should only set the title if the bookmark is for the root + // (i.e. www.google.com), as website settings act on the origin + // as a whole rather than a single page under that origin. If the + // user has bookmarked a page under the root but *not* the root, + // then we risk displaying the title of that page which may or + // may not have any relevance to the origin. + if (url.equals(site.getOrigin()) || + (new String(site.getOrigin()+"/")).equals(url)) { + mDataSetChanged = true; + site.setTitle(title); + } + + if (bmp != null) { + mDataSetChanged = true; + site.setIcon(bmp); + } + } + } + } while (c.moveToNext()); + } + c.close(); + } + return null; + } + + protected void onPostExecute(Void unused) { + if (mDataSetChanged) { + notifyDataSetChanged(); + } + } + } + + + public void populateOrigins(Map<String, Site> sites) { + clear(); + + // We can now simply populate our array with Site instances + Set<Map.Entry<String, Site>> elements = sites.entrySet(); + Iterator<Map.Entry<String, Site>> entryIterator = elements.iterator(); + while (entryIterator.hasNext()) { + Map.Entry<String, Site> entry = entryIterator.next(); + Site site = entry.getValue(); + add(site); + } + + notifyDataSetChanged(); + + if (getCount() == 0) { + finish(); // we close the screen + } + } + + public int getCount() { + if (mCurrentSite == null) { + return super.getCount(); + } + return mCurrentSite.getFeatureCount(); + } + + public String sizeValueToString(long bytes) { + // We display the size in MB, to 1dp, rounding up to the next 0.1MB. + // bytes should always be greater than zero. + if (bytes <= 0) { + Log.e(LOGTAG, "sizeValueToString called with non-positive value: " + bytes); + return "0"; + } + float megabytes = (float) bytes / (1024.0F * 1024.0F); + int truncated = (int) Math.ceil(megabytes * 10.0F); + float result = (float) (truncated / 10.0F); + return String.valueOf(result); + } + + /* + * If we receive the back event and are displaying + * site's settings, we want to go back to the main + * list view. If not, we just do nothing (see + * dispatchKeyEvent() below). + */ + public boolean backKeyPressed() { + if (mCurrentSite != null) { + mCurrentSite = null; + askForOrigins(); + return true; + } + return false; + } + + /** + * @hide + * Utility function + * Set the icon according to the usage + */ + public void setIconForUsage(ImageView usageIcon, long usageInBytes) { + float usageInMegabytes = (float) usageInBytes / (1024.0F * 1024.0F); + // We set the correct icon: + // 0 < empty < 0.1MB + // 0.1MB < low < 5MB + // 5MB < high + if (usageInMegabytes <= 0.1) { + usageIcon.setImageBitmap(mUsageEmptyIcon); + } else if (usageInMegabytes > 0.1 && usageInMegabytes <= 5) { + usageIcon.setImageBitmap(mUsageLowIcon); + } else if (usageInMegabytes > 5) { + usageIcon.setImageBitmap(mUsageHighIcon); + } + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View view; + final TextView title; + final TextView subtitle; + final ImageView icon; + final ImageView usageIcon; + final ImageView locationIcon; + final ImageView featureIcon; + + if (convertView == null) { + view = mInflater.inflate(mResource, parent, false); + } else { + view = convertView; + } + + title = (TextView) view.findViewById(R.id.title); + subtitle = (TextView) view.findViewById(R.id.subtitle); + icon = (ImageView) view.findViewById(R.id.icon); + featureIcon = (ImageView) view.findViewById(R.id.feature_icon); + usageIcon = (ImageView) view.findViewById(R.id.usage_icon); + locationIcon = (ImageView) view.findViewById(R.id.location_icon); + usageIcon.setVisibility(View.GONE); + locationIcon.setVisibility(View.GONE); + + if (mCurrentSite == null) { + + Site site = getItem(position); + title.setText(site.getPrettyTitle()); + String subtitleText = site.getPrettyOrigin(); + if (subtitleText != null) { + title.setMaxLines(1); + title.setSingleLine(true); + subtitle.setVisibility(View.VISIBLE); + subtitle.setText(subtitleText); + } else { + subtitle.setVisibility(View.GONE); + title.setMaxLines(2); + title.setSingleLine(false); + } + + icon.setVisibility(View.VISIBLE); + usageIcon.setVisibility(View.INVISIBLE); + locationIcon.setVisibility(View.INVISIBLE); + featureIcon.setVisibility(View.GONE); + Bitmap bmp = site.getIcon(); + if (bmp == null) { + bmp = mDefaultIcon; + } + icon.setImageBitmap(bmp); + // We set the site as the view's tag, + // so that we can get it in onItemClick() + view.setTag(site); + + String origin = site.getOrigin(); + if (site.hasFeature(Site.FEATURE_WEB_STORAGE)) { + WebStorage.getInstance().getUsageForOrigin(origin, new ValueCallback<Long>() { + public void onReceiveValue(Long value) { + if (value != null) { + setIconForUsage(usageIcon, value.longValue()); + usageIcon.setVisibility(View.VISIBLE); + } + } + }); + } + + if (site.hasFeature(Site.FEATURE_GEOLOCATION)) { + locationIcon.setVisibility(View.VISIBLE); + GeolocationPermissions.getInstance().getAllowed(origin, new ValueCallback<Boolean>() { + public void onReceiveValue(Boolean allowed) { + if (allowed != null) { + if (allowed.booleanValue()) { + locationIcon.setImageBitmap(mLocationAllowedIcon); + } else { + locationIcon.setImageBitmap(mLocationDisallowedIcon); + } + } + } + }); + } + } else { + icon.setVisibility(View.GONE); + locationIcon.setVisibility(View.GONE); + usageIcon.setVisibility(View.GONE); + featureIcon.setVisibility(View.VISIBLE); + String origin = mCurrentSite.getOrigin(); + switch (mCurrentSite.getFeatureByIndex(position)) { + case Site.FEATURE_WEB_STORAGE: + WebStorage.getInstance().getUsageForOrigin(origin, new ValueCallback<Long>() { + public void onReceiveValue(Long value) { + if (value != null) { + String usage = sizeValueToString(value.longValue()) + " " + sMBStored; + title.setText(R.string.webstorage_clear_data_title); + subtitle.setText(usage); + subtitle.setVisibility(View.VISIBLE); + setIconForUsage(featureIcon, value.longValue()); + } + } + }); + break; + case Site.FEATURE_GEOLOCATION: + title.setText(R.string.geolocation_settings_page_title); + GeolocationPermissions.getInstance().getAllowed(origin, new ValueCallback<Boolean>() { + public void onReceiveValue(Boolean allowed) { + if (allowed != null) { + if (allowed.booleanValue()) { + subtitle.setText(R.string.geolocation_settings_page_summary_allowed); + featureIcon.setImageBitmap(mLocationAllowedIcon); + } else { + subtitle.setText(R.string.geolocation_settings_page_summary_not_allowed); + featureIcon.setImageBitmap(mLocationDisallowedIcon); + } + subtitle.setVisibility(View.VISIBLE); + } + } + }); + break; + } + } + + return view; + } + + public void onItemClick(AdapterView<?> parent, + View view, + int position, + long id) { + if (mCurrentSite != null) { + switch (mCurrentSite.getFeatureByIndex(position)) { + case Site.FEATURE_WEB_STORAGE: + new AlertDialog.Builder(getContext()) + .setTitle(R.string.webstorage_clear_data_dialog_title) + .setMessage(R.string.webstorage_clear_data_dialog_message) + .setPositiveButton(R.string.webstorage_clear_data_dialog_ok_button, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dlg, int which) { + WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin()); + // If this site has no more features, then go back to the + // origins list. + mCurrentSite.removeFeature(Site.FEATURE_WEB_STORAGE); + if (mCurrentSite.getFeatureCount() == 0) { + finish(); + } + askForOrigins(); + notifyDataSetChanged(); + }}) + .setNegativeButton(R.string.webstorage_clear_data_dialog_cancel_button, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + break; + case Site.FEATURE_GEOLOCATION: + new AlertDialog.Builder(getContext()) + .setTitle(R.string.geolocation_settings_page_dialog_title) + .setMessage(R.string.geolocation_settings_page_dialog_message) + .setPositiveButton(R.string.geolocation_settings_page_dialog_ok_button, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dlg, int which) { + GeolocationPermissions.getInstance().clear(mCurrentSite.getOrigin()); + mCurrentSite.removeFeature(Site.FEATURE_GEOLOCATION); + if (mCurrentSite.getFeatureCount() == 0) { + finish(); + } + askForOrigins(); + notifyDataSetChanged(); + }}) + .setNegativeButton(R.string.geolocation_settings_page_dialog_cancel_button, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + break; + } + } else { + Site site = (Site) view.getTag(); + PreferenceActivity activity = (PreferenceActivity) getActivity(); + if (activity != null) { + Bundle args = new Bundle(); + args.putSerializable(EXTRA_SITE, site); + activity.startPreferencePanel(WebsiteSettingsFragment.class.getName(), args, 0, + site.getPrettyTitle(), null, 0); + } + } + } + + public Site currentSite() { + return mCurrentSite; + } + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.website_settings, container, false); + Bundle args = getArguments(); + if (args != null) { + mSite = (Site) args.getSerializable(EXTRA_SITE); + } + if (mSite == null) { + View clear = view.findViewById(R.id.clear_all_button); + clear.setVisibility(View.VISIBLE); + clear.setOnClickListener(this); + } + return view; + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + if (sMBStored == null) { + sMBStored = getString(R.string.webstorage_origin_summary_mb_stored); + } + mAdapter = new SiteAdapter(getActivity(), R.layout.website_settings_row); + if (mSite != null) { + mAdapter.mCurrentSite = mSite; + } + getListView().setAdapter(mAdapter); + getListView().setOnItemClickListener(mAdapter); + } + + private void finish() { + PreferenceActivity activity = (PreferenceActivity) getActivity(); + if (activity != null) { + activity.finishPreferencePanel(this, 0, null); + } + } + + @Override + public void onClick(View v) { + switch (v.getId()) { + case R.id.clear_all_button: + // Show the prompt to clear all origins of their data and geolocation permissions. + new AlertDialog.Builder(getActivity()) + .setTitle(R.string.website_settings_clear_all_dialog_title) + .setMessage(R.string.website_settings_clear_all_dialog_message) + .setPositiveButton(R.string.website_settings_clear_all_dialog_ok_button, + new AlertDialog.OnClickListener() { + public void onClick(DialogInterface dlg, int which) { + WebStorage.getInstance().deleteAllData(); + GeolocationPermissions.getInstance().clearAll(); + WebStorageSizeManager.resetLastOutOfSpaceNotificationTime(); + mAdapter.askForOrigins(); + finish(); + }}) + .setNegativeButton(R.string.website_settings_clear_all_dialog_cancel_button, null) + .setIcon(android.R.drawable.ic_dialog_alert) + .show(); + break; + } + } +} |
