summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRuslan Abdikeev <aruslan@google.com>2012-05-14 08:14:11 -0700
committerRuslan Abdikeev <aruslan@google.com>2012-05-14 08:14:11 -0700
commit60c131771453fcb65e03ae77f8a378bfa48e6498 (patch)
tree4603f6b3bd3f3afb731368b727f8e34f270d8568
parent6b04cb56970c528aaf379a64c1492af01ce9ef7e (diff)
downloadandroid_packages_providers_PartnerBookmarksProvider-60c131771453fcb65e03ae77f8a378bfa48e6498.tar.gz
android_packages_providers_PartnerBookmarksProvider-60c131771453fcb65e03ae77f8a378bfa48e6498.tar.bz2
android_packages_providers_PartnerBookmarksProvider-60c131771453fcb65e03ae77f8a378bfa48e6498.zip
Default implementation of PartnerBookmarksProvider
A “Default” Partner Bookmark content provider implements the Partner Bookmarks Content Provider API, and uses xml and raw resources to extract the set of bookmarks and corresponding icons. Bug: 6399404 Change-Id: I416e87320b2ba958aa4f260277e4d6de20856c48
-rw-r--r--Android.mk30
-rw-r--r--AndroidManifest.xml26
-rw-r--r--res/values/bookmarks_icons.xml19
-rw-r--r--res/values/strings.xml21
-rw-r--r--src/com/android/providers/partnerbookmarks/PartnerBookmarksContract.java163
-rw-r--r--src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java450
-rw-r--r--tests/Android.mk31
-rw-r--r--tests/AndroidManifest.xml37
-rw-r--r--tests/src/com/android/providers/partnerbookmarks/PartnerBookmarksProviderTest.java108
9 files changed, 885 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk
new file mode 100644
index 0000000..6978cfa
--- /dev/null
+++ b/Android.mk
@@ -0,0 +1,30 @@
+#
+# Copyright (C) 2012 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)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-common
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PartnerBookmarksProvider
+
+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
new file mode 100644
index 0000000..46dc555
--- /dev/null
+++ b/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2012 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.
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.partnerbookmarks">
+
+ <!-- We add an application tag here just to indicate the authorities -->
+ <application>
+ <provider android:name="PartnerBookmarksProvider"
+ android:authorities="com.android.partnerbookmarks" />
+ </application>
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />
+</manifest>
diff --git a/res/values/bookmarks_icons.xml b/res/values/bookmarks_icons.xml
new file mode 100644
index 0000000..516f0ef
--- /dev/null
+++ b/res/values/bookmarks_icons.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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" />
+</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
new file mode 100644
index 0000000..f614a2f
--- /dev/null
+++ b/res/values/strings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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 xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="bookmarks_folder_name" translatable="false">Partner Bookmarks</string>
+ <string-array name="bookmarks" />
+</resources>
+
diff --git a/src/com/android/providers/partnerbookmarks/PartnerBookmarksContract.java b/src/com/android/providers/partnerbookmarks/PartnerBookmarksContract.java
new file mode 100644
index 0000000..2b86062
--- /dev/null
+++ b/src/com/android/providers/partnerbookmarks/PartnerBookmarksContract.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2012 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.providers.partnerbookmarks;
+
+import android.content.ContentUris;
+import android.net.Uri;
+
+/**
+ * <p>
+ * The contract between the partner bookmarks provider and applications.
+ * Contains the definition for the supported URIs and columns.
+ * </p>
+ * <p>
+ * Authority URI: content://com.android.partnerbookmarks
+ * </p>
+ * <p>
+ * Partner bookmarks URI: content://com.android.partnerbookmarks/bookmarks
+ * </p>
+ * <p>
+ * If the provider is found, and the set of bookmarks is non-empty, exactly one
+ * top-level folder with “parent” set to {@link #BOOKMARK_PARENT_ROOT_ID}
+ * shall be provided; more than one bookmark with “parent” set to
+ * {@link #BOOKMARK_PARENT_ROOT_ID} will cause the import to fail.
+ * </p>
+ */
+public class PartnerBookmarksContract {
+ /** The authority for the partner bookmarks provider */
+ public static final String AUTHORITY = "com.android.partnerbookmarks";
+
+ /** A content:// style uri to the authority for the partner bookmarks provider */
+ public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * A parameter for use when querying any table that allows specifying
+ * a limit on the number of rows returned.
+ */
+ public static final String PARAM_LIMIT = "limit";
+
+ /**
+ * A parameter for use when querying any table that allows specifying
+ * grouping of the rows returned.
+ */
+ public static final String PARAM_GROUP_BY = "groupBy";
+
+ /**
+ * The bookmarks table, which holds the partner bookmarks.
+ */
+ public static final class Bookmarks {
+ /**
+ * This utility class cannot be instantiated.
+ */
+ private Bookmarks() {}
+
+ /**
+ * The content:// style URI for this table
+ */
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "bookmarks");
+
+ /**
+ * The content:// style URI for the root partner bookmarks folder
+ */
+ public static final Uri CONTENT_URI_PARTNER_BOOKMARKS_FOLDER =
+ Uri.withAppendedPath(CONTENT_URI, "folder");
+
+ /**
+ * Builds a URI that points to a specific folder.
+ * @param folderId the ID of the folder to point to
+ */
+ public static final Uri buildFolderUri(long folderId) {
+ return ContentUris.withAppendedId(CONTENT_URI_PARTNER_BOOKMARKS_FOLDER, folderId);
+ }
+
+ /**
+ * The MIME type of {@link #CONTENT_URI} providing a directory of bookmarks.
+ */
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/partnerbookmark";
+
+ /**
+ * The MIME type of a {@link #CONTENT_URI} of a single bookmark.
+ */
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/partnerbookmark";
+
+ /**
+ * Used in {@link #TYPE} column and indicates the row is a bookmark.
+ */
+ public static final int BOOKMARK_TYPE_BOOKMARK = 1;
+
+ /**
+ * Used in {@link #TYPE} column and indicates the row is a folder.
+ */
+ public static final int BOOKMARK_TYPE_FOLDER = 2;
+
+ /**
+ * Used in {@link #PARENT} column and indicates the row doesn't have a parent.
+ */
+ public static final int BOOKMARK_PARENT_ROOT_ID = 0;
+
+ /**
+ * The type of the item.
+ * <p>Type: INTEGER</p>
+ * <p>Allowed values are:</p>
+ * <p>
+ * <ul>
+ * <li>{@link #BOOKMARK_TYPE_BOOKMARK}</li>
+ * <li>{@link #BOOKMARK_TYPE_FOLDER}</li>
+ * </ul>
+ * </p>
+ */
+ public static final String TYPE = "type";
+
+ /**
+ * The unique ID for a row. Cannot be BOOKMARK_PARENT_ROOT_ID.
+ * <p>Type: INTEGER (long)</p>
+ */
+ public static final String ID = "_id";
+
+ /**
+ * This column is valid when the row is not a folder.
+ * <p>Type: TEXT (URL)</p>
+ */
+ public static final String URL = "url";
+
+ /**
+ * The user visible title.
+ * <p>Type: TEXT</p>
+ */
+ public static final String TITLE = "title";
+
+ /**
+ * The favicon of the bookmark, may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ */
+ public static final String FAVICON = "favicon";
+
+ /**
+ * The touch icon for the web page, may be NULL.
+ * Must decode via {@link BitmapFactory#decodeByteArray}.
+ * <p>Type: BLOB (image)</p>
+ */
+ public static final String TOUCHICON = "touchicon";
+
+ /**
+ * The ID of the parent folder. BOOKMARK_PARENT_ROOT_ID is the root folder.
+ * <p>Type: INTEGER (long) (reference to item in the same table)</p>
+ */
+ public static final String PARENT = "parent";
+ }
+}
diff --git a/src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java b/src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java
new file mode 100644
index 0000000..2ad371e
--- /dev/null
+++ b/src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2012 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.providers.partnerbookmarks;
+
+import android.content.ContentProvider;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.UriMatcher;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+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.text.TextUtils;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Default partner bookmarks provider implementation of {@link PartnerBookmarksContract} API.
+ * It reads the flat list of bookmarks and the name of the root partner
+ * bookmarks folder using getResources() API.
+ *
+ * Sample resources structure:
+ * res/
+ * values/
+ * strings.xml
+ * string name="bookmarks_folder_name"
+ * string-array name="bookmarks"
+ * item TITLE1
+ * item URL1
+ * item TITLE2
+ * item URL2...
+ * bookmarks_icons.xml
+ * array name="bookmark_preloads"
+ * item @raw/favicon1
+ * item @raw/touchicon1
+ * item @raw/favicon2
+ * item @raw/touchicon2
+ * ...
+ */
+public class PartnerBookmarksProvider extends ContentProvider {
+ private static final String TAG = "PartnerBookmarksProvider";
+
+ // URI matcher
+ private static final int URI_MATCH_BOOKMARKS = 1000;
+ private static final int URI_MATCH_BOOKMARKS_ID = 1001;
+ private static final int URI_MATCH_BOOKMARKS_FOLDER = 1002;
+ private static final int URI_MATCH_BOOKMARKS_FOLDER_ID = 1003;
+ private static final int URI_MATCH_BOOKMARKS_PARTNER_BOOKMARKS_FOLDER_ID = 1004;
+
+ private static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
+ private static final Map<String, String> BOOKMARKS_PROJECTION_MAP
+ = new HashMap<String, String>();
+
+ // Default sort order for unsync'd bookmarks
+ private static final String DEFAULT_BOOKMARKS_SORT_ORDER =
+ PartnerBookmarksContract.Bookmarks.ID + " DESC, "
+ + PartnerBookmarksContract.Bookmarks.ID + " ASC";
+
+ // Initial bookmark id when for getResources() importing
+ // Make sure to fix tests if you are changing this
+ private static final long FIXED_ID_PARTNER_BOOKMARKS_ROOT =
+ PartnerBookmarksContract.Bookmarks.BOOKMARK_PARENT_ROOT_ID + 1;
+
+ // DB table name
+ private static final String TABLE_BOOKMARKS = "bookmarks";
+
+ static {
+ final UriMatcher matcher = URI_MATCHER;
+ final String authority = PartnerBookmarksContract.AUTHORITY;
+ matcher.addURI(authority, "bookmarks", URI_MATCH_BOOKMARKS);
+ matcher.addURI(authority, "bookmarks/#", URI_MATCH_BOOKMARKS_ID);
+ matcher.addURI(authority, "bookmarks/folder", URI_MATCH_BOOKMARKS_FOLDER);
+ matcher.addURI(authority, "bookmarks/folder/#", URI_MATCH_BOOKMARKS_FOLDER_ID);
+ matcher.addURI(authority, "bookmarks/folder/id",
+ URI_MATCH_BOOKMARKS_PARTNER_BOOKMARKS_FOLDER_ID);
+ // Projection maps
+ Map<String, String> map = BOOKMARKS_PROJECTION_MAP;
+ map.put(PartnerBookmarksContract.Bookmarks.ID,
+ PartnerBookmarksContract.Bookmarks.ID);
+ map.put(PartnerBookmarksContract.Bookmarks.TITLE,
+ PartnerBookmarksContract.Bookmarks.TITLE);
+ map.put(PartnerBookmarksContract.Bookmarks.URL,
+ PartnerBookmarksContract.Bookmarks.URL);
+ map.put(PartnerBookmarksContract.Bookmarks.TYPE,
+ PartnerBookmarksContract.Bookmarks.TYPE);
+ map.put(PartnerBookmarksContract.Bookmarks.PARENT,
+ PartnerBookmarksContract.Bookmarks.PARENT);
+ map.put(PartnerBookmarksContract.Bookmarks.FAVICON,
+ PartnerBookmarksContract.Bookmarks.FAVICON);
+ map.put(PartnerBookmarksContract.Bookmarks.TOUCHICON,
+ PartnerBookmarksContract.Bookmarks.TOUCHICON);
+ }
+
+ private final class DatabaseHelper extends SQLiteOpenHelper {
+ private static final String DATABASE_FILENAME = "partnerBookmarks.db";
+ private static final int DATABASE_VERSION = 1;
+ private static final String PREFERENCES_FILENAME = "pbppref";
+ private static final String ACTIVE_CONFIGURATION_PREFNAME = "config";
+ private final SharedPreferences sharedPreferences;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_FILENAME, null, DATABASE_VERSION);
+ sharedPreferences = context.getSharedPreferences(
+ PREFERENCES_FILENAME, Context.MODE_PRIVATE);
+ }
+
+ private String getConfigSignature(Configuration config) {
+ return "mmc=" + Integer.toString(config.mcc)
+ + "-mnc=" + Integer.toString(config.mnc)
+ + "-loc=" + config.locale.toString();
+ }
+
+ public synchronized void prepareForConfiguration(Configuration config) {
+ final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+ String newSignature = getConfigSignature(config);
+ String activeSignature =
+ sharedPreferences.getString(ACTIVE_CONFIGURATION_PREFNAME, null);
+ if (activeSignature == null || !activeSignature.equals(newSignature)) {
+ db.delete(TABLE_BOOKMARKS, null, null);
+ if (!createDefaultBookmarks(db)) {
+ // Failure to read/insert bookmarks should be treated as "no bookmarks"
+ db.delete(TABLE_BOOKMARKS, null, null);
+ }
+ }
+ }
+
+ private void setActiveConfiguration(Configuration config) {
+ Editor editor = sharedPreferences.edit();
+ editor.putString(ACTIVE_CONFIGURATION_PREFNAME, getConfigSignature(config));
+ editor.apply();
+ }
+
+ private void createTable(SQLiteDatabase db) {
+ db.execSQL("CREATE TABLE " + TABLE_BOOKMARKS + "(" +
+ PartnerBookmarksContract.Bookmarks.ID +
+ " INTEGER NOT NULL DEFAULT 0," +
+ PartnerBookmarksContract.Bookmarks.TITLE +
+ " TEXT," +
+ PartnerBookmarksContract.Bookmarks.URL +
+ " TEXT," +
+ PartnerBookmarksContract.Bookmarks.TYPE +
+ " INTEGER NOT NULL DEFAULT 0," +
+ PartnerBookmarksContract.Bookmarks.PARENT +
+ " INTEGER," +
+ PartnerBookmarksContract.Bookmarks.FAVICON +
+ " BLOB," +
+ PartnerBookmarksContract.Bookmarks.TOUCHICON +
+ " BLOB" + ");");
+ }
+
+ private void dropTable(SQLiteDatabase db) {
+ db.execSQL("DROP TABLE IF EXISTS " + TABLE_BOOKMARKS);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ synchronized (this) {
+ createTable(db);
+ if (!createDefaultBookmarks(db)) {
+ // Failure to read/insert bookmarks should be treated as "no bookmarks"
+ dropTable(db);
+ createTable(db);
+ }
+ }
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ dropTable(db);
+ onCreate(db);
+ }
+
+ @Override
+ public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ dropTable(db);
+ onCreate(db);
+ }
+
+ private boolean createDefaultBookmarks(SQLiteDatabase db) {
+ Resources res = getContext().getResources();
+ try {
+ CharSequence bookmarksFolderName = res.getText(R.string.bookmarks_folder_name);
+ final CharSequence[] bookmarks = res.getTextArray(R.array.bookmarks);
+ if (bookmarks.length >= 1) {
+ if (bookmarksFolderName.length() < 1) {
+ Log.i(TAG, "bookmarks_folder_name was not specified; bailing out");
+ return false;
+ }
+ if (!addRootFolder(db,
+ FIXED_ID_PARTNER_BOOKMARKS_ROOT, bookmarksFolderName.toString())) {
+ Log.i(TAG, "failed to insert root folder; bailing out");
+ return false;
+ }
+ if (!addDefaultBookmarks(db,
+ FIXED_ID_PARTNER_BOOKMARKS_ROOT, FIXED_ID_PARTNER_BOOKMARKS_ROOT + 1)) {
+ Log.i(TAG, "failed to insert bookmarks; bailing out");
+ return false;
+ }
+ }
+ setActiveConfiguration(res.getConfiguration());
+ } catch (android.content.res.Resources.NotFoundException e) {
+ Log.i(TAG, "failed to fetch resources; bailing out");
+ return false;
+ }
+ return true;
+ }
+
+ private boolean addRootFolder(SQLiteDatabase db, long id, String bookmarksFolderName) {
+ ContentValues values = new ContentValues();
+ values.put(PartnerBookmarksContract.Bookmarks.ID, id);
+ values.put(PartnerBookmarksContract.Bookmarks.TITLE,
+ bookmarksFolderName);
+ values.put(PartnerBookmarksContract.Bookmarks.PARENT,
+ PartnerBookmarksContract.Bookmarks.BOOKMARK_PARENT_ROOT_ID);
+ values.put(PartnerBookmarksContract.Bookmarks.TYPE,
+ PartnerBookmarksContract.Bookmarks.BOOKMARK_TYPE_FOLDER);
+ return db.insertOrThrow(TABLE_BOOKMARKS, null, values) != -1;
+ }
+
+ private boolean addDefaultBookmarks(SQLiteDatabase db, long parentId, long firstBookmarkId) {
+ long bookmarkId = firstBookmarkId;
+ Resources res = getContext().getResources();
+ final CharSequence[] bookmarks = res.getTextArray(R.array.bookmarks);
+ int size = bookmarks.length;
+ TypedArray preloads = res.obtainTypedArray(R.array.bookmark_preloads);
+ DatabaseUtils.InsertHelper insertHelper = null;
+ try {
+ insertHelper = new DatabaseUtils.InsertHelper(db, TABLE_BOOKMARKS);
+ final int idColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.ID);
+ final int titleColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.TITLE);
+ final int urlColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.URL);
+ final int typeColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.TYPE);
+ final int parentColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.PARENT);
+ final int faviconColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.FAVICON);
+ final int touchiconColumn = insertHelper.getColumnIndex(
+ PartnerBookmarksContract.Bookmarks.TOUCHICON);
+
+ for (int i = 0; i + 1 < size; i = i + 2) {
+ CharSequence bookmarkDestination = bookmarks[i + 1];
+
+ String bookmarkTitle = bookmarks[i].toString();
+ String bookmarkUrl = bookmarkDestination.toString();
+ byte[] favicon = null;
+ if (i < preloads.length()) {
+ int faviconId = preloads.getResourceId(i, 0);
+ try {
+ favicon = readRaw(res, faviconId);
+ } catch (IOException e) {
+ Log.i(TAG, "Failed to read favicon for " + bookmarkTitle, e);
+ }
+ }
+ byte[] touchicon = null;
+ if (i + 1 < preloads.length()) {
+ int touchiconId = preloads.getResourceId(i + 1, 0);
+ try {
+ touchicon = readRaw(res, touchiconId);
+ } catch (IOException e) {
+ Log.i(TAG, "Failed to read touchicon for " + bookmarkTitle, e);
+ }
+ }
+ insertHelper.prepareForInsert();
+ insertHelper.bind(idColumn, bookmarkId);
+ insertHelper.bind(titleColumn, bookmarkTitle);
+ insertHelper.bind(urlColumn, bookmarkUrl);
+ insertHelper.bind(typeColumn,
+ PartnerBookmarksContract.Bookmarks.BOOKMARK_TYPE_BOOKMARK);
+ insertHelper.bind(parentColumn, parentId);
+ if (favicon != null) {
+ insertHelper.bind(faviconColumn, favicon);
+ }
+ if (touchicon != null) {
+ insertHelper.bind(touchiconColumn, touchicon);
+ }
+ bookmarkId++;
+ if (insertHelper.execute() == -1) {
+ Log.i(TAG, "Failed to insert bookmark " + bookmarkTitle);
+ return false;
+ }
+ }
+ } finally {
+ preloads.recycle();
+ insertHelper.close();
+ }
+ return true;
+ }
+
+ private byte[] readRaw(Resources res, int id) throws IOException {
+ if (id == 0) return null;
+ InputStream is = res.openRawResource(id);
+ ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ try {
+ 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();
+ bos.close();
+ }
+ }
+ }
+
+ private DatabaseHelper mOpenHelper;
+
+ @Override
+ public boolean onCreate() {
+ mOpenHelper = new DatabaseHelper(getContext());
+ return true;
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ mOpenHelper.prepareForConfiguration(getContext().getResources().getConfiguration());
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection,
+ String selection, String[] selectionArgs, String sortOrder) {
+ final int match = URI_MATCHER.match(uri);
+ mOpenHelper.prepareForConfiguration(getContext().getResources().getConfiguration());
+ final SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ String limit = uri.getQueryParameter(PartnerBookmarksContract.PARAM_LIMIT);
+ String groupBy = uri.getQueryParameter(PartnerBookmarksContract.PARAM_GROUP_BY);
+ switch (match) {
+ case URI_MATCH_BOOKMARKS_FOLDER_ID:
+ case URI_MATCH_BOOKMARKS_ID:
+ case URI_MATCH_BOOKMARKS: {
+ if (match == URI_MATCH_BOOKMARKS_ID) {
+ // Tack on the ID of the specific bookmark requested
+ selection = DatabaseUtils.concatenateWhere(selection,
+ TABLE_BOOKMARKS + "." +
+ PartnerBookmarksContract.Bookmarks.ID + "=?");
+ selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
+ new String[] { Long.toString(ContentUris.parseId(uri)) });
+ } else if (match == URI_MATCH_BOOKMARKS_FOLDER_ID) {
+ // Tack on the ID of the specific folder requested
+ selection = DatabaseUtils.concatenateWhere(selection,
+ TABLE_BOOKMARKS + "." +
+ PartnerBookmarksContract.Bookmarks.PARENT + "=?");
+ selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs,
+ new String[] { Long.toString(ContentUris.parseId(uri)) });
+ }
+ // Set a default sort order if one isn't specified
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER;
+ }
+ qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
+ qb.setTables(TABLE_BOOKMARKS);
+ break;
+ }
+
+ case URI_MATCH_BOOKMARKS_FOLDER: {
+ qb.setTables(TABLE_BOOKMARKS);
+ String[] args;
+ String query;
+ // Set a default sort order if one isn't specified
+ if (TextUtils.isEmpty(sortOrder)) {
+ sortOrder = DEFAULT_BOOKMARKS_SORT_ORDER;
+ }
+ qb.setProjectionMap(BOOKMARKS_PROJECTION_MAP);
+ String where = PartnerBookmarksContract.Bookmarks.PARENT + "=?";
+ where = DatabaseUtils.concatenateWhere(where, selection);
+ args = new String[] { Long.toString(FIXED_ID_PARTNER_BOOKMARKS_ROOT) };
+ if (selectionArgs != null) {
+ args = DatabaseUtils.appendSelectionArgs(args, selectionArgs);
+ }
+ query = qb.buildQuery(projection, where, null, null, sortOrder, null);
+ Cursor cursor = db.rawQuery(query, args);
+ return cursor;
+ }
+
+ case URI_MATCH_BOOKMARKS_PARTNER_BOOKMARKS_FOLDER_ID: {
+ MatrixCursor c = new MatrixCursor(
+ new String[] {PartnerBookmarksContract.Bookmarks.ID});
+ c.newRow().add(FIXED_ID_PARTNER_BOOKMARKS_ROOT);
+ return c;
+ }
+
+ default: {
+ throw new UnsupportedOperationException("Unknown URL " + uri.toString());
+ }
+ }
+
+ return qb.query(db, projection, selection, selectionArgs, groupBy, null, sortOrder, limit);
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ final int match = URI_MATCHER.match(uri);
+ if (match == UriMatcher.NO_MATCH) return null;
+ return PartnerBookmarksContract.Bookmarks.CONTENT_ITEM_TYPE;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..7177397
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2012 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
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := PartnerBookmarksProviderTest
+
+LOCAL_JAVA_LIBRARIES := ext android.test.runner
+
+LOCAL_INSTRUMENTATION_FOR := PartnerBookmarksProvider
+
+include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 0000000..6e6ba14
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2012 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" -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.providers.partnerbookmarks.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!--
+ The test declared in this instrumentation will be run along with tests
+ declared by all other applications via the command: "adb shell itr".
+ The "itr" command will find all tests declared by all applications.
+ If you want to run just these tests on their own then use the command:
+ "adb shell am instrument -w com.android.providers.partnerbookmarks.tests/android.test.InstrumentationTestRunner"
+ -->
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.providers.partnerbookmarks"
+ android:label="partner bookmarks provider tests"/>
+
+ <uses-sdk android:minSdkVersion="14" android:targetSdkVersion="14" />
+</manifest>
diff --git a/tests/src/com/android/providers/partnerbookmarks/PartnerBookmarksProviderTest.java b/tests/src/com/android/providers/partnerbookmarks/PartnerBookmarksProviderTest.java
new file mode 100644
index 0000000..9014d14
--- /dev/null
+++ b/tests/src/com/android/providers/partnerbookmarks/PartnerBookmarksProviderTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2012 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.providers.partnerbookmarks;
+
+import android.content.ContentProviderClient;
+import android.database.Cursor;
+import android.test.InstrumentationTestCase;
+import android.test.mock.MockContext;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+
+public class PartnerBookmarksProviderTest extends InstrumentationTestCase {
+ private static final String TAG = "PartnerBookmarksProviderTest";
+ private static final long FIXED_ID_PARTNER_BOOKMARKS_ROOT =
+ PartnerBookmarksContract.Bookmarks.BOOKMARK_PARENT_ROOT_ID + 1;
+ private static final long NO_FOLDER_FILTER = -1;
+
+ @Override
+ public void setUp() {
+ }
+
+ @Override
+ public void tearDown() {
+ }
+
+ private int countBookmarksInFolder(long folderFilter) throws android.os.RemoteException {
+ ContentProviderClient providerClient =
+ getInstrumentation().getTargetContext().
+ getContentResolver().acquireContentProviderClient(
+ PartnerBookmarksContract.Bookmarks.CONTENT_URI);
+ assertNotNull(
+ "Failed to acquire " + PartnerBookmarksContract.Bookmarks.CONTENT_URI,
+ providerClient);
+ Cursor cursor = providerClient.query(PartnerBookmarksContract.Bookmarks.CONTENT_URI,
+ null, null, null, null);
+ assertNotNull("Failed to query for bookmarks", cursor);
+ int bookmarksCount = 0;
+ while (!cursor.isLast() && !cursor.isAfterLast()) {
+ cursor.moveToNext();
+ long id = cursor.getLong(
+ cursor.getColumnIndexOrThrow(PartnerBookmarksContract.Bookmarks.ID));
+ long parent = cursor.getLong(
+ cursor.getColumnIndexOrThrow(PartnerBookmarksContract.Bookmarks.PARENT));
+ if (folderFilter != NO_FOLDER_FILTER && folderFilter != parent)
+ continue;
+ boolean isFolder = cursor.getInt(
+ cursor.getColumnIndexOrThrow(PartnerBookmarksContract.Bookmarks.TYPE))
+ == PartnerBookmarksContract.Bookmarks.BOOKMARK_TYPE_FOLDER;
+ String url = cursor.getString(
+ cursor.getColumnIndexOrThrow(PartnerBookmarksContract.Bookmarks.URL));
+ String title = cursor.getString(
+ cursor.getColumnIndexOrThrow(PartnerBookmarksContract.Bookmarks.TITLE));
+ bookmarksCount++;
+ }
+ return bookmarksCount;
+ }
+
+ @SmallTest
+ public void testExactlyOneRoot() throws android.os.RemoteException {
+ int totalBookmarks = countBookmarksInFolder(NO_FOLDER_FILTER);
+ if (totalBookmarks > 0) {
+ assertEquals("There must be at most one root",
+ countBookmarksInFolder(
+ PartnerBookmarksContract.Bookmarks.BOOKMARK_PARENT_ROOT_ID),
+ 1);
+ }
+ }
+
+ @SmallTest
+ public void testRootMustBeNonEmptyIfPresent() throws android.os.RemoteException {
+ int totalBookmarks = countBookmarksInFolder(NO_FOLDER_FILTER);
+ if (totalBookmarks > 0) {
+ assertTrue("If the root exists, it must be non-empty",
+ countBookmarksInFolder(FIXED_ID_PARTNER_BOOKMARKS_ROOT) > 0);
+ }
+ }
+
+ @SmallTest
+ public void testDefaultPBPSupportsOnlyFlatListOfBookmarks()
+ throws android.os.RemoteException {
+ int totalBookmarks = countBookmarksInFolder(NO_FOLDER_FILTER);
+ if (totalBookmarks > 0) {
+ assertEquals("Default PBP supports only flat list of bookmarks",
+ countBookmarksInFolder(FIXED_ID_PARTNER_BOOKMARKS_ROOT),
+ totalBookmarks - 1);
+ }
+ }
+}