diff options
Diffstat (limited to 'src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java')
-rw-r--r-- | src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java | 450 |
1 files changed, 450 insertions, 0 deletions
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(); + } +} |