diff options
author | Svetoslav <svetoslavganov@google.com> | 2015-06-14 20:23:40 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2015-06-14 20:23:40 +0000 |
commit | e77acbf15e68f346cc8a98e05e45b7cc8307fb4f (patch) | |
tree | bea1177b8ebabc011e835541c10203ef1408dcf7 | |
parent | 9bb35b0f1e891a78a67f68474795443ac9a4c378 (diff) | |
parent | de0e38896ad08818b26695187476683870fb836b (diff) | |
download | BookmarkProvider-e77acbf15e68f346cc8a98e05e45b7cc8307fb4f.tar.gz |
am de0e3889: Remove bookmarks provider and associated permissions - provider
* commit 'de0e38896ad08818b26695187476683870fb836b':
Remove bookmarks provider and associated permissions - provider
62 files changed, 21 insertions, 3485 deletions
@@ -13,21 +13,15 @@ # the License. LOCAL_PATH:= $(call my-dir) + include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional -LOCAL_STATIC_JAVA_LIBRARIES := \ - android-common \ - LOCAL_SRC_FILES := \ $(call all-java-files-under, src) LOCAL_PACKAGE_NAME := BookmarkProvider -LOCAL_EMMA_COVERAGE_FILTER := *,-com.android.common.* - include $(BUILD_PACKAGE) -# additionally, build tests in sub-folders in a separate .apk -include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 558cd7e..91d4700 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -15,24 +15,13 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.bookmarkprovider"> - <!-- Required for getAccountsByType, getAccounts --> - <uses-permission android:name="android.permission.GET_ACCOUNTS" /> - <!-- Required for getIsSyncable --> - <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" /> - <!-- Required for setIsSyncable, setSyncAutomatically --> - <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" /> - <application android:label="@string/application_name" android:icon="@mipmap/ic_launcher_shortcut_browser_bookmark" > <provider android:name=".BookmarkProvider" - android:authorities="com.android.browser;browser" + android:authorities="com.android.browser;browser;com.android.chrome.browser" android:multiprocess="false" - android:exported="true" - android:readPermission="com.android.browser.permission.READ_HISTORY_BOOKMARKS" - android:writePermission="com.android.browser.permission.WRITE_HISTORY_BOOKMARKS"> - <path-permission android:path="/bookmarks/search_suggest_query" - android:readPermission="android.permission.GLOBAL_SEARCH" /> + android:exported="true"> </provider> </application> diff --git a/res/drawable-hdpi/ic_bookmark_off_holo_dark.png b/res/drawable-hdpi/ic_bookmark_off_holo_dark.png Binary files differdeleted file mode 100644 index f827be5..0000000 --- a/res/drawable-hdpi/ic_bookmark_off_holo_dark.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_history_holo_dark.png b/res/drawable-hdpi/ic_history_holo_dark.png Binary files differdeleted file mode 100644 index 4f2ee7d..0000000 --- a/res/drawable-hdpi/ic_history_holo_dark.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_search_category_bookmark.png b/res/drawable-hdpi/ic_search_category_bookmark.png Binary files differdeleted file mode 100644 index bdecfd0..0000000 --- a/res/drawable-hdpi/ic_search_category_bookmark.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_search_category_history.png b/res/drawable-hdpi/ic_search_category_history.png Binary files differdeleted file mode 100644 index 94c89ae..0000000 --- a/res/drawable-hdpi/ic_search_category_history.png +++ /dev/null diff --git a/res/drawable-hdpi/ic_search_category_suggest.png b/res/drawable-hdpi/ic_search_category_suggest.png Binary files differdeleted file mode 100644 index 165a11d..0000000 --- a/res/drawable-hdpi/ic_search_category_suggest.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_bookmark_off_holo_dark.png b/res/drawable-mdpi/ic_bookmark_off_holo_dark.png Binary files differdeleted file mode 100644 index 825970c..0000000 --- a/res/drawable-mdpi/ic_bookmark_off_holo_dark.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_history_holo_dark.png b/res/drawable-mdpi/ic_history_holo_dark.png Binary files differdeleted file mode 100644 index 0e4e118..0000000 --- a/res/drawable-mdpi/ic_history_holo_dark.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_search_category_bookmark.png b/res/drawable-mdpi/ic_search_category_bookmark.png Binary files differdeleted file mode 100644 index cdf4be1..0000000 --- a/res/drawable-mdpi/ic_search_category_bookmark.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_search_category_history.png b/res/drawable-mdpi/ic_search_category_history.png Binary files differdeleted file mode 100644 index 0f63144..0000000 --- a/res/drawable-mdpi/ic_search_category_history.png +++ /dev/null diff --git a/res/drawable-mdpi/ic_search_category_suggest.png b/res/drawable-mdpi/ic_search_category_suggest.png Binary files differdeleted file mode 100644 index 0f04fc4..0000000 --- a/res/drawable-mdpi/ic_search_category_suggest.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_bookmark_off_holo_dark.png b/res/drawable-xhdpi/ic_bookmark_off_holo_dark.png Binary files differdeleted file mode 100644 index c60aad8..0000000 --- a/res/drawable-xhdpi/ic_bookmark_off_holo_dark.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_history_holo_dark.png b/res/drawable-xhdpi/ic_history_holo_dark.png Binary files differdeleted file mode 100644 index c916bde..0000000 --- a/res/drawable-xhdpi/ic_history_holo_dark.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_search_category_bookmark.png b/res/drawable-xhdpi/ic_search_category_bookmark.png Binary files differdeleted file mode 100644 index 6046a95..0000000 --- a/res/drawable-xhdpi/ic_search_category_bookmark.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_search_category_history.png b/res/drawable-xhdpi/ic_search_category_history.png Binary files differdeleted file mode 100644 index 681e4b4..0000000 --- a/res/drawable-xhdpi/ic_search_category_history.png +++ /dev/null diff --git a/res/drawable-xhdpi/ic_search_category_suggest.png b/res/drawable-xhdpi/ic_search_category_suggest.png Binary files differdeleted file mode 100644 index be0388c..0000000 --- a/res/drawable-xhdpi/ic_search_category_suggest.png +++ /dev/null diff --git a/res/mipmap-hdpi/ic_launcher_browser.png b/res/mipmap-hdpi/ic_launcher_browser.png Binary files differdeleted file mode 100644 index 4f9377f..0000000 --- a/res/mipmap-hdpi/ic_launcher_browser.png +++ /dev/null diff --git a/res/mipmap-mdpi/ic_launcher_browser.png b/res/mipmap-mdpi/ic_launcher_browser.png Binary files differdeleted file mode 100644 index 9bc6817..0000000 --- a/res/mipmap-mdpi/ic_launcher_browser.png +++ /dev/null diff --git a/res/mipmap-xhdpi/ic_launcher_browser.png b/res/mipmap-xhdpi/ic_launcher_browser.png Binary files differdeleted file mode 100644 index 9412fbe..0000000 --- a/res/mipmap-xhdpi/ic_launcher_browser.png +++ /dev/null diff --git a/res/mipmap-xxhdpi/ic_launcher_browser.png b/res/mipmap-xxhdpi/ic_launcher_browser.png Binary files differdeleted file mode 100644 index bd8c447..0000000 --- a/res/mipmap-xxhdpi/ic_launcher_browser.png +++ /dev/null diff --git a/res/raw/favicon_amazon.ico b/res/raw/favicon_amazon.ico Binary files differdeleted file mode 100644 index 20a959c..0000000 --- a/res/raw/favicon_amazon.ico +++ /dev/null diff --git a/res/raw/favicon_bbc.ico b/res/raw/favicon_bbc.ico Binary files differdeleted file mode 100644 index 8f62b07..0000000 --- a/res/raw/favicon_bbc.ico +++ /dev/null diff --git a/res/raw/favicon_cnn.ico b/res/raw/favicon_cnn.ico Binary files differdeleted file mode 100644 index 4a8421d..0000000 --- a/res/raw/favicon_cnn.ico +++ /dev/null diff --git a/res/raw/favicon_ebay.ico b/res/raw/favicon_ebay.ico Binary files differdeleted file mode 100644 index d7c8a5e..0000000 --- a/res/raw/favicon_ebay.ico +++ /dev/null diff --git a/res/raw/favicon_espn.ico b/res/raw/favicon_espn.ico Binary files differdeleted file mode 100644 index 2dbe28a..0000000 --- a/res/raw/favicon_espn.ico +++ /dev/null diff --git a/res/raw/favicon_facebook.ico b/res/raw/favicon_facebook.ico Binary files differdeleted file mode 100644 index ea0c6a3..0000000 --- a/res/raw/favicon_facebook.ico +++ /dev/null diff --git a/res/raw/favicon_google.ico b/res/raw/favicon_google.ico Binary files differdeleted file mode 100644 index ee7c943..0000000 --- a/res/raw/favicon_google.ico +++ /dev/null diff --git a/res/raw/favicon_msn.ico b/res/raw/favicon_msn.ico Binary files differdeleted file mode 100644 index b2279f5..0000000 --- a/res/raw/favicon_msn.ico +++ /dev/null diff --git a/res/raw/favicon_nytimes.ico b/res/raw/favicon_nytimes.ico Binary files differdeleted file mode 100644 index fb4d000..0000000 --- a/res/raw/favicon_nytimes.ico +++ /dev/null diff --git a/res/raw/favicon_picasa.ico b/res/raw/favicon_picasa.ico Binary files differdeleted file mode 100644 index 22133b8..0000000 --- a/res/raw/favicon_picasa.ico +++ /dev/null diff --git a/res/raw/favicon_twitter.ico b/res/raw/favicon_twitter.ico Binary files differdeleted file mode 100644 index 00450d4..0000000 --- a/res/raw/favicon_twitter.ico +++ /dev/null diff --git a/res/raw/favicon_weatherchannel.ico b/res/raw/favicon_weatherchannel.ico Binary files differdeleted file mode 100644 index 5a56818..0000000 --- a/res/raw/favicon_weatherchannel.ico +++ /dev/null diff --git a/res/raw/favicon_wikipedia.ico b/res/raw/favicon_wikipedia.ico Binary files differdeleted file mode 100644 index 31b0e38..0000000 --- a/res/raw/favicon_wikipedia.ico +++ /dev/null diff --git a/res/raw/favicon_yahoo.ico b/res/raw/favicon_yahoo.ico Binary files differdeleted file mode 100644 index d7761e5..0000000 --- a/res/raw/favicon_yahoo.ico +++ /dev/null diff --git a/res/raw/thumb_amazon.png b/res/raw/thumb_amazon.png Binary files differdeleted file mode 100644 index 4dd2f35..0000000 --- a/res/raw/thumb_amazon.png +++ /dev/null diff --git a/res/raw/thumb_bbc.png b/res/raw/thumb_bbc.png Binary files differdeleted file mode 100644 index dff0424..0000000 --- a/res/raw/thumb_bbc.png +++ /dev/null diff --git a/res/raw/thumb_cnn.png b/res/raw/thumb_cnn.png Binary files differdeleted file mode 100644 index 3d6fdc9..0000000 --- a/res/raw/thumb_cnn.png +++ /dev/null diff --git a/res/raw/thumb_ebay.png b/res/raw/thumb_ebay.png Binary files differdeleted file mode 100644 index 023f678..0000000 --- a/res/raw/thumb_ebay.png +++ /dev/null diff --git a/res/raw/thumb_espn.png b/res/raw/thumb_espn.png Binary files differdeleted file mode 100644 index c0882eb..0000000 --- a/res/raw/thumb_espn.png +++ /dev/null diff --git a/res/raw/thumb_facebook.png b/res/raw/thumb_facebook.png Binary files differdeleted file mode 100644 index a8c047f..0000000 --- a/res/raw/thumb_facebook.png +++ /dev/null diff --git a/res/raw/thumb_google.png b/res/raw/thumb_google.png Binary files differdeleted file mode 100644 index c54e6ea..0000000 --- a/res/raw/thumb_google.png +++ /dev/null diff --git a/res/raw/thumb_msn.png b/res/raw/thumb_msn.png Binary files differdeleted file mode 100644 index 4ad710f..0000000 --- a/res/raw/thumb_msn.png +++ /dev/null diff --git a/res/raw/thumb_nytimes.png b/res/raw/thumb_nytimes.png Binary files differdeleted file mode 100644 index d6849c3..0000000 --- a/res/raw/thumb_nytimes.png +++ /dev/null diff --git a/res/raw/thumb_picasa.png b/res/raw/thumb_picasa.png Binary files differdeleted file mode 100644 index 5ec2d7a..0000000 --- a/res/raw/thumb_picasa.png +++ /dev/null diff --git a/res/raw/thumb_twitter.png b/res/raw/thumb_twitter.png Binary files differdeleted file mode 100644 index 95e247f..0000000 --- a/res/raw/thumb_twitter.png +++ /dev/null diff --git a/res/raw/thumb_weatherchannel.png b/res/raw/thumb_weatherchannel.png Binary files differdeleted file mode 100644 index a190a0c..0000000 --- a/res/raw/thumb_weatherchannel.png +++ /dev/null diff --git a/res/raw/thumb_wikipedia.png b/res/raw/thumb_wikipedia.png Binary files differdeleted file mode 100644 index 7929468..0000000 --- a/res/raw/thumb_wikipedia.png +++ /dev/null diff --git a/res/raw/thumb_yahoo.png b/res/raw/thumb_yahoo.png Binary files differdeleted file mode 100644 index 08dd759..0000000 --- a/res/raw/thumb_yahoo.png +++ /dev/null diff --git a/res/values/bookmarks_icons.xml b/res/values/bookmarks_icons.xml deleted file mode 100644 index a22ce6c..0000000 --- a/res/values/bookmarks_icons.xml +++ /dev/null @@ -1,48 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2015 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<resources> - <array name="bookmark_preloads"> - <item>@raw/favicon_google</item> - <item>@raw/thumb_google</item> - <item>@raw/favicon_picasa</item> - <item>@raw/thumb_picasa</item> - <item>@raw/favicon_yahoo</item> - <item>@raw/thumb_yahoo</item> - <item>@raw/favicon_msn</item> - <item>@raw/thumb_msn</item> - <item>@raw/favicon_twitter</item> - <item>@raw/thumb_twitter</item> - <item>@raw/favicon_facebook</item> - <item>@raw/thumb_facebook</item> - <item>@raw/favicon_wikipedia</item> - <item>@raw/thumb_wikipedia</item> - <item>@raw/favicon_ebay</item> - <item>@raw/thumb_ebay</item> - <item>@raw/favicon_cnn</item> - <item>@raw/thumb_cnn</item> - <item>@raw/favicon_nytimes</item> - <item>@raw/thumb_nytimes</item> - <item>@raw/favicon_espn</item> - <item>@raw/thumb_espn</item> - <item>@raw/favicon_amazon</item> - <item>@raw/thumb_amazon</item> - <item>@raw/favicon_weatherchannel</item> - <item>@raw/thumb_weatherchannel</item> - <item>@raw/favicon_bbc</item> - <item>@raw/thumb_bbc</item> - </array> -</resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index fa379b7..1a4d7b5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -18,43 +18,4 @@ <!-- The name of the application. --> <string name="application_name">Bookmark Provider</string> - <!-- Bookmarks --> - <string-array name="bookmarks" translatable="false"> - <item>Google</item> - <item>http://www.google.com/</item> - <item>Picasa</item> - <item>http://picasaweb.google.com/</item> - <item>Yahoo!</item> - <item>http://www.yahoo.com/</item> - <item>MSN</item> - <item>http://www.msn.com/</item> - <item>Twitter</item> - <item>http://twitter.com/</item> - <item>Facebook</item> - <item>http://www.facebook.com/</item> - <item>Wikipedia</item> - <item>http://www.wikipedia.org/</item> - <item>eBay</item> - <item>http://www.ebay.com/</item> - <item>CNN</item> - <item>http://www.cnn.com/</item> - <item>NY Times</item> - <item>http://www.nytimes.com/</item> - <item>ESPN</item> - <item>http://espn.com/</item> - <item>Amazon</item> - <item>http://www.amazon.com/</item> - <item>Weather Channel</item> - <item>http://www.weather.com/</item> - <item>BBC</item> - <item>http://www.bbc.co.uk/</item> - </string-array> - - <!-- Bookmark name for Picasa website --> - <string name="picasa" translatable="false">Picasa</string> - - <!-- The description of the suggestion that lets the user search the web for the query currently - in the text field. --> - <string name="search_the_web">Search the web</string> - </resources> diff --git a/src/com/android/bookmarkstore/BookmarkProvider.java b/src/com/android/bookmarkstore/BookmarkProvider.java index 9f4f08f..12724c6 100644 --- a/src/com/android/bookmarkstore/BookmarkProvider.java +++ b/src/com/android/bookmarkstore/BookmarkProvider.java @@ -16,2131 +16,48 @@ package com.android.bookmarkprovider; -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.SearchManager; -import android.content.ContentResolver; -import android.content.ContentUris; +import android.content.ContentProvider; import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.content.UriMatcher; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.database.AbstractCursor; import android.database.Cursor; -import android.database.DatabaseUtils; import android.database.MatrixCursor; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; -import android.provider.BaseColumns; -import android.provider.Browser.BookmarkColumns; import android.provider.Browser; -import android.provider.BrowserContract.Accounts; -import android.provider.BrowserContract.Bookmarks; -import android.provider.BrowserContract.ChromeSyncColumns; -import android.provider.BrowserContract.Combined; -import android.provider.BrowserContract.History; -import android.provider.BrowserContract.Images; -import android.provider.BrowserContract.Searches; -import android.provider.BrowserContract.Settings; -import android.provider.BrowserContract.SyncState; import android.provider.BrowserContract; -import android.provider.ContactsContract.RawContacts; -import android.provider.SyncStateContract; -import android.text.TextUtils; -import com.android.bookmarkprovider.R; -import com.android.common.content.SyncStateContentProviderHelper; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class BookmarkProvider extends SQLiteContentProvider { - - public static final String PARAM_GROUP_BY = "groupBy"; - public static final String PARAM_ALLOW_EMPTY_ACCOUNTS = "allowEmptyAccounts"; - - public static final String LEGACY_AUTHORITY = "browser"; - static final Uri LEGACY_AUTHORITY_URI = new Uri.Builder() - .authority(LEGACY_AUTHORITY).scheme("content").build(); - - public static interface Thumbnails { - public static final Uri CONTENT_URI = Uri.withAppendedPath( - BrowserContract.AUTHORITY_URI, "thumbnails"); - public static final String _ID = "_id"; - public static final String THUMBNAIL = "thumbnail"; - } - - public static interface OmniboxSuggestions { - public static final Uri CONTENT_URI = Uri.withAppendedPath( - BrowserContract.AUTHORITY_URI, "omnibox_suggestions"); - public static final String _ID = "_id"; - public static final String URL = "url"; - public static final String TITLE = "title"; - public static final String IS_BOOKMARK = "bookmark"; - } - - static final String TABLE_BOOKMARKS = "bookmarks"; - static final String TABLE_HISTORY = "history"; - static final String TABLE_IMAGES = "images"; - static final String TABLE_SEARCHES = "searches"; - static final String TABLE_SYNC_STATE = "syncstate"; - static final String TABLE_SETTINGS = "settings"; - static final String TABLE_SNAPSHOTS = "snapshots"; - static final String TABLE_THUMBNAILS = "thumbnails"; - - static final String TABLE_BOOKMARKS_JOIN_IMAGES = "bookmarks LEFT OUTER JOIN images " + - "ON bookmarks.url = images." + Images.URL; - static final String TABLE_HISTORY_JOIN_IMAGES = "history LEFT OUTER JOIN images " + - "ON history.url = images." + Images.URL; - - static final String VIEW_ACCOUNTS = "v_accounts"; - static final String VIEW_SNAPSHOTS_COMBINED = "v_snapshots_combined"; - static final String VIEW_OMNIBOX_SUGGESTIONS = "v_omnibox_suggestions"; - - static final String FORMAT_COMBINED_JOIN_SUBQUERY_JOIN_IMAGES = - "history LEFT OUTER JOIN (%s) bookmarks " + - "ON history.url = bookmarks.url LEFT OUTER JOIN images " + - "ON history.url = images.url_key"; - - static final String DEFAULT_SORT_HISTORY = History.DATE_LAST_VISITED + " DESC"; - static final String DEFAULT_SORT_ACCOUNTS = - Accounts.ACCOUNT_NAME + " IS NOT NULL DESC, " - + Accounts.ACCOUNT_NAME + " ASC"; - - private static final String TABLE_BOOKMARKS_JOIN_HISTORY = - "history LEFT OUTER JOIN bookmarks ON history.url = bookmarks.url"; - - private static final String[] SUGGEST_PROJECTION = new String[] { - qualifyColumn(TABLE_HISTORY, History._ID), - qualifyColumn(TABLE_HISTORY, History.URL), - bookmarkOrHistoryColumn(Combined.TITLE), - bookmarkOrHistoryLiteral(Combined.URL, - Integer.toString(R.drawable.ic_bookmark_off_holo_dark), - Integer.toString(R.drawable.ic_history_holo_dark)), - qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED)}; - - private static final String SUGGEST_SELECTION = - "history.url LIKE ? OR history.url LIKE ? OR history.url LIKE ? OR history.url LIKE ?" - + " OR history.title LIKE ? OR bookmarks.title LIKE ?"; - - private static final String ZERO_QUERY_SUGGEST_SELECTION = - TABLE_HISTORY + "." + History.DATE_LAST_VISITED + " != 0"; - - private static final String IMAGE_PRUNE = - "url_key NOT IN (SELECT url FROM bookmarks " + - "WHERE url IS NOT NULL AND deleted == 0) AND url_key NOT IN " + - "(SELECT url FROM history WHERE url IS NOT NULL)"; - - // WHERE clause to find a deleted bookmark - private static final String DELETED_BOOKMARK_BY_ID = - Bookmarks._ID + "=? AND " + Bookmarks.IS_DELETED + "=1"; - - static final int THUMBNAILS = 10; - static final int THUMBNAILS_ID = 11; - static final int OMNIBOX_SUGGESTIONS = 20; - - static final int BOOKMARKS = 1000; - static final int BOOKMARKS_ID = 1001; - static final int BOOKMARKS_FOLDER = 1002; - static final int BOOKMARKS_FOLDER_ID = 1003; - static final int BOOKMARKS_SUGGESTIONS = 1004; - static final int BOOKMARKS_DEFAULT_FOLDER_ID = 1005; - - static final int HISTORY = 2000; - static final int HISTORY_ID = 2001; - - static final int SEARCHES = 3000; - static final int SEARCHES_ID = 3001; - - static final int SYNCSTATE = 4000; - static final int SYNCSTATE_ID = 4001; - - static final int IMAGES = 5000; - - static final int COMBINED = 6000; - static final int COMBINED_ID = 6001; - - static final int ACCOUNTS = 7000; - - static final int SETTINGS = 8000; - - static final int LEGACY = 9000; - static final int LEGACY_ID = 9001; - - public static final long FIXED_ID_ROOT = 1; - - // Default sort order for unsync'd bookmarks - static final String DEFAULT_BOOKMARKS_SORT_ORDER = - Bookmarks.IS_FOLDER + " DESC, position ASC, _id ASC"; - - // Default sort order for sync'd bookmarks - static final String DEFAULT_BOOKMARKS_SORT_ORDER_SYNC = "position ASC, _id ASC"; - - static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH); - - static final HashMap<String, String> ACCOUNTS_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> BOOKMARKS_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> OTHER_BOOKMARKS_PROJECTION_MAP = - new HashMap<String, String>(); - static final HashMap<String, String> HISTORY_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> SYNC_STATE_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> IMAGES_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> COMBINED_HISTORY_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> COMBINED_BOOKMARK_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> SEARCHES_PROJECTION_MAP = new HashMap<String, String>(); - static final HashMap<String, String> SETTINGS_PROJECTION_MAP = new HashMap<String, String>(); - - static { - final UriMatcher matcher = URI_MATCHER; - final String authority = BrowserContract.AUTHORITY; - matcher.addURI(authority, "accounts", ACCOUNTS); - matcher.addURI(authority, "bookmarks", BOOKMARKS); - matcher.addURI(authority, "bookmarks/#", BOOKMARKS_ID); - matcher.addURI(authority, "bookmarks/folder", BOOKMARKS_FOLDER); - matcher.addURI(authority, "bookmarks/folder/#", BOOKMARKS_FOLDER_ID); - matcher.addURI(authority, "bookmarks/folder/id", BOOKMARKS_DEFAULT_FOLDER_ID); - matcher.addURI(authority, - SearchManager.SUGGEST_URI_PATH_QUERY, - BOOKMARKS_SUGGESTIONS); - matcher.addURI(authority, - "bookmarks/" + SearchManager.SUGGEST_URI_PATH_QUERY, - BOOKMARKS_SUGGESTIONS); - matcher.addURI(authority, "history", HISTORY); - matcher.addURI(authority, "history/#", HISTORY_ID); - matcher.addURI(authority, "searches", SEARCHES); - matcher.addURI(authority, "searches/#", SEARCHES_ID); - matcher.addURI(authority, "syncstate", SYNCSTATE); - matcher.addURI(authority, "syncstate/#", SYNCSTATE_ID); - matcher.addURI(authority, "images", IMAGES); - matcher.addURI(authority, "combined", COMBINED); - matcher.addURI(authority, "combined/#", COMBINED_ID); - matcher.addURI(authority, "settings", SETTINGS); - matcher.addURI(authority, "thumbnails", THUMBNAILS); - matcher.addURI(authority, "thumbnails/#", THUMBNAILS_ID); - matcher.addURI(authority, "omnibox_suggestions", OMNIBOX_SUGGESTIONS); - - // Legacy - matcher.addURI(LEGACY_AUTHORITY, "searches", SEARCHES); - matcher.addURI(LEGACY_AUTHORITY, "searches/#", SEARCHES_ID); - matcher.addURI(LEGACY_AUTHORITY, "bookmarks", LEGACY); - matcher.addURI(LEGACY_AUTHORITY, "bookmarks/#", LEGACY_ID); - matcher.addURI(LEGACY_AUTHORITY, - SearchManager.SUGGEST_URI_PATH_QUERY, - BOOKMARKS_SUGGESTIONS); - matcher.addURI(LEGACY_AUTHORITY, - "bookmarks/" + SearchManager.SUGGEST_URI_PATH_QUERY, - BOOKMARKS_SUGGESTIONS); - - // Projection maps - HashMap<String, String> map; - - // Accounts - map = ACCOUNTS_PROJECTION_MAP; - map.put(Accounts.ACCOUNT_TYPE, Accounts.ACCOUNT_TYPE); - map.put(Accounts.ACCOUNT_NAME, Accounts.ACCOUNT_NAME); - map.put(Accounts.ROOT_ID, Accounts.ROOT_ID); - - // Bookmarks - map = BOOKMARKS_PROJECTION_MAP; - map.put(Bookmarks._ID, qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID)); - map.put(Bookmarks.TITLE, Bookmarks.TITLE); - map.put(Bookmarks.URL, Bookmarks.URL); - map.put(Bookmarks.FAVICON, Bookmarks.FAVICON); - map.put(Bookmarks.THUMBNAIL, Bookmarks.THUMBNAIL); - map.put(Bookmarks.TOUCH_ICON, Bookmarks.TOUCH_ICON); - map.put(Bookmarks.IS_FOLDER, Bookmarks.IS_FOLDER); - map.put(Bookmarks.PARENT, Bookmarks.PARENT); - map.put(Bookmarks.POSITION, Bookmarks.POSITION); - map.put(Bookmarks.INSERT_AFTER, Bookmarks.INSERT_AFTER); - map.put(Bookmarks.IS_DELETED, Bookmarks.IS_DELETED); - map.put(Bookmarks.ACCOUNT_NAME, Bookmarks.ACCOUNT_NAME); - map.put(Bookmarks.ACCOUNT_TYPE, Bookmarks.ACCOUNT_TYPE); - map.put(Bookmarks.SOURCE_ID, Bookmarks.SOURCE_ID); - map.put(Bookmarks.VERSION, Bookmarks.VERSION); - map.put(Bookmarks.DATE_CREATED, Bookmarks.DATE_CREATED); - map.put(Bookmarks.DATE_MODIFIED, Bookmarks.DATE_MODIFIED); - map.put(Bookmarks.DIRTY, Bookmarks.DIRTY); - map.put(Bookmarks.SYNC1, Bookmarks.SYNC1); - map.put(Bookmarks.SYNC2, Bookmarks.SYNC2); - map.put(Bookmarks.SYNC3, Bookmarks.SYNC3); - map.put(Bookmarks.SYNC4, Bookmarks.SYNC4); - map.put(Bookmarks.SYNC5, Bookmarks.SYNC5); - map.put(Bookmarks.PARENT_SOURCE_ID, "(SELECT " + Bookmarks.SOURCE_ID + - " FROM " + TABLE_BOOKMARKS + " A WHERE " + - "A." + Bookmarks._ID + "=" + TABLE_BOOKMARKS + "." + Bookmarks.PARENT + - ") AS " + Bookmarks.PARENT_SOURCE_ID); - map.put(Bookmarks.INSERT_AFTER_SOURCE_ID, "(SELECT " + Bookmarks.SOURCE_ID + - " FROM " + TABLE_BOOKMARKS + " A WHERE " + - "A." + Bookmarks._ID + "=" + TABLE_BOOKMARKS + "." + Bookmarks.INSERT_AFTER + - ") AS " + Bookmarks.INSERT_AFTER_SOURCE_ID); - map.put(Bookmarks.TYPE, "CASE " - + " WHEN " + Bookmarks.IS_FOLDER + "=0 THEN " - + Bookmarks.BOOKMARK_TYPE_BOOKMARK - + " WHEN " + ChromeSyncColumns.SERVER_UNIQUE + "='" - + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR + "' THEN " - + Bookmarks.BOOKMARK_TYPE_BOOKMARK_BAR_FOLDER - + " WHEN " + ChromeSyncColumns.SERVER_UNIQUE + "='" - + ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS + "' THEN " - + Bookmarks.BOOKMARK_TYPE_OTHER_FOLDER - + " ELSE " + Bookmarks.BOOKMARK_TYPE_FOLDER - + " END AS " + Bookmarks.TYPE); - - // Other bookmarks - OTHER_BOOKMARKS_PROJECTION_MAP.putAll(BOOKMARKS_PROJECTION_MAP); - OTHER_BOOKMARKS_PROJECTION_MAP.put(Bookmarks.POSITION, - Long.toString(Long.MAX_VALUE) + " AS " + Bookmarks.POSITION); - - // History - map = HISTORY_PROJECTION_MAP; - map.put(History._ID, qualifyColumn(TABLE_HISTORY, History._ID)); - map.put(History.TITLE, History.TITLE); - map.put(History.URL, History.URL); - map.put(History.FAVICON, History.FAVICON); - map.put(History.THUMBNAIL, History.THUMBNAIL); - map.put(History.TOUCH_ICON, History.TOUCH_ICON); - map.put(History.DATE_CREATED, History.DATE_CREATED); - map.put(History.DATE_LAST_VISITED, History.DATE_LAST_VISITED); - map.put(History.VISITS, History.VISITS); - map.put(History.USER_ENTERED, History.USER_ENTERED); - - // Sync state - map = SYNC_STATE_PROJECTION_MAP; - map.put(SyncState._ID, SyncState._ID); - map.put(SyncState.ACCOUNT_NAME, SyncState.ACCOUNT_NAME); - map.put(SyncState.ACCOUNT_TYPE, SyncState.ACCOUNT_TYPE); - map.put(SyncState.DATA, SyncState.DATA); - - // Images - map = IMAGES_PROJECTION_MAP; - map.put(Images.URL, Images.URL); - map.put(Images.FAVICON, Images.FAVICON); - map.put(Images.THUMBNAIL, Images.THUMBNAIL); - map.put(Images.TOUCH_ICON, Images.TOUCH_ICON); - - // Combined history half - map = COMBINED_HISTORY_PROJECTION_MAP; - map.put(Combined._ID, bookmarkOrHistoryColumn(Combined._ID)); - map.put(Combined.TITLE, bookmarkOrHistoryColumn(Combined.TITLE)); - map.put(Combined.URL, qualifyColumn(TABLE_HISTORY, Combined.URL)); - map.put(Combined.DATE_CREATED, qualifyColumn(TABLE_HISTORY, Combined.DATE_CREATED)); - map.put(Combined.DATE_LAST_VISITED, Combined.DATE_LAST_VISITED); - map.put(Combined.IS_BOOKMARK, "CASE WHEN " + - TABLE_BOOKMARKS + "." + Bookmarks._ID + - " IS NOT NULL THEN 1 ELSE 0 END AS " + Combined.IS_BOOKMARK); - map.put(Combined.VISITS, Combined.VISITS); - map.put(Combined.FAVICON, Combined.FAVICON); - map.put(Combined.THUMBNAIL, Combined.THUMBNAIL); - map.put(Combined.TOUCH_ICON, Combined.TOUCH_ICON); - map.put(Combined.USER_ENTERED, "NULL AS " + Combined.USER_ENTERED); - - // Combined bookmark half - map = COMBINED_BOOKMARK_PROJECTION_MAP; - map.put(Combined._ID, Combined._ID); - map.put(Combined.TITLE, Combined.TITLE); - map.put(Combined.URL, Combined.URL); - map.put(Combined.DATE_CREATED, Combined.DATE_CREATED); - map.put(Combined.DATE_LAST_VISITED, "NULL AS " + Combined.DATE_LAST_VISITED); - map.put(Combined.IS_BOOKMARK, "1 AS " + Combined.IS_BOOKMARK); - map.put(Combined.VISITS, "0 AS " + Combined.VISITS); - map.put(Combined.FAVICON, Combined.FAVICON); - map.put(Combined.THUMBNAIL, Combined.THUMBNAIL); - map.put(Combined.TOUCH_ICON, Combined.TOUCH_ICON); - map.put(Combined.USER_ENTERED, "NULL AS " + Combined.USER_ENTERED); - - // Searches - map = SEARCHES_PROJECTION_MAP; - map.put(Searches._ID, Searches._ID); - map.put(Searches.SEARCH, Searches.SEARCH); - map.put(Searches.DATE, Searches.DATE); - - // Settings - map = SETTINGS_PROJECTION_MAP; - map.put(Settings.KEY, Settings.KEY); - map.put(Settings.VALUE, Settings.VALUE); - } - - static final String bookmarkOrHistoryColumn(String column) { - return "CASE WHEN bookmarks." + column + " IS NOT NULL THEN " + - "bookmarks." + column + " ELSE history." + column + " END AS " + column; - } - - static final String bookmarkOrHistoryLiteral(String column, String bookmarkValue, - String historyValue) { - return "CASE WHEN bookmarks." + column + " IS NOT NULL THEN \"" + bookmarkValue + - "\" ELSE \"" + historyValue + "\" END"; - } - - static final String qualifyColumn(String table, String column) { - return table + "." + column + " AS " + column; - } - - DatabaseHelper mOpenHelper; - SyncStateContentProviderHelper mSyncHelper = new SyncStateContentProviderHelper(); - - final class DatabaseHelper extends SQLiteOpenHelper { - static final String DATABASE_NAME = "browser2.db"; - static final int DATABASE_VERSION = 32; - public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - setWriteAheadLoggingEnabled(true); - } - - @Override - public void onCreate(SQLiteDatabase db) { - db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" + - Bookmarks._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Bookmarks.TITLE + " TEXT," + - Bookmarks.URL + " TEXT," + - Bookmarks.IS_FOLDER + " INTEGER NOT NULL DEFAULT 0," + - Bookmarks.PARENT + " INTEGER," + - Bookmarks.POSITION + " INTEGER NOT NULL," + - Bookmarks.INSERT_AFTER + " INTEGER," + - Bookmarks.IS_DELETED + " INTEGER NOT NULL DEFAULT 0," + - Bookmarks.ACCOUNT_NAME + " TEXT," + - Bookmarks.ACCOUNT_TYPE + " TEXT," + - Bookmarks.SOURCE_ID + " TEXT," + - Bookmarks.VERSION + " INTEGER NOT NULL DEFAULT 1," + - Bookmarks.DATE_CREATED + " INTEGER," + - Bookmarks.DATE_MODIFIED + " INTEGER," + - Bookmarks.DIRTY + " INTEGER NOT NULL DEFAULT 0," + - Bookmarks.SYNC1 + " TEXT," + - Bookmarks.SYNC2 + " TEXT," + - Bookmarks.SYNC3 + " TEXT," + - Bookmarks.SYNC4 + " TEXT," + - Bookmarks.SYNC5 + " TEXT" + - ");"); - - // TODO indices - - db.execSQL("CREATE TABLE " + TABLE_HISTORY + "(" + - History._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - History.TITLE + " TEXT," + - History.URL + " TEXT NOT NULL," + - History.DATE_CREATED + " INTEGER," + - History.DATE_LAST_VISITED + " INTEGER," + - History.VISITS + " INTEGER NOT NULL DEFAULT 0," + - History.USER_ENTERED + " INTEGER" + - ");"); - - db.execSQL("CREATE TABLE " + TABLE_IMAGES + " (" + - Images.URL + " TEXT UNIQUE NOT NULL," + - Images.FAVICON + " BLOB," + - Images.THUMBNAIL + " BLOB," + - Images.TOUCH_ICON + " BLOB" + - ");"); - db.execSQL("CREATE INDEX imagesUrlIndex ON " + TABLE_IMAGES + - "(" + Images.URL + ")"); - - db.execSQL("CREATE TABLE " + TABLE_SEARCHES + " (" + - Searches._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Searches.SEARCH + " TEXT," + - Searches.DATE + " LONG" + - ");"); - - db.execSQL("CREATE TABLE " + TABLE_SETTINGS + " (" + - Settings.KEY + " TEXT PRIMARY KEY," + - Settings.VALUE + " TEXT NOT NULL" + - ");"); - - createAccountsView(db); - createThumbnails(db); - - mSyncHelper.createDatabase(db); - - createDefaultBookmarks(db); - - enableSync(db); - createOmniboxSuggestions(db); - } - - void createOmniboxSuggestions(SQLiteDatabase db) { - db.execSQL(SQL_CREATE_VIEW_OMNIBOX_SUGGESTIONS); - } - - void createThumbnails(SQLiteDatabase db) { - db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_THUMBNAILS + " (" + - Thumbnails._ID + " INTEGER PRIMARY KEY," + - Thumbnails.THUMBNAIL + " BLOB NOT NULL" + - ");"); - } - - void enableSync(SQLiteDatabase db) { - ContentValues values = new ContentValues(); - values.put(Settings.KEY, Settings.KEY_SYNC_ENABLED); - values.put(Settings.VALUE, 1); - insertSettingsInTransaction(db, values); - // Enable bookmark sync on all accounts - AccountManager am = (AccountManager) getContext().getSystemService( - Context.ACCOUNT_SERVICE); - if (am == null) { - return; - } - Account[] accounts = am.getAccountsByType("com.google"); - if (accounts == null || accounts.length == 0) { - return; - } - for (Account account : accounts) { - if (ContentResolver.getIsSyncable( - account, BrowserContract.AUTHORITY) == 0) { - // Account wasn't syncable, enable it - ContentResolver.setIsSyncable( - account, BrowserContract.AUTHORITY, 1); - ContentResolver.setSyncAutomatically( - account, BrowserContract.AUTHORITY, true); - } - } - } - - void createAccountsView(SQLiteDatabase db) { - db.execSQL("CREATE VIEW IF NOT EXISTS v_accounts AS " - + "SELECT NULL AS " + Accounts.ACCOUNT_NAME - + ", NULL AS " + Accounts.ACCOUNT_TYPE - + ", " + FIXED_ID_ROOT + " AS " + Accounts.ROOT_ID - + " UNION ALL SELECT " + Accounts.ACCOUNT_NAME - + ", " + Accounts.ACCOUNT_TYPE + ", " - + Bookmarks._ID + " AS " + Accounts.ROOT_ID - + " FROM " + TABLE_BOOKMARKS + " WHERE " - + ChromeSyncColumns.SERVER_UNIQUE + " = \"" - + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR + "\" AND " - + Bookmarks.IS_DELETED + " = 0"); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - } - - public void onOpen(SQLiteDatabase db) { - mSyncHelper.onDatabaseOpened(db); - } - - private void createDefaultBookmarks(SQLiteDatabase db) { - ContentValues values = new ContentValues(); - // TODO figure out how to deal with localization for the defaults - - // Bookmarks folder - values.put(Bookmarks._ID, FIXED_ID_ROOT); - values.put(ChromeSyncColumns.SERVER_UNIQUE, ChromeSyncColumns.FOLDER_NAME_BOOKMARKS); - values.put(Bookmarks.TITLE, "Bookmarks"); - values.putNull(Bookmarks.PARENT); - values.put(Bookmarks.POSITION, 0); - values.put(Bookmarks.IS_FOLDER, true); - values.put(Bookmarks.DIRTY, true); - db.insertOrThrow(TABLE_BOOKMARKS, null, values); - - addDefaultBookmarks(db, FIXED_ID_ROOT); - } - - private void addDefaultBookmarks(SQLiteDatabase db, long parentId) { - Resources res = getContext().getResources(); - final CharSequence[] bookmarks = res.getTextArray( - R.array.bookmarks); - int size = bookmarks.length; - TypedArray preloads = res.obtainTypedArray(R.array.bookmark_preloads); - try { - String parent = Long.toString(parentId); - String now = Long.toString(System.currentTimeMillis()); - for (int i = 0; i < size; i = i + 2) { - CharSequence bookmarkDestination = replaceSystemPropertyInString(getContext(), - bookmarks[i + 1]); - db.execSQL("INSERT INTO bookmarks (" + - Bookmarks.TITLE + ", " + - Bookmarks.URL + ", " + - Bookmarks.IS_FOLDER + "," + - Bookmarks.PARENT + "," + - Bookmarks.POSITION + "," + - Bookmarks.DATE_CREATED + - ") VALUES (" + - "'" + bookmarks[i] + "', " + - "'" + bookmarkDestination + "', " + - "0," + - parent + "," + - Integer.toString(i) + "," + - now + - ");"); - - int faviconId = preloads.getResourceId(i, 0); - int thumbId = preloads.getResourceId(i + 1, 0); - byte[] thumb = null, favicon = null; - try { - thumb = readRaw(res, thumbId); - } catch (IOException e) { - } - try { - favicon = readRaw(res, faviconId); - } catch (IOException e) { - } - if (thumb != null || favicon != null) { - ContentValues imageValues = new ContentValues(); - imageValues.put(Images.URL, bookmarkDestination.toString()); - if (favicon != null) { - imageValues.put(Images.FAVICON, favicon); - } - if (thumb != null) { - imageValues.put(Images.THUMBNAIL, thumb); - } - db.insert(TABLE_IMAGES, Images.FAVICON, imageValues); - } - } - } catch (ArrayIndexOutOfBoundsException e) { - } finally { - preloads.recycle(); - } - } - - private byte[] readRaw(Resources res, int id) throws IOException { - if (id == 0) { - return null; - } - InputStream is = res.openRawResource(id); - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - byte[] buf = new byte[4096]; - int read; - while ((read = is.read(buf)) > 0) { - bos.write(buf, 0, read); - } - bos.flush(); - return bos.toByteArray(); - } finally { - is.close(); - } - } - - // XXX: This is a major hack to remove our dependency on gsf constants and - // its content provider. http://b/issue?id=2425179 - private String getClientId(ContentResolver cr) { - String ret = "android-google"; - Cursor c = null; - try { - c = cr.query(Uri.parse("content://com.google.settings/partner"), - new String[] { "value" }, "name='client_id'", null, null); - if (c != null && c.moveToNext()) { - ret = c.getString(0); - } - } catch (RuntimeException ex) { - // fall through to return the default - } finally { - if (c != null) { - c.close(); - } - } - return ret; - } - - private CharSequence replaceSystemPropertyInString(Context context, CharSequence srcString) { - StringBuffer sb = new StringBuffer(); - int lastCharLoc = 0; - - final String client_id = getClientId(context.getContentResolver()); - - for (int i = 0; i < srcString.length(); ++i) { - char c = srcString.charAt(i); - if (c == '{') { - sb.append(srcString.subSequence(lastCharLoc, i)); - lastCharLoc = i; - inner: - for (int j = i; j < srcString.length(); ++j) { - char k = srcString.charAt(j); - if (k == '}') { - String propertyKeyValue = srcString.subSequence(i + 1, j).toString(); - if (propertyKeyValue.equals("CLIENT_ID")) { - sb.append(client_id); - } else { - sb.append("unknown"); - } - lastCharLoc = j + 1; - i = j; - break inner; - } - } - } - } - if (srcString.length() - lastCharLoc > 0) { - // Put on the tail, if there is one - sb.append(srcString.subSequence(lastCharLoc, srcString.length())); - } - return sb; - } - } - - @Override - public SQLiteOpenHelper getDatabaseHelper(Context context) { - synchronized (this) { - if (mOpenHelper == null) { - mOpenHelper = new DatabaseHelper(context); - } - return mOpenHelper; - } - } +/** + * This class exists only for compatibility reasons to prevent apps + * querying it from getting null cursors they do not expect and crash. + */ +public class BookmarkProvider extends ContentProvider { @Override - public boolean isCallerSyncAdapter(Uri uri) { - return uri.getBooleanQueryParameter(BrowserContract.CALLER_IS_SYNCADAPTER, false); + public boolean onCreate() { + return true; } @Override public String getType(Uri uri) { - final int match = URI_MATCHER.match(uri); - switch (match) { - case LEGACY: - case BOOKMARKS: - return Bookmarks.CONTENT_TYPE; - case LEGACY_ID: - case BOOKMARKS_ID: - return Bookmarks.CONTENT_ITEM_TYPE; - case HISTORY: - return History.CONTENT_TYPE; - case HISTORY_ID: - return History.CONTENT_ITEM_TYPE; - case SEARCHES: - return Searches.CONTENT_TYPE; - case SEARCHES_ID: - return Searches.CONTENT_ITEM_TYPE; - } - return null; - } - - boolean isNullAccount(String account) { - if (account == null) return true; - account = account.trim(); - return account.length() == 0 || account.equals("null"); - } - - Object[] getSelectionWithAccounts(Uri uri, String selection, String[] selectionArgs) { - // Look for account info - String accountType = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_TYPE); - String accountName = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_NAME); - boolean hasAccounts = false; - if (accountType != null && accountName != null) { - if (!isNullAccount(accountType) && !isNullAccount(accountName)) { - selection = DatabaseUtils.concatenateWhere(selection, - Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=? "); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { accountType, accountName }); - hasAccounts = true; - } else { - selection = DatabaseUtils.concatenateWhere(selection, - Bookmarks.ACCOUNT_NAME + " IS NULL AND " + - Bookmarks.ACCOUNT_TYPE + " IS NULL"); - } - } - return new Object[] { selection, selectionArgs, hasAccounts }; + return BrowserContract.Bookmarks.CONTENT_TYPE; } @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - final int match = URI_MATCHER.match(uri); - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - String limit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT); - String groupBy = uri.getQueryParameter(PARAM_GROUP_BY); - switch (match) { - case ACCOUNTS: { - qb.setTables(VIEW_ACCOUNTS); - qb.setProjectionMap(ACCOUNTS_PROJECTION_MAP); - String allowEmpty = uri.getQueryParameter(PARAM_ALLOW_EMPTY_ACCOUNTS); - if ("false".equals(allowEmpty)) { - selection = DatabaseUtils.concatenateWhere(selection, - SQL_WHERE_ACCOUNT_HAS_BOOKMARKS); - } - if (sortOrder == null) { - sortOrder = DEFAULT_SORT_ACCOUNTS; - } - break; - } - - case BOOKMARKS_FOLDER_ID: - case BOOKMARKS_ID: - case BOOKMARKS: { - // Only show deleted bookmarks if requested to do so - if (!uri.getBooleanQueryParameter(Bookmarks.QUERY_PARAMETER_SHOW_DELETED, false)) { - selection = DatabaseUtils.concatenateWhere( - Bookmarks.IS_DELETED + "=0", selection); - } - - if (match == BOOKMARKS_ID) { - // Tack on the ID of the specific bookmark requested - selection = DatabaseUtils.concatenateWhere(selection, - TABLE_BOOKMARKS + "." + Bookmarks._ID + "=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - } else if (match == BOOKMARKS_FOLDER_ID) { - // Tack on the ID of the specific folder requested - selection = DatabaseUtils.concatenateWhere(selection, - TABLE_BOOKMARKS + "." + Bookmarks.PARENT + "=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - } - - Object[] withAccount = getSelectionWithAccounts(uri, selection, selectionArgs); - selection = (String) withAccount[0]; - selectionArgs = (String[]) withAccount[1]; - boolean hasAccounts = (Boolean) withAccount[2]; - - // Set a default sort order if one isn't specified - if (TextUtils.isEmpty(sortOrder)) { - if (hasAccounts) { - sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER_SYNC; - } else { - sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER; - } - } - - qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); - qb.setTables(TABLE_BOOKMARKS_JOIN_IMAGES); - break; - } - - case BOOKMARKS_FOLDER: { - // Look for an account - boolean useAccount = false; - String accountType = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_TYPE); - String accountName = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_NAME); - if (!isNullAccount(accountType) && !isNullAccount(accountName)) { - useAccount = true; - } - - qb.setTables(TABLE_BOOKMARKS_JOIN_IMAGES); - String[] args; - String query; - // Set a default sort order if one isn't specified - if (TextUtils.isEmpty(sortOrder)) { - if (useAccount) { - sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER_SYNC; - } else { - sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER; - } - } - if (!useAccount) { - qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); - String where = Bookmarks.PARENT + "=? AND " + Bookmarks.IS_DELETED + "=0"; - where = DatabaseUtils.concatenateWhere(where, selection); - args = new String[] { Long.toString(FIXED_ID_ROOT) }; - if (selectionArgs != null) { - args = DatabaseUtils.appendSelectionArgs(args, selectionArgs); - } - query = qb.buildQuery(projection, where, null, null, sortOrder, null); - } else { - qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP); - String where = Bookmarks.ACCOUNT_TYPE + "=? AND " + - Bookmarks.ACCOUNT_NAME + "=? " + - "AND parent = " + - "(SELECT _id FROM " + TABLE_BOOKMARKS + " WHERE " + - ChromeSyncColumns.SERVER_UNIQUE + "=" + - "'" + ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR + "' " + - "AND account_type = ? AND account_name = ?) " + - "AND " + Bookmarks.IS_DELETED + "=0"; - where = DatabaseUtils.concatenateWhere(where, selection); - String bookmarksBarQuery = qb.buildQuery(projection, - where, null, null, null, null); - args = new String[] {accountType, accountName, - accountType, accountName}; - if (selectionArgs != null) { - args = DatabaseUtils.appendSelectionArgs(args, selectionArgs); - } - - where = Bookmarks.ACCOUNT_TYPE + "=? AND " + Bookmarks.ACCOUNT_NAME + "=?" + - " AND " + ChromeSyncColumns.SERVER_UNIQUE + "=?"; - where = DatabaseUtils.concatenateWhere(where, selection); - qb.setProjectionMap(OTHER_BOOKMARKS_PROJECTION_MAP); - String otherBookmarksQuery = qb.buildQuery(projection, - where, null, null, null, null); - - query = qb.buildUnionQuery( - new String[] { bookmarksBarQuery, otherBookmarksQuery }, - sortOrder, limit); - - args = DatabaseUtils.appendSelectionArgs(args, new String[] { - accountType, accountName, ChromeSyncColumns.FOLDER_NAME_OTHER_BOOKMARKS, - }); - if (selectionArgs != null) { - args = DatabaseUtils.appendSelectionArgs(args, selectionArgs); - } - } - - Cursor cursor = db.rawQuery(query, args); - if (cursor != null) { - cursor.setNotificationUri(getContext().getContentResolver(), - BrowserContract.AUTHORITY_URI); - } - return cursor; - } - - case BOOKMARKS_DEFAULT_FOLDER_ID: { - String accountName = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_NAME); - String accountType = uri.getQueryParameter(Bookmarks.PARAM_ACCOUNT_TYPE); - long id = queryDefaultFolderId(accountName, accountType); - MatrixCursor c = new MatrixCursor(new String[] {Bookmarks._ID}); - c.newRow().add(id); - return c; - } - - case BOOKMARKS_SUGGESTIONS: { - return doSuggestQuery(selection, selectionArgs, limit); - } - - case HISTORY_ID: { - selection = DatabaseUtils.concatenateWhere(selection, TABLE_HISTORY + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case HISTORY: { - filterSearchClient(selectionArgs); - if (sortOrder == null) { - sortOrder = DEFAULT_SORT_HISTORY; - } - qb.setProjectionMap(HISTORY_PROJECTION_MAP); - qb.setTables(TABLE_HISTORY_JOIN_IMAGES); - break; - } - - case SEARCHES_ID: { - selection = DatabaseUtils.concatenateWhere(selection, TABLE_SEARCHES + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case SEARCHES: { - qb.setTables(TABLE_SEARCHES); - qb.setProjectionMap(SEARCHES_PROJECTION_MAP); - break; - } - - case SYNCSTATE: { - return mSyncHelper.query(db, projection, selection, selectionArgs, sortOrder); - } - - case SYNCSTATE_ID: { - selection = appendAccountToSelection(uri, selection); - String selectionWithId = - (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") - + (selection == null ? "" : " AND (" + selection + ")"); - return mSyncHelper.query(db, projection, selectionWithId, selectionArgs, sortOrder); - } - - case IMAGES: { - qb.setTables(TABLE_IMAGES); - qb.setProjectionMap(IMAGES_PROJECTION_MAP); - break; - } - - case LEGACY_ID: - case COMBINED_ID: { - selection = DatabaseUtils.concatenateWhere( - selection, Combined._ID + " = CAST(? AS INTEGER)"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case LEGACY: - case COMBINED: { - if ((match == LEGACY || match == LEGACY_ID) - && projection == null) { - projection = Browser.HISTORY_PROJECTION; - } - String[] args = createCombinedQuery(uri, projection, qb); - if (selectionArgs == null) { - selectionArgs = args; - } else { - selectionArgs = DatabaseUtils.appendSelectionArgs(args, selectionArgs); - } - break; - } - - case SETTINGS: { - qb.setTables(TABLE_SETTINGS); - qb.setProjectionMap(SETTINGS_PROJECTION_MAP); - break; - } - - case THUMBNAILS_ID: { - selection = DatabaseUtils.concatenateWhere( - selection, Thumbnails._ID + " = ?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case THUMBNAILS: { - qb.setTables(TABLE_THUMBNAILS); - break; - } - - case OMNIBOX_SUGGESTIONS: { - qb.setTables(VIEW_OMNIBOX_SUGGESTIONS); - break; - } - - default: { - throw new UnsupportedOperationException("Unknown URL " + uri.toString()); - } - } - - Cursor cursor = qb.query(db, projection, selection, selectionArgs, groupBy, - null, sortOrder, limit); - cursor.setNotificationUri(getContext().getContentResolver(), BrowserContract.AUTHORITY_URI); - return cursor; - } - - private Cursor doSuggestQuery(String selection, String[] selectionArgs, String limit) { - if (TextUtils.isEmpty(selectionArgs[0])) { - selection = ZERO_QUERY_SUGGEST_SELECTION; - selectionArgs = null; - } else { - String like = selectionArgs[0] + "%"; - if (selectionArgs[0].startsWith("http") - || selectionArgs[0].startsWith("file")) { - selectionArgs[0] = like; - } else { - selectionArgs = new String[6]; - selectionArgs[0] = "http://" + like; - selectionArgs[1] = "http://www." + like; - selectionArgs[2] = "https://" + like; - selectionArgs[3] = "https://www." + like; - // To match against titles. - selectionArgs[4] = like; - selectionArgs[5] = like; - selection = SUGGEST_SELECTION; - } - selection = DatabaseUtils.concatenateWhere(selection, - Bookmarks.IS_DELETED + "=0 AND " + Bookmarks.IS_FOLDER + "=0"); - } - - Cursor c = mOpenHelper.getReadableDatabase().query(TABLE_BOOKMARKS_JOIN_HISTORY, - SUGGEST_PROJECTION, selection, selectionArgs, null, null, - null, null); - - return new SuggestionsCursor(c); - } - - private String[] createCombinedQuery( - Uri uri, String[] projection, SQLiteQueryBuilder qb) { - String[] args = null; - StringBuilder whereBuilder = new StringBuilder(128); - whereBuilder.append(Bookmarks.IS_DELETED); - whereBuilder.append(" = 0"); - // Look for account info - Object[] withAccount = getSelectionWithAccounts(uri, null, null); - String selection = (String) withAccount[0]; - String[] selectionArgs = (String[]) withAccount[1]; - if (selection != null) { - whereBuilder.append(" AND " + selection); - if (selectionArgs != null) { - // We use the selection twice, hence we need to duplicate the args - args = new String[selectionArgs.length * 2]; - System.arraycopy(selectionArgs, 0, args, 0, selectionArgs.length); - System.arraycopy(selectionArgs, 0, args, selectionArgs.length, - selectionArgs.length); - } - } - String where = whereBuilder.toString(); - // Build the bookmark subquery for history union subquery - qb.setTables(TABLE_BOOKMARKS); - String subQuery = qb.buildQuery(null, where, null, null, null, null); - // Build the history union subquery - qb.setTables(String.format(FORMAT_COMBINED_JOIN_SUBQUERY_JOIN_IMAGES, subQuery)); - qb.setProjectionMap(COMBINED_HISTORY_PROJECTION_MAP); - String historySubQuery = qb.buildQuery(null, - null, null, null, null, null); - // Build the bookmark union subquery - qb.setTables(TABLE_BOOKMARKS_JOIN_IMAGES); - qb.setProjectionMap(COMBINED_BOOKMARK_PROJECTION_MAP); - where += String.format(" AND %s NOT IN (SELECT %s FROM %s)", - Combined.URL, History.URL, TABLE_HISTORY); - String bookmarksSubQuery = qb.buildQuery(null, where, - null, null, null, null); - // Put it all together - String query = qb.buildUnionQuery( - new String[] {historySubQuery, bookmarksSubQuery}, - null, null); - qb.setTables("(" + query + ")"); - qb.setProjectionMap(null); - return args; - } - - int deleteBookmarks(String selection, String[] selectionArgs, - boolean callerIsSyncAdapter) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - if (callerIsSyncAdapter) { - return db.delete(TABLE_BOOKMARKS, selection, selectionArgs); - } - - Object[] appendedBookmarks = appendBookmarksIfFolder(selection, selectionArgs); - selection = (String) appendedBookmarks[0]; - selectionArgs = (String[]) appendedBookmarks[1]; - - ContentValues values = new ContentValues(); - values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis()); - values.put(Bookmarks.IS_DELETED, 1); - return updateBookmarksInTransaction(values, selection, selectionArgs, - callerIsSyncAdapter); - } - - private Object[] appendBookmarksIfFolder(String selection, String[] selectionArgs) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - final String[] bookmarksProjection = new String[] { - Bookmarks._ID, // 0 - Bookmarks.IS_FOLDER // 1 - }; - StringBuilder newSelection = new StringBuilder(selection); - List<String> newSelectionArgs = new ArrayList<String>(); - - Cursor cursor = null; - try { - cursor = db.query(TABLE_BOOKMARKS, bookmarksProjection, - selection, selectionArgs, null, null, null); - if (cursor != null) { - while (cursor.moveToNext()) { - String id = Long.toString(cursor.getLong(0)); - newSelectionArgs.add(id); - if (cursor.getInt(1) != 0) { - // collect bookmarks in this folder - Object[] bookmarks = appendBookmarksIfFolder( - Bookmarks.PARENT + "=?", new String[] { id }); - String[] bookmarkIds = (String[]) bookmarks[1]; - if (bookmarkIds.length > 0) { - newSelection.append(" OR " + TABLE_BOOKMARKS + "._id IN ("); - for (String bookmarkId : bookmarkIds) { - newSelection.append("?,"); - newSelectionArgs.add(bookmarkId); - } - newSelection.deleteCharAt(newSelection.length() - 1); - newSelection.append(")"); - } - } - } - } - } finally { - if (cursor != null) { - cursor.close(); - } - } - - return new Object[] { - newSelection.toString(), - newSelectionArgs.toArray(new String[newSelectionArgs.size()]) - }; + public Uri insert(Uri uri, ContentValues values) { + return null; } @Override - public int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, - boolean callerIsSyncAdapter) { - final int match = URI_MATCHER.match(uri); - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - int deleted = 0; - switch (match) { - case BOOKMARKS_ID: { - selection = DatabaseUtils.concatenateWhere(selection, - TABLE_BOOKMARKS + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case BOOKMARKS: { - // Look for account info - Object[] withAccount = getSelectionWithAccounts(uri, selection, selectionArgs); - selection = (String) withAccount[0]; - selectionArgs = (String[]) withAccount[1]; - deleted = deleteBookmarks(selection, selectionArgs, callerIsSyncAdapter); - pruneImages(); - break; - } - - case HISTORY_ID: { - selection = DatabaseUtils.concatenateWhere(selection, TABLE_HISTORY + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case HISTORY: { - filterSearchClient(selectionArgs); - deleted = db.delete(TABLE_HISTORY, selection, selectionArgs); - pruneImages(); - break; - } - - case SEARCHES_ID: { - selection = DatabaseUtils.concatenateWhere(selection, TABLE_SEARCHES + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case SEARCHES: { - deleted = db.delete(TABLE_SEARCHES, selection, selectionArgs); - break; - } - - case SYNCSTATE: { - deleted = mSyncHelper.delete(db, selection, selectionArgs); - break; - } - case SYNCSTATE_ID: { - String selectionWithId = - (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") - + (selection == null ? "" : " AND (" + selection + ")"); - deleted = mSyncHelper.delete(db, selectionWithId, selectionArgs); - break; - } - case LEGACY_ID: { - selection = DatabaseUtils.concatenateWhere( - selection, Combined._ID + " = CAST(? AS INTEGER)"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case LEGACY: { - String[] projection = new String[] { Combined._ID, - Combined.IS_BOOKMARK, Combined.URL }; - SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - String[] args = createCombinedQuery(uri, projection, qb); - if (selectionArgs == null) { - selectionArgs = args; - } else { - selectionArgs = DatabaseUtils.appendSelectionArgs( - args, selectionArgs); - } - Cursor c = qb.query(db, projection, selection, selectionArgs, - null, null, null); - while (c.moveToNext()) { - long id = c.getLong(0); - boolean isBookmark = c.getInt(1) != 0; - String url = c.getString(2); - if (isBookmark) { - deleted += deleteBookmarks(Bookmarks._ID + "=?", - new String[] { Long.toString(id) }, - callerIsSyncAdapter); - db.delete(TABLE_HISTORY, History.URL + "=?", - new String[] { url }); - } else { - deleted += db.delete(TABLE_HISTORY, - Bookmarks._ID + "=?", - new String[] { Long.toString(id) }); - } - } - c.close(); - break; - } - case THUMBNAILS_ID: { - selection = DatabaseUtils.concatenateWhere( - selection, Thumbnails._ID + " = ?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case THUMBNAILS: { - deleted = db.delete(TABLE_THUMBNAILS, selection, selectionArgs); - break; - } - default: { - throw new UnsupportedOperationException("Unknown delete URI " + uri); - } - } - if (deleted > 0) { - postNotifyUri(uri); - if (shouldNotifyLegacy(uri)) { - postNotifyUri(LEGACY_AUTHORITY_URI); - } - } - return deleted; - } - - long queryDefaultFolderId(String accountName, String accountType) { - if (!isNullAccount(accountName) && !isNullAccount(accountType)) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); - Cursor c = db.query(TABLE_BOOKMARKS, new String[] { Bookmarks._ID }, - ChromeSyncColumns.SERVER_UNIQUE + " = ?" + - " AND account_type = ? AND account_name = ?", - new String[] { ChromeSyncColumns.FOLDER_NAME_BOOKMARKS_BAR, - accountType, accountName }, null, null, null); - try { - if (c.moveToFirst()) { - return c.getLong(0); - } - } finally { - c.close(); - } - } - return FIXED_ID_ROOT; + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; } @Override - public Uri insertInTransaction(Uri uri, ContentValues values, boolean callerIsSyncAdapter) { - int match = URI_MATCHER.match(uri); - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - long id = -1; - if (match == LEGACY) { - // Intercept and route to the correct table - Integer bookmark = values.getAsInteger(BookmarkColumns.BOOKMARK); - values.remove(BookmarkColumns.BOOKMARK); - if (bookmark == null || bookmark == 0) { - match = HISTORY; - } else { - match = BOOKMARKS; - values.remove(BookmarkColumns.DATE); - values.remove(BookmarkColumns.VISITS); - values.remove(BookmarkColumns.USER_ENTERED); - values.put(Bookmarks.IS_FOLDER, 0); - } - } - switch (match) { - case BOOKMARKS: { - // Mark rows dirty if they're not coming from a sync adapter - if (!callerIsSyncAdapter) { - long now = System.currentTimeMillis(); - values.put(Bookmarks.DATE_CREATED, now); - values.put(Bookmarks.DATE_MODIFIED, now); - values.put(Bookmarks.DIRTY, 1); - - boolean hasAccounts = values.containsKey(Bookmarks.ACCOUNT_TYPE) - || values.containsKey(Bookmarks.ACCOUNT_NAME); - String accountType = values - .getAsString(Bookmarks.ACCOUNT_TYPE); - String accountName = values - .getAsString(Bookmarks.ACCOUNT_NAME); - boolean hasParent = values.containsKey(Bookmarks.PARENT); - if (hasParent && hasAccounts) { - // Let's make sure it's valid - long parentId = values.getAsLong(Bookmarks.PARENT); - hasParent = isValidParent( - accountType, accountName, parentId); - } else if (hasParent && !hasAccounts) { - long parentId = values.getAsLong(Bookmarks.PARENT); - hasParent = setParentValues(parentId, values); - } - - // If no parent is set default to the "Bookmarks Bar" folder - if (!hasParent) { - values.put(Bookmarks.PARENT, - queryDefaultFolderId(accountName, accountType)); - } - } - - // If no position is requested put the bookmark at the beginning of the list - if (!values.containsKey(Bookmarks.POSITION)) { - values.put(Bookmarks.POSITION, Long.toString(Long.MIN_VALUE)); - } - - // Extract out the image values so they can be inserted into the images table - String url = values.getAsString(Bookmarks.URL); - ContentValues imageValues = extractImageValues(values, url); - Boolean isFolder = values.getAsBoolean(Bookmarks.IS_FOLDER); - if ((isFolder == null || !isFolder) - && imageValues != null && !TextUtils.isEmpty(url)) { - int count = db.update(TABLE_IMAGES, imageValues, Images.URL + "=?", - new String[] { url }); - if (count == 0) { - db.insertOrThrow(TABLE_IMAGES, Images.FAVICON, imageValues); - } - } - - id = db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.DIRTY, values); - break; - } - - case HISTORY: { - // If no created time is specified set it to now - if (!values.containsKey(History.DATE_CREATED)) { - values.put(History.DATE_CREATED, System.currentTimeMillis()); - } - String url = values.getAsString(History.URL); - url = filterSearchClient(url); - values.put(History.URL, url); - - // Extract out the image values so they can be inserted into the images table - ContentValues imageValues = extractImageValues(values, - values.getAsString(History.URL)); - if (imageValues != null) { - db.insertOrThrow(TABLE_IMAGES, Images.FAVICON, imageValues); - } - - id = db.insertOrThrow(TABLE_HISTORY, History.VISITS, values); - break; - } - - case SEARCHES: { - id = insertSearchesInTransaction(db, values); - break; - } - - case SYNCSTATE: { - id = mSyncHelper.insert(db, values); - break; - } - - case SETTINGS: { - id = 0; - insertSettingsInTransaction(db, values); - break; - } - - case THUMBNAILS: { - id = db.replaceOrThrow(TABLE_THUMBNAILS, null, values); - break; - } - - default: { - throw new UnsupportedOperationException("Unknown insert URI " + uri); - } - } - - if (id >= 0) { - postNotifyUri(uri); - if (shouldNotifyLegacy(uri)) { - postNotifyUri(LEGACY_AUTHORITY_URI); - } - return ContentUris.withAppendedId(uri, id); - } else { - return null; - } - } - - private String[] getAccountNameAndType(long id) { - if (id <= 0) { - return null; - } - Uri uri = ContentUris.withAppendedId(Bookmarks.CONTENT_URI, id); - Cursor c = query(uri, - new String[] { Bookmarks.ACCOUNT_NAME, Bookmarks.ACCOUNT_TYPE }, - null, null, null); - try { - if (c.moveToFirst()) { - String parentName = c.getString(0); - String parentType = c.getString(1); - return new String[] { parentName, parentType }; - } - return null; - } finally { - c.close(); - } - } - - private boolean setParentValues(long parentId, ContentValues values) { - String[] parent = getAccountNameAndType(parentId); - if (parent == null) { - return false; - } - values.put(Bookmarks.ACCOUNT_NAME, parent[0]); - values.put(Bookmarks.ACCOUNT_TYPE, parent[1]); - return true; - } - - private boolean isValidParent(String accountType, String accountName, - long parentId) { - String[] parent = getAccountNameAndType(parentId); - if (parent != null - && TextUtils.equals(accountName, parent[0]) - && TextUtils.equals(accountType, parent[1])) { - return true; - } - return false; - } - - private void filterSearchClient(String[] selectionArgs) { - if (selectionArgs != null) { - for (int i = 0; i < selectionArgs.length; i++) { - selectionArgs[i] = filterSearchClient(selectionArgs[i]); - } - } - } - - // Filters out the client= param for search urls - private String filterSearchClient(String url) { - // remove "client" before updating it to the history so that it wont - // show up in the auto-complete list. - int index = url.indexOf("client="); - if (index > 0 && url.contains(".google.")) { - int end = url.indexOf('&', index); - if (end > 0) { - url = url.substring(0, index) - .concat(url.substring(end + 1)); - } else { - // the url.charAt(index-1) should be either '?' or '&' - url = url.substring(0, index-1); - } - } - return url; - } - - /** - * Searches are unique, so perform an UPSERT manually since SQLite doesn't support them. - */ - private long insertSearchesInTransaction(SQLiteDatabase db, ContentValues values) { - String search = values.getAsString(Searches.SEARCH); - if (TextUtils.isEmpty(search)) { - throw new IllegalArgumentException("Must include the SEARCH field"); - } - Cursor cursor = null; - try { - cursor = db.query(TABLE_SEARCHES, new String[] { Searches._ID }, - Searches.SEARCH + "=?", new String[] { search }, null, null, null); - if (cursor.moveToNext()) { - long id = cursor.getLong(0); - db.update(TABLE_SEARCHES, values, Searches._ID + "=?", - new String[] { Long.toString(id) }); - return id; - } else { - return db.insertOrThrow(TABLE_SEARCHES, Searches.SEARCH, values); - } - } finally { - if (cursor != null) cursor.close(); - } - } - - /** - * Settings are unique, so perform an UPSERT manually since SQLite doesn't support them. - */ - private long insertSettingsInTransaction(SQLiteDatabase db, ContentValues values) { - String key = values.getAsString(Settings.KEY); - if (TextUtils.isEmpty(key)) { - throw new IllegalArgumentException("Must include the KEY field"); - } - String[] keyArray = new String[] { key }; - Cursor cursor = null; - try { - cursor = db.query(TABLE_SETTINGS, new String[] { Settings.KEY }, - Settings.KEY + "=?", keyArray, null, null, null); - if (cursor.moveToNext()) { - long id = cursor.getLong(0); - db.update(TABLE_SETTINGS, values, Settings.KEY + "=?", keyArray); - return id; - } else { - return db.insertOrThrow(TABLE_SETTINGS, Settings.VALUE, values); - } - } finally { - if (cursor != null) cursor.close(); - } + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; } @Override - public int updateInTransaction(Uri uri, ContentValues values, String selection, - String[] selectionArgs, boolean callerIsSyncAdapter) { - int match = URI_MATCHER.match(uri); - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - if (match == LEGACY || match == LEGACY_ID) { - // Intercept and route to the correct table - Integer bookmark = values.getAsInteger(BookmarkColumns.BOOKMARK); - values.remove(BookmarkColumns.BOOKMARK); - if (bookmark == null || bookmark == 0) { - if (match == LEGACY) { - match = HISTORY; - } else { - match = HISTORY_ID; - } - } else { - if (match == LEGACY) { - match = BOOKMARKS; - } else { - match = BOOKMARKS_ID; - } - values.remove(BookmarkColumns.DATE); - values.remove(BookmarkColumns.VISITS); - values.remove(BookmarkColumns.USER_ENTERED); - } - } - int modified = 0; - switch (match) { - case BOOKMARKS_ID: { - selection = DatabaseUtils.concatenateWhere(selection, - TABLE_BOOKMARKS + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case BOOKMARKS: { - Object[] withAccount = getSelectionWithAccounts(uri, selection, selectionArgs); - selection = (String) withAccount[0]; - selectionArgs = (String[]) withAccount[1]; - modified = updateBookmarksInTransaction(values, selection, selectionArgs, - callerIsSyncAdapter); - break; - } - - case HISTORY_ID: { - selection = DatabaseUtils.concatenateWhere(selection, TABLE_HISTORY + "._id=?"); - selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, - new String[] { Long.toString(ContentUris.parseId(uri)) }); - // fall through - } - case HISTORY: { - modified = updateHistoryInTransaction(values, selection, selectionArgs); - break; - } - - case SYNCSTATE: { - modified = mSyncHelper.update(mDb, values, - appendAccountToSelection(uri, selection), selectionArgs); - break; - } - - case SYNCSTATE_ID: { - selection = appendAccountToSelection(uri, selection); - String selectionWithId = - (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") - + (selection == null ? "" : " AND (" + selection + ")"); - modified = mSyncHelper.update(mDb, values, - selectionWithId, selectionArgs); - break; - } - - case IMAGES: { - String url = values.getAsString(Images.URL); - if (TextUtils.isEmpty(url)) { - throw new IllegalArgumentException("Images.URL is required"); - } - if (!shouldUpdateImages(db, url, values)) { - return 0; - } - int count = db.update(TABLE_IMAGES, values, Images.URL + "=?", - new String[] { url }); - if (count == 0) { - db.insertOrThrow(TABLE_IMAGES, Images.FAVICON, values); - count = 1; - } - // Only favicon is exposed in the public API. If we updated - // the thumbnail or touch icon don't bother notifying the - // legacy authority since it can't read it anyway. - boolean updatedLegacy = false; - if (getUrlCount(db, TABLE_BOOKMARKS, url) > 0) { - postNotifyUri(Bookmarks.CONTENT_URI); - updatedLegacy = values.containsKey(Images.FAVICON); - } - if (getUrlCount(db, TABLE_HISTORY, url) > 0) { - postNotifyUri(History.CONTENT_URI); - updatedLegacy = values.containsKey(Images.FAVICON); - } - if (pruneImages() > 0 || updatedLegacy) { - postNotifyUri(LEGACY_AUTHORITY_URI); - } - return count; - } - - case SEARCHES: { - modified = db.update(TABLE_SEARCHES, values, selection, selectionArgs); - break; - } - - case ACCOUNTS: { - Account[] accounts = AccountManager.get(getContext()).getAccounts(); - mSyncHelper.onAccountsChanged(mDb, accounts); - break; - } - - case THUMBNAILS: { - modified = db.update(TABLE_THUMBNAILS, values, - selection, selectionArgs); - break; - } - - default: { - throw new UnsupportedOperationException("Unknown update URI " + uri); - } - } - pruneImages(); - if (modified > 0) { - postNotifyUri(uri); - if (shouldNotifyLegacy(uri)) { - postNotifyUri(LEGACY_AUTHORITY_URI); - } - } - return modified; - } - - // We want to avoid sending out more URI notifications than we have to - // Thus, we check to see if the images we are about to store are already there - // This is used because things like a site's favion or touch icon is rarely - // changed, but the browser tries to update it every time the page loads. - // Without this, we will always send out 3 URI notifications per page load. - // With this, that drops to 0 or 1, depending on if the thumbnail changed. - private boolean shouldUpdateImages( - SQLiteDatabase db, String url, ContentValues values) { - final String[] projection = new String[] { - Images.FAVICON, - Images.THUMBNAIL, - Images.TOUCH_ICON, - }; - Cursor cursor = db.query(TABLE_IMAGES, projection, Images.URL + "=?", - new String[] { url }, null, null, null); - byte[] nfavicon = values.getAsByteArray(Images.FAVICON); - byte[] nthumb = values.getAsByteArray(Images.THUMBNAIL); - byte[] ntouch = values.getAsByteArray(Images.TOUCH_ICON); - byte[] cfavicon = null; - byte[] cthumb = null; - byte[] ctouch = null; - try { - if (cursor.getCount() <= 0) { - return nfavicon != null || nthumb != null || ntouch != null; - } - while (cursor.moveToNext()) { - if (nfavicon != null) { - cfavicon = cursor.getBlob(0); - if (!Arrays.equals(nfavicon, cfavicon)) { - return true; - } - } - if (nthumb != null) { - cthumb = cursor.getBlob(1); - if (!Arrays.equals(nthumb, cthumb)) { - return true; - } - } - if (ntouch != null) { - ctouch = cursor.getBlob(2); - if (!Arrays.equals(ntouch, ctouch)) { - return true; - } - } - } - } finally { - cursor.close(); - } - return false; - } - - int getUrlCount(SQLiteDatabase db, String table, String url) { - Cursor c = db.query(table, new String[] { "COUNT(*)" }, - "url = ?", new String[] { url }, null, null, null); - try { - int count = 0; - if (c.moveToFirst()) { - count = c.getInt(0); - } - return count; - } finally { - c.close(); - } - } - - /** - * Does a query to find the matching bookmarks and updates each one with the provided values. - */ - int updateBookmarksInTransaction(ContentValues values, String selection, - String[] selectionArgs, boolean callerIsSyncAdapter) { - int count = 0; - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - final String[] bookmarksProjection = new String[] { - Bookmarks._ID, // 0 - Bookmarks.VERSION, // 1 - Bookmarks.URL, // 2 - Bookmarks.TITLE, // 3 - Bookmarks.IS_FOLDER, // 4 - Bookmarks.ACCOUNT_NAME, // 5 - Bookmarks.ACCOUNT_TYPE, // 6 - }; - Cursor cursor = db.query(TABLE_BOOKMARKS, bookmarksProjection, - selection, selectionArgs, null, null, null); - boolean updatingParent = values.containsKey(Bookmarks.PARENT); - String parentAccountName = null; - String parentAccountType = null; - if (updatingParent) { - long parent = values.getAsLong(Bookmarks.PARENT); - Cursor c = db.query(TABLE_BOOKMARKS, new String[] { - Bookmarks.ACCOUNT_NAME, Bookmarks.ACCOUNT_TYPE}, - "_id = ?", new String[] { Long.toString(parent) }, - null, null, null); - if (c.moveToFirst()) { - parentAccountName = c.getString(0); - parentAccountType = c.getString(1); - } - c.close(); - } else if (values.containsKey(Bookmarks.ACCOUNT_NAME) - || values.containsKey(Bookmarks.ACCOUNT_TYPE)) { - // TODO: Implement if needed (no one needs this yet) - } - try { - String[] args = new String[1]; - // Mark the bookmark dirty if the caller isn't a sync adapter - if (!callerIsSyncAdapter) { - values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis()); - values.put(Bookmarks.DIRTY, 1); - } - - boolean updatingUrl = values.containsKey(Bookmarks.URL); - String url = null; - if (updatingUrl) { - url = values.getAsString(Bookmarks.URL); - } - ContentValues imageValues = extractImageValues(values, url); - - while (cursor.moveToNext()) { - long id = cursor.getLong(0); - args[0] = Long.toString(id); - String accountName = cursor.getString(5); - String accountType = cursor.getString(6); - // If we are updating the parent and either the account name or - // type do not match that of the new parent - if (updatingParent - && (!TextUtils.equals(accountName, parentAccountName) - || !TextUtils.equals(accountType, parentAccountType))) { - // Parent is a different account - // First, insert a new bookmark/folder with the new account - // Then, if this is a folder, reparent all it's children - // Finally, delete the old bookmark/folder - ContentValues newValues = valuesFromCursor(cursor); - newValues.putAll(values); - newValues.remove(Bookmarks._ID); - newValues.remove(Bookmarks.VERSION); - newValues.put(Bookmarks.ACCOUNT_NAME, parentAccountName); - newValues.put(Bookmarks.ACCOUNT_TYPE, parentAccountType); - Uri insertUri = insertInTransaction(Bookmarks.CONTENT_URI, - newValues, callerIsSyncAdapter); - long newId = ContentUris.parseId(insertUri); - if (cursor.getInt(4) != 0) { - // This is a folder, reparent - ContentValues updateChildren = new ContentValues(1); - updateChildren.put(Bookmarks.PARENT, newId); - count += updateBookmarksInTransaction(updateChildren, - Bookmarks.PARENT + "=?", new String[] { - Long.toString(id)}, callerIsSyncAdapter); - } - // Now, delete the old one - Uri uri = ContentUris.withAppendedId(Bookmarks.CONTENT_URI, id); - deleteInTransaction(uri, null, null, callerIsSyncAdapter); - count += 1; - } else { - if (!callerIsSyncAdapter) { - // increase the local version for non-sync changes - values.put(Bookmarks.VERSION, cursor.getLong(1) + 1); - } - count += db.update(TABLE_BOOKMARKS, values, "_id=?", args); - } - - // Update the images over in their table - if (imageValues != null) { - if (!updatingUrl) { - url = cursor.getString(2); - imageValues.put(Images.URL, url); - } - - if (!TextUtils.isEmpty(url)) { - args[0] = url; - if (db.update(TABLE_IMAGES, imageValues, Images.URL + "=?", args) == 0) { - db.insert(TABLE_IMAGES, Images.FAVICON, imageValues); - } - } - } - } - } finally { - if (cursor != null) cursor.close(); - } - return count; - } - - ContentValues valuesFromCursor(Cursor c) { - int count = c.getColumnCount(); - ContentValues values = new ContentValues(count); - String[] colNames = c.getColumnNames(); - for (int i = 0; i < count; i++) { - switch (c.getType(i)) { - case Cursor.FIELD_TYPE_BLOB: - values.put(colNames[i], c.getBlob(i)); - break; - case Cursor.FIELD_TYPE_FLOAT: - values.put(colNames[i], c.getFloat(i)); - break; - case Cursor.FIELD_TYPE_INTEGER: - values.put(colNames[i], c.getLong(i)); - break; - case Cursor.FIELD_TYPE_STRING: - values.put(colNames[i], c.getString(i)); - break; - } - } - return values; - } - - /** - * Does a query to find the matching bookmarks and updates each one with the provided values. - */ - int updateHistoryInTransaction(ContentValues values, String selection, String[] selectionArgs) { - int count = 0; - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - filterSearchClient(selectionArgs); - Cursor cursor = query(History.CONTENT_URI, - new String[] { History._ID, History.URL }, - selection, selectionArgs, null); - try { - String[] args = new String[1]; - - boolean updatingUrl = values.containsKey(History.URL); - String url = null; - if (updatingUrl) { - url = filterSearchClient(values.getAsString(History.URL)); - values.put(History.URL, url); - } - ContentValues imageValues = extractImageValues(values, url); - - while (cursor.moveToNext()) { - args[0] = cursor.getString(0); - count += db.update(TABLE_HISTORY, values, "_id=?", args); - - // Update the images over in their table - if (imageValues != null) { - if (!updatingUrl) { - url = cursor.getString(1); - imageValues.put(Images.URL, url); - } - args[0] = url; - if (db.update(TABLE_IMAGES, imageValues, Images.URL + "=?", args) == 0) { - db.insert(TABLE_IMAGES, Images.FAVICON, imageValues); - } - } - } - } finally { - if (cursor != null) cursor.close(); - } - return count; - } - - String appendAccountToSelection(Uri uri, String selection) { - final String accountName = uri.getQueryParameter(RawContacts.ACCOUNT_NAME); - final String accountType = uri.getQueryParameter(RawContacts.ACCOUNT_TYPE); - - final boolean partialUri = TextUtils.isEmpty(accountName) ^ TextUtils.isEmpty(accountType); - if (partialUri) { - // Throw when either account is incomplete - throw new IllegalArgumentException( - "Must specify both or neither of ACCOUNT_NAME and ACCOUNT_TYPE for " + uri); - } - - // Accounts are valid by only checking one parameter, since we've - // already ruled out partial accounts. - final boolean validAccount = !TextUtils.isEmpty(accountName); - if (validAccount) { - StringBuilder selectionSb = new StringBuilder(RawContacts.ACCOUNT_NAME + "=" - + DatabaseUtils.sqlEscapeString(accountName) + " AND " - + RawContacts.ACCOUNT_TYPE + "=" - + DatabaseUtils.sqlEscapeString(accountType)); - if (!TextUtils.isEmpty(selection)) { - selectionSb.append(" AND ("); - selectionSb.append(selection); - selectionSb.append(')'); - } - return selectionSb.toString(); - } else { - return selection; - } - } - - ContentValues extractImageValues(ContentValues values, String url) { - ContentValues imageValues = null; - // favicon - if (values.containsKey(Bookmarks.FAVICON)) { - imageValues = new ContentValues(); - imageValues.put(Images.FAVICON, values.getAsByteArray(Bookmarks.FAVICON)); - values.remove(Bookmarks.FAVICON); - } - - // thumbnail - if (values.containsKey(Bookmarks.THUMBNAIL)) { - if (imageValues == null) { - imageValues = new ContentValues(); - } - imageValues.put(Images.THUMBNAIL, values.getAsByteArray(Bookmarks.THUMBNAIL)); - values.remove(Bookmarks.THUMBNAIL); - } - - // touch icon - if (values.containsKey(Bookmarks.TOUCH_ICON)) { - if (imageValues == null) { - imageValues = new ContentValues(); - } - imageValues.put(Images.TOUCH_ICON, values.getAsByteArray(Bookmarks.TOUCH_ICON)); - values.remove(Bookmarks.TOUCH_ICON); - } - - if (imageValues != null) { - imageValues.put(Images.URL, url); - } - return imageValues; - } - - int pruneImages() { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - return db.delete(TABLE_IMAGES, IMAGE_PRUNE, null); - } - - boolean shouldNotifyLegacy(Uri uri) { - if (uri.getPathSegments().contains("history") - || uri.getPathSegments().contains("bookmarks") - || uri.getPathSegments().contains("searches")) { - return true; - } - return false; - } - - static class SuggestionsCursor extends AbstractCursor { - private static final int ID_INDEX = 0; - private static final int URL_INDEX = 1; - private static final int TITLE_INDEX = 2; - private static final int ICON_INDEX = 3; - private static final int LAST_ACCESS_TIME_INDEX = 4; - // shared suggestion array index, make sure to match COLUMNS - private static final int SUGGEST_COLUMN_INTENT_ACTION_ID = 1; - private static final int SUGGEST_COLUMN_INTENT_DATA_ID = 2; - private static final int SUGGEST_COLUMN_TEXT_1_ID = 3; - private static final int SUGGEST_COLUMN_TEXT_2_TEXT_ID = 4; - private static final int SUGGEST_COLUMN_TEXT_2_URL_ID = 5; - private static final int SUGGEST_COLUMN_ICON_1_ID = 6; - private static final int SUGGEST_COLUMN_LAST_ACCESS_HINT_ID = 7; - - // shared suggestion columns - private static final String[] COLUMNS = new String[] { - BaseColumns._ID, - SearchManager.SUGGEST_COLUMN_INTENT_ACTION, - SearchManager.SUGGEST_COLUMN_INTENT_DATA, - SearchManager.SUGGEST_COLUMN_TEXT_1, - SearchManager.SUGGEST_COLUMN_TEXT_2, - SearchManager.SUGGEST_COLUMN_TEXT_2_URL, - SearchManager.SUGGEST_COLUMN_ICON_1, - SearchManager.SUGGEST_COLUMN_LAST_ACCESS_HINT}; - - private final Cursor mSource; - - public SuggestionsCursor(Cursor cursor) { - mSource = cursor; - } - - @Override - public String[] getColumnNames() { - return COLUMNS; - } - - // Regular expression to strip http:// and optionally - // the trailing slash - private static final Pattern STRIP_URL_PATTERN = - Pattern.compile("^http://(.*?)/?$"); - - /** - * Strips the provided url of preceding "http://" and any trailing "/". Does not - * strip "https://". If the provided string cannot be stripped, the original string - * is returned. - * - * TODO: Put this in TextUtils to be used by other packages doing something similar. - * - * @param url a url to strip, like "http://www.google.com/" - * @return a stripped url like "www.google.com", or the original string if it could - * not be stripped - */ - private static String stripUrl(String url) { - if (url == null) return null; - Matcher m = STRIP_URL_PATTERN.matcher(url); - if (m.matches()) { - return m.group(1); - } else { - return url; - } - } - - @Override - public String getString(int columnIndex) { - switch (columnIndex) { - case ID_INDEX: - return mSource.getString(columnIndex); - case SUGGEST_COLUMN_INTENT_ACTION_ID: - return Intent.ACTION_VIEW; - case SUGGEST_COLUMN_INTENT_DATA_ID: - return mSource.getString(URL_INDEX); - case SUGGEST_COLUMN_TEXT_2_TEXT_ID: - case SUGGEST_COLUMN_TEXT_2_URL_ID: - return stripUrl(mSource.getString(URL_INDEX)); - case SUGGEST_COLUMN_TEXT_1_ID: - return mSource.getString(TITLE_INDEX); - case SUGGEST_COLUMN_ICON_1_ID: - return mSource.getString(ICON_INDEX); - case SUGGEST_COLUMN_LAST_ACCESS_HINT_ID: - return mSource.getString(LAST_ACCESS_TIME_INDEX); - } - return null; - } - - @Override - public int getCount() { - return mSource.getCount(); - } - - @Override - public double getDouble(int column) { - throw new UnsupportedOperationException(); - } - - @Override - public float getFloat(int column) { - throw new UnsupportedOperationException(); - } - - @Override - public int getInt(int column) { - throw new UnsupportedOperationException(); - } - - @Override - public long getLong(int column) { - switch (column) { - case ID_INDEX: - return mSource.getLong(ID_INDEX); - case SUGGEST_COLUMN_LAST_ACCESS_HINT_ID: - return mSource.getLong(LAST_ACCESS_TIME_INDEX); - } - throw new UnsupportedOperationException(); - } - - @Override - public short getShort(int column) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isNull(int column) { - return mSource.isNull(column); - } - - @Override - public boolean onMove(int oldPosition, int newPosition) { - return mSource.moveToPosition(newPosition); - } + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return new MatrixCursor(Browser.HISTORY_PROJECTION, 0); } - - // --------------------------------------------------- - // SQL below, be warned - // --------------------------------------------------- - - private static final String SQL_CREATE_VIEW_OMNIBOX_SUGGESTIONS = - "CREATE VIEW IF NOT EXISTS v_omnibox_suggestions " - + " AS " - + " SELECT _id, url, title, 1 AS bookmark, 0 AS visits, 0 AS date" - + " FROM bookmarks " - + " WHERE deleted = 0 AND folder = 0 " - + " UNION ALL " - + " SELECT _id, url, title, 0 AS bookmark, visits, date " - + " FROM history " - + " WHERE url NOT IN (SELECT url FROM bookmarks" - + " WHERE deleted = 0 AND folder = 0) " - + " ORDER BY bookmark DESC, visits DESC, date DESC "; - - private static final String SQL_WHERE_ACCOUNT_HAS_BOOKMARKS = - "0 < ( " - + "SELECT count(*) " - + "FROM bookmarks " - + "WHERE deleted = 0 AND folder = 0 " - + " AND ( " - + " v_accounts.account_name = bookmarks.account_name " - + " OR (v_accounts.account_name IS NULL AND bookmarks.account_name IS NULL) " - + " ) " - + " AND ( " - + " v_accounts.account_type = bookmarks.account_type " - + " OR (v_accounts.account_type IS NULL AND bookmarks.account_type IS NULL) " - + " ) " - + ")"; } diff --git a/src/com/android/bookmarkstore/SQLiteContentProvider.java b/src/com/android/bookmarkstore/SQLiteContentProvider.java deleted file mode 100644 index c202a57..0000000 --- a/src/com/android/bookmarkstore/SQLiteContentProvider.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2009 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.bookmarkprovider; - -import android.content.ContentProvider; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.net.Uri; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; - -/** - * General purpose {@link ContentProvider} base class that uses SQLiteDatabase for storage. - */ -public abstract class SQLiteContentProvider extends ContentProvider { - - private static final String TAG = "SQLiteContentProvider"; - - private SQLiteOpenHelper mOpenHelper; - private Set<Uri> mChangedUris; - protected SQLiteDatabase mDb; - - private final ThreadLocal<Boolean> mApplyingBatch = new ThreadLocal<Boolean>(); - private static final int SLEEP_AFTER_YIELD_DELAY = 4000; - - /** - * Maximum number of operations allowed in a batch between yield points. - */ - private static final int MAX_OPERATIONS_PER_YIELD_POINT = 500; - - @Override - public boolean onCreate() { - Context context = getContext(); - mOpenHelper = getDatabaseHelper(context); - mChangedUris = new HashSet<Uri>(); - return true; - } - - /** - * Returns a {@link SQLiteOpenHelper} that can open the database. - */ - public abstract SQLiteOpenHelper getDatabaseHelper(Context context); - - /** - * The equivalent of the {@link #insert} method, but invoked within a transaction. - */ - public abstract Uri insertInTransaction(Uri uri, ContentValues values, - boolean callerIsSyncAdapter); - - /** - * The equivalent of the {@link #update} method, but invoked within a transaction. - */ - public abstract int updateInTransaction(Uri uri, ContentValues values, String selection, - String[] selectionArgs, boolean callerIsSyncAdapter); - - /** - * The equivalent of the {@link #delete} method, but invoked within a transaction. - */ - public abstract int deleteInTransaction(Uri uri, String selection, String[] selectionArgs, - boolean callerIsSyncAdapter); - - /** - * Call this to add a URI to the list of URIs to be notified when the transaction - * is committed. - */ - protected void postNotifyUri(Uri uri) { - synchronized (mChangedUris) { - mChangedUris.add(uri); - } - } - - public boolean isCallerSyncAdapter(Uri uri) { - return false; - } - - public SQLiteOpenHelper getDatabaseHelper() { - return mOpenHelper; - } - - private boolean applyingBatch() { - return mApplyingBatch.get() != null && mApplyingBatch.get(); - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - Uri result = null; - boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); - boolean applyingBatch = applyingBatch(); - if (!applyingBatch) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); - try { - result = insertInTransaction(uri, values, callerIsSyncAdapter); - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - - onEndTransaction(callerIsSyncAdapter); - } else { - result = insertInTransaction(uri, values, callerIsSyncAdapter); - } - return result; - } - - @Override - public int bulkInsert(Uri uri, ContentValues[] values) { - int numValues = values.length; - boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); - try { - for (int i = 0; i < numValues; i++) { - Uri result = insertInTransaction(uri, values[i], callerIsSyncAdapter); - mDb.yieldIfContendedSafely(); - } - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - - onEndTransaction(callerIsSyncAdapter); - return numValues; - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - int count = 0; - boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); - boolean applyingBatch = applyingBatch(); - if (!applyingBatch) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); - try { - count = updateInTransaction(uri, values, selection, selectionArgs, - callerIsSyncAdapter); - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - - onEndTransaction(callerIsSyncAdapter); - } else { - count = updateInTransaction(uri, values, selection, selectionArgs, callerIsSyncAdapter); - } - - return count; - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - int count = 0; - boolean callerIsSyncAdapter = isCallerSyncAdapter(uri); - boolean applyingBatch = applyingBatch(); - if (!applyingBatch) { - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); - try { - count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter); - mDb.setTransactionSuccessful(); - } finally { - mDb.endTransaction(); - } - - onEndTransaction(callerIsSyncAdapter); - } else { - count = deleteInTransaction(uri, selection, selectionArgs, callerIsSyncAdapter); - } - return count; - } - - @Override - public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations) - throws OperationApplicationException { - int ypCount = 0; - int opCount = 0; - boolean callerIsSyncAdapter = false; - mDb = mOpenHelper.getWritableDatabase(); - mDb.beginTransaction(); - try { - mApplyingBatch.set(true); - final int numOperations = operations.size(); - final ContentProviderResult[] results = new ContentProviderResult[numOperations]; - for (int i = 0; i < numOperations; i++) { - if (++opCount >= MAX_OPERATIONS_PER_YIELD_POINT) { - throw new OperationApplicationException( - "Too many content provider operations between yield points. " - + "The maximum number of operations per yield point is " - + MAX_OPERATIONS_PER_YIELD_POINT, ypCount); - } - final ContentProviderOperation operation = operations.get(i); - if (!callerIsSyncAdapter && isCallerSyncAdapter(operation.getUri())) { - callerIsSyncAdapter = true; - } - if (i > 0 && operation.isYieldAllowed()) { - opCount = 0; - if (mDb.yieldIfContendedSafely(SLEEP_AFTER_YIELD_DELAY)) { - ypCount++; - } - } - results[i] = operation.apply(this, results, i); - } - mDb.setTransactionSuccessful(); - return results; - } finally { - mApplyingBatch.set(false); - mDb.endTransaction(); - onEndTransaction(callerIsSyncAdapter); - } - } - - protected void onEndTransaction(boolean callerIsSyncAdapter) { - Set<Uri> changed; - synchronized (mChangedUris) { - changed = new HashSet<Uri>(mChangedUris); - mChangedUris.clear(); - } - ContentResolver resolver = getContext().getContentResolver(); - for (Uri uri : changed) { - boolean syncToNetwork = !callerIsSyncAdapter && syncToNetwork(uri); - resolver.notifyChange(uri, null, syncToNetwork); - } - } - - protected boolean syncToNetwork(Uri uri) { - return false; - } -} diff --git a/tests/Android.mk b/tests/Android.mk deleted file mode 100644 index 61f6a2a..0000000 --- a/tests/Android.mk +++ /dev/null @@ -1,35 +0,0 @@ -# Copyright 2008, The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -LOCAL_PATH:= $(call my-dir) -include $(CLEAR_VARS) - -# We only want this apk build for tests. -LOCAL_MODULE_TAGS := tests - -LOCAL_JAVA_LIBRARIES := android.test.runner - -# Include all test java files. -LOCAL_SRC_FILES := $(call all-java-files-under, src) - -# Notice that we don't have to include the src files of BookmarkProvider -# because, by running the tests using an instrumentation targeting -# BookmarkProvider, we automatically get all of its classes loaded into our -# environment. - -LOCAL_PACKAGE_NAME := BookmarkProviderTests - -LOCAL_INSTRUMENTATION_FOR := BookmarkProvider - -include $(BUILD_PACKAGE) diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml deleted file mode 100644 index c0583fe..0000000 --- a/tests/AndroidManifest.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2008 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<!-- package name must be unique so suffix with "tests" so package loader doesn't ignore us --> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.bookmarkprovider.tests"> - - <!-- We add an application tag here just so that we can indicate that - this package needs to link against the android.test library, - which is needed when building test cases. --> - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <!-- - This declares that this app uses the instrumentation test runner targeting - the package of com.android.email. To run the tests use the command: - "adb shell am instrument -w com.android.bookmarkprovider.tests/android.test.InstrumentationTestRunner" - --> - <instrumentation android:name="android.test.InstrumentationTestRunner" - android:targetPackage="com.android.bookmarkprovider" - android:label="Tests for BookmarkProvider." /> - -</manifest> diff --git a/tests/src/com/android/bookmarkstore/BrowserProviderTests.java b/tests/src/com/android/bookmarkstore/BrowserProviderTests.java deleted file mode 100644 index e746890..0000000 --- a/tests/src/com/android/bookmarkstore/BrowserProviderTests.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider; - -import com.android.bookmarkprovider.BookmarkProvider; -import com.android.bookmarkprovider.tests.utils.ProviderTestCase3; - -import android.content.ContentValues; -import android.database.Cursor; -import android.net.Uri; -import android.provider.BrowserContract; -import android.provider.BrowserContract.Bookmarks; -import android.test.suitebuilder.annotation.MediumTest; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Unit tests for {@link BookmarkProvider}. - */ -@MediumTest -public class BrowserProviderTests extends ProviderTestCase3<BookmarkProvider> { - - private ArrayList<Uri> mDeleteUris; - - public BrowserProviderTests() { - super(BookmarkProvider.class, - BrowserContract.AUTHORITY, BookmarkProvider.LEGACY_AUTHORITY); - } - - @Override - protected void setUp() throws Exception { - mDeleteUris = new ArrayList<Uri>(); - super.setUp(); - } - - @Override - protected void tearDown() throws Exception { - for (Uri uri : mDeleteUris) { - deleteUri(uri); - } - super.tearDown(); - } - - public void testHasDefaultBookmarks() { - Cursor c = getBookmarksByPrefix(""); - try { - assertTrue("No default bookmarks", c.getCount() > 0); - } finally { - c.close(); - } - } - - public void testPartialFirstTitleWord() { - assertInsertQuery("http://www.example.com/rasdfe", "nfgjra sdfywe", "nfgj"); - } - - public void testFullFirstTitleWord() { - assertInsertQuery("http://www.example.com/", "nfgjra dfger", "nfgjra"); - } - - public void testFullFirstTitleWordPartialSecond() { - assertInsertQuery("http://www.example.com/", "nfgjra dfger", "nfgjra df"); - } - - public void testFullTitle() { - assertInsertQuery("http://www.example.com/", "nfgjra dfger", "nfgjra dfger"); - } - - public void testFullTitleJapanese() { - String title = "\u30ae\u30e3\u30e9\u30ea\u30fc\u30fcGoogle\u691c\u7d22"; - assertInsertQuery("http://www.example.com/sdaga", title, title); - } - - public void testPartialTitleJapanese() { - String title = "\u30ae\u30e3\u30e9\u30ea\u30fc\u30fcGoogle\u691c\u7d22"; - String query = "\u30ae\u30e3\u30e9\u30ea\u30fc"; - assertInsertQuery("http://www.example.com/sdaga", title, query); - } - - // - // Utilities - // - - private void assertInsertQuery(String url, String title, String query) { - addBookmark(url, title); - assertQueryReturns(url, title, query); - } - - private void assertQueryReturns(String url, String title, String query) { - Cursor c = getBookmarksByPrefix(query); - try { - assertTrue(title + " not matched by " + query, c.getCount() > 0); - assertTrue("More than one result for " + query, c.getCount() == 1); - while (c.moveToNext()) { - String gotUrl = getCol(c, Bookmarks.URL); - assertNotNull(gotUrl); - assertEquals("Bad URL", url, gotUrl); - - String gotTitle = getCol(c, Bookmarks.TITLE); - assertNotNull(gotTitle); - assertEquals("Bad title", title, gotTitle); - } - } finally { - c.close(); - } - } - - private Cursor getBookmarksByPrefix(String query) { - Uri suggestUri = Uri.parse("content://browser/bookmarks"); - String[] selectionArgs = { query + "%" }; - Cursor c = getMockContentResolver().query(suggestUri, null, "title LIKE ?", - selectionArgs, null); - assertNotNull(c); - return c; - } - - private void addBookmark(String url, String title) { - Uri uri = insertBookmark(url, title); - assertNotNull(uri); - assertFalse(android.provider.Browser.BOOKMARKS_URI.equals(uri)); - mDeleteUris.add(uri); - } - - private Uri insertBookmark(String url, String title) { - ContentValues values = new ContentValues(); - values.put("title", title); - values.put("url", url); - values.put("visits", 0); - values.put("date", 0); - values.put("created", 0); - values.put("bookmark", 1); - return getMockContentResolver().insert(android.provider.Browser.BOOKMARKS_URI, - values); - } - - private void deleteUri(Uri uri) { - int count = getMockContentResolver().delete(uri, null, null); - assertEquals("Failed to delete " + uri, 1, count); - } - - private static String getCol(Cursor c, String name) { - int col = c.getColumnIndex(name); - String msg = "Column " + name + " not found, columns: " - + Arrays.toString(c.getColumnNames()); - assertTrue(msg, col >= 0); - return c.getString(col); - } -} diff --git a/tests/src/com/android/bookmarkstore/tests/BP2ProviderTests.java b/tests/src/com/android/bookmarkstore/tests/BP2ProviderTests.java deleted file mode 100644 index ff31109..0000000 --- a/tests/src/com/android/bookmarkstore/tests/BP2ProviderTests.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider.tests; - -import com.android.bookmarkprovider.tests.utils.BP2TestCaseHelper; - -import android.content.ContentUris; -import android.content.ContentValues; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.graphics.Bitmap.Config; -import android.net.Uri; -import android.provider.BrowserContract; -import android.provider.BrowserContract.Images; -import android.test.suitebuilder.annotation.SmallTest; - -import java.io.ByteArrayOutputStream; - -@SmallTest -public class BP2ProviderTests extends BP2TestCaseHelper { - - static final String[] PROJECTION = new String[] { - BrowserContract.Bookmarks.PARENT, - BrowserContract.Bookmarks.ACCOUNT_NAME, - BrowserContract.Bookmarks.ACCOUNT_TYPE, - }; - static final int INDEX_PARENT = 0; - static final int INDEX_ACCOUNT_NAME = 1; - static final int INDEX_ACCOUNT_TYPE = 2; - - public void testUpdateImage() { - String url = "http://stub1.com"; - insertBookmark(url, "stub 1"); - ContentValues values = new ContentValues(); - values.put(Images.URL, url); - Bitmap bitmap = Bitmap.createBitmap(1, 1, Config.ARGB_8888); - ByteArrayOutputStream os = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); - values.put(Images.THUMBNAIL, os.toByteArray()); - // Use updateBookmarks because the bookmarks URI observer should - // be triggered, even though we aren't giving it a bookmarks URI - assertTrue(updateBookmark(Images.CONTENT_URI, values)); - } - - public void testIsValidParentNullAccount() { - doTestIsValidParent(null, null); - } - - public void testIsValidParentWithAccount() { - doTestIsValidParent("test@gmail.com", "com.google"); - } - - private void doTestIsValidParent(String accountName, String accountType) { - // Create the folder - ContentValues values = new ContentValues(); - values.put(BrowserContract.Bookmarks.TITLE, "New Folder"); - values.put(BrowserContract.Bookmarks.IS_FOLDER, 1); - values.put(BrowserContract.Bookmarks.ACCOUNT_NAME, accountName); - values.put(BrowserContract.Bookmarks.ACCOUNT_TYPE, accountType); - Uri folderUri = insertBookmark(values); - assertNotNull(folderUri); - long folderId = ContentUris.parseId(folderUri); - assertTrue("Failed to parse folder id!", folderId > 0); - // Insert a bookmark with the same ACCOUNT_* info as parent - values.put(BrowserContract.Bookmarks.TITLE, "google"); - values.put(BrowserContract.Bookmarks.URL, "http://google.com"); - values.put(BrowserContract.Bookmarks.IS_FOLDER, 0); - values.put(BrowserContract.Bookmarks.PARENT, folderId); - Uri insertedUri = insertBookmark(values); - assertNotNull(insertedUri); - Cursor c = getMockContentResolver().query(insertedUri, - PROJECTION, null, null, null); - try { - assertNotNull(c); - assertTrue(c.moveToFirst()); - long insertedParentId = c.getLong(INDEX_PARENT); - String insertedAccountName = c.getString(INDEX_ACCOUNT_NAME); - String insertedAccountType = c.getString(INDEX_ACCOUNT_TYPE); - assertEquals(folderId, insertedParentId); - assertEquals(accountName, insertedAccountName); - assertEquals(accountType, insertedAccountType); - - // Insert a bookmark with no ACCOUNT_* set, BUT with a valid parent - // The inserted should end up with the ACCOUNT_* of the parent - values.remove(BrowserContract.Bookmarks.ACCOUNT_NAME); - values.remove(BrowserContract.Bookmarks.ACCOUNT_TYPE); - insertedUri = insertBookmark(values); - assertNotNull(insertedUri); - c.close(); - c = getMockContentResolver().query(insertedUri, - PROJECTION, null, null, null); - assertNotNull(c); - assertTrue(c.moveToFirst()); - insertedParentId = c.getLong(INDEX_PARENT); - insertedAccountName = c.getString(INDEX_ACCOUNT_NAME); - insertedAccountType = c.getString(INDEX_ACCOUNT_TYPE); - assertEquals(folderId, insertedParentId); - assertEquals(accountName, insertedAccountName); - assertEquals(accountType, insertedAccountType); - - // Insert a bookmark with a different ACCOUNT_* than it's parent - // ACCOUNT_* should override parent - accountName = accountName + "@something.else"; - accountType = "com.google"; - values.put(BrowserContract.Bookmarks.ACCOUNT_NAME, accountName); - values.put(BrowserContract.Bookmarks.ACCOUNT_TYPE, accountType); - insertedUri = insertBookmark(values); - assertNotNull(insertedUri); - c.close(); - c = getMockContentResolver().query(insertedUri, - PROJECTION, null, null, null); - assertNotNull(c); - assertTrue(c.moveToFirst()); - insertedParentId = c.getLong(INDEX_PARENT); - insertedAccountName = c.getString(INDEX_ACCOUNT_NAME); - insertedAccountType = c.getString(INDEX_ACCOUNT_TYPE); - assertNotSame(folderId, insertedParentId); - assertEquals(accountName, insertedAccountName); - assertEquals(accountType, insertedAccountType); - } finally { - c.close(); - } - } -} diff --git a/tests/src/com/android/bookmarkstore/tests/BookmarksTests.java b/tests/src/com/android/bookmarkstore/tests/BookmarksTests.java deleted file mode 100644 index e6a6b90..0000000 --- a/tests/src/com/android/bookmarkstore/tests/BookmarksTests.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider.tests; - -import com.android.bookmarkprovider.tests.utils.BP2TestCaseHelper; - -import android.content.ContentResolver; -import android.database.Cursor; -import android.provider.BrowserContract.Combined; -import android.test.suitebuilder.annotation.SmallTest; - -/** - * Extends from BP2TestCaseHelper for the helper methods - * and to get the mock database - */ -@SmallTest -public class BookmarksTests extends BP2TestCaseHelper { - private static final String QUERY_BOOKMARKS_WHERE = - Combined.URL + " == ? OR " + - Combined.URL + " == ?"; - - private static Cursor queryCombinedForUrl(ContentResolver cr, - String originalUrl, String url) { - if (cr == null || url == null) { - return null; - } - - // If originalUrl is null, just set it to url. - if (originalUrl == null) { - originalUrl = url; - } - - // Look for both the original url and the actual url. This takes in to - // account redirects. - - final String[] selArgs = new String[] { originalUrl, url }; - final String[] projection = new String[] { Combined.URL }; - return cr.query(Combined.CONTENT_URI, projection, QUERY_BOOKMARKS_WHERE, selArgs, null); - } - - public void testQueryCombinedForUrl() { - // First, add some bookmarks - assertNotNull(insertBookmark( - "http://google.com/search?q=test", "Test search")); - assertNotNull(insertBookmark( - "http://google.com/search?q=mustang", "Mustang search")); - assertNotNull(insertBookmark( - "http://google.com/search?q=aliens", "Aliens search")); - ContentResolver cr = getMockContentResolver(); - - Cursor c = null; - try { - // First, search for a match - String url = "http://google.com/search?q=test"; - c = queryCombinedForUrl(cr, null, url); - assertEquals(1, c.getCount()); - assertTrue(c.moveToFirst()); - assertEquals(url, c.getString(0)); - c.close(); - - // Next, search for no match - url = "http://google.com/search"; - c = queryCombinedForUrl(cr, null, url); - assertEquals(0, c.getCount()); - assertFalse(c.moveToFirst()); - c.close(); - } finally { - if (c != null) c.close(); - } - } -} diff --git a/tests/src/com/android/bookmarkstore/tests/utils/BP2TestCaseHelper.java b/tests/src/com/android/bookmarkstore/tests/utils/BP2TestCaseHelper.java deleted file mode 100644 index 57ce669..0000000 --- a/tests/src/com/android/bookmarkstore/tests/utils/BP2TestCaseHelper.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider.tests.utils; - -import com.android.bookmarkprovider.BookmarkProvider; - -import java.io.File; -import java.io.FilenameFilter; - -import android.content.ContentValues; -import android.net.Uri; -import android.provider.BrowserContract; -import android.provider.BrowserContract.Bookmarks; - -/** - * This is a replacement for ProviderTestCase2 that can handle notifyChange testing. - * It also has helper methods specifically for testing BookmarkProvider - */ -public abstract class BP2TestCaseHelper extends ProviderTestCase3<BookmarkProvider> { - - // Tag for potential performance impacts - private static final String PERFTAG = "BP2-PerfCheck"; - - @Override - protected void setUp() throws Exception { - super.setUp(); - } - - public BP2TestCaseHelper() { - super(BookmarkProvider.class, - BrowserContract.AUTHORITY, BookmarkProvider.LEGACY_AUTHORITY); - } - - Uri mockInsert(Uri uri, ContentValues values) { - return getMockContentResolver().insert(uri, values); - } - - int mockUpdate(Uri uri, ContentValues values, String where, - String[] selectionArgs) { - return getMockContentResolver().update(uri, values, where, selectionArgs); - } - - public Uri insertBookmark(String url, String title) { - ContentValues values = new ContentValues(); - values.put(BrowserContract.Bookmarks.TITLE, title); - values.put(BrowserContract.Bookmarks.URL, url); - values.put(BrowserContract.Bookmarks.IS_FOLDER, 0); - return insertBookmark(values); - } - - public Uri insertBookmark(ContentValues values) { - return mockInsert(Bookmarks.CONTENT_URI, values); - } - - public boolean updateBookmark(Uri uri, String url, String title) { - ContentValues values = new ContentValues(); - values.put(BrowserContract.Bookmarks.TITLE, title); - values.put(BrowserContract.Bookmarks.URL, url); - return updateBookmark(uri, values); - } - - public boolean updateBookmark(Uri uri, ContentValues values) { - int modifyCount = mockUpdate(uri, values, null, null); - assertTrue("UpdatedBookmark modified too much! " + uri, modifyCount <= 1); - return modifyCount == 1; - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - // Delete the test databases so that subsequent runs have a clean slate - File f = getMockContext().getDatabasePath("test"); - File dir = f.getParentFile(); - File testFiles[] = dir.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File dir, String filename) { - return filename.startsWith(ProviderTestCase3.FILENAME_PREFIX); - } - }); - for (File testFile : testFiles) { - testFile.delete(); - } - } -} diff --git a/tests/src/com/android/bookmarkstore/tests/utils/MockContentResolver2.java b/tests/src/com/android/bookmarkstore/tests/utils/MockContentResolver2.java deleted file mode 100644 index 2d91425..0000000 --- a/tests/src/com/android/bookmarkstore/tests/utils/MockContentResolver2.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider.tests.utils; - -import com.google.android.collect.Maps; - -import android.content.ContentProvider; -import android.content.ContentResolver; -import android.content.Context; -import android.content.IContentProvider; -import android.database.ContentObserver; -import android.net.Uri; - -import java.util.Map; - -public class MockContentResolver2 extends ContentResolver { - - Map<String, ContentProvider> mProviders; - private final MockObserverNode mRootNode = new MockObserverNode(""); - - /* - * Creates a local map of providers. This map is used instead of the global map when an - * API call tries to acquire a provider. - */ - public MockContentResolver2() { - super(null); - mProviders = Maps.newHashMap(); - } - - /** - * Adds access to a provider based on its authority - * - * @param name The authority name associated with the provider. - * @param provider An instance of {@link android.content.ContentProvider} or one of its - * subclasses, or null. - */ - public void addProvider(String name, ContentProvider provider) { - /* - * Maps the authority to the provider locally. - */ - mProviders.put(name, provider); - } - - /** @hide */ - @Override - protected IContentProvider acquireProvider(Context context, String name) { - return acquireExistingProvider(context, name); - } - - /** @hide */ - @Override - protected IContentProvider acquireExistingProvider(Context context, String name) { - - /* - * Gets the content provider from the local map - */ - final ContentProvider provider = mProviders.get(name); - - if (provider != null) { - return provider.getIContentProvider(); - } else { - return null; - } - } - - /** @hide */ - @Override - public boolean releaseProvider(IContentProvider provider) { - return true; - } - - /** @hide */ - protected IContentProvider acquireUnstableProvider(Context c, String name) { - return acquireProvider(c, name); - } - - /** @hide */ - public boolean releaseUnstableProvider(IContentProvider icp) { - return releaseProvider(icp); - } - - /** @hide */ - public void unstableProviderDied(IContentProvider icp) { - } - - @Override - public void notifyChange(Uri uri, ContentObserver observer, - boolean syncToNetwork) { - mRootNode.notifyMyObservers(uri, 0, observer, false); - } - - public void safeRegisterContentObserver(Uri uri, boolean notifyForDescendents, - ContentObserver observer) { - mRootNode.addObserver(uri, observer, notifyForDescendents); - } - - public void safeUnregisterContentObserver(ContentObserver observer) { - mRootNode.removeObserver(observer); - } - -} diff --git a/tests/src/com/android/bookmarkstore/tests/utils/MockObserverNode.java b/tests/src/com/android/bookmarkstore/tests/utils/MockObserverNode.java deleted file mode 100644 index 996b3b3..0000000 --- a/tests/src/com/android/bookmarkstore/tests/utils/MockObserverNode.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider.tests.utils; - -import android.database.ContentObserver; -import android.net.Uri; - -import java.util.ArrayList; - -public final class MockObserverNode { - private class MockObserverEntry { - public final ContentObserver observer; - public final boolean notifyForDescendents; - - public MockObserverEntry(ContentObserver o, boolean n) { - observer = o; - notifyForDescendents = n; - } - } - - public static final int INSERT_TYPE = 0; - public static final int UPDATE_TYPE = 1; - public static final int DELETE_TYPE = 2; - - private String mName; - private ArrayList<MockObserverNode> mChildren = new ArrayList<MockObserverNode>(); - private ArrayList<MockObserverEntry> mObservers = new ArrayList<MockObserverEntry>(); - - public MockObserverNode(String name) { - mName = name; - } - - private String getUriSegment(Uri uri, int index) { - if (uri != null) { - if (index == 0) { - return uri.getAuthority(); - } else { - return uri.getPathSegments().get(index - 1); - } - } else { - return null; - } - } - - private int countUriSegments(Uri uri) { - if (uri == null) { - return 0; - } - return uri.getPathSegments().size() + 1; - } - - public void addObserver(Uri uri, ContentObserver observer, - boolean notifyForDescendents) { - addObserver(uri, 0, observer, notifyForDescendents); - } - - private void addObserver(Uri uri, int index, ContentObserver observer, - boolean notifyForDescendents) { - // If this is the leaf node add the observer - if (index == countUriSegments(uri)) { - mObservers.add(new MockObserverEntry(observer, notifyForDescendents)); - return; - } - - // Look to see if the proper child already exists - String segment = getUriSegment(uri, index); - if (segment == null) { - throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer"); - } - int N = mChildren.size(); - for (int i = 0; i < N; i++) { - MockObserverNode node = mChildren.get(i); - if (node.mName.equals(segment)) { - node.addObserver(uri, index + 1, observer, notifyForDescendents); - return; - } - } - - // No child found, create one - MockObserverNode node = new MockObserverNode(segment); - mChildren.add(node); - node.addObserver(uri, index + 1, observer, notifyForDescendents); - } - - public boolean removeObserver(ContentObserver observer) { - int size = mChildren.size(); - for (int i = 0; i < size; i++) { - boolean empty = mChildren.get(i).removeObserver(observer); - if (empty) { - mChildren.remove(i); - i--; - size--; - } - } - - size = mObservers.size(); - for (int i = 0; i < size; i++) { - MockObserverEntry entry = mObservers.get(i); - if (entry.observer == observer) { - mObservers.remove(i); - break; - } - } - - if (mChildren.size() == 0 && mObservers.size() == 0) { - return true; - } - return false; - } - - private void notifyMyObservers(boolean leaf, ContentObserver observer, - boolean selfNotify) { - int N = mObservers.size(); - for (int i = 0; i < N; i++) { - MockObserverEntry entry = mObservers.get(i); - - // Don't notify the observer if it sent the notification and isn't interesed - // in self notifications - if (entry.observer == observer && !selfNotify) { - continue; - } - - // Make sure the observer is interested in the notification - if (leaf || (!leaf && entry.notifyForDescendents)) { - entry.observer.onChange(selfNotify); - } - } - } - - public void notifyMyObservers(Uri uri, int index, ContentObserver observer, - boolean selfNotify) { - String segment = null; - int segmentCount = countUriSegments(uri); - if (index >= segmentCount) { - // This is the leaf node, notify all observers - notifyMyObservers(true, observer, selfNotify); - } else if (index < segmentCount){ - segment = getUriSegment(uri, index); - // Notify any observers at this level who are interested in descendents - notifyMyObservers(false, observer, selfNotify); - } - - int N = mChildren.size(); - for (int i = 0; i < N; i++) { - MockObserverNode node = mChildren.get(i); - if (segment == null || node.mName.equals(segment)) { - // We found the child, - node.notifyMyObservers(uri, index + 1, observer, selfNotify); - if (segment != null) { - break; - } - } - } - } -} diff --git a/tests/src/com/android/bookmarkstore/tests/utils/ProviderTestCase3.java b/tests/src/com/android/bookmarkstore/tests/utils/ProviderTestCase3.java deleted file mode 100644 index f8eb161..0000000 --- a/tests/src/com/android/bookmarkstore/tests/utils/ProviderTestCase3.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.bookmarkprovider.tests.utils; - -import android.content.ContentProvider; -import android.content.Context; -import android.content.SharedPreferences; -import android.content.res.Resources; -import android.database.ContentObserver; -import android.net.Uri; -import android.test.AndroidTestCase; -import android.test.IsolatedContext; -import android.test.RenamingDelegatingContext; -import android.test.mock.MockContext; - -import java.io.File; - -import android.content.pm.ProviderInfo; - -/** - * Replacement for ProviderTestCase2 that keeps calls to ContentResolver.notifyChanged - * internal to observers registered with ProviderTestCase3.registerContentObserver - */ -public abstract class ProviderTestCase3<T extends ContentProvider> extends AndroidTestCase { - - public static final String FILENAME_PREFIX = "test."; - - private static final String BROWSER_AUTHORITIES = "com.android.browser;browser"; - - Class<T> mProviderClass; - String[] mProviderAuthority; - - private IsolatedContext mProviderContext; - private MockContentResolver2 mResolver; - - private class MockContext2 extends MockContext { - - @Override - public Resources getResources() { - return getContext().getResources(); - } - - @Override - public File getDir(String name, int mode) { - // name the directory so the directory will be separated from - // one created through the regular Context - return getContext().getDir("mockcontext2_" + name, mode); - } - - @Override - public String getPackageName() { - return getContext().getPackageName(); - } - - @Override - public SharedPreferences getSharedPreferences(String name, int mode) { - return getContext().getSharedPreferences("mockcontext2_" + name, mode); - } - - @Override - public Context getApplicationContext() { - return this; - } - - @Override - public Object getSystemService(String name) { - return null; - } - } - /** - * Constructor. - * - * @param providerClass The class name of the provider under test - * @param providerAuthorities The provider's authority string - */ - public ProviderTestCase3(Class<T> providerClass, String... providerAuthorities) { - mProviderClass = providerClass; - mProviderAuthority = providerAuthorities; - } - - private T mProvider; - - /** - * Returns the content provider created by this class in the {@link #setUp()} method. - * @return T An instance of the provider class given as a parameter to the test case class. - */ - public T getProvider() { - return mProvider; - } - - /** - * Sets up the environment for the test fixture. - * <p> - * Creates a new - * {@link com.android.bookmarkprovider.tests.utils.MockContentResolver2}, a new IsolatedContext - * that isolates the provider's file operations, and a new instance of - * the provider under test within the isolated environment. - * </p> - * - * @throws Exception - */ - @Override - protected void setUp() throws Exception { - super.setUp(); - - mResolver = new MockContentResolver2(); - RenamingDelegatingContext targetContextWrapper = new - RenamingDelegatingContext( - new MockContext2(), // The context that most methods are - //delegated to - getContext(), // The context that file methods are delegated to - FILENAME_PREFIX); - // The default IsolatedContext has a mock AccountManager that doesn't - // work for us, so override getSystemService to always return null - mProviderContext = new IsolatedContext(mResolver, targetContextWrapper) { - - @Override - public Object getSystemService(String name) { - return null; - } - }; - - mProvider = mProviderClass.newInstance(); - ProviderInfo info = new ProviderInfo(); - info.authority = BROWSER_AUTHORITIES; - mProvider.attachInfoForTesting(mProviderContext, info); - assertNotNull(mProvider); - for (String auth : mProviderAuthority) { - mResolver.addProvider(auth, getProvider()); - } - } - - /** - * Tears down the environment for the test fixture. - * <p> - * Calls {@link android.content.ContentProvider#shutdown()} on the - * {@link android.content.ContentProvider} represented by mProvider. - */ - @Override - protected void tearDown() throws Exception { - mProvider.shutdown(); - super.tearDown(); - } - - /** - * Gets the {@link MockContentResolver2} created by this class during initialization. You - * must use the methods of this resolver to access the provider under test. - * - * @return A {@link MockContentResolver2} instance. - */ - public MockContentResolver2 getMockContentResolver() { - return mResolver; - } - - /** - * Gets the {@link IsolatedContext} created by this class during initialization. - * @return The {@link IsolatedContext} instance - */ - public IsolatedContext getMockContext() { - return mProviderContext; - } - - public void registerContentObserver(Uri uri, boolean notifyForDescendents, - ContentObserver observer) { - mResolver.safeRegisterContentObserver(uri, notifyForDescendents, observer); - } - - public void unregisterContentObserver(ContentObserver observer) { - mResolver.safeUnregisterContentObserver(observer); - } - -} |