Bug 1283025 - Add support for PartnerBookmarksProvider. r=grisha, a=gchang
authorSebastian Kaspari <s.kaspari@gmail.com>
Thu, 21 Jul 2016 14:43:00 +0200
changeset 340015 f8b494dbf1ea9c6dca06c6c820ff5160bffccdd7
parent 340014 9af7cca3f8b600a85ae8afdd0ead3e6e73345642
child 340016 661d3548825aff461b50901da7ec5e32a8366bf4
push id6249
push userjlund@mozilla.com
push dateMon, 01 Aug 2016 13:59:36 +0000
treeherdermozilla-beta@bad9d4f5bf7e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgrisha, gchang
bugs1283025
milestone49.0a2
Bug 1283025 - Add support for PartnerBookmarksProvider. r=grisha, a=gchang If a distribution sets "distribution.read_partner_bookmarks_provider" (Android preference) to 'true' then Fennec will read from Android's PartnerBookmarksProvider [1] and add them to the bookmarks panel UI. [1] https://android.googlesource.com/platform/packages/providers/PartnerBookmarksProvider/+/master/src/com/android/providers/partnerbookmarks/PartnerBookmarksProvider.java
mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
mobile/android/base/java/org/mozilla/gecko/distribution/PartnerBookmarksProviderClient.java
mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
mobile/android/base/moz.build
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
@@ -229,16 +229,22 @@ public class BrowserContract {
 
         public static final int FIXED_ROOT_ID = 0;
         public static final int FAKE_DESKTOP_FOLDER_ID = -1;
         public static final int FIXED_READING_LIST_ID = -2;
         public static final int FIXED_PINNED_LIST_ID = -3;
         public static final int FIXED_SCREENSHOT_FOLDER_ID = -4;
         public static final int FAKE_READINGLIST_SMARTFOLDER_ID = -5;
 
+        /**
+         * This ID and the following negative IDs are reserved for bookmarks from Android's partner
+         * bookmark provider.
+         */
+        public static final long FAKE_PARTNER_BOOKMARKS_START = -1000;
+
         public static final String MOBILE_FOLDER_GUID = "mobile";
         public static final String PLACES_FOLDER_GUID = "places";
         public static final String MENU_FOLDER_GUID = "menu";
         public static final String TAGS_FOLDER_GUID = "tags";
         public static final String TOOLBAR_FOLDER_GUID = "toolbar";
         public static final String UNFILED_FOLDER_GUID = "unfiled";
         public static final String FAKE_DESKTOP_FOLDER_GUID = "desktop";
         public static final String PINNED_FOLDER_GUID = "pinned";
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/distribution/PartnerBookmarksProviderClient.java
@@ -0,0 +1,71 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; 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.distribution;
+
+import android.content.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+
+import org.mozilla.gecko.db.BrowserContract;
+
+/**
+ * Client for reading Android's PartnerBookmarksProvider.
+ *
+ * Note: This client is only invoked for distributions. Without a distribution the content provider
+ *       will not be read and no bookmarks will be added to the UI.
+ */
+public class PartnerBookmarksProviderClient {
+    /**
+     * The contract between the partner bookmarks provider and applications. Contains the definition
+     * for the supported URIs and columns.
+     */
+    private static class PartnerContract {
+        public static final Uri CONTENT_URI = Uri.parse("content://com.android.partnerbookmarks/bookmarks");
+
+        public static final int TYPE_BOOKMARK = 1;
+        public static final int TYPE_FOLDER = 2;
+
+        public static final int PARENT_ROOT_ID = 0;
+
+        public static final String ID = "_id";
+        public static final String TYPE = "type";
+        public static final String URL = "url";
+        public static final String TITLE = "title";
+        public static final String FAVICON = "favicon";
+        public static final String TOUCHICON = "touchicon";
+        public static final String PARENT = "parent";
+    }
+
+    public static Cursor getBookmarksInFolder(ContentResolver contentResolver, int folderId) {
+        // Use root folder id or transform negative id into actual (positive) folder id.
+        final long actualFolderId = folderId == BrowserContract.Bookmarks.FIXED_ROOT_ID
+                ? PartnerContract.PARENT_ROOT_ID
+                : BrowserContract.Bookmarks.FAKE_PARTNER_BOOKMARKS_START - folderId;
+
+        return contentResolver.query(
+                PartnerContract.CONTENT_URI,
+                new String[] {
+                        // Transform ids into negative values starting with FAKE_PARTNER_BOOKMARKS_START.
+                        "(" + BrowserContract.Bookmarks.FAKE_PARTNER_BOOKMARKS_START + " - " + PartnerContract.ID + ") as " + BrowserContract.Bookmarks._ID,
+                        PartnerContract.TITLE + " as " + BrowserContract.Bookmarks.TITLE,
+                        PartnerContract.URL +  " as " + BrowserContract.Bookmarks.URL,
+                        // Transform parent ids to negative ids as well
+                        "(" + BrowserContract.Bookmarks.FAKE_PARTNER_BOOKMARKS_START + " - " + PartnerContract.PARENT + ") as " + BrowserContract.Bookmarks.PARENT,
+                        // Convert types (we use 0-1 and the partner provider 1-2)
+                        "(2 - " + PartnerContract.TYPE + ") as " + BrowserContract.Bookmarks.TYPE,
+                        // Use the ID of the entry as GUID
+                        PartnerContract.ID + " as " + BrowserContract.Bookmarks.GUID
+                },
+                PartnerContract.PARENT + " = ?"
+                        // Only select entries with valid type
+                        + " AND (" + BrowserContract.Bookmarks.TYPE + " = 1 OR " + BrowserContract.Bookmarks.TYPE + " = 2)"
+                        // Only select entries with non empty title
+                        + " AND " + BrowserContract.Bookmarks.TITLE + " <> ''",
+                new String[] { String.valueOf(actualFolderId) },
+                // Same order we use in our content provider (without position)
+                BrowserContract.Bookmarks.TYPE + " ASC, " + BrowserContract.Bookmarks._ID + " ASC");
+    }
+}
--- a/mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/home/BookmarksPanel.java
@@ -5,29 +5,35 @@
 
 package org.mozilla.gecko.home;
 
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
 
 import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.GeckoSharedPrefs;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.distribution.PartnerBookmarksProviderClient;
 import org.mozilla.gecko.home.BookmarksListAdapter.FolderInfo;
 import org.mozilla.gecko.home.BookmarksListAdapter.OnRefreshFolderListener;
 import org.mozilla.gecko.home.BookmarksListAdapter.RefreshType;
 import org.mozilla.gecko.home.HomeContextMenuInfo.RemoveItemType;
 import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
+import org.mozilla.gecko.preferences.GeckoPreferences;
 
 import android.app.Activity;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.res.Configuration;
 import android.database.Cursor;
+import android.database.MergeCursor;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.support.annotation.NonNull;
 import android.support.v4.app.LoaderManager;
 import android.support.v4.content.Loader;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -215,17 +221,42 @@ public class BookmarksPanel extends Home
             super(context);
             mFolderInfo = folderInfo;
             mRefreshType = refreshType;
             mDB = GeckoProfile.get(context).getDB();
         }
 
         @Override
         public Cursor loadCursor() {
-            return mDB.getBookmarksInFolder(getContext().getContentResolver(), mFolderInfo.id);
+            final boolean isRootFolder = mFolderInfo.id == BrowserContract.Bookmarks.FIXED_ROOT_ID;
+
+            final ContentResolver contentResolver = getContext().getContentResolver();
+
+            Cursor partnerCursor = null;
+            Cursor userCursor = null;
+
+            if (GeckoSharedPrefs.forProfile(getContext()).getBoolean(GeckoPreferences.PREFS_READ_PARTNER_BOOKMARKS_PROVIDER, false)
+                    && (isRootFolder || mFolderInfo.id <= Bookmarks.FAKE_PARTNER_BOOKMARKS_START)) {
+                partnerCursor = PartnerBookmarksProviderClient.getBookmarksInFolder(contentResolver, mFolderInfo.id);
+            }
+
+            if (isRootFolder || mFolderInfo.id > Bookmarks.FAKE_PARTNER_BOOKMARKS_START) {
+                userCursor = mDB.getBookmarksInFolder(contentResolver, mFolderInfo.id);
+            }
+
+
+            if (partnerCursor == null && userCursor == null) {
+                return null;
+            } else if (partnerCursor == null) {
+                return userCursor;
+            } else if (userCursor == null) {
+                return partnerCursor;
+            } else {
+                return new MergeCursor(new Cursor[] { partnerCursor, userCursor });
+            }
         }
 
         @Override
         public void onContentChanged() {
             // Invalidate the cached value that keeps track of whether or
             // not desktop bookmarks exist.
             mDB.invalidate();
             super.onContentChanged();
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -156,16 +156,17 @@ OnSharedPreferenceChangeListener
     public static final String PREFS_HOMEPAGE_PARTNER_COPY = GeckoPreferences.PREFS_HOMEPAGE + ".partner";
     public static final String PREFS_HISTORY_SAVED_SEARCH = NON_PREF_PREFIX + "search.search_history.enabled";
     private static final String PREFS_FAQ_LINK = NON_PREF_PREFIX + "faq.link";
     private static final String PREFS_FEEDBACK_LINK = NON_PREF_PREFIX + "feedback.link";
     public static final String PREFS_NOTIFICATIONS_CONTENT = NON_PREF_PREFIX + "notifications.content";
     public static final String PREFS_NOTIFICATIONS_CONTENT_LEARN_MORE = NON_PREF_PREFIX + "notifications.content.learn_more";
     public static final String PREFS_NOTIFICATIONS_WHATS_NEW = NON_PREF_PREFIX + "notifications.whats_new";
     public static final String PREFS_READ_PARTNER_CUSTOMIZATIONS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_customizations_provider";
+    public static final String PREFS_READ_PARTNER_BOOKMARKS_PROVIDER = NON_PREF_PREFIX + "distribution.read_partner_bookmarks_provider";
 
     private static final String ACTION_STUMBLER_UPLOAD_PREF = AppConstants.ANDROID_PACKAGE_NAME + ".STUMBLER_PREF";
 
 
     // This isn't a Gecko pref, even if it looks like one.
     private static final String PREFS_BROWSER_LOCALE = "locale";
 
     public static final String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession3";
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -255,16 +255,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'db/URLMetadataTable.java',
     'delegates/BookmarkStateChangeDelegate.java',
     'delegates/BrowserAppDelegate.java',
     'delegates/BrowserAppDelegateWithReference.java',
     'delegates/ScreenshotDelegate.java',
     'DevToolsAuthHelper.java',
     'distribution/Distribution.java',
     'distribution/DistributionStoreCallback.java',
+    'distribution/PartnerBookmarksProviderClient.java',
     'distribution/PartnerBrowserCustomizationsClient.java',
     'distribution/ReferrerDescriptor.java',
     'distribution/ReferrerReceiver.java',
     'dlc/BaseAction.java',
     'dlc/catalog/DownloadContent.java',
     'dlc/catalog/DownloadContentBootstrap.java',
     'dlc/catalog/DownloadContentBuilder.java',
     'dlc/catalog/DownloadContentCatalog.java',