Bug 1293710 - Obtain a list of "highlights" for the Activity Stream panel. r?ahunt,grisha
This query resembles how the Activity Stream add-on for desktop Firefox selectes highlights[1].
The desktop version uses the 'rev_host' (reversed host) column to filter the results based
on a blacklist and group results by hostname. This has been omitted here (See
bug 1298785 and
bug 1298786).
Additionally the desktop version uses a block list to filter "highlights" that have been removed
manually by the user. This is not added here until needed from the UI side (see
bug 1298783).
[1] https://github.com/mozilla/activity-stream/blob/9eb9f451b553bb62ae9b8d6b41a8ef94a2e020ea/addon/PlacesProvider.js#L578
MozReview-Commit-ID: 9TyIaK21MNJ
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserContract.java
@@ -585,16 +585,20 @@ public class BrowserContract {
public static final String BOOKMARK_ID = "bookmark_id";
public static final String HISTORY_ID = "history_id";
public static final String TYPE = "type";
public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "topsites");
}
+ public static final class Highlights {
+ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "highlights");
+ }
+
@RobocopTarget
public static final class SearchHistory implements CommonColumns, HistoryColumns {
private SearchHistory() {}
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/searchhistory";
public static final String QUERY = "query";
public static final String DATE = "date";
public static final String TABLE_NAME = "searchhistory";
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
@@ -170,9 +170,17 @@ public interface BrowserDB {
public abstract void unpinSite(ContentResolver cr, int position);
public abstract boolean hideSuggestedSite(String url);
public abstract void setSuggestedSites(SuggestedSites suggestedSites);
public abstract SuggestedSites getSuggestedSites();
public abstract boolean hasSuggestedImageUrl(String url);
public abstract String getSuggestedImageUrlForUrl(String url);
public abstract int getSuggestedBackgroundColorForUrl(String url);
+
+ /**
+ * Obtain a set of links for highlights from bookmarks and history.
+ *
+ * @param context The context to load the cursor.
+ * @param limit Maximum number of results to return.
+ */
+ CursorLoader getHighlights(Context context, int limit);
}
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
@@ -49,16 +49,18 @@ import android.database.SQLException;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
+import static org.mozilla.gecko.db.DBUtils.qualifyColumn;
+
public class BrowserProvider extends SharedBrowserDatabaseProvider {
public static final String ACTION_SHRINK_MEMORY = "org.mozilla.gecko.db.intent.action.SHRINK_MEMORY";
private static final String LOGTAG = "GeckoBrowserProvider";
// How many records to reposition in a single query.
// This should be less than the SQLite maximum number of query variables
// (currently 999) divided by the number of variables used per positioning
@@ -121,16 +123,20 @@ public class BrowserProvider extends Sha
static final int THUMBNAIL_ID = 801;
static final int URL_ANNOTATIONS = 900;
static final int TOPSITES = 1000;
static final int VISITS = 1100;
+ static final int METADATA = 1200;
+
+ static final int HIGHLIGHTS = 1300;
+
static final String DEFAULT_BOOKMARKS_SORT_ORDER = Bookmarks.TYPE
+ " ASC, " + Bookmarks.POSITION + " ASC, " + Bookmarks._ID
+ " ASC";
static final String DEFAULT_HISTORY_SORT_ORDER = History.DATE_LAST_VISITED + " DESC";
static final String DEFAULT_VISITS_SORT_ORDER = Visits.DATE_VISITED + " DESC";
static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
@@ -283,16 +289,18 @@ public class BrowserProvider extends Sha
for (Table table : sTables) {
for (Table.ContentProviderInfo type : table.getContentProviderInfo()) {
URI_MATCHER.addURI(BrowserContract.AUTHORITY, type.name, type.id);
}
}
// Combined pinned sites, top visited sites, and suggested sites
URI_MATCHER.addURI(BrowserContract.AUTHORITY, "topsites", TOPSITES);
+
+ URI_MATCHER.addURI(BrowserContract.AUTHORITY, "highlights", HIGHLIGHTS);
}
private static class ShrinkMemoryReceiver extends BroadcastReceiver {
private final WeakReference<BrowserProvider> mBrowserProviderWeakReference;
public ShrinkMemoryReceiver(final BrowserProvider browserProvider) {
mBrowserProviderWeakReference = new WeakReference<>(browserProvider);
}
@@ -1131,16 +1139,70 @@ public class BrowserProvider extends Sha
db.setTransactionSuccessful();
return c;
} finally {
db.endTransaction();
}
}
+ /**
+ * Obtain a set of links for highlights (from bookmarks and history).
+ *
+ * Based on the query for Activity^ Stream (desktop):
+ * https://github.com/mozilla/activity-stream/blob/9eb9f451b553bb62ae9b8d6b41a8ef94a2e020ea/addon/PlacesProvider.js#L578
+ */
+ public Cursor getHighlights(final SQLiteDatabase db, String limit) {
+ final int totalLimit = limit == null ? 20 : Integer.parseInt(limit);
+
+ final long threeDaysAgo = System.currentTimeMillis() - (1000 * 60 * 60 * 24 * 3);
+ final long bookmarkLimit = 1;
+
+ // Select recent bookmarks that have not been visited much
+ final String bookmarksQuery = "SELECT * FROM (SELECT " +
+ DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " +
+ "-1 AS " + Combined.HISTORY_ID + ", " +
+ DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks.URL) + ", " +
+ DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks.TITLE) + " " +
+ "FROM " + Bookmarks.TABLE_NAME + " " +
+ "LEFT JOIN " + History.TABLE_NAME + " ON " +
+ DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks.URL) + " = " +
+ DBUtils.qualifyColumn(History.TABLE_NAME, History.URL) + " " +
+ "WHERE " + DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks.DATE_CREATED) + " > " + threeDaysAgo + " " +
+ "AND " + DBUtils.qualifyColumn(History.TABLE_NAME, History.VISITS) + " <= 3 " +
+ "AND " + DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks.IS_DELETED) + " = 0 " +
+ // TODO: Implement block list (bug 1298783)
+ "ORDER BY " + DBUtils.qualifyColumn(Bookmarks.TABLE_NAME, Bookmarks.DATE_CREATED) + " DESC " +
+ "LIMIT " + bookmarkLimit + ")";
+
+ final long last30Minutes = System.currentTimeMillis() - (1000 * 60 * 30);
+ final long historyLimit = totalLimit - bookmarkLimit;
+
+ // Select recent history that has not been visited much.
+ final String historyQuery = "SELECT * FROM (SELECT " +
+ History._ID + " AS " + Combined.HISTORY_ID + ", " +
+ "-1 AS " + Combined.BOOKMARK_ID + ", " +
+ History.URL + ", " +
+ History.TITLE + " " +
+ "FROM " + History.TABLE_NAME + " " +
+ "WHERE " + History.DATE_LAST_VISITED + " < " + last30Minutes + " " +
+ "AND " + History.VISITS + " <= 3 " +
+ "AND " + History.TITLE + " NOT NULL AND " + History.TITLE + " != '' " +
+ "AND " + History.IS_DELETED + " = 0 " +
+ // TODO: Implement block list (bug 1298783)
+ // TODO: Implement domain black list (bug 1298786)
+ // TODO: Group by host (bug 1298785)
+ "ORDER BY " + History.DATE_LAST_VISITED + " DESC " +
+ "LIMIT " + historyLimit + ")";
+
+ final String query = "SELECT DISTINCT * FROM (" + bookmarksQuery + " UNION ALL " + historyQuery + ");";
+
+ return db.rawQuery(query, null);
+ }
+
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
final int match = URI_MATCHER.match(uri);
if (match == TOPSITES) {
if (uri.getBooleanQueryParameter(BrowserContract.PARAM_TOPSITES_DISABLE_PINNED, false)) {
return getPlainTopSites(uri);
@@ -1287,16 +1349,22 @@ public class BrowserProvider extends Sha
if (hasFaviconsInProjection(projection))
qb.setTables(VIEW_COMBINED_WITH_FAVICONS);
else
qb.setTables(Combined.VIEW_NAME);
break;
}
+ case HIGHLIGHTS: {
+ debug("Highlights query: " + uri);
+
+ return getHighlights(db, limit);
+ }
+
default: {
Table table = findTableFor(match);
if (table == null) {
throw new UnsupportedOperationException("Unknown query URI " + uri);
}
trace("Update TABLE: " + uri);
return table.query(db, uri, match, projection, selection, selectionArgs, sortOrder, groupBy, limit);
}
--- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
@@ -30,16 +30,17 @@ 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;
import org.mozilla.gecko.db.BrowserContract.Favicons;
import org.mozilla.gecko.db.BrowserContract.History;
import org.mozilla.gecko.db.BrowserContract.SyncColumns;
import org.mozilla.gecko.db.BrowserContract.Thumbnails;
import org.mozilla.gecko.db.BrowserContract.TopSites;
+import org.mozilla.gecko.db.BrowserContract.Highlights;
import org.mozilla.gecko.distribution.Distribution;
import org.mozilla.gecko.icons.decoders.FaviconDecoder;
import org.mozilla.gecko.icons.decoders.LoadFaviconResult;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.restrictions.Restrictions;
import org.mozilla.gecko.sync.Utils;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.StringUtils;
@@ -110,16 +111,17 @@ public class LocalBrowserDB implements B
private final Uri mParentsUriWithProfile;
private final Uri mHistoryUriWithProfile;
private final Uri mHistoryExpireUriWithProfile;
private final Uri mCombinedUriWithProfile;
private final Uri mUpdateHistoryUriWithProfile;
private final Uri mFaviconsUriWithProfile;
private final Uri mThumbnailsUriWithProfile;
private final Uri mTopSitesUriWithProfile;
+ private final Uri mHighlightsUriWithProfile;
private final Uri mSearchHistoryUri;
private LocalSearches searches;
private LocalTabsAccessor tabsAccessor;
private LocalURLMetadata urlMetadata;
private LocalUrlAnnotations urlAnnotations;
private static final String[] DEFAULT_BOOKMARK_COLUMNS =
@@ -136,16 +138,17 @@ public class LocalBrowserDB implements B
mBookmarksUriWithProfile = DBUtils.appendProfile(profile, Bookmarks.CONTENT_URI);
mParentsUriWithProfile = DBUtils.appendProfile(profile, Bookmarks.PARENTS_CONTENT_URI);
mHistoryUriWithProfile = DBUtils.appendProfile(profile, History.CONTENT_URI);
mHistoryExpireUriWithProfile = DBUtils.appendProfile(profile, History.CONTENT_OLD_URI);
mCombinedUriWithProfile = DBUtils.appendProfile(profile, Combined.CONTENT_URI);
mFaviconsUriWithProfile = DBUtils.appendProfile(profile, Favicons.CONTENT_URI);
mTopSitesUriWithProfile = DBUtils.appendProfile(profile, TopSites.CONTENT_URI);
+ mHighlightsUriWithProfile = DBUtils.appendProfile(profile, Highlights.CONTENT_URI);
mThumbnailsUriWithProfile = DBUtils.appendProfile(profile, Thumbnails.CONTENT_URI);
mSearchHistoryUri = BrowserContract.SearchHistory.CONTENT_URI;
mUpdateHistoryUriWithProfile =
mHistoryUriWithProfile.buildUpon()
.appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true")
.appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true")
@@ -1825,11 +1828,19 @@ public class LocalBrowserDB implements B
rb.add(-1);
rb.add(-1);
rb.add(-1);
rb.add("");
rb.add("");
rb.add(TopSites.TYPE_BLANK);
return new MergeCursor(new Cursor[] {topSitesCursor, blanksCursor});
+ }
+ @Override
+ public CursorLoader getHighlights(Context context, int limit) {
+ final Uri uri = mHighlightsUriWithProfile.buildUpon()
+ .appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(limit))
+ .build();
+
+ return new CursorLoader(context, uri, null, null, null, null);
}
}
--- a/mobile/android/base/java/org/mozilla/gecko/db/StubBrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/StubBrowserDB.java
@@ -356,16 +356,21 @@ public class StubBrowserDB implements Br
public String getSuggestedImageUrlForUrl(String url) {
return null;
}
public int getSuggestedBackgroundColorForUrl(String url) {
return 0;
}
+ @Override
+ public CursorLoader getHighlights(Context context, int limit) {
+ return null;
+ }
+
public Cursor getTopSites(ContentResolver cr, int suggestedRangeLimit, int limit) {
return null;
}
public CursorLoader getActivityStreamTopSites(Context context, int limit) {
return null;
}
--- a/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/URLMetadataTable.java
@@ -12,17 +12,17 @@ import org.mozilla.gecko.db.BrowserContr
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
// Holds metadata info about urls. Supports some helper functions for getting back a HashMap of key value data.
public class URLMetadataTable extends BaseTable {
private static final String LOGTAG = "GeckoURLMetadataTable";
private static final String TABLE = "metadata"; // Name of the table in the db
- private static final int TABLE_ID_NUMBER = 1200;
+ private static final int TABLE_ID_NUMBER = BrowserProvider.METADATA;
// Uri for querying this table
public static final Uri CONTENT_URI = Uri.withAppendedPath(BrowserContract.AUTHORITY_URI, "metadata");
// Columns in the table
public static final String ID_COLUMN = "id";
public static final String URL_COLUMN = "url";
public static final String TILE_IMAGE_URL_COLUMN = "tileImage";