Browser.java revision e77852c8853465ce69a567a5446f251274f98a15
1/*
2 * Copyright (C) 2006 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 android.provider;
18
19import android.content.ContentResolver;
20import android.content.ContentValues;
21import android.content.Context;
22import android.content.Intent;
23import android.database.Cursor;
24import android.database.DatabaseUtils;
25import android.net.Uri;
26import android.os.AsyncTask;
27import android.util.Log;
28import android.webkit.WebIconDatabase;
29
30import java.util.Date;
31
32public class Browser {
33    private static final String LOGTAG = "browser";
34    public static final Uri BOOKMARKS_URI =
35        Uri.parse("content://browser/bookmarks");
36
37    /**
38     * The name of extra data when starting Browser with ACTION_VIEW or
39     * ACTION_SEARCH intent.
40     * <p>
41     * The value should be an integer between 0 and 1000. If not set or set to
42     * 0, the Browser will use default. If set to 100, the Browser will start
43     * with 100%.
44     */
45    public static final String INITIAL_ZOOM_LEVEL = "browser.initialZoomLevel";
46
47    /**
48     * The name of the extra data when starting the Browser from another
49     * application.
50     * <p>
51     * The value is a unique identification string that will be used to
52     * indentify the calling application. The Browser will attempt to reuse the
53     * same window each time the application launches the Browser with the same
54     * identifier.
55     */
56    public static final String EXTRA_APPLICATION_ID =
57        "com.android.browser.application_id";
58
59    /**
60     * The name of the extra data in the VIEW intent. The data are key/value
61     * pairs in the format of Bundle. They will be sent in the HTTP request
62     * headers for the provided url. The keys can't be the standard HTTP headers
63     * as they are set by the WebView. The url's schema must be http(s).
64     * <p>
65     */
66    public static final String EXTRA_HEADERS = "com.android.browser.headers";
67
68    /* if you change column order you must also change indices
69       below */
70    public static final String[] HISTORY_PROJECTION = new String[] {
71        BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS,
72        BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE,
73        BookmarkColumns.FAVICON, BookmarkColumns.THUMBNAIL,
74        BookmarkColumns.TOUCH_ICON, BookmarkColumns.USER_ENTERED };
75
76    /* these indices dependent on HISTORY_PROJECTION */
77    public static final int HISTORY_PROJECTION_ID_INDEX = 0;
78    public static final int HISTORY_PROJECTION_URL_INDEX = 1;
79    public static final int HISTORY_PROJECTION_VISITS_INDEX = 2;
80    public static final int HISTORY_PROJECTION_DATE_INDEX = 3;
81    public static final int HISTORY_PROJECTION_BOOKMARK_INDEX = 4;
82    public static final int HISTORY_PROJECTION_TITLE_INDEX = 5;
83    public static final int HISTORY_PROJECTION_FAVICON_INDEX = 6;
84    /**
85     * @hide
86     */
87    public static final int HISTORY_PROJECTION_THUMBNAIL_INDEX = 7;
88    /**
89     * @hide
90     */
91    public static final int HISTORY_PROJECTION_TOUCH_ICON_INDEX = 8;
92
93    /* columns needed to determine whether to truncate history */
94    public static final String[] TRUNCATE_HISTORY_PROJECTION = new String[] {
95        BookmarkColumns._ID, BookmarkColumns.DATE, };
96    public static final int TRUNCATE_HISTORY_PROJECTION_ID_INDEX = 0;
97
98    /* truncate this many history items at a time */
99    public static final int TRUNCATE_N_OLDEST = 5;
100
101    public static final Uri SEARCHES_URI =
102        Uri.parse("content://browser/searches");
103
104    /* if you change column order you must also change indices
105       below */
106    public static final String[] SEARCHES_PROJECTION = new String[] {
107        SearchColumns._ID, SearchColumns.SEARCH, SearchColumns.DATE };
108
109    /* these indices dependent on SEARCHES_PROJECTION */
110    public static final int SEARCHES_PROJECTION_SEARCH_INDEX = 1;
111    public static final int SEARCHES_PROJECTION_DATE_INDEX = 2;
112
113    private static final String SEARCHES_WHERE_CLAUSE = "search = ?";
114
115    /* Set a cap on the count of history items in the history/bookmark
116       table, to prevent db and layout operations from dragging to a
117       crawl.  Revisit this cap when/if db/layout performance
118       improvements are made.  Note: this does not affect bookmark
119       entries -- if the user wants more bookmarks than the cap, they
120       get them. */
121    private static final int MAX_HISTORY_COUNT = 250;
122
123    /**
124     * URI for writing geolocation permissions. This requires the
125     * {@link android.Manifest.permission#WRITE_GEOLOCATION_PERMISSIONS}.
126     */
127    public static final Uri GEOLOCATION_URI =
128            Uri.parse("content://browser/geolocation");
129
130    private static final String GEOLOCATION_WHERE_CLAUSE = GeolocationColumns.ORIGIN + " = ?";
131
132    /**
133     *  Open the AddBookmark activity to save a bookmark.  Launch with
134     *  and/or url, which can be edited by the user before saving.
135     *  @param c        Context used to launch the AddBookmark activity.
136     *  @param title    Title for the bookmark. Can be null or empty string.
137     *  @param url      Url for the bookmark. Can be null or empty string.
138     */
139    public static final void saveBookmark(Context c,
140                                          String title,
141                                          String url) {
142        Intent i = new Intent(Intent.ACTION_INSERT, Browser.BOOKMARKS_URI);
143        i.putExtra("title", title);
144        i.putExtra("url", url);
145        c.startActivity(i);
146    }
147
148    /**
149     * Stores a Bitmap extra in an {@link Intent} representing the screenshot of
150     * a page to share.  When receiving an {@link Intent#ACTION_SEND} from the
151     * Browser, use this to access the screenshot.
152     * @hide
153     */
154    public final static String EXTRA_SHARE_SCREENSHOT = "share_screenshot";
155
156    /**
157     * Stores a Bitmap extra in an {@link Intent} representing the favicon of a
158     * page to share.  When receiving an {@link Intent#ACTION_SEND} from the
159     * Browser, use this to access the favicon.
160     * @hide
161     */
162    public final static String EXTRA_SHARE_FAVICON = "share_favicon";
163
164    public static final void sendString(Context c, String s) {
165        sendString(c, s, c.getString(com.android.internal.R.string.sendText));
166    }
167
168    /**
169     *  Find an application to handle the given string and, if found, invoke
170     *  it with the given string as a parameter.
171     *  @param c Context used to launch the new activity.
172     *  @param stringToSend The string to be handled.
173     *  @param chooserDialogTitle The title of the dialog that allows the user
174     *  to select between multiple applications that are all capable of handling
175     *  the string.
176     *  @hide pending API council approval
177     */
178    public static final void sendString(Context c,
179                                        String stringToSend,
180                                        String chooserDialogTitle) {
181        Intent send = new Intent(Intent.ACTION_SEND);
182        send.setType("text/plain");
183        send.putExtra(Intent.EXTRA_TEXT, stringToSend);
184
185        try {
186            c.startActivity(Intent.createChooser(send, chooserDialogTitle));
187        } catch(android.content.ActivityNotFoundException ex) {
188            // if no app handles it, do nothing
189        }
190    }
191
192    /**
193     *  Return a cursor pointing to a list of all the bookmarks.
194     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
195     *  @param cr   The ContentResolver used to access the database.
196     */
197    public static final Cursor getAllBookmarks(ContentResolver cr) throws
198            IllegalStateException {
199        return cr.query(BOOKMARKS_URI,
200                new String[] { BookmarkColumns.URL },
201                "bookmark = 1", null, null);
202    }
203
204    /**
205     *  Return a cursor pointing to a list of all visited site urls.
206     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
207     *  @param cr   The ContentResolver used to access the database.
208     */
209    public static final Cursor getAllVisitedUrls(ContentResolver cr) throws
210            IllegalStateException {
211        return cr.query(BOOKMARKS_URI,
212                new String[] { BookmarkColumns.URL }, null, null, null);
213    }
214
215    private static final void addOrUrlEquals(StringBuilder sb) {
216        sb.append(" OR " + BookmarkColumns.URL + " = ");
217    }
218
219    /**
220     *  Return a Cursor with all history/bookmarks that are similar to url,
221     *  where similar means 'http(s)://' and 'www.' are optional, but the rest
222     *  of the url is the same.
223     *  @param cr   The ContentResolver used to access the database.
224     *  @param url  The url to compare to.
225     *  @hide
226     */
227    public static final Cursor getVisitedLike(ContentResolver cr, String url) {
228        boolean secure = false;
229        String compareString = url;
230        if (compareString.startsWith("http://")) {
231            compareString = compareString.substring(7);
232        } else if (compareString.startsWith("https://")) {
233            compareString = compareString.substring(8);
234            secure = true;
235        }
236        if (compareString.startsWith("www.")) {
237            compareString = compareString.substring(4);
238        }
239        StringBuilder whereClause = null;
240        if (secure) {
241            whereClause = new StringBuilder(BookmarkColumns.URL + " = ");
242            DatabaseUtils.appendEscapedSQLString(whereClause,
243                    "https://" + compareString);
244            addOrUrlEquals(whereClause);
245            DatabaseUtils.appendEscapedSQLString(whereClause,
246                    "https://www." + compareString);
247        } else {
248            whereClause = new StringBuilder(BookmarkColumns.URL + " = ");
249            DatabaseUtils.appendEscapedSQLString(whereClause,
250                    compareString);
251            addOrUrlEquals(whereClause);
252            String wwwString = "www." + compareString;
253            DatabaseUtils.appendEscapedSQLString(whereClause,
254                    wwwString);
255            addOrUrlEquals(whereClause);
256            DatabaseUtils.appendEscapedSQLString(whereClause,
257                    "http://" + compareString);
258            addOrUrlEquals(whereClause);
259            DatabaseUtils.appendEscapedSQLString(whereClause,
260                    "http://" + wwwString);
261        }
262        return cr.query(BOOKMARKS_URI, HISTORY_PROJECTION,
263                whereClause.toString(), null, null);
264    }
265
266    /**
267     *  Update the visited history to acknowledge that a site has been
268     *  visited.
269     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
270     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
271     *  @param cr   The ContentResolver used to access the database.
272     *  @param url  The site being visited.
273     *  @param real If true, this is an actual visit, and should add to the
274     *              number of visits.  If false, the user entered it manually.
275     */
276    public static final void updateVisitedHistory(ContentResolver cr,
277                                                  String url, boolean real) {
278        long now = new Date().getTime();
279        Cursor c = null;
280        try {
281            c = getVisitedLike(cr, url);
282            /* We should only get one answer that is exactly the same. */
283            if (c.moveToFirst()) {
284                ContentValues map = new ContentValues();
285                if (real) {
286                    map.put(BookmarkColumns.VISITS, c
287                            .getInt(HISTORY_PROJECTION_VISITS_INDEX) + 1);
288                } else {
289                    map.put(BookmarkColumns.USER_ENTERED, 1);
290                }
291                map.put(BookmarkColumns.DATE, now);
292                String[] projection = new String[]
293                        { Integer.valueOf(c.getInt(0)).toString() };
294                cr.update(BOOKMARKS_URI, map, "_id = ?", projection);
295            } else {
296                truncateHistory(cr);
297                ContentValues map = new ContentValues();
298                int visits;
299                int user_entered;
300                if (real) {
301                    visits = 1;
302                    user_entered = 0;
303                } else {
304                    visits = 0;
305                    user_entered = 1;
306                }
307                map.put(BookmarkColumns.URL, url);
308                map.put(BookmarkColumns.VISITS, visits);
309                map.put(BookmarkColumns.DATE, now);
310                map.put(BookmarkColumns.BOOKMARK, 0);
311                map.put(BookmarkColumns.TITLE, url);
312                map.put(BookmarkColumns.CREATED, 0);
313                map.put(BookmarkColumns.USER_ENTERED, user_entered);
314                cr.insert(BOOKMARKS_URI, map);
315            }
316        } catch (IllegalStateException e) {
317            Log.e(LOGTAG, "updateVisitedHistory", e);
318        } finally {
319            if (c != null) c.close();
320        }
321    }
322
323    /**
324     *  Returns all the URLs in the history.
325     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
326     *  @param cr   The ContentResolver used to access the database.
327     *  @hide pending API council approval
328     */
329    public static final String[] getVisitedHistory(ContentResolver cr) {
330        Cursor c = null;
331        String[] str = null;
332        try {
333            String[] projection = new String[] {
334                "url"
335            };
336            c = cr.query(BOOKMARKS_URI, projection, "visits > 0", null,
337                    null);
338            str = new String[c.getCount()];
339            int i = 0;
340            while (c.moveToNext()) {
341                str[i] = c.getString(0);
342                i++;
343            }
344        } catch (IllegalStateException e) {
345            Log.e(LOGTAG, "getVisitedHistory", e);
346            str = new String[0];
347        } finally {
348            if (c != null) c.close();
349        }
350        return str;
351    }
352
353    /**
354     * If there are more than MAX_HISTORY_COUNT non-bookmark history
355     * items in the bookmark/history table, delete TRUNCATE_N_OLDEST
356     * of them.  This is used to keep our history table to a
357     * reasonable size.  Note: it does not prune bookmarks.  If the
358     * user wants 1000 bookmarks, the user gets 1000 bookmarks.
359     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
360     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
361     *
362     * @param cr The ContentResolver used to access the database.
363     */
364    public static final void truncateHistory(ContentResolver cr) {
365        Cursor c = null;
366        try {
367            // Select non-bookmark history, ordered by date
368            c = cr.query(
369                    BOOKMARKS_URI,
370                    TRUNCATE_HISTORY_PROJECTION,
371                    "bookmark = 0",
372                    null,
373                    BookmarkColumns.DATE);
374            // Log.v(LOGTAG, "history count " + c.count());
375            if (c.moveToFirst() && c.getCount() >= MAX_HISTORY_COUNT) {
376                /* eliminate oldest history items */
377                for (int i = 0; i < TRUNCATE_N_OLDEST; i++) {
378                    // Log.v(LOGTAG, "truncate history " +
379                    // c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX));
380                    cr.delete(BOOKMARKS_URI, "_id = " +
381                            c.getInt(TRUNCATE_HISTORY_PROJECTION_ID_INDEX),
382                            null);
383                    if (!c.moveToNext()) break;
384                }
385            }
386        } catch (IllegalStateException e) {
387            Log.e(LOGTAG, "truncateHistory", e);
388        } finally {
389            if (c != null) c.close();
390        }
391    }
392
393    /**
394     * Returns whether there is any history to clear.
395     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
396     * @param cr   The ContentResolver used to access the database.
397     * @return boolean  True if the history can be cleared.
398     */
399    public static final boolean canClearHistory(ContentResolver cr) {
400        Cursor c = null;
401        boolean ret = false;
402        try {
403            c = cr.query(
404                BOOKMARKS_URI,
405                new String [] { BookmarkColumns._ID,
406                                BookmarkColumns.BOOKMARK,
407                                BookmarkColumns.VISITS },
408                "bookmark = 0 OR visits > 0",
409                null,
410                null
411                );
412            ret = c.moveToFirst();
413        } catch (IllegalStateException e) {
414            Log.e(LOGTAG, "canClearHistory", e);
415        } finally {
416            if (c != null) c.close();
417        }
418        return ret;
419    }
420
421    /**
422     *  Delete all entries from the bookmarks/history table which are
423     *  not bookmarks.  Also set all visited bookmarks to unvisited.
424     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
425     *  @param cr   The ContentResolver used to access the database.
426     */
427    public static final void clearHistory(ContentResolver cr) {
428        deleteHistoryWhere(cr, null);
429    }
430
431    /**
432     * Helper function to delete all history items and revert all
433     * bookmarks to zero visits which meet the criteria provided.
434     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
435     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
436     * @param cr   The ContentResolver used to access the database.
437     * @param whereClause   String to limit the items affected.
438     *                      null means all items.
439     */
440    private static final void deleteHistoryWhere(ContentResolver cr,
441            String whereClause) {
442        Cursor c = null;
443        try {
444            c = cr.query(BOOKMARKS_URI,
445                HISTORY_PROJECTION,
446                whereClause,
447                null,
448                null);
449            if (c.moveToFirst()) {
450                final WebIconDatabase iconDb = WebIconDatabase.getInstance();
451                /* Delete favicons, and revert bookmarks which have been visited
452                 * to simply bookmarks.
453                 */
454                StringBuffer sb = new StringBuffer();
455                boolean firstTime = true;
456                do {
457                    String url = c.getString(HISTORY_PROJECTION_URL_INDEX);
458                    boolean isBookmark =
459                        c.getInt(HISTORY_PROJECTION_BOOKMARK_INDEX) == 1;
460                    if (isBookmark) {
461                        if (firstTime) {
462                            firstTime = false;
463                        } else {
464                            sb.append(" OR ");
465                        }
466                        sb.append("( _id = ");
467                        sb.append(c.getInt(0));
468                        sb.append(" )");
469                    } else {
470                        iconDb.releaseIconForPageUrl(url);
471                    }
472                } while (c.moveToNext());
473
474                if (!firstTime) {
475                    ContentValues map = new ContentValues();
476                    map.put(BookmarkColumns.VISITS, 0);
477                    map.put(BookmarkColumns.DATE, 0);
478                    /* FIXME: Should I also remove the title? */
479                    cr.update(BOOKMARKS_URI, map, sb.toString(), null);
480                }
481
482                String deleteWhereClause = BookmarkColumns.BOOKMARK + " = 0";
483                if (whereClause != null) {
484                    deleteWhereClause += " AND " + whereClause;
485                }
486                cr.delete(BOOKMARKS_URI, deleteWhereClause, null);
487            }
488        } catch (IllegalStateException e) {
489            Log.e(LOGTAG, "deleteHistoryWhere", e);
490            return;
491        } finally {
492            if (c != null) c.close();
493        }
494    }
495
496    /**
497     * Delete all history items from begin to end.
498     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
499     * @param cr    The ContentResolver used to access the database.
500     * @param begin First date to remove.  If -1, all dates before end.
501     *              Inclusive.
502     * @param end   Last date to remove. If -1, all dates after begin.
503     *              Non-inclusive.
504     */
505    public static final void deleteHistoryTimeFrame(ContentResolver cr,
506            long begin, long end) {
507        String whereClause;
508        String date = BookmarkColumns.DATE;
509        if (-1 == begin) {
510            if (-1 == end) {
511                clearHistory(cr);
512                return;
513            }
514            whereClause = date + " < " + Long.toString(end);
515        } else if (-1 == end) {
516            whereClause = date + " >= " + Long.toString(begin);
517        } else {
518            whereClause = date + " >= " + Long.toString(begin) + " AND " + date
519                    + " < " + Long.toString(end);
520        }
521        deleteHistoryWhere(cr, whereClause);
522    }
523
524    /**
525     * Remove a specific url from the history database.
526     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
527     * @param cr    The ContentResolver used to access the database.
528     * @param url   url to remove.
529     */
530    public static final void deleteFromHistory(ContentResolver cr,
531                                               String url) {
532        StringBuilder sb = new StringBuilder(BookmarkColumns.URL + " = ");
533        DatabaseUtils.appendEscapedSQLString(sb, url);
534        String matchesUrl = sb.toString();
535        deleteHistoryWhere(cr, matchesUrl);
536    }
537
538    /**
539     * Add a search string to the searches database.
540     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
541     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
542     * @param cr   The ContentResolver used to access the database.
543     * @param search    The string to add to the searches database.
544     */
545    public static final void addSearchUrl(ContentResolver cr, String search) {
546        long now = new Date().getTime();
547        Cursor c = null;
548        try {
549            c = cr.query(
550                SEARCHES_URI,
551                SEARCHES_PROJECTION,
552                SEARCHES_WHERE_CLAUSE,
553                new String [] { search },
554                null);
555            ContentValues map = new ContentValues();
556            map.put(SearchColumns.SEARCH, search);
557            map.put(SearchColumns.DATE, now);
558            /* We should only get one answer that is exactly the same. */
559            if (c.moveToFirst()) {
560                cr.update(SEARCHES_URI, map, "_id = " + c.getInt(0), null);
561            } else {
562                cr.insert(SEARCHES_URI, map);
563            }
564        } catch (IllegalStateException e) {
565            Log.e(LOGTAG, "addSearchUrl", e);
566        } finally {
567            if (c != null) c.close();
568        }
569    }
570    /**
571     * Remove all searches from the search database.
572     *  Requires {@link android.Manifest.permission#WRITE_HISTORY_BOOKMARKS}
573     * @param cr   The ContentResolver used to access the database.
574     */
575    public static final void clearSearches(ContentResolver cr) {
576        // FIXME: Should this clear the urls to which these searches lead?
577        // (i.e. remove google.com/query= blah blah blah)
578        try {
579            cr.delete(SEARCHES_URI, null, null);
580        } catch (IllegalStateException e) {
581            Log.e(LOGTAG, "clearSearches", e);
582        }
583    }
584
585    /**
586     *  Request all icons from the database.
587     *  Requires {@link android.Manifest.permission#READ_HISTORY_BOOKMARKS}
588     *  @param  cr The ContentResolver used to access the database.
589     *  @param  where Clause to be used to limit the query from the database.
590     *          Must be an allowable string to be passed into a database query.
591     *  @param  listener IconListener that gets the icons once they are
592     *          retrieved.
593     */
594    public static final void requestAllIcons(ContentResolver cr, String where,
595            WebIconDatabase.IconListener listener) {
596        Cursor c = null;
597        try {
598            c = cr.query(
599                    BOOKMARKS_URI,
600                    new String[] { BookmarkColumns.URL },
601                    where, null, null);
602            if (c.moveToFirst()) {
603                final WebIconDatabase db = WebIconDatabase.getInstance();
604                do {
605                    db.requestIconForPageUrl(c.getString(0), listener);
606                } while (c.moveToNext());
607            }
608        } catch (IllegalStateException e) {
609            Log.e(LOGTAG, "requestAllIcons", e);
610        } finally {
611            if (c != null) {
612                c.close();
613            }
614        }
615    }
616
617    /**
618     * Allows geolocation for the specified origin.
619     * This requires the {@link android.Manifest.permission#WRITE_GEOLOCATION_PERMISSIONS}
620     * permission.
621     *
622     * @param origin The origin to allow geolocation for, e.g. "http://www.google.com". The string
623     *        should not include a trailing slash.
624     */
625    public static void allowGeolocation(ContentResolver cr, String origin) {
626        try {
627            ContentValues map = new ContentValues();
628            map.put(GeolocationColumns.ORIGIN, origin);
629            cr.insert(GEOLOCATION_URI, map);
630        } catch (IllegalStateException e) {
631            Log.e(LOGTAG, "allowGeolocation", e);
632            return;
633        }
634    }
635
636    /**
637     * Clears the geolocation permission state for the specified origin.
638     * This requires the {@link android.Manifest.permission#WRITE_GEOLOCATION_PERMISSIONS}
639     * permission.
640     *
641     * @param origin The origin to allow geolocation for, e.g. "http://www.google.com". The string
642     *        should not include a trailing slash.
643     */
644    public static void clearGeolocation(ContentResolver cr, String origin) {
645        try {
646            String[] whereArgs = { origin };
647            cr.delete(GEOLOCATION_URI, GEOLOCATION_WHERE_CLAUSE, whereArgs);
648        } catch (IllegalStateException e) {
649            Log.e(LOGTAG, "clearGeolocation", e);
650        }
651    }
652
653    public static class BookmarkColumns implements BaseColumns {
654        public static final String URL = "url";
655        public static final String VISITS = "visits";
656        public static final String DATE = "date";
657        public static final String BOOKMARK = "bookmark";
658        public static final String TITLE = "title";
659        public static final String CREATED = "created";
660        public static final String FAVICON = "favicon";
661        /**
662         * @hide
663         */
664        public static final String THUMBNAIL = "thumbnail";
665        /**
666         * @hide
667         */
668        public static final String TOUCH_ICON = "touch_icon";
669        /**
670         * @hide
671         */
672        public static final String USER_ENTERED = "user_entered";
673    }
674
675    public static class SearchColumns implements BaseColumns {
676        public static final String URL = "url";
677        public static final String SEARCH = "search";
678        public static final String DATE = "date";
679    }
680
681    public static class GeolocationColumns {
682        public static final String ORIGIN = "origin";
683    }
684}
685