From 9318372b135a9d1f001c44c36d76ac841e4a01b5 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Wed, 13 Feb 2013 17:19:00 -0800 Subject: [PATCH] Bug 836450 - Add default bookmark support for distributions. r=mfinkle,wesj --- mobile/android/base/Distribution.java | 6 +- .../android/base/db/BrowserProvider.java.in | 238 +++++++++++++----- 2 files changed, 184 insertions(+), 60 deletions(-) diff --git a/mobile/android/base/Distribution.java b/mobile/android/base/Distribution.java index 17ad1a6d2d66..ac7517ecfd28 100644 --- a/mobile/android/base/Distribution.java +++ b/mobile/android/base/Distribution.java @@ -28,9 +28,9 @@ import java.util.zip.ZipFile; public final class Distribution { private static final String LOGTAG = "GeckoDistribution"; - private static final int STATE_UNKNOWN = 0; - private static final int STATE_NONE = 1; - private static final int STATE_SET = 2; + public static final int STATE_UNKNOWN = 0; + public static final int STATE_NONE = 1; + public static final int STATE_SET = 2; /** * Initializes distribution if it hasn't already been initalized. diff --git a/mobile/android/base/db/BrowserProvider.java.in b/mobile/android/base/db/BrowserProvider.java.in index 726450b7c2af..bf0218792bed 100644 --- a/mobile/android/base/db/BrowserProvider.java.in +++ b/mobile/android/base/db/BrowserProvider.java.in @@ -6,21 +6,31 @@ #filter substitution package @ANDROID_PACKAGE_NAME@.db; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.lang.Class; import java.lang.reflect.Field; +import java.lang.StringBuilder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Iterator; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; import java.util.regex.Matcher; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.mozilla.gecko.Distribution; +import org.mozilla.gecko.GeckoApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.R; @@ -38,19 +48,22 @@ import org.mozilla.gecko.db.BrowserContract.URLColumns; import org.mozilla.gecko.db.BrowserContract; import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.DBUtils; +import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.ProfileMigrator; import org.mozilla.gecko.sync.Utils; import org.mozilla.gecko.util.GeckoBackgroundThread; import org.mozilla.gecko.util.GeckoJarReader; +import android.app.Activity; import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.ContentProviderResult; import android.content.ContentProviderOperation; -import android.content.OperationApplicationException; import android.content.Context; +import android.content.OperationApplicationException; +import android.content.SharedPreferences; import android.content.UriMatcher; import android.database.Cursor; import android.database.DatabaseUtils; @@ -67,6 +80,10 @@ import android.os.Build; import android.text.TextUtils; import android.util.Log; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + public class BrowserProvider extends ContentProvider { private static final String LOGTAG = "GeckoBrowserProvider"; private Context mContext; @@ -973,28 +990,133 @@ public class BrowserProvider extends ContentProvider { createOrUpdateAllSpecialFolders(db); - createDefaultBookmarks(db, "^bookmarkdefaults_title_"); + // Create distribution bookmarks before our own default bookmarks + int pos = createDistributionBookmarks(db); + createDefaultBookmarks(db, pos); } - private void createDefaultBookmarks(SQLiteDatabase db, String pattern) { - Class stringsClass = R.string.class; + private JSONArray inputStreamToJSONArray(InputStream inputStream) throws IOException, JSONException { + BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); + StringBuilder stringBuilder = new StringBuilder(); + String s; + while ((s = reader.readLine()) != null) { + stringBuilder.append(s); + } + return new JSONArray(stringBuilder.toString()); + } - Field[] fields = stringsClass.getFields(); - Pattern p = Pattern.compile(pattern); + private JSONArray getDistributionBookmarks() { + SharedPreferences settings = mContext.getSharedPreferences(GeckoApp.PREFS_NAME, Activity.MODE_PRIVATE); + String keyName = mContext.getPackageName() + ".distribution_state"; + int state = settings.getInt(keyName, Distribution.STATE_UNKNOWN); + if (state == Distribution.STATE_NONE) { + return null; + } - ContentValues bookmarksValues = new ContentValues(); - bookmarksValues.put(Bookmarks.PARENT, guidToID(db, Bookmarks.MOBILE_FOLDER_GUID)); - long now = System.currentTimeMillis(); - bookmarksValues.put(Bookmarks.DATE_CREATED, now); - bookmarksValues.put(Bookmarks.DATE_MODIFIED, now); + ZipFile zip = null; + InputStream inputStream = null; + try { + if (state == Distribution.STATE_UNKNOWN) { + // If the distribution hasn't been set, get bookmarks.json out of the APK + File applicationPackage = new File(mContext.getPackageResourcePath()); + zip = new ZipFile(applicationPackage); + ZipEntry zipEntry = zip.getEntry("distribution/bookmarks.json"); + if (zipEntry == null) { + return null; + } + inputStream = zip.getInputStream(zipEntry); + } else { + // Otherwise, get bookmarks.json out of the data directory + File dataDir = new File(mContext.getApplicationInfo().dataDir); + File file = new File(dataDir, "distribution/bookmarks.json"); + inputStream = new FileInputStream(file); + } + return inputStreamToJSONArray(inputStream); + } catch (IOException e) { + Log.e(LOGTAG, "Error getting distribution bookmarks", e); + } catch (JSONException e) { + Log.e(LOGTAG, "Error parsing bookmarks.json", e); + } finally { + try { + if (zip != null) { + zip.close(); + } + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + Log.e(LOGTAG, "Error closing distribution streams", e); + } + } + return null; + } + private String getLocalizedProperty(JSONObject bookmark, String property, Locale locale) throws JSONException { + // Try the full locale + String fullLocale = property + "." + locale.toString(); + if (bookmark.has(fullLocale)) { + return bookmark.getString(fullLocale); + } + // Try without a variant + if (!TextUtils.isEmpty(locale.getVariant())) { + String noVariant = fullLocale.substring(0, fullLocale.lastIndexOf("_")); + if (bookmark.has(noVariant)) { + return bookmark.getString(noVariant); + } + } + // Try just the language + String lang = property + "." + locale.getLanguage(); + if (bookmark.has(lang)) { + return bookmark.getString(lang); + } + // Default to the non-localized property name + return bookmark.getString(property); + } + + // Returns the number of bookmarks inserted in the db + private int createDistributionBookmarks(SQLiteDatabase db) { + JSONArray bookmarks = getDistributionBookmarks(); + if (bookmarks == null) { + return 0; + } + + Locale locale = Locale.getDefault(); int pos = 0; + for (int i = 0; i < bookmarks.length(); i++) { + try { + JSONObject bookmark = bookmarks.getJSONObject(i); + + String title = getLocalizedProperty(bookmark, "title", locale); + String url = getLocalizedProperty(bookmark, "url", locale); + + // Look for an optional icon data URI + Bitmap icon = null; + if (bookmark.has("icon")) { + String iconData = bookmark.getString("icon"); + icon = BitmapUtils.getBitmapFromDataURI(iconData); + } + + createBookmark(db, title, url, pos, icon); + pos++; + } catch (JSONException e) { + Log.e(LOGTAG, "Error creating distribution bookmark", e); + } + } + return pos; + } + + // Inserts default bookmarks, starting at a specified position + private void createDefaultBookmarks(SQLiteDatabase db, int pos) { + Class stringsClass = R.string.class; + Field[] fields = stringsClass.getFields(); + Pattern p = Pattern.compile("^bookmarkdefaults_title_"); + for (int i = 0; i < fields.length; i++) { String name = fields[i].getName(); Matcher m = p.matcher(name); - if (!m.find()) + if (!m.find()) { continue; - + } try { int titleid = fields[i].getInt(null); String title = mContext.getString(titleid); @@ -1003,13 +1125,11 @@ public class BrowserProvider extends ContentProvider { int urlId = urlField.getInt(null); String url = mContext.getString(urlId); - bookmarksValues.put(Bookmarks.TITLE, title); - bookmarksValues.put(Bookmarks.URL, url); - bookmarksValues.put(Bookmarks.GUID, Utils.generateGuid()); - bookmarksValues.put(Bookmarks.POSITION, pos); - db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.TITLE, bookmarksValues); - - setDefaultFavicon(db, name, url); + Bitmap icon = getDefaultFaviconFromPath(name); + if (icon == null) { + icon = getDefaultFaviconFromDrawable(name); + } + createBookmark(db, title, url, pos, icon); pos++; } catch (java.lang.IllegalAccessException ex) { Log.e(LOGTAG, "Can't create bookmark " + name, ex); @@ -1019,27 +1139,42 @@ public class BrowserProvider extends ContentProvider { } } - private void setDefaultFavicon(SQLiteDatabase db, String name, String url) { - ByteArrayOutputStream stream = getDefaultFaviconFromPath(db, name, url); - if (stream == null) { - stream = getDefaultFaviconFromDrawable(db, name, url); - } - if (stream != null) { - ContentValues values = new ContentValues(); - values.put(Favicons.DATA, stream.toByteArray()); - values.put(Favicons.PAGE_URL, url); - insertFavicon(db, values); + private void createBookmark(SQLiteDatabase db, String title, String url, int pos, Bitmap icon) { + ContentValues bookmarkValues = new ContentValues(); + bookmarkValues.put(Bookmarks.PARENT, guidToID(db, Bookmarks.MOBILE_FOLDER_GUID)); + + long now = System.currentTimeMillis(); + bookmarkValues.put(Bookmarks.DATE_CREATED, now); + bookmarkValues.put(Bookmarks.DATE_MODIFIED, now); + + bookmarkValues.put(Bookmarks.TITLE, title); + bookmarkValues.put(Bookmarks.URL, url); + bookmarkValues.put(Bookmarks.GUID, Utils.generateGuid()); + bookmarkValues.put(Bookmarks.POSITION, pos); + db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.TITLE, bookmarkValues); + + // Return early if there's no icon to set + if (icon == null) { + return; } + + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + icon.compress(Bitmap.CompressFormat.PNG, 100, stream); + + ContentValues iconValues = new ContentValues(); + iconValues.put(Favicons.DATA, stream.toByteArray()); + iconValues.put(Favicons.PAGE_URL, url); + insertFavicon(db, iconValues); } - private ByteArrayOutputStream getDefaultFaviconFromPath(SQLiteDatabase db, String name, String url) { - ByteArrayOutputStream stream = null; + private Bitmap getDefaultFaviconFromPath(String name) { Class stringClass = R.string.class; try { // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* Field faviconField = stringClass.getField(name.replace("_title_", "_favicon_")); - if (faviconField == null) - return null; + if (faviconField == null) { + return null; + } int faviconId = faviconField.getInt(null); String path = mContext.getString(faviconId); @@ -1048,44 +1183,33 @@ public class BrowserProvider extends ContentProvider { BitmapDrawable bitmapDrawable = GeckoJarReader.getBitmapDrawable(mContext.getResources(), "jar:jar:" + apkFile.toURI() + "!/omni.ja!/" + path); if (bitmapDrawable == null) { - return null; + return null; } - Bitmap bitmap = bitmapDrawable.getBitmap(); - if (bitmap == null) { - return null; - } - stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + return bitmapDrawable.getBitmap(); } catch (java.lang.IllegalAccessException ex) { Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex); } catch (java.lang.NoSuchFieldException ex) { - // if there is no such field, create the bookmark without a favicon - Log.d(LOGTAG, "[Path] Can't create favicon " + name); + Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex); } - return stream; + return null; } - private ByteArrayOutputStream getDefaultFaviconFromDrawable(SQLiteDatabase db, String name, String url) { + private Bitmap getDefaultFaviconFromDrawable(String name) { Class drawablesClass = R.drawable.class; - ByteArrayOutputStream stream = null; try { // Look for a drawable with the id R.drawable.bookmarkdefaults_favicon_* Field faviconField = drawablesClass.getField(name.replace("_title_", "_favicon_")); - if (faviconField == null) - return null; - + if (faviconField == null) { + return null; + } int faviconId = faviconField.getInt(null); - Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), faviconId); - stream = new ByteArrayOutputStream(); - bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream); + return BitmapFactory.decodeResource(mContext.getResources(), faviconId); } catch (java.lang.IllegalAccessException ex) { Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex); } catch (java.lang.NoSuchFieldException ex) { - // if there is no such field, create the bookmark without a favicon - Log.d(LOGTAG, "[Drawable] Can't create favicon " + name); + Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex); } - - return stream; + return null; } private void createOrUpdateAllSpecialFolders(SQLiteDatabase db) {