Index: chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fa168082a907986f651a7f9ee8c1eb54cbbaa1f8 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java |
@@ -0,0 +1,290 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser.preferences.privacy; |
+ |
+import android.app.Activity; |
+import android.app.ActivityManager; |
+import android.app.Dialog; |
+import android.app.DialogFragment; |
+import android.content.Context; |
+import android.content.DialogInterface; |
+import android.content.Intent; |
+import android.content.res.Resources; |
+import android.graphics.Bitmap; |
+import android.graphics.drawable.BitmapDrawable; |
+import android.graphics.drawable.Drawable; |
+import android.os.Bundle; |
+import android.support.v4.graphics.drawable.RoundedBitmapDrawable; |
+import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory; |
+import android.support.v7.app.AlertDialog; |
+import android.view.LayoutInflater; |
+import android.view.View; |
+import android.view.ViewGroup; |
+import android.widget.AdapterView; |
+import android.widget.ArrayAdapter; |
+import android.widget.CheckBox; |
+import android.widget.ImageView; |
+import android.widget.ListView; |
+ |
+import org.chromium.base.ApiCompatibilityUtils; |
+import org.chromium.base.ContextUtils; |
+import org.chromium.base.VisibleForTesting; |
+import org.chromium.chrome.R; |
+import org.chromium.chrome.browser.favicon.LargeIconBridge; |
+import org.chromium.chrome.browser.favicon.LargeIconBridge.LargeIconCallback; |
+import org.chromium.chrome.browser.profiles.Profile; |
+import org.chromium.chrome.browser.widget.RoundedIconGenerator; |
+ |
+import java.util.HashSet; |
+import java.util.Set; |
+ |
+/** |
+ * Modal dialog that shows a list of important domains to the user which they can uncheck. Used to |
+ * allow the user to exclude domains from being cleared by the clear browsing data function. |
+ * We use proper bundle construction (through the {@link #newInstance(String[], String[])} method) |
+ * and onActivityResult return conventions. |
+ * TODO(dmurph): Add UMA metrics for cancel and clear (with and without dechecking a site). |
+ */ |
+public class ConfirmImportantSitesDialogFragment extends DialogFragment { |
+ private class ClearBrowsingDataAdapter extends ArrayAdapter<String> |
+ implements AdapterView.OnItemClickListener { |
+ private final String[] mDomains; |
+ private final String[] mFaviconURLs; |
+ private final int mCornerRadius; |
+ private final int mFaviconSize; |
+ private RoundedIconGenerator mIconGenerator; |
+ |
+ private ClearBrowsingDataAdapter( |
+ String[] domains, String[] faviconURLs, Resources resources) { |
+ super(getActivity(), R.layout.confirm_important_sites_list_row, domains); |
+ mDomains = domains; |
+ mFaviconURLs = faviconURLs; |
+ mFaviconSize = resources.getDimensionPixelSize(R.dimen.default_favicon_size); |
+ mCornerRadius = resources.getDimensionPixelSize(R.dimen.default_favicon_corner_radius); |
+ int textSize = resources.getDimensionPixelSize(R.dimen.default_favicon_icon_text_size); |
+ int iconColor = ApiCompatibilityUtils.getColor( |
+ resources, R.color.default_favicon_background_color); |
+ mIconGenerator = new RoundedIconGenerator( |
+ mFaviconSize, mFaviconSize, mCornerRadius, iconColor, textSize); |
+ } |
+ |
+ @Override |
+ public boolean hasStableIds() { |
+ return true; |
+ } |
+ |
+ @Override |
+ public View getView(int position, View convertView, ViewGroup parent) { |
+ View childView = convertView; |
+ if (childView == null) { |
+ LayoutInflater inflater = LayoutInflater.from(getActivity()); |
+ childView = |
+ inflater.inflate(R.layout.confirm_important_sites_list_row, parent, false); |
+ |
+ ViewAndFaviconHolder viewHolder = new ViewAndFaviconHolder(); |
+ viewHolder.checkboxView = (CheckBox) childView.findViewById(R.id.icon_row_checkbox); |
+ viewHolder.imageView = (ImageView) childView.findViewById(R.id.icon_row_image); |
+ childView.setTag(viewHolder); |
+ } |
+ ViewAndFaviconHolder viewHolder = (ViewAndFaviconHolder) childView.getTag(); |
+ configureChildView(position, viewHolder); |
+ return childView; |
+ } |
+ |
+ private void configureChildView(int position, ViewAndFaviconHolder viewHolder) { |
+ String domain = mDomains[position]; |
+ viewHolder.checkboxView.setChecked(!mDeselectedDomains.contains(domain)); |
+ viewHolder.checkboxView.setText(domain); |
+ loadFavicon(viewHolder, mFaviconURLs[position]); |
+ } |
+ |
+ /** |
+ * Called when a list item is clicked. We toggle the checkbox and update our selected |
+ * domains list. |
+ */ |
+ @Override |
+ public void onItemClick(AdapterView<?> parent, View view, int position, long id) { |
+ String domain = mDomains[position]; |
+ ViewAndFaviconHolder viewHolder = (ViewAndFaviconHolder) view.getTag(); |
+ if (mDeselectedDomains.contains(domain)) { |
+ mDeselectedDomains.remove(domain); |
+ viewHolder.checkboxView.setChecked(true); |
+ } else { |
+ mDeselectedDomains.add(domain); |
+ viewHolder.checkboxView.setChecked(false); |
+ } |
+ } |
+ |
+ private void loadFavicon(final ViewAndFaviconHolder viewHolder, final String url) { |
+ viewHolder.imageCallback = new LargeIconCallback() { |
+ @Override |
+ public void onLargeIconAvailable(Bitmap icon, int fallbackColor) { |
+ if (this != viewHolder.imageCallback) return; |
+ Drawable image = getFaviconDrawable(icon, fallbackColor, url); |
+ viewHolder.imageView.setImageDrawable(image); |
+ } |
+ }; |
+ mLargeIconBridge.getLargeIconForUrl(url, mFaviconSize, viewHolder.imageCallback); |
+ } |
+ |
+ private Drawable getFaviconDrawable(Bitmap icon, int fallbackColor, String url) { |
+ if (icon == null) { |
+ mIconGenerator.setBackgroundColor(fallbackColor); |
+ icon = mIconGenerator.generateIconForUrl(url); |
+ return new BitmapDrawable(getResources(), icon); |
+ } else { |
+ RoundedBitmapDrawable roundedIcon = |
+ RoundedBitmapDrawableFactory.create(getResources(), |
+ Bitmap.createScaledBitmap(icon, mFaviconSize, mFaviconSize, false)); |
+ roundedIcon.setCornerRadius(mCornerRadius); |
+ return roundedIcon; |
+ } |
+ } |
+ } |
+ |
+ /** |
+ * ViewHolder class optimizes looking up table row fields. findViewById is only called once |
+ * per row view initialization, and the references are cached here. Also stores a reference to |
+ * the favicon image callback so that we can make sure we load the correct favicon. |
+ */ |
+ private static class ViewAndFaviconHolder { |
+ public CheckBox checkboxView; |
+ public ImageView imageView; |
+ public LargeIconCallback imageCallback; |
+ } |
+ |
+ /** |
+ * Constructs a new instance of the important sites dialog fragment. |
+ * @param importantDomains The list of important domains to display. |
+ * @param faviconURLs The list of favicon urls that correspond to each importantDomains. |
+ * @return An instance of ConfirmImportantSitesDialogFragment with the bundle arguments set. |
+ */ |
+ public static ConfirmImportantSitesDialogFragment newInstance( |
+ String[] importantDomains, String[] faviconURLs) { |
+ ConfirmImportantSitesDialogFragment dialogFragment = |
+ new ConfirmImportantSitesDialogFragment(); |
+ Bundle bundle = new Bundle(); |
+ bundle.putStringArray(IMPORTANT_DOMAINS_TAG, importantDomains); |
+ bundle.putStringArray(FAVICON_URLS_TAG, faviconURLs); |
+ dialogFragment.setArguments(bundle); |
+ return dialogFragment; |
+ } |
+ |
+ private static final int FAVICON_MAX_CACHE_SIZE_BYTES = 100 * 1024; // 100KB |
+ |
+ /** The tag used when showing the clear browsing fragment. */ |
+ public static final String FRAGMENT_TAG = "ConfirmImportantSitesDialogFragment"; |
+ |
+ /** |
+ * The tag used to return the string array of deselected domains. These are meant to NOT be |
+ * cleared. |
+ */ |
+ public static final String DESELECTED_DOMAINS_TAG = "DeselectedDomains"; |
+ |
+ /** The tag used for logging. */ |
+ public static final String TAG = "ConfirmImportantSitesDialogFragment"; |
+ |
+ /** The tag used to store the important domains in the bundle. */ |
+ private static final String IMPORTANT_DOMAINS_TAG = "ImportantDomains"; |
+ |
+ /** The tag used to store the favicon urls corresponding to each important domain. */ |
+ private static final String FAVICON_URLS_TAG = "FaviconURLs"; |
+ |
+ /** Array of important registerable domains we're showing to the user. */ |
+ private String[] mImportantDomains; |
+ /** Array of favicon urls to use for each important domain above. */ |
+ private String[] mFaviconURLs; |
+ /** The set of domains that the user has deselected. */ |
+ private Set<String> mDeselectedDomains; |
+ /** The alert dialog shown to the user. */ |
+ private AlertDialog mDialog; |
+ /** Our adapter that we use with the list view in the dialog. */ |
+ private ClearBrowsingDataAdapter mAdapter; |
+ |
+ private LargeIconBridge mLargeIconBridge; |
+ |
+ private Profile mProfile; |
+ |
+ /** We store the custom list view for testing */ |
+ private ListView mSitesListView; |
+ |
+ public ConfirmImportantSitesDialogFragment() { |
+ mDeselectedDomains = new HashSet<>(); |
+ } |
+ |
+ @Override |
+ public void setArguments(Bundle args) { |
+ super.setArguments(args); |
+ mImportantDomains = args.getStringArray(IMPORTANT_DOMAINS_TAG); |
+ mFaviconURLs = args.getStringArray(FAVICON_URLS_TAG); |
+ } |
+ |
+ @VisibleForTesting |
+ public Set<String> getDeselectedDomains() { |
+ return mDeselectedDomains; |
+ } |
+ |
+ @VisibleForTesting |
+ public ListView getSitesList() { |
+ return mSitesListView; |
+ } |
+ |
+ @Override |
+ public void onDismiss(DialogInterface dialog) { |
+ super.onDismiss(dialog); |
+ if (mLargeIconBridge != null) { |
+ mLargeIconBridge.destroy(); |
+ } |
+ } |
+ |
+ @Override |
+ public Dialog onCreateDialog(Bundle savedInstanceState) { |
+ if (savedInstanceState != null) { |
+ dismiss(); |
+ } |
+ mProfile = Profile.getLastUsedProfile().getOriginalProfile(); |
+ mLargeIconBridge = new LargeIconBridge(mProfile); |
+ ActivityManager activityManager = |
+ ((ActivityManager) ContextUtils.getApplicationContext().getSystemService( |
+ Context.ACTIVITY_SERVICE)); |
+ int maxSize = Math.min( |
+ activityManager.getMemoryClass() / 16 * 25 * 1024, FAVICON_MAX_CACHE_SIZE_BYTES); |
+ mLargeIconBridge.createCache(maxSize); |
+ |
+ mAdapter = new ClearBrowsingDataAdapter(mImportantDomains, mFaviconURLs, getResources()); |
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { |
+ @Override |
+ public void onClick(DialogInterface dialog, int which) { |
+ if (which == AlertDialog.BUTTON_POSITIVE) { |
+ // Return our deselected domains. |
+ Intent data = new Intent(); |
+ String[] deselectedDomains = mDeselectedDomains.toArray(new String[0]); |
+ data.putExtra(DESELECTED_DOMAINS_TAG, deselectedDomains); |
+ getTargetFragment().onActivityResult( |
+ getTargetRequestCode(), Activity.RESULT_OK, data); |
+ } else { |
+ getTargetFragment().onActivityResult(getTargetRequestCode(), |
+ Activity.RESULT_CANCELED, getActivity().getIntent()); |
+ } |
+ } |
+ }; |
+ // We create our own ListView, as AlertDialog doesn't let us set a message and a list |
+ // adapter at the same time. |
+ View messageAndListView = getActivity().getLayoutInflater().inflate( |
+ R.layout.clear_browsing_important_dialog_listview, null); |
+ mSitesListView = (ListView) messageAndListView.findViewById(R.id.select_dialog_listview); |
+ mSitesListView.setAdapter(mAdapter); |
+ mSitesListView.setOnItemClickListener(mAdapter); |
+ final AlertDialog.Builder builder = |
+ new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme) |
+ .setTitle(R.string.storage_clear_site_storage_title) |
+ .setPositiveButton(R.string.clear_data_delete, listener) |
+ .setNegativeButton(R.string.cancel, listener) |
+ .setView(messageAndListView); |
+ mDialog = builder.create(); |
+ |
+ return mDialog; |
+ } |
+} |