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