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
17package com.android.browser.preferences;
18
19import android.app.AlertDialog;
20import android.app.ListFragment;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.database.Cursor;
24import android.graphics.Bitmap;
25import android.graphics.BitmapFactory;
26import android.net.Uri;
27import android.os.AsyncTask;
28import android.os.Bundle;
29import android.os.Parcel;
30import android.os.Parcelable;
31import android.preference.PreferenceActivity;
32import android.provider.BrowserContract.Bookmarks;
33import android.util.Log;
34import android.view.LayoutInflater;
35import android.view.View;
36import android.view.View.OnClickListener;
37import android.view.ViewGroup;
38import android.webkit.GeolocationPermissions;
39import android.webkit.ValueCallback;
40import android.webkit.WebStorage;
41import android.widget.AdapterView;
42import android.widget.ArrayAdapter;
43import android.widget.ImageView;
44import android.widget.TextView;
45
46import com.android.browser.R;
47import com.android.browser.WebStorageSizeManager;
48
49import java.util.HashMap;
50import java.util.HashSet;
51import java.util.Iterator;
52import java.util.Map;
53import 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 */
60public 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