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