PageRenderTime 68ms CodeModel.GetById 15ms app.highlight 47ms RepoModel.GetById 1ms app.codeStats 0ms

/WebVox/src/com/marvin/webvox/WebsiteSettingsActivity.java

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