summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/preferences/AndroidImport.java
diff options
context:
space:
mode:
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/preferences/AndroidImport.java')
-rw-r--r--mobile/android/base/java/org/mozilla/gecko/preferences/AndroidImport.java230
1 files changed, 230 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/preferences/AndroidImport.java b/mobile/android/base/java/org/mozilla/gecko/preferences/AndroidImport.java
new file mode 100644
index 000000000..bb71ce78b
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/AndroidImport.java
@@ -0,0 +1,230 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+
+package org.mozilla.gecko.preferences;
+
+import android.content.ContentValues;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.os.Build;
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+import org.mozilla.gecko.db.LocalBrowserDB;
+import org.mozilla.gecko.icons.IconResponse;
+import org.mozilla.gecko.icons.IconsHelper;
+import org.mozilla.gecko.icons.storage.DiskStorage;
+
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.provider.BaseColumns;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class AndroidImport implements Runnable {
+ /**
+ * The Android M SDK removed several fields and methods from android.provider.Browser. This class is used as a
+ * replacement to support building with the new SDK but at the same time still use these fields on lower Android
+ * versions.
+ */
+ private static class LegacyBrowserProvider {
+ public static final Uri BOOKMARKS_URI = Uri.parse("content://browser/bookmarks");
+
+ // Incomplete: This are just the fields we currently use in our code base
+ public static class BookmarkColumns implements BaseColumns {
+ public static final String URL = "url";
+ public static final String VISITS = "visits";
+ public static final String DATE = "date";
+ public static final String BOOKMARK = "bookmark";
+ public static final String TITLE = "title";
+ public static final String CREATED = "created";
+ public static final String FAVICON = "favicon";
+ }
+ }
+
+ public static final Uri SAMSUNG_BOOKMARKS_URI = Uri.parse("content://com.sec.android.app.sbrowser.browser/bookmarks");
+ public static final Uri SAMSUNG_HISTORY_URI = Uri.parse("content://com.sec.android.app.sbrowser.browser/history");
+ public static final String SAMSUNG_MANUFACTURER = "samsung";
+
+ private static final String LOGTAG = "AndroidImport";
+ private final Context mContext;
+ private final Runnable mOnDoneRunnable;
+ private final ArrayList<ContentProviderOperation> mOperations;
+ private final ContentResolver mCr;
+ private final LocalBrowserDB mDB;
+ private final boolean mImportBookmarks;
+ private final boolean mImportHistory;
+
+ public AndroidImport(Context context, Runnable onDoneRunnable,
+ boolean doBookmarks, boolean doHistory) {
+ mContext = context;
+ mOnDoneRunnable = onDoneRunnable;
+ mOperations = new ArrayList<ContentProviderOperation>();
+ mCr = mContext.getContentResolver();
+ mDB = new LocalBrowserDB(GeckoProfile.get(context).getName());
+ mImportBookmarks = doBookmarks;
+ mImportHistory = doHistory;
+ }
+
+ public void mergeBookmarks() {
+ Cursor cursor = null;
+ try {
+ cursor = query(LegacyBrowserProvider.BOOKMARKS_URI,
+ SAMSUNG_BOOKMARKS_URI,
+ LegacyBrowserProvider.BookmarkColumns.BOOKMARK + " = 1");
+
+ if (cursor != null) {
+ final int faviconCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.FAVICON);
+ final int titleCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.TITLE);
+ final int urlCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.URL);
+ // http://code.google.com/p/android/issues/detail?id=17969
+ final int createCol = cursor.getColumnIndex(LegacyBrowserProvider.BookmarkColumns.CREATED);
+
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ String url = cursor.getString(urlCol);
+ String title = cursor.getString(titleCol);
+ long created;
+ if (createCol >= 0) {
+ created = cursor.getLong(createCol);
+ } else {
+ created = System.currentTimeMillis();
+ }
+ // Need to set it to the current time so Sync picks it up.
+ long modified = System.currentTimeMillis();
+ byte[] data = cursor.getBlob(faviconCol);
+ mDB.updateBookmarkInBatch(mCr, mOperations,
+ url, title, null, -1,
+ created, modified,
+ BrowserContract.Bookmarks.DEFAULT_POSITION,
+ null, Bookmarks.TYPE_BOOKMARK);
+ if (data != null) {
+ storeBitmap(data, url);
+ }
+ cursor.moveToNext();
+ }
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+
+ flushBatchOperations();
+ }
+
+ public void mergeHistory() {
+ ArrayList<ContentValues> visitsToSynthesize = new ArrayList<>();
+ Cursor cursor = null;
+ try {
+ cursor = query (LegacyBrowserProvider.BOOKMARKS_URI,
+ SAMSUNG_HISTORY_URI,
+ LegacyBrowserProvider.BookmarkColumns.BOOKMARK + " = 0 AND " +
+ LegacyBrowserProvider.BookmarkColumns.VISITS + " > 0");
+
+ if (cursor != null) {
+ final int dateCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.DATE);
+ final int faviconCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.FAVICON);
+ final int titleCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.TITLE);
+ final int urlCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.URL);
+ final int visitsCol = cursor.getColumnIndexOrThrow(LegacyBrowserProvider.BookmarkColumns.VISITS);
+
+ cursor.moveToFirst();
+ while (!cursor.isAfterLast()) {
+ String url = cursor.getString(urlCol);
+ String title = cursor.getString(titleCol);
+ long date = cursor.getLong(dateCol);
+ int visits = cursor.getInt(visitsCol);
+ byte[] data = cursor.getBlob(faviconCol);
+ mDB.updateHistoryInBatch(mCr, mOperations, url, title, date, visits);
+ if (data != null) {
+ storeBitmap(data, url);
+ }
+ ContentValues visitData = new ContentValues();
+ visitData.put(LocalBrowserDB.HISTORY_VISITS_DATE, date);
+ visitData.put(LocalBrowserDB.HISTORY_VISITS_URL, url);
+ visitData.put(LocalBrowserDB.HISTORY_VISITS_COUNT, visits);
+ visitsToSynthesize.add(visitData);
+ cursor.moveToNext();
+ }
+ }
+ } finally {
+ if (cursor != null)
+ cursor.close();
+ }
+
+ flushBatchOperations();
+
+ // Now that we have flushed history records, we need to synthesize individual visits. We have
+ // gathered information about all of the visits we need to synthesize into visitsForSynthesis.
+ mDB.insertVisitsFromImportHistoryInBatch(mCr, mOperations, visitsToSynthesize);
+
+ flushBatchOperations();
+ }
+
+ private void storeBitmap(byte[] data, String url) {
+ if (TextUtils.isEmpty(url) || data == null) {
+ return;
+ }
+
+ final Bitmap bitmap = BitmapFactory.decodeByteArray(data, 0, data.length);
+ if (bitmap == null) {
+ return;
+ }
+
+ final String iconUrl = IconsHelper.guessDefaultFaviconURL(url);
+ if (iconUrl == null) {
+ return;
+ }
+
+ final DiskStorage storage = DiskStorage.get(mContext);
+
+ storage.putIcon(url, bitmap);
+ storage.putMapping(url, iconUrl);
+ }
+
+ protected Cursor query(Uri mainUri, Uri fallbackUri, String condition) {
+ final Cursor cursor = mCr.query(mainUri, null, condition, null, null);
+ if (Build.MANUFACTURER.equals(SAMSUNG_MANUFACTURER) && (cursor == null || cursor.getCount() == 0)) {
+ if (cursor != null) {
+ cursor.close();
+ }
+ return mCr.query(fallbackUri, null, null, null, null);
+ }
+ return cursor;
+ }
+
+ protected void flushBatchOperations() {
+ Log.d(LOGTAG, "Flushing " + mOperations.size() + " DB operations");
+ try {
+ // We don't really care for the results, this is best-effort.
+ mCr.applyBatch(BrowserContract.AUTHORITY, mOperations);
+ } catch (RemoteException e) {
+ Log.e(LOGTAG, "Remote exception while updating db: ", e);
+ } catch (OperationApplicationException e) {
+ // Bug 716729 means this happens even in normal circumstances
+ Log.d(LOGTAG, "Error while applying database updates: ", e);
+ }
+ mOperations.clear();
+ }
+
+ @Override
+ public void run() {
+ if (mImportBookmarks) {
+ mergeBookmarks();
+ }
+ if (mImportHistory) {
+ mergeHistory();
+ }
+
+ mOnDoneRunnable.run();
+ }
+}