Home | History | Annotate | Download | only in preferences
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.browser.preferences;
     18 
     19 import android.app.AlertDialog;
     20 import android.app.ListFragment;
     21 import android.content.Context;
     22 import android.content.DialogInterface;
     23 import android.database.Cursor;
     24 import android.graphics.Bitmap;
     25 import android.graphics.BitmapFactory;
     26 import android.net.Uri;
     27 import android.os.AsyncTask;
     28 import android.os.Bundle;
     29 import android.os.Parcel;
     30 import android.os.Parcelable;
     31 import android.preference.PreferenceActivity;
     32 import android.provider.BrowserContract.Bookmarks;
     33 import android.util.Log;
     34 import android.view.LayoutInflater;
     35 import android.view.View;
     36 import android.view.View.OnClickListener;
     37 import android.view.ViewGroup;
     38 import android.webkit.GeolocationPermissions;
     39 import android.webkit.ValueCallback;
     40 import android.webkit.WebStorage;
     41 import android.widget.AdapterView;
     42 import android.widget.ArrayAdapter;
     43 import android.widget.ImageView;
     44 import android.widget.TextView;
     45 
     46 import com.android.browser.R;
     47 import com.android.browser.WebStorageSizeManager;
     48 
     49 import java.util.HashMap;
     50 import java.util.HashSet;
     51 import java.util.Iterator;
     52 import java.util.Map;
     53 import java.util.Set;
     54 
     55 /**
     56  * Manage the settings for an origin.
     57  * We use it to keep track of the 'HTML5' settings, i.e. database (webstorage)
     58  * and Geolocation.
     59  */
     60 public class WebsiteSettingsFragment extends ListFragment implements OnClickListener {
     61 
     62     private static final String EXTRA_SITE = "site";
     63     private String LOGTAG = "WebsiteSettingsActivity";
     64     private static String sMBStored = null;
     65     private SiteAdapter mAdapter = null;
     66     private Site mSite = null;
     67 
     68     static class Site implements Parcelable {
     69         private String mOrigin;
     70         private String mTitle;
     71         private Bitmap mIcon;
     72         private int mFeatures;
     73 
     74         // These constants provide the set of features that a site may support
     75         // They must be consecutive. To add a new feature, add a new FEATURE_XXX
     76         // variable with value equal to the current value of FEATURE_COUNT, then
     77         // increment FEATURE_COUNT.
     78         final static int FEATURE_WEB_STORAGE = 0;
     79         final static int FEATURE_GEOLOCATION = 1;
     80         // The number of features available.
     81         final static int FEATURE_COUNT = 2;
     82 
     83         public Site(String origin) {
     84             mOrigin = origin;
     85             mTitle = null;
     86             mIcon = null;
     87             mFeatures = 0;
     88         }
     89 
     90         public void addFeature(int feature) {
     91             mFeatures |= (1 << feature);
     92         }
     93 
     94         public void removeFeature(int feature) {
     95             mFeatures &= ~(1 << feature);
     96         }
     97 
     98         public boolean hasFeature(int feature) {
     99             return (mFeatures & (1 << feature)) != 0;
    100         }
    101 
    102         /**
    103          * Gets the number of features supported by this site.
    104          */
    105         public int getFeatureCount() {
    106             int count = 0;
    107             for (int i = 0; i < FEATURE_COUNT; ++i) {
    108                 count += hasFeature(i) ? 1 : 0;
    109             }
    110             return count;
    111         }
    112 
    113         /**
    114          * Gets the ID of the nth (zero-based) feature supported by this site.
    115          * The return value is a feature ID - one of the FEATURE_XXX values.
    116          * This is required to determine which feature is displayed at a given
    117          * position in the list of features for this site. This is used both
    118          * when populating the view and when responding to clicks on the list.
    119          */
    120         public int getFeatureByIndex(int n) {
    121             int j = -1;
    122             for (int i = 0; i < FEATURE_COUNT; ++i) {
    123                 j += hasFeature(i) ? 1 : 0;
    124                 if (j == n) {
    125                     return i;
    126                 }
    127             }
    128             return -1;
    129         }
    130 
    131         public String getOrigin() {
    132             return mOrigin;
    133         }
    134 
    135         public void setTitle(String title) {
    136             mTitle = title;
    137         }
    138 
    139         public void setIcon(Bitmap icon) {
    140             mIcon = icon;
    141         }
    142 
    143         public Bitmap getIcon() {
    144             return mIcon;
    145         }
    146 
    147         public String getPrettyOrigin() {
    148             return mTitle == null ? null : hideHttp(mOrigin);
    149         }
    150 
    151         public String getPrettyTitle() {
    152             return mTitle == null ? hideHttp(mOrigin) : mTitle;
    153         }
    154 
    155         private String hideHttp(String str) {
    156             Uri uri = Uri.parse(str);
    157             return "http".equals(uri.getScheme()) ?  str.substring(7) : str;
    158         }
    159 
    160         @Override
    161         public int describeContents() {
    162             return 0;
    163         }
    164 
    165         @Override
    166         public void writeToParcel(Parcel dest, int flags) {
    167             dest.writeString(mOrigin);
    168             dest.writeString(mTitle);
    169             dest.writeInt(mFeatures);
    170             dest.writeParcelable(mIcon, flags);
    171         }
    172 
    173         private Site(Parcel in) {
    174             mOrigin = in.readString();
    175             mTitle = in.readString();
    176             mFeatures = in.readInt();
    177             mIcon = in.readParcelable(null);
    178         }
    179 
    180         public static final Parcelable.Creator<Site> CREATOR
    181                 = new Parcelable.Creator<Site>() {
    182             public Site createFromParcel(Parcel in) {
    183                 return new Site(in);
    184             }
    185 
    186             public Site[] newArray(int size) {
    187                 return new Site[size];
    188             }
    189         };
    190 
    191     }
    192 
    193     class SiteAdapter extends ArrayAdapter<Site>
    194             implements AdapterView.OnItemClickListener {
    195         private int mResource;
    196         private LayoutInflater mInflater;
    197         private Bitmap mDefaultIcon;
    198         private Bitmap mUsageEmptyIcon;
    199         private Bitmap mUsageLowIcon;
    200         private Bitmap mUsageHighIcon;
    201         private Bitmap mLocationAllowedIcon;
    202         private Bitmap mLocationDisallowedIcon;
    203         private Site mCurrentSite;
    204 
    205         public SiteAdapter(Context context, int rsc) {
    206             this(context, rsc, null);
    207         }
    208 
    209         public SiteAdapter(Context context, int rsc, Site site) {
    210             super(context, rsc);
    211             mResource = rsc;
    212             mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    213             mDefaultIcon = BitmapFactory.decodeResource(getResources(),
    214                     R.drawable.app_web_browser_sm);
    215             mUsageEmptyIcon = BitmapFactory.decodeResource(getResources(),
    216                     R.drawable.ic_list_data_off);
    217             mUsageLowIcon = BitmapFactory.decodeResource(getResources(),
    218                     R.drawable.ic_list_data_small);
    219             mUsageHighIcon = BitmapFactory.decodeResource(getResources(),
    220                     R.drawable.ic_list_data_large);
    221             mLocationAllowedIcon = BitmapFactory.decodeResource(getResources(),
    222                     R.drawable.ic_gps_on_holo_dark);
    223             mLocationDisallowedIcon = BitmapFactory.decodeResource(getResources(),
    224                     R.drawable.ic_gps_denied_holo_dark);
    225             mCurrentSite = site;
    226             if (mCurrentSite == null) {
    227                 askForOrigins();
    228             }
    229         }
    230 
    231         /**
    232          * Adds the specified feature to the site corresponding to supplied
    233          * origin in the map. Creates the site if it does not already exist.
    234          */
    235         private void addFeatureToSite(Map<String, Site> sites, String origin, int feature) {
    236             Site site = null;
    237             if (sites.containsKey(origin)) {
    238                 site = (Site) sites.get(origin);
    239             } else {
    240                 site = new Site(origin);
    241                 sites.put(origin, site);
    242             }
    243             site.addFeature(feature);
    244         }
    245 
    246         public void askForOrigins() {
    247             // Get the list of origins we want to display.
    248             // All 'HTML 5 modules' (Database, Geolocation etc) form these
    249             // origin strings using WebCore::SecurityOrigin::toString(), so it's
    250             // safe to group origins here. Note that WebCore::SecurityOrigin
    251             // uses 0 (which is not printed) for the port if the port is the
    252             // default for the protocol. Eg http://www.google.com and
    253             // http://www.google.com:80 both record a port of 0 and hence
    254             // toString() == 'http://www.google.com' for both.
    255 
    256             WebStorage.getInstance().getOrigins(new ValueCallback<Map>() {
    257                 public void onReceiveValue(Map origins) {
    258                     Map<String, Site> sites = new HashMap<String, Site>();
    259                     if (origins != null) {
    260                         Iterator<String> iter = origins.keySet().iterator();
    261                         while (iter.hasNext()) {
    262                             addFeatureToSite(sites, iter.next(), Site.FEATURE_WEB_STORAGE);
    263                         }
    264                     }
    265                     askForGeolocation(sites);
    266                 }
    267             });
    268         }
    269 
    270         public void askForGeolocation(final Map<String, Site> sites) {
    271             GeolocationPermissions.getInstance().getOrigins(new ValueCallback<Set<String> >() {
    272                 public void onReceiveValue(Set<String> origins) {
    273                     if (origins != null) {
    274                         Iterator<String> iter = origins.iterator();
    275                         while (iter.hasNext()) {
    276                             addFeatureToSite(sites, iter.next(), Site.FEATURE_GEOLOCATION);
    277                         }
    278                     }
    279                     populateIcons(sites);
    280                     populateOrigins(sites);
    281                 }
    282             });
    283         }
    284 
    285         public void populateIcons(Map<String, Site> sites) {
    286             // Create a map from host to origin. This is used to add metadata
    287             // (title, icon) for this origin from the bookmarks DB. We must do
    288             // the DB access on a background thread.
    289             new UpdateFromBookmarksDbTask(this.getContext(), sites).execute();
    290         }
    291 
    292         private class UpdateFromBookmarksDbTask extends AsyncTask<Void, Void, Void> {
    293 
    294             private Context mContext;
    295             private boolean mDataSetChanged;
    296             private Map<String, Site> mSites;
    297 
    298             public UpdateFromBookmarksDbTask(Context ctx, Map<String, Site> sites) {
    299                 mContext = ctx.getApplicationContext();
    300                 mSites = sites;
    301             }
    302 
    303             protected Void doInBackground(Void... unused) {
    304                 HashMap<String, Set<Site>> hosts = new HashMap<String, Set<Site>>();
    305                 Set<Map.Entry<String, Site>> elements = mSites.entrySet();
    306                 Iterator<Map.Entry<String, Site>> originIter = elements.iterator();
    307                 while (originIter.hasNext()) {
    308                     Map.Entry<String, Site> entry = originIter.next();
    309                     Site site = entry.getValue();
    310                     String host = Uri.parse(entry.getKey()).getHost();
    311                     Set<Site> hostSites = null;
    312                     if (hosts.containsKey(host)) {
    313                         hostSites = (Set<Site>)hosts.get(host);
    314                     } else {
    315                         hostSites = new HashSet<Site>();
    316                         hosts.put(host, hostSites);
    317                     }
    318                     hostSites.add(site);
    319                 }
    320 
    321                 // Check the bookmark DB. If we have data for a host used by any of
    322                 // our origins, use it to set their title and favicon
    323                 Cursor c = mContext.getContentResolver().query(Bookmarks.CONTENT_URI,
    324                         new String[] { Bookmarks.URL, Bookmarks.TITLE, Bookmarks.FAVICON },
    325                         Bookmarks.IS_FOLDER + " == 0", null, null);
    326 
    327                 if (c != null) {
    328                     if (c.moveToFirst()) {
    329                         int urlIndex = c.getColumnIndex(Bookmarks.URL);
    330                         int titleIndex = c.getColumnIndex(Bookmarks.TITLE);
    331                         int faviconIndex = c.getColumnIndex(Bookmarks.FAVICON);
    332                         do {
    333                             String url = c.getString(urlIndex);
    334                             String host = Uri.parse(url).getHost();
    335                             if (hosts.containsKey(host)) {
    336                                 String title = c.getString(titleIndex);
    337                                 Bitmap bmp = null;
    338                                 byte[] data = c.getBlob(faviconIndex);
    339                                 if (data != null) {
    340                                     bmp = BitmapFactory.decodeByteArray(data, 0, data.length);
    341                                 }
    342                                 Set matchingSites = (Set) hosts.get(host);
    343                                 Iterator<Site> sitesIter = matchingSites.iterator();
    344                                 while (sitesIter.hasNext()) {
    345                                     Site site = sitesIter.next();
    346                                     // We should only set the title if the bookmark is for the root
    347                                     // (i.e. www.google.com), as website settings act on the origin
    348                                     // as a whole rather than a single page under that origin. If the
    349                                     // user has bookmarked a page under the root but *not* the root,
    350                                     // then we risk displaying the title of that page which may or
    351                                     // may not have any relevance to the origin.
    352                                     if (url.equals(site.getOrigin()) ||
    353                                             (new String(site.getOrigin()+"/")).equals(url)) {
    354                                         mDataSetChanged = true;
    355                                         site.setTitle(title);
    356                                     }
    357 
    358                                     if (bmp != null) {
    359                                         mDataSetChanged = true;
    360                                         site.setIcon(bmp);
    361                                     }
    362                                 }
    363                             }
    364                         } while (c.moveToNext());
    365                     }
    366                     c.close();
    367                 }
    368             return null;
    369             }
    370 
    371             protected void onPostExecute(Void unused) {
    372                 if (mDataSetChanged) {
    373                     notifyDataSetChanged();
    374                 }
    375             }
    376         }
    377 
    378 
    379         public void populateOrigins(Map<String, Site> sites) {
    380             clear();
    381 
    382             // We can now simply populate our array with Site instances
    383             Set<Map.Entry<String, Site>> elements = sites.entrySet();
    384             Iterator<Map.Entry<String, Site>> entryIterator = elements.iterator();
    385             while (entryIterator.hasNext()) {
    386                 Map.Entry<String, Site> entry = entryIterator.next();
    387                 Site site = entry.getValue();
    388                 add(site);
    389             }
    390 
    391             notifyDataSetChanged();
    392 
    393             if (getCount() == 0) {
    394                 finish(); // we close the screen
    395             }
    396         }
    397 
    398         public int getCount() {
    399             if (mCurrentSite == null) {
    400                 return super.getCount();
    401             }
    402             return mCurrentSite.getFeatureCount();
    403         }
    404 
    405         public String sizeValueToString(long bytes) {
    406             // We display the size in MB, to 1dp, rounding up to the next 0.1MB.
    407             // bytes should always be greater than zero.
    408             if (bytes <= 0) {
    409                 Log.e(LOGTAG, "sizeValueToString called with non-positive value: " + bytes);
    410                 return "0";
    411             }
    412             float megabytes = (float) bytes / (1024.0F * 1024.0F);
    413             int truncated = (int) Math.ceil(megabytes * 10.0F);
    414             float result = (float) (truncated / 10.0F);
    415             return String.valueOf(result);
    416         }
    417 
    418         /*
    419          * If we receive the back event and are displaying
    420          * site's settings, we want to go back to the main
    421          * list view. If not, we just do nothing (see
    422          * dispatchKeyEvent() below).
    423          */
    424         public boolean backKeyPressed() {
    425             if (mCurrentSite != null) {
    426                 mCurrentSite = null;
    427                 askForOrigins();
    428                 return true;
    429             }
    430             return false;
    431         }
    432 
    433         /**
    434          * @hide
    435          * Utility function
    436          * Set the icon according to the usage
    437          */
    438         public void setIconForUsage(ImageView usageIcon, long usageInBytes) {
    439             float usageInMegabytes = (float) usageInBytes / (1024.0F * 1024.0F);
    440             // We set the correct icon:
    441             // 0 < empty < 0.1MB
    442             // 0.1MB < low < 5MB
    443             // 5MB < high
    444             if (usageInMegabytes <= 0.1) {
    445                 usageIcon.setImageBitmap(mUsageEmptyIcon);
    446             } else if (usageInMegabytes > 0.1 && usageInMegabytes <= 5) {
    447                 usageIcon.setImageBitmap(mUsageLowIcon);
    448             } else if (usageInMegabytes > 5) {
    449                 usageIcon.setImageBitmap(mUsageHighIcon);
    450             }
    451         }
    452 
    453         @Override
    454         public View getView(int position, View convertView, ViewGroup parent) {
    455             View view;
    456             final TextView title;
    457             final TextView subtitle;
    458             final ImageView icon;
    459             final ImageView usageIcon;
    460             final ImageView locationIcon;
    461             final ImageView featureIcon;
    462 
    463             if (convertView == null) {
    464                 view = mInflater.inflate(mResource, parent, false);
    465             } else {
    466                 view = convertView;
    467             }
    468 
    469             title = (TextView) view.findViewById(R.id.title);
    470             subtitle = (TextView) view.findViewById(R.id.subtitle);
    471             icon = (ImageView) view.findViewById(R.id.icon);
    472             featureIcon = (ImageView) view.findViewById(R.id.feature_icon);
    473             usageIcon = (ImageView) view.findViewById(R.id.usage_icon);
    474             locationIcon = (ImageView) view.findViewById(R.id.location_icon);
    475             usageIcon.setVisibility(View.GONE);
    476             locationIcon.setVisibility(View.GONE);
    477 
    478             if (mCurrentSite == null) {
    479 
    480                 Site site = getItem(position);
    481                 title.setText(site.getPrettyTitle());
    482                 String subtitleText = site.getPrettyOrigin();
    483                 if (subtitleText != null) {
    484                     title.setMaxLines(1);
    485                     title.setSingleLine(true);
    486                     subtitle.setVisibility(View.VISIBLE);
    487                     subtitle.setText(subtitleText);
    488                 } else {
    489                     subtitle.setVisibility(View.GONE);
    490                     title.setMaxLines(2);
    491                     title.setSingleLine(false);
    492                 }
    493 
    494                 icon.setVisibility(View.VISIBLE);
    495                 usageIcon.setVisibility(View.INVISIBLE);
    496                 locationIcon.setVisibility(View.INVISIBLE);
    497                 featureIcon.setVisibility(View.GONE);
    498                 Bitmap bmp = site.getIcon();
    499                 if (bmp == null) {
    500                     bmp = mDefaultIcon;
    501                 }
    502                 icon.setImageBitmap(bmp);
    503                 // We set the site as the view's tag,
    504                 // so that we can get it in onItemClick()
    505                 view.setTag(site);
    506 
    507                 String origin = site.getOrigin();
    508                 if (site.hasFeature(Site.FEATURE_WEB_STORAGE)) {
    509                     WebStorage.getInstance().getUsageForOrigin(origin, new ValueCallback<Long>() {
    510                         public void onReceiveValue(Long value) {
    511                             if (value != null) {
    512                                 setIconForUsage(usageIcon, value.longValue());
    513                                 usageIcon.setVisibility(View.VISIBLE);
    514                             }
    515                         }
    516                     });
    517                 }
    518 
    519                 if (site.hasFeature(Site.FEATURE_GEOLOCATION)) {
    520                     locationIcon.setVisibility(View.VISIBLE);
    521                     GeolocationPermissions.getInstance().getAllowed(origin, new ValueCallback<Boolean>() {
    522                         public void onReceiveValue(Boolean allowed) {
    523                             if (allowed != null) {
    524                                 if (allowed.booleanValue()) {
    525                                     locationIcon.setImageBitmap(mLocationAllowedIcon);
    526                                 } else {
    527                                     locationIcon.setImageBitmap(mLocationDisallowedIcon);
    528                                 }
    529                             }
    530                         }
    531                     });
    532                 }
    533             } else {
    534                 icon.setVisibility(View.GONE);
    535                 locationIcon.setVisibility(View.GONE);
    536                 usageIcon.setVisibility(View.GONE);
    537                 featureIcon.setVisibility(View.VISIBLE);
    538                 String origin = mCurrentSite.getOrigin();
    539                 switch (mCurrentSite.getFeatureByIndex(position)) {
    540                     case Site.FEATURE_WEB_STORAGE:
    541                         WebStorage.getInstance().getUsageForOrigin(origin, new ValueCallback<Long>() {
    542                             public void onReceiveValue(Long value) {
    543                                 if (value != null) {
    544                                     String usage = sizeValueToString(value.longValue()) + " " + sMBStored;
    545                                     title.setText(R.string.webstorage_clear_data_title);
    546                                     subtitle.setText(usage);
    547                                     subtitle.setVisibility(View.VISIBLE);
    548                                     setIconForUsage(featureIcon, value.longValue());
    549                                 }
    550                             }
    551                         });
    552                         break;
    553                     case Site.FEATURE_GEOLOCATION:
    554                         title.setText(R.string.geolocation_settings_page_title);
    555                         GeolocationPermissions.getInstance().getAllowed(origin, new ValueCallback<Boolean>() {
    556                             public void onReceiveValue(Boolean allowed) {
    557                                 if (allowed != null) {
    558                                     if (allowed.booleanValue()) {
    559                                         subtitle.setText(R.string.geolocation_settings_page_summary_allowed);
    560                                         featureIcon.setImageBitmap(mLocationAllowedIcon);
    561                                     } else {
    562                                         subtitle.setText(R.string.geolocation_settings_page_summary_not_allowed);
    563                                         featureIcon.setImageBitmap(mLocationDisallowedIcon);
    564                                     }
    565                                     subtitle.setVisibility(View.VISIBLE);
    566                                 }
    567                             }
    568                         });
    569                         break;
    570                 }
    571             }
    572 
    573             return view;
    574         }
    575 
    576         public void onItemClick(AdapterView<?> parent,
    577                                 View view,
    578                                 int position,
    579                                 long id) {
    580             if (mCurrentSite != null) {
    581                 switch (mCurrentSite.getFeatureByIndex(position)) {
    582                     case Site.FEATURE_WEB_STORAGE:
    583                         new AlertDialog.Builder(getContext())
    584                             .setMessage(R.string.webstorage_clear_data_dialog_message)
    585                             .setPositiveButton(R.string.webstorage_clear_data_dialog_ok_button,
    586                                                new AlertDialog.OnClickListener() {
    587                                 public void onClick(DialogInterface dlg, int which) {
    588                                     WebStorage.getInstance().deleteOrigin(mCurrentSite.getOrigin());
    589                                     // If this site has no more features, then go back to the
    590                                     // origins list.
    591                                     mCurrentSite.removeFeature(Site.FEATURE_WEB_STORAGE);
    592                                     if (mCurrentSite.getFeatureCount() == 0) {
    593                                         finish();
    594                                     }
    595                                     askForOrigins();
    596                                     notifyDataSetChanged();
    597                                 }})
    598                             .setNegativeButton(R.string.webstorage_clear_data_dialog_cancel_button, null)
    599                             .setIconAttribute(android.R.attr.alertDialogIcon)
    600                             .show();
    601                         break;
    602                     case Site.FEATURE_GEOLOCATION:
    603                         new AlertDialog.Builder(getContext())
    604                             .setMessage(R.string.geolocation_settings_page_dialog_message)
    605                             .setPositiveButton(R.string.geolocation_settings_page_dialog_ok_button,
    606                                                new AlertDialog.OnClickListener() {
    607                                 public void onClick(DialogInterface dlg, int which) {
    608                                     GeolocationPermissions.getInstance().clear(mCurrentSite.getOrigin());
    609                                     mCurrentSite.removeFeature(Site.FEATURE_GEOLOCATION);
    610                                     if (mCurrentSite.getFeatureCount() == 0) {
    611                                         finish();
    612                                     }
    613                                     askForOrigins();
    614                                     notifyDataSetChanged();
    615                                 }})
    616                             .setNegativeButton(R.string.geolocation_settings_page_dialog_cancel_button, null)
    617                             .setIconAttribute(android.R.attr.alertDialogIcon)
    618                             .show();
    619                         break;
    620                 }
    621             } else {
    622                 Site site = (Site) view.getTag();
    623                 PreferenceActivity activity = (PreferenceActivity) getActivity();
    624                 if (activity != null) {
    625                     Bundle args = new Bundle();
    626                     args.putParcelable(EXTRA_SITE, site);
    627                     activity.startPreferencePanel(WebsiteSettingsFragment.class.getName(), args, 0,
    628                             site.getPrettyTitle(), null, 0);
    629                 }
    630             }
    631         }
    632 
    633         public Site currentSite() {
    634             return mCurrentSite;
    635         }
    636     }
    637 
    638     @Override
    639     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    640             Bundle savedInstanceState) {
    641         View view = inflater.inflate(R.layout.website_settings, container, false);
    642         Bundle args = getArguments();
    643         if (args != null) {
    644             mSite = (Site) args.getParcelable(EXTRA_SITE);
    645         }
    646         if (mSite == null) {
    647             View clear = view.findViewById(R.id.clear_all_button);
    648             clear.setVisibility(View.VISIBLE);
    649             clear.setOnClickListener(this);
    650         }
    651         return view;
    652     }
    653 
    654     @Override
    655     public void onActivityCreated(Bundle savedInstanceState) {
    656         super.onActivityCreated(savedInstanceState);
    657         if (sMBStored == null) {
    658             sMBStored = getString(R.string.webstorage_origin_summary_mb_stored);
    659         }
    660         mAdapter = new SiteAdapter(getActivity(), R.layout.website_settings_row);
    661         if (mSite != null) {
    662             mAdapter.mCurrentSite = mSite;
    663         }
    664         getListView().setAdapter(mAdapter);
    665         getListView().setOnItemClickListener(mAdapter);
    666     }
    667 
    668     @Override
    669     public void onResume() {
    670         super.onResume();
    671         mAdapter.askForOrigins();
    672     }
    673 
    674     private void finish() {
    675         PreferenceActivity activity = (PreferenceActivity) getActivity();
    676         if (activity != null) {
    677             activity.finishPreferencePanel(this, 0, null);
    678         }
    679     }
    680 
    681     @Override
    682     public void onClick(View v) {
    683         switch (v.getId()) {
    684         case R.id.clear_all_button:
    685          // Show the prompt to clear all origins of their data and geolocation permissions.
    686             new AlertDialog.Builder(getActivity())
    687                     .setMessage(R.string.website_settings_clear_all_dialog_message)
    688                     .setPositiveButton(R.string.website_settings_clear_all_dialog_ok_button,
    689                             new AlertDialog.OnClickListener() {
    690                                 public void onClick(DialogInterface dlg, int which) {
    691                                     WebStorage.getInstance().deleteAllData();
    692                                     GeolocationPermissions.getInstance().clearAll();
    693                                     WebStorageSizeManager.resetLastOutOfSpaceNotificationTime();
    694                                     mAdapter.askForOrigins();
    695                                     finish();
    696                                 }})
    697                     .setNegativeButton(R.string.website_settings_clear_all_dialog_cancel_button, null)
    698                     .setIconAttribute(android.R.attr.alertDialogIcon)
    699                     .show();
    700             break;
    701         }
    702     }
    703 }
    704