author | Margaret Leibovic <margaret.leibovic@gmail.com> |
Thu, 14 Feb 2013 15:35:39 -0800 | |
changeset 121983 | 458969f6b3690ad647f3349b6ff9aaf50e773807 |
parent 121982 | 3be58b112a33bb9c94725378cf880fc7b101a76f |
child 121984 | df6fbdf62e13933bde8593f623306846f0306de5 |
push id | 24314 |
push user | ryanvm@gmail.com |
push date | Fri, 15 Feb 2013 14:39:46 +0000 |
treeherder | mozilla-central@326c5e4868fe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | mfinkle, wesj |
bugs | 836450 |
milestone | 21.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
mobile/android/base/Distribution.java | file | annotate | diff | comparison | revisions | |
mobile/android/base/db/BrowserProvider.java.in | file | annotate | diff | comparison | revisions |
--- a/mobile/android/base/Distribution.java +++ b/mobile/android/base/Distribution.java @@ -7,59 +7,66 @@ * * ***** END LICENSE BLOCK ***** */ package org.mozilla.gecko; import org.mozilla.gecko.util.GeckoBackgroundThread; import android.app.Activity; +import android.content.Context; import android.content.SharedPreferences; import android.util.Log; +import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; - import java.util.Enumeration; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + 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; /** * Initializes distribution if it hasn't already been initalized. */ - public static void init(final Activity activity) { + public static void init(final Context context) { // Read/write preferences and files on the background thread. GeckoBackgroundThread.getHandler().post(new Runnable() { public void run() { // Bail if we've already initialized the distribution. - SharedPreferences settings = activity.getPreferences(Activity.MODE_PRIVATE); - String keyName = activity.getPackageName() + ".distribution_state"; + SharedPreferences settings = context.getSharedPreferences(GeckoApp.PREFS_NAME, Activity.MODE_PRIVATE); + String keyName = context.getPackageName() + ".distribution_state"; int state = settings.getInt(keyName, STATE_UNKNOWN); if (state == STATE_NONE) return; // Send a message to Gecko if we've set a distribution. if (state == STATE_SET) { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", null)); return; } boolean distributionSet = false; try { - distributionSet = copyFiles(activity); + distributionSet = copyFiles(context); } catch (IOException e) { Log.e(LOGTAG, "Error copying distribution files", e); } if (distributionSet) { GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Distribution:Set", null)); settings.edit().putInt(keyName, STATE_SET).commit(); } else { @@ -68,32 +75,32 @@ public final class Distribution { } }); } /** * Copies the /distribution folder out of the APK and into the app's data directory. * Returns true if distribution files were found and copied. */ - private static boolean copyFiles(Activity activity) throws IOException { - File applicationPackage = new File(activity.getPackageResourcePath()); + private static boolean copyFiles(Context context) throws IOException { + File applicationPackage = new File(context.getPackageResourcePath()); ZipFile zip = new ZipFile(applicationPackage); boolean distributionSet = false; Enumeration<? extends ZipEntry> zipEntries = zip.entries(); while (zipEntries.hasMoreElements()) { ZipEntry fileEntry = zipEntries.nextElement(); String name = fileEntry.getName(); if (!name.startsWith("distribution/")) continue; distributionSet = true; - File dataDir = new File(activity.getApplicationInfo().dataDir); + File dataDir = new File(context.getApplicationInfo().dataDir); File outFile = new File(dataDir, name); File dir = outFile.getParentFile(); if (!dir.exists()) dir.mkdirs(); InputStream fileStream = zip.getInputStream(fileEntry); OutputStream outStream = new FileOutputStream(outFile); @@ -106,9 +113,67 @@ public final class Distribution { outStream.close(); outFile.setLastModified(fileEntry.getTime()); } zip.close(); return distributionSet; } + + /** + * Returns parsed contents of bookmarks.json. + * This method should only be called from a background thread. + */ + public static JSONArray getBookmarks(Context context) { + SharedPreferences settings = context.getSharedPreferences(GeckoApp.PREFS_NAME, Activity.MODE_PRIVATE); + String keyName = context.getPackageName() + ".distribution_state"; + int state = settings.getInt(keyName, STATE_UNKNOWN); + if (state == STATE_NONE) { + return null; + } + + ZipFile zip = null; + InputStream inputStream = null; + try { + if (state == STATE_UNKNOWN) { + // If the distribution hasn't been set yet, get bookmarks.json out of the APK + File applicationPackage = new File(context.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(context.getApplicationInfo().dataDir); + File file = new File(dataDir, "distribution/bookmarks.json"); + inputStream = new FileInputStream(file); + } + + // Convert input stream to JSONArray + 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()); + } catch (IOException e) { + Log.e(LOGTAG, "Error getting 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 streams", e); + } + } + return null; + } }
--- a/mobile/android/base/db/BrowserProvider.java.in +++ b/mobile/android/base/db/BrowserProvider.java.in @@ -3,75 +3,79 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #filter substitution package @ANDROID_PACKAGE_NAME@.db; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.IOException; import java.lang.Class; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; -import java.util.Iterator; +import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.regex.Pattern; import java.util.regex.Matcher; -import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.Distribution; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.R; import org.mozilla.gecko.db.BrowserContract.Bookmarks; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserContract.CommonColumns; import org.mozilla.gecko.db.BrowserContract.Control; import org.mozilla.gecko.db.BrowserContract.Favicons; import org.mozilla.gecko.db.BrowserContract.FaviconColumns; import org.mozilla.gecko.db.BrowserContract.History; import org.mozilla.gecko.db.BrowserContract.Schema; import org.mozilla.gecko.db.BrowserContract.SyncColumns; import org.mozilla.gecko.db.BrowserContract.Thumbnails; 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.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.ContentProviderResult; import android.content.ContentProviderOperation; +import android.content.Context; import android.content.OperationApplicationException; -import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.DatabaseUtils; import android.database.MatrixCursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.graphics.BitmapFactory; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.net.Uri; 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; static final String DATABASE_NAME = "browser.db"; static final int DATABASE_VERSION = 14; @@ -968,129 +972,180 @@ public class BrowserProvider extends Con createHistoryWithFaviconsView(db); createCombinedViewOn13(db); createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID, R.string.bookmarks_folder_places, 0); createOrUpdateAllSpecialFolders(db); - createDefaultBookmarks(db, "^bookmarkdefaults_title_"); + // Create distribution bookmarks before our own default bookmarks + int pos = createDistributionBookmarks(db); + createDefaultBookmarks(db, pos); + } + + 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); } - private void createDefaultBookmarks(SQLiteDatabase db, String pattern) { + // Returns the number of bookmarks inserted in the db + private int createDistributionBookmarks(SQLiteDatabase db) { + JSONArray bookmarks = Distribution.getBookmarks(mContext); + 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(pattern); - - 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); - - int pos = 0; + 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); Field urlField = stringsClass.getField(name.replace("_title_", "_url_")); 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); } catch (java.lang.NoSuchFieldException ex) { Log.e(LOGTAG, "Can't create bookmark " + name, ex); } } } - private void setDefaultFavicon(SQLiteDatabase db, String name, String url) { - ByteArrayOutputStream stream = getDefaultFaviconFromPath(db, name, url); - if (stream == null) { - stream = getDefaultFaviconFromDrawable(db, name, url); + 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; } - if (stream != null) { - ContentValues values = new ContentValues(); - values.put(Favicons.DATA, stream.toByteArray()); - values.put(Favicons.PAGE_URL, url); - insertFavicon(db, values); - } + + 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); String apkPath = mContext.getPackageResourcePath(); File apkFile = new File(apkPath); 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) { createOrUpdateSpecialFolder(db, Bookmarks.MOBILE_FOLDER_GUID, R.string.bookmarks_folder_mobile, 0); createOrUpdateSpecialFolder(db, Bookmarks.TOOLBAR_FOLDER_GUID, R.string.bookmarks_folder_toolbar, 1); createOrUpdateSpecialFolder(db, Bookmarks.MENU_FOLDER_GUID,