Bug 1016611 - Don't process distribution files for webapp profiles. r=margaret

This commit is contained in:
Richard Newman 2014-06-16 16:07:43 -07:00
parent 415e6019ea
commit 92dec37a4c
6 changed files with 358 additions and 313 deletions

View File

@ -58,7 +58,6 @@ import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UiAsyncTask;
import org.mozilla.gecko.webapp.EventListener;
import org.mozilla.gecko.webapp.UninstallListener;
import org.mozilla.gecko.widget.ButtonToast;
@ -111,7 +110,6 @@ import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;

View File

@ -5,17 +5,6 @@
package org.mozilla.gecko;
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
import org.mozilla.gecko.GeckoProfileDirectories.NoSuchProfileException;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.util.INIParser;
import org.mozilla.gecko.util.INISection;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
@ -26,6 +15,18 @@ import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
import org.mozilla.gecko.GeckoProfileDirectories.NoSuchProfileException;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.util.INIParser;
import org.mozilla.gecko.util.INISection;
import android.content.ContentResolver;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
public final class GeckoProfile {
private static final String LOGTAG = "GeckoProfile";
@ -37,11 +38,25 @@ public final class GeckoProfile {
private static HashMap<String, GeckoProfile> sProfileCache = new HashMap<String, GeckoProfile>();
private static String sDefaultProfileName = null;
// Caches the guest profile dir.
private static File sGuestDir = null;
private static GeckoProfile sGuestProfile = null;
public static boolean sIsUsingCustomProfile = false;
private final String mName;
private final File mMozillaDir;
private final boolean mIsWebAppProfile;
private final Context mApplicationContext;
private File mProfileDir; // Not final because this is lazily computed.
// Caches whether or not a profile is "locked".
// Only used by the guest profile to determine if it should be reused or
// deleted on startup
private LockState mLocked = LockState.UNDEFINED;
private boolean mInGuestMode = false;
// Constants to cache whether or not a profile is "locked".
private enum LockState {
LOCKED,
@ -49,17 +64,6 @@ public final class GeckoProfile {
UNDEFINED
};
// Caches whether or not a profile is "locked". Only used by the guest profile to determine if it should
// be reused or deleted on startup
private LockState mLocked = LockState.UNDEFINED;
// Caches the guest profile dir.
private static File sGuestDir = null;
private static GeckoProfile sGuestProfile = null;
private boolean mInGuestMode = false;
public static GeckoProfile get(Context context) {
boolean isGeckoApp = false;
try {
@ -155,6 +159,11 @@ public final class GeckoProfile {
}
public static boolean removeProfile(Context context, String profileName) {
if (profileName == null) {
Log.w(LOGTAG, "Unable to remove profile: null profile name.");
return false;
}
final boolean success;
try {
success = new GeckoProfile(context, profileName).remove();
@ -266,7 +275,13 @@ public final class GeckoProfile {
}
private GeckoProfile(Context context, String profileName) throws NoMozillaDirectoryException {
if (TextUtils.isEmpty(profileName)) {
throw new IllegalArgumentException("Unable to create GeckoProfile for empty profile name.");
}
mApplicationContext = context.getApplicationContext();
mName = profileName;
mIsWebAppProfile = profileName.startsWith("webapp");
mMozillaDir = GeckoProfileDirectories.getMozillaDirectory(context);
}
@ -576,7 +591,7 @@ public final class GeckoProfile {
parser.addSection(generalSection);
}
if (!isDefaultSet && !mName.startsWith("webapp")) {
if (!isDefaultSet && !mIsWebAppProfile) {
// only set as default if this is the first non-webapp
// profile we're creating
profileSection.setProperty("Default", 1);
@ -590,6 +605,11 @@ public final class GeckoProfile {
parser.addSection(profileSection);
parser.write();
// Trigger init for non-webapp profiles.
if (!mIsWebAppProfile) {
enqueueInitialization();
}
// Write out profile creation time, mirroring the logic in nsToolkitProfileService.
try {
FileOutputStream stream = new FileOutputStream(profileDir.getAbsolutePath() + File.separator + "times.json");
@ -606,4 +626,29 @@ public final class GeckoProfile {
return profileDir;
}
/**
* This method is called once, immediately before creation of the profile
* directory completes.
*
* It queues up work to be done in the background to prepare the profile,
* such as adding default bookmarks.
*/
private void enqueueInitialization() {
final Context context = mApplicationContext;
// Add everything when we're done loading the distribution.
final Distribution distribution = Distribution.getInstance(context);
distribution.addOnDistributionReadyCallback(new Runnable() {
@Override
public void run() {
Log.d(LOGTAG, "Running post-distribution task: bookmarks.");
final ContentResolver cr = context.getContentResolver();
final int offset = BrowserDB.addDistributionBookmarks(cr, distribution, 0);
BrowserDB.addDefaultBookmarks(context, cr, offset);
}
});
}
}

View File

@ -10,11 +10,13 @@ import java.util.List;
import org.mozilla.gecko.db.BrowserContract.ExpirePriority;
import org.mozilla.gecko.db.SuggestedSites;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import org.mozilla.gecko.mozglue.RobocopTarget;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.graphics.drawable.BitmapDrawable;
@ -138,6 +140,9 @@ public class BrowserDB {
@RobocopTarget
public Cursor getBookmarkForUrl(ContentResolver cr, String url);
public int addDefaultBookmarks(Context context, ContentResolver cr, int offset);
public int addDistributionBookmarks(ContentResolver cr, Distribution distribution, int offset);
}
static {
@ -149,6 +154,14 @@ public class BrowserDB {
sDb = new LocalBrowserDB(profile);
}
public static int addDefaultBookmarks(Context context, ContentResolver cr, final int offset) {
return sDb.addDefaultBookmarks(context, cr, offset);
}
public static int addDistributionBookmarks(ContentResolver cr, Distribution distribution, int offset) {
return sDb.addDistributionBookmarks(cr, distribution, offset);
}
public static void setSuggestedSites(SuggestedSites suggestedSites) {
sSuggestedSites = suggestedSites;
}

View File

@ -5,19 +5,9 @@
package org.mozilla.gecko.db;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AppConstants;
import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserContract.Combined;
@ -27,11 +17,7 @@ import org.mozilla.gecko.db.BrowserContract.History;
import org.mozilla.gecko.db.BrowserContract.Obsolete;
import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
import org.mozilla.gecko.db.BrowserContract.Thumbnails;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.ContentValues;
import android.content.Context;
@ -40,7 +26,6 @@ import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.text.TextUtils;
@ -764,125 +749,9 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
R.string.bookmarks_folder_places, 0);
createOrUpdateAllSpecialFolders(db);
int offset = createDefaultBookmarks(db, 0);
// We'd like to create distribution bookmarks before our own default bookmarks,
// but we won't necessarily have loaded the distribution yet. Oh well.
enqueueDistributionBookmarkLoad(db, offset);
createReadingListTable(db);
}
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);
}
// We can't rely on the distribution file having been loaded,
// so we delegate that work to the distribution handler.
private void enqueueDistributionBookmarkLoad(final SQLiteDatabase db, final int start) {
final Distribution distribution = Distribution.getInstance(mContext);
distribution.addOnDistributionReadyCallback(new Runnable() {
@Override
public void run() {
Log.d(LOGTAG, "Running post-distribution task: bookmarks.");
if (!distribution.exists()) {
return;
}
// This runnable is always executed on the background thread,
// thanks to Distribution's guarantees.
// Yes, the user might have added a bookmark in the interim,
// in which case `start` will be wrong. Sync will need to fix
// up the indices.
createDistributionBookmarks(distribution, db, start);
}
});
}
/**
* Fetch distribution bookmarks and insert them into the database.
*
* @param db the destination database.
* @param start the initial index at which to insert.
* @return the number of inserted bookmarks.
*/
private int createDistributionBookmarks(Distribution distribution, SQLiteDatabase db, int start) {
JSONArray bookmarks = distribution.getBookmarks();
if (bookmarks == null) {
return 0;
}
Locale locale = Locale.getDefault();
int pos = start;
Integer mobileFolderId = getMobileFolderId(db);
if (mobileFolderId == null) {
Log.e(LOGTAG, "Error creating distribution bookmarks: mobileFolderId is null.");
return 0;
}
for (int i = 0; i < bookmarks.length(); i++) {
try {
final JSONObject bookmark = bookmarks.getJSONObject(i);
String title = getLocalizedProperty(bookmark, "title", locale);
final String url = getLocalizedProperty(bookmark, "url", locale);
createBookmark(db, title, url, pos, mobileFolderId);
if (bookmark.has("pinned")) {
try {
// Create a fake bookmark in the hidden pinned folder to pin bookmark
// to about:home top sites. Pass pos as the pinned position to pin
// sites in the order that bookmarks are specified in bookmarks.json.
if (bookmark.getBoolean("pinned")) {
createBookmark(db, title, url, pos, Bookmarks.FIXED_PINNED_LIST_ID);
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error pinning bookmark to top sites.", e);
}
}
pos++;
// Return early if there is no icon for this bookmark.
if (!bookmark.has("icon")) {
continue;
}
try {
final String iconData = bookmark.getString("icon");
final Bitmap icon = BitmapUtils.getBitmapFromDataURI(iconData);
if (icon != null) {
createFavicon(db, url, icon);
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating distribution bookmark icon.", e);
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating distribution bookmark.", e);
}
}
return pos - start;
}
private void createReadingListTable(SQLiteDatabase db) {
debug("Creating " + TABLE_READING_LIST + " table");
@ -904,139 +773,6 @@ final class BrowserDatabaseHelper extends SQLiteOpenHelper {
+ ReadingListItems.GUID + ")");
}
/**
* Insert default bookmarks into the database.
*
* @param db the destination database.
* @param start the initial index at which to insert.
* @return the number of inserted bookmarks.
*/
private int createDefaultBookmarks(SQLiteDatabase db, int start) {
Class<?> stringsClass = R.string.class;
Field[] fields = stringsClass.getFields();
Pattern p = Pattern.compile("^bookmarkdefaults_title_");
Integer mobileFolderId = getMobileFolderId(db);
if (mobileFolderId == null) {
Log.e(LOGTAG, "Error creating default bookmarks: mobileFolderId is null");
return 0;
}
int pos = start;
for (int i = 0; i < fields.length; i++) {
final String name = fields[i].getName();
Matcher m = p.matcher(name);
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);
final String url = mContext.getString(urlId);
createBookmark(db, title, url, pos, mobileFolderId);
// Create icons in a separate thread to avoid blocking about:home on startup.
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
SQLiteDatabase db = getWritableDatabase();
Bitmap icon = getDefaultFaviconFromPath(name);
if (icon == null) {
icon = getDefaultFaviconFromDrawable(name);
}
if (icon != null) {
createFavicon(db, url, 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);
}
}
return pos - start;
}
private void createBookmark(SQLiteDatabase db, String title, String url, int pos, int parent) {
ContentValues bookmarkValues = new ContentValues();
bookmarkValues.put(Bookmarks.PARENT, parent);
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);
}
private void createFavicon(SQLiteDatabase db, String url, Bitmap icon) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ContentValues iconValues = new ContentValues();
iconValues.put(Favicons.PAGE_URL, url);
byte[] data = null;
if (icon.compress(Bitmap.CompressFormat.PNG, 100, stream)) {
data = stream.toByteArray();
} else {
Log.w(LOGTAG, "Favicon compression failed.");
}
iconValues.put(Favicons.DATA, data);
insertFavicon(db, iconValues);
}
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;
}
int faviconId = faviconField.getInt(null);
String path = mContext.getString(faviconId);
String apkPath = mContext.getPackageResourcePath();
File apkFile = new File(apkPath);
String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path;
return GeckoJarReader.getBitmap(mContext.getResources(), bitmapPath);
} catch (java.lang.IllegalAccessException ex) {
Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex);
} catch (java.lang.NoSuchFieldException ex) {
// If the field does not exist, that means we intend to load via a drawable
}
return null;
}
private Bitmap getDefaultFaviconFromDrawable(String name) {
Class<?> drawablesClass = R.drawable.class;
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;
}
int faviconId = faviconField.getInt(null);
return BitmapUtils.decodeResource(mContext, faviconId);
} catch (java.lang.IllegalAccessException ex) {
Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex);
} catch (java.lang.NoSuchFieldException ex) {
// If the field does not exist, that means we intend to load via a file path
}
return null;
}
private void createOrUpdateAllSpecialFolders(SQLiteDatabase db) {
createOrUpdateSpecialFolder(db, Bookmarks.MOBILE_FOLDER_GUID,
R.string.bookmarks_folder_mobile, 0);

View File

@ -12,12 +12,10 @@ import java.util.Map;
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.FaviconColumns;
import org.mozilla.gecko.db.BrowserContract.Favicons;
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.sync.Utils;
@ -1100,8 +1098,8 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
}
long insertFavicon(SQLiteDatabase db, ContentValues values) {
// This method is a dupicate of BrowserDatabaseHelper.insertFavicon.
// If changes are needed, please update both
// This method is a duplicate of BrowserDatabaseHelper.insertFavicon.
// If changes are needed, please update both.
String faviconUrl = values.getAsString(Favicons.URL);
String pageUrl = null;

View File

@ -6,11 +6,22 @@
package org.mozilla.gecko.db;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.AppConstants;
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.ExpirePriority;
@ -21,12 +32,17 @@ import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
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.distribution.Distribution;
import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.util.GeckoJarReader;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.CursorWrapper;
@ -99,6 +115,246 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
}
/**
* Add default bookmarks to the database.
* Takes an offset; returns a new offset.
*/
@Override
public int addDefaultBookmarks(Context context, ContentResolver cr, final int offset) {
long folderId = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
if (folderId == -1L) {
Log.e(LOGTAG, "No mobile folder: cannot add default bookmarks.");
return offset;
}
// Use reflection to walk the set of bookmark defaults.
// This is horrible.
final Class<?> stringsClass = R.string.class;
final Field[] fields = stringsClass.getFields();
final Pattern p = Pattern.compile("^bookmarkdefaults_title_");
int pos = offset;
final long now = System.currentTimeMillis();
final ArrayList<ContentValues> bookmarkValues = new ArrayList<ContentValues>();
final ArrayList<ContentValues> faviconValues = new ArrayList<ContentValues>();
for (int i = 0; i < fields.length; i++) {
final String name = fields[i].getName();
final Matcher m = p.matcher(name);
if (!m.find()) {
continue;
}
try {
final int titleid = fields[i].getInt(null);
final String title = context.getString(titleid);
final Field urlField = stringsClass.getField(name.replace("_title_", "_url_"));
final int urlId = urlField.getInt(null);
final String url = context.getString(urlId);
bookmarkValues.add(createBookmark(now, title, url, pos++, folderId));
Bitmap icon = getDefaultFaviconFromPath(context, name);
if (icon == null) {
icon = getDefaultFaviconFromDrawable(context, name);
}
if (icon == null) {
continue;
}
final ContentValues iconValue = createFavicon(url, icon);
if (iconValue != null) {
faviconValues.add(iconValue);
}
} catch (IllegalAccessException e) {
Log.wtf(LOGTAG, "Reflection failure.", e);
} catch (IllegalArgumentException e) {
Log.wtf(LOGTAG, "Reflection failure.", e);
} catch (NoSuchFieldException e) {
Log.wtf(LOGTAG, "Reflection failure.", e);
}
}
cr.bulkInsert(mFaviconsUriWithProfile, faviconValues.toArray(new ContentValues[faviconValues.size()]));
final int inserted = cr.bulkInsert(mBookmarksUriWithProfile, bookmarkValues.toArray(new ContentValues[bookmarkValues.size()]));
return offset + inserted;
}
/**
* Add bookmarks from the provided distribution.
* Takes an offset; returns a new offset.
*/
@Override
public int addDistributionBookmarks(ContentResolver cr, Distribution distribution, int offset) {
if (!distribution.exists()) {
Log.d(LOGTAG, "No distribution from which to add bookmarks.");
return offset;
}
final JSONArray bookmarks = distribution.getBookmarks();
if (bookmarks == null) {
Log.d(LOGTAG, "No distribution bookmarks.");
return offset;
}
long folderId = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
if (folderId == -1L) {
Log.e(LOGTAG, "No mobile folder: cannot add distribution bookmarks.");
return offset;
}
final Locale locale = Locale.getDefault();
final long now = System.currentTimeMillis();
int mobilePos = offset;
int pinnedPos = 0; // Assume nobody has pinned anything yet.
final ArrayList<ContentValues> bookmarkValues = new ArrayList<ContentValues>();
final ArrayList<ContentValues> faviconValues = new ArrayList<ContentValues>();
for (int i = 0; i < bookmarks.length(); i++) {
try {
final JSONObject bookmark = bookmarks.getJSONObject(i);
final String title = getLocalizedProperty(bookmark, "title", locale);
final String url = getLocalizedProperty(bookmark, "url", locale);
final long parent;
final int pos;
if (bookmark.has("pinned")) {
parent = Bookmarks.FIXED_PINNED_LIST_ID;
pos = pinnedPos++;
} else {
parent = folderId;
pos = mobilePos++;
}
bookmarkValues.add(createBookmark(now, title, url, pos, parent));
// Return early if there is no icon for this bookmark.
if (!bookmark.has("icon")) {
continue;
}
try {
final String iconData = bookmark.getString("icon");
final Bitmap icon = BitmapUtils.getBitmapFromDataURI(iconData);
if (icon == null) {
continue;
}
final ContentValues iconValue = createFavicon(url, icon);
if (iconValue != null) {
faviconValues.add(iconValue);
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating distribution bookmark icon.", e);
}
} catch (JSONException e) {
Log.e(LOGTAG, "Error creating distribution bookmark.", e);
}
}
cr.bulkInsert(mFaviconsUriWithProfile, faviconValues.toArray(new ContentValues[faviconValues.size()]));
final int inserted = cr.bulkInsert(mBookmarksUriWithProfile, bookmarkValues.toArray(new ContentValues[bookmarkValues.size()]));
return offset + inserted;
}
private static ContentValues createBookmark(final long timestamp, final String title, final String url, final int pos, final long parent) {
final ContentValues v = new ContentValues();
v.put(Bookmarks.DATE_CREATED, timestamp);
v.put(Bookmarks.DATE_MODIFIED, timestamp);
v.put(Bookmarks.GUID, Utils.generateGuid());
v.put(Bookmarks.PARENT, parent);
v.put(Bookmarks.POSITION, pos);
v.put(Bookmarks.TITLE, title);
v.put(Bookmarks.URL, url);
return v;
}
private static ContentValues createFavicon(final String url, final Bitmap icon) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
ContentValues iconValues = new ContentValues();
iconValues.put(Favicons.PAGE_URL, url);
byte[] data = null;
if (icon.compress(Bitmap.CompressFormat.PNG, 100, stream)) {
data = stream.toByteArray();
} else {
Log.w(LOGTAG, "Favicon compression failed.");
return null;
}
iconValues.put(Favicons.DATA, data);
return iconValues;
}
private static String getLocalizedProperty(final JSONObject bookmark, final String property, final Locale locale) throws JSONException {
// Try the full locale.
final 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 static Bitmap getDefaultFaviconFromPath(Context context, 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;
}
int faviconId = faviconField.getInt(null);
String path = context.getString(faviconId);
String apkPath = context.getPackageResourcePath();
File apkFile = new File(apkPath);
String bitmapPath = "jar:jar:" + apkFile.toURI() + "!/" + AppConstants.OMNIJAR_NAME + "!/" + path;
return GeckoJarReader.getBitmap(context.getResources(), bitmapPath);
} catch (java.lang.IllegalAccessException ex) {
Log.e(LOGTAG, "[Path] Can't create favicon " + name, ex);
} catch (java.lang.NoSuchFieldException ex) {
// If the field does not exist, that means we intend to load via a drawable.
}
return null;
}
private static Bitmap getDefaultFaviconFromDrawable(Context context, String name) {
Class<?> drawablesClass = R.drawable.class;
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;
}
int faviconId = faviconField.getInt(null);
return BitmapUtils.decodeResource(context, faviconId);
} catch (java.lang.IllegalAccessException ex) {
Log.e(LOGTAG, "[Drawable] Can't create favicon " + name, ex);
} catch (java.lang.NoSuchFieldException ex) {
Log.wtf(LOGTAG, "No field, and presumably no drawable, for " + name);
}
return null;
}
// Invalidate cached data
@Override
public void invalidateCachedState() {
@ -577,29 +833,28 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
return null;
}
private synchronized long getFolderIdFromGuid(ContentResolver cr, String guid) {
if (mFolderIdMap.containsKey(guid))
private synchronized long getFolderIdFromGuid(final ContentResolver cr, final String guid) {
if (mFolderIdMap.containsKey(guid)) {
return mFolderIdMap.get(guid);
long folderId = -1;
Cursor c = null;
try {
c = cr.query(mBookmarksUriWithProfile,
new String[] { Bookmarks._ID },
Bookmarks.GUID + " = ?",
new String[] { guid },
null);
if (c.moveToFirst())
folderId = c.getLong(c.getColumnIndexOrThrow(Bookmarks._ID));
} finally {
if (c != null)
c.close();
}
mFolderIdMap.put(guid, folderId);
return folderId;
final Cursor c = cr.query(mBookmarksUriWithProfile,
new String[] { Bookmarks._ID },
Bookmarks.GUID + " = ?",
new String[] { guid },
null);
try {
final int col = c.getColumnIndexOrThrow(Bookmarks._ID);
if (!c.moveToFirst() || c.isNull(col)) {
return -1;
}
final long id = c.getLong(col);
mFolderIdMap.put(guid, id);
return id;
} finally {
c.close();
}
}
/**