Chromium Code Reviews| 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..9a8fc9fe2dcf8240dd6f6eacbabdf9d0d2e9eaf0 |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/preferences/privacy/ConfirmImportantSitesDialogFragment.java |
| @@ -0,0 +1,278 @@ |
| +// 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.ApplicationStatus; |
| +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[])} method) and |
| + * onActivityResult return conventions. |
| + */ |
| +public class ConfirmImportantSitesDialogFragment extends DialogFragment { |
| + private class ClearBrowsingDataAdapter extends ArrayAdapter<String> { |
| + String[] mDomains; |
| + private final int mCornerRadius; |
| + private final int mFaviconSize; |
| + private RoundedIconGenerator mIconGenerator; |
| + |
| + private ClearBrowsingDataAdapter(String[] domains, Resources resources) { |
| + super(getActivity(), R.layout.list_row_checked_icon, domains); |
| + mDomains = domains; |
| + // We use the bookmark icon configuration. |
| + 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 int getCount() { |
| + return mDomains.length; |
| + } |
| + |
| + @Override |
| + public boolean hasStableIds() { |
| + return true; |
|
Theresa
2016/05/13 06:02:30
What does this override do?
dmurph
2016/05/13 23:46:23
If this is true, then the list doesn't re-generate
Theresa
2016/05/16 22:37:08
You can leave it in, I was just curious.
|
| + } |
| + |
| + @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.list_row_checked_icon, 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; |
| + } |
| + |
| + public void configureChildView(int position, ViewAndFaviconHolder viewHolder) { |
| + String domain = mDomains[position]; |
| + viewHolder.checkboxView.setChecked(!mDeselectedDomains.contains(domain)); |
| + viewHolder.checkboxView.setText(domain); |
| + loadLocalFavicon(viewHolder, transformDomainToOrigin(domain)); |
| + } |
| + |
| + public void onClick(int position, View view) { |
| + 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); |
| + } |
| + loadLocalFavicon(viewHolder, domain); |
| + } |
| + |
| + private void loadLocalFavicon(final ViewAndFaviconHolder viewHolder, final String url) { |
|
Theresa
2016/05/13 06:02:30
nit: just loadFavicon()?
|
| + 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. |
|
Theresa
2016/05/13 06:02:30
nit: replace the semi-colon with a comma
dmurph
2016/05/13 23:46:23
haha not sure why that was there. Removed.
|
| + */ |
| + 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. |
| + * @return An instance of ConfirmImportantSitesDialogFragment with the bundle arguments set. |
| + */ |
| + public static ConfirmImportantSitesDialogFragment newInstance(String[] importantDomains) { |
| + ConfirmImportantSitesDialogFragment dialogFragment = |
| + new ConfirmImportantSitesDialogFragment(); |
| + Bundle bundle = new Bundle(); |
| + bundle.putStringArray(IMPORTANT_DOMAINS_TAG, importantDomains); |
| + dialogFragment.setArguments(bundle); |
| + return dialogFragment; |
| + } |
| + |
| + private static final int FAVICON_MAX_CACHE_SIZE_BYTES = 1 * 1024 * 1024; // 1MB |
| + |
| + /** 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 to store the important domains in the bundle. */ |
| + private static final String IMPORTANT_DOMAINS_TAG = "ImportantDomains"; |
| + |
| + /** Transforms a domain into an origin for favicon lookup. */ |
| + private static String transformDomainToOrigin(String domain) { |
| + return "http://www." + domain; |
| + } |
| + |
| + /** Array of important registerable domains we're showing to the user. */ |
| + private String[] mImportantDomains; |
| + /** 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<>(); |
| + mProfile = Profile.getLastUsedProfile(); |
| + mLargeIconBridge = new LargeIconBridge(mProfile); |
| + ActivityManager activityManager = |
| + ((ActivityManager) ApplicationStatus.getApplicationContext().getSystemService( |
| + Context.ACTIVITY_SERVICE)); |
| + int maxSize = Math.min( |
| + activityManager.getMemoryClass() / 32 * 1024 * 1024, FAVICON_MAX_CACHE_SIZE_BYTES); |
| + mLargeIconBridge.createCache(maxSize); |
| + } |
| + |
| + @Override |
| + public void setArguments(Bundle args) { |
| + super.setArguments(args); |
| + mImportantDomains = args.getStringArray(IMPORTANT_DOMAINS_TAG); |
| + } |
| + |
| + @VisibleForTesting |
| + public Set<String> getDeselectedDomains() { |
| + return mDeselectedDomains; |
| + } |
| + |
| + @VisibleForTesting |
| + public ListView getSitesList() { |
| + return mSitesListView; |
| + } |
| + |
| + @Override |
| + public void onDismiss(DialogInterface dialog) { |
| + super.onDismiss(dialog); |
| + mLargeIconBridge.destroy(); |
| + } |
| + |
| + @Override |
| + public Dialog onCreateDialog(Bundle savedInstanceState) { |
| + mAdapter = new ClearBrowsingDataAdapter(mImportantDomains, 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 = getActivity().getIntent(); |
| + 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. |
| + mSitesListView = (ListView) getActivity().getLayoutInflater().inflate( |
| + R.layout.select_dialog_listview, null); |
| + mSitesListView.setAdapter(mAdapter); |
| + mSitesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() { |
| + @Override |
| + public void onItemClick(AdapterView<?> parent, View v, int position, long id) { |
| + mAdapter.onClick(position, v); |
| + } |
| + }); |
| + final AlertDialog.Builder builder = |
| + new AlertDialog.Builder(getActivity(), R.style.AlertDialogTheme) |
| + .setTitle(R.string.storage_clear_site_storage_title) |
| + .setMessage(R.string.clear_browsing_data_important_dialog_text) |
| + .setPositiveButton(R.string.clear_data_delete, listener) |
| + .setNegativeButton(R.string.cancel, listener) |
| + .setView(mSitesListView); |
| + mDialog = builder.create(); |
| + |
| + return mDialog; |
| + } |
| +} |