--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
@@ -119,37 +119,51 @@ public abstract class BrowserDB {
public abstract void removeHistoryEntry(ContentResolver cr, String url);
public abstract void clearHistory(ContentResolver cr, boolean clearSearchHistory);
public abstract String getUrlForKeyword(ContentResolver cr, String keyword);
public abstract boolean isBookmark(ContentResolver cr, String uri);
+
+ /**
+ * Adds the bookmark to the "Mobile Bookmarks" base folder.
+ */
public abstract boolean addBookmark(ContentResolver cr, String title, String uri);
public abstract Cursor getBookmarkForUrl(ContentResolver cr, String url);
public abstract Cursor getBookmarksForPartialUrl(ContentResolver cr, String partialUrl);
public abstract void removeBookmarksWithURL(ContentResolver cr, String uri);
public abstract void removeBookmarkWithID(ContentResolver cr, int id);
public abstract void registerBookmarkObserver(ContentResolver cr, ContentObserver observer);
public abstract void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword);
+ public abstract void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword, long folderId);
public abstract boolean hasBookmarkWithGuid(ContentResolver cr, String guid);
public abstract boolean insertPageMetadata(ContentProviderClient contentProviderClient, String pageUrl, boolean hasImage, String metadataJSON);
public abstract int deletePageMetadata(ContentProviderClient contentProviderClient, String pageUrl);
/**
* Can return <code>null</code>.
*/
public abstract Cursor getBookmarksInFolder(ContentResolver cr, long folderId);
public abstract BookmarkFolderTree getBookmarkFolders(ContentResolver cr);
+ public abstract String getBookmarkFolderName(ContentResolver cr, long folderId);
+
public abstract int getBookmarkCountForFolder(ContentResolver cr, long folderId);
/**
+ * Return the id of the described folder, or null if no such folder exists.
+ */
+ public abstract Long getIDForFolder(ContentResolver cr, String folder, long parentId);
+
+ public abstract void addBookmarkFolder(ContentResolver cr, String folderName, long parentId);
+
+ /**
* Get the favicon from the database, if any, associated with the given favicon URL. (That is,
* the URL of the actual favicon image, not the URL of the page with which the favicon is associated.)
* @param cr The ContentResolver to use.
* @param faviconURL The URL of the favicon to fetch from the database.
* @return The decoded Bitmap from the database, if any. null if none is stored.
*/
public abstract LoadFaviconResult getFaviconForUrl(Context context, ContentResolver cr, String faviconURL);
--- a/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
@@ -665,17 +665,17 @@ public class BrowserProvider extends Sha
trace("Calling insert in transaction on URI: " + uri);
int match = URI_MATCHER.match(uri);
long id = -1;
switch (match) {
case BOOKMARKS: {
trace("Insert on BOOKMARKS: " + uri);
- id = insertBookmark(uri, values);
+ id = insertBookmarkOrFolder(uri, values);
break;
}
case HISTORY: {
trace("Insert on HISTORY: " + uri);
id = insertHistory(uri, values);
break;
}
@@ -777,19 +777,19 @@ public class BrowserProvider extends Sha
selection = DBUtils.concatenateWhere(selection, TABLE_BOOKMARKS + "._id = ?");
selectionArgs = DBUtils.appendSelectionArgs(selectionArgs,
new String[] { Long.toString(ContentUris.parseId(uri)) });
// fall through
case BOOKMARKS: {
debug("Updating bookmark: " + uri);
if (shouldUpdateOrInsert(uri)) {
- updated = updateOrInsertBookmark(uri, values, selection, selectionArgs);
+ updated = updateOrInsertBookmarkOrFolder(uri, values, selection, selectionArgs);
} else {
- updated = updateBookmarks(uri, values, selection, selectionArgs);
+ updated = updateBookmarksOrFolder(uri, values, selection, selectionArgs);
}
break;
}
case HISTORY_ID:
debug("Update on HISTORY_ID: " + uri);
selection = DBUtils.concatenateWhere(selection, TABLE_HISTORY + "._id = ?");
@@ -1530,97 +1530,99 @@ public class BrowserProvider extends Sha
trace("Updating bookmark parents of " + selection + " (" + selectionArgs[0] + ")");
String where = Bookmarks._ID + " IN (" +
" SELECT DISTINCT " + Bookmarks.PARENT +
" FROM " + TABLE_BOOKMARKS +
" WHERE " + selection + " )";
return db.update(TABLE_BOOKMARKS, values, where, selectionArgs);
}
- private long insertBookmark(Uri uri, ContentValues values) {
+ private long insertBookmarkOrFolder(Uri uri, ContentValues values) {
// Generate values if not specified. Don't overwrite
// if specified by caller.
- long now = System.currentTimeMillis();
+ final long now = System.currentTimeMillis();
if (!values.containsKey(Bookmarks.DATE_CREATED)) {
values.put(Bookmarks.DATE_CREATED, now);
}
if (!values.containsKey(Bookmarks.DATE_MODIFIED)) {
values.put(Bookmarks.DATE_MODIFIED, now);
}
if (!values.containsKey(Bookmarks.GUID)) {
values.put(Bookmarks.GUID, Utils.generateGuid());
}
if (!values.containsKey(Bookmarks.POSITION)) {
- debug("Inserting bookmark with no position for URI");
+ debug("Inserting in bookmarks with no position");
values.put(Bookmarks.POSITION,
Long.toString(BrowserContract.Bookmarks.DEFAULT_POSITION));
}
if (!values.containsKey(Bookmarks.TITLE)) {
// Desktop Places barfs on insertion of a bookmark with no title,
// so we don't store them that way.
values.put(Bookmarks.TITLE, "");
}
- String url = values.getAsString(Bookmarks.URL);
+ if (values.containsKey(Bookmarks.URL)) {
+ debug("Inserting bookmark in database with URL: " + values.getAsString(Bookmarks.URL));
+ } else {
+ debug("Inserting bookmark folder in database with TITLE: " + values.getAsString(Bookmarks.TITLE));
+ }
- debug("Inserting bookmark in database with URL: " + url);
final SQLiteDatabase db = getWritableDatabase(uri);
beginWrite(db);
return db.insertOrThrow(TABLE_BOOKMARKS, Bookmarks.TITLE, values);
}
-
- private int updateOrInsertBookmark(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- int updated = updateBookmarks(uri, values, selection, selectionArgs);
+ private int updateOrInsertBookmarkOrFolder(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ final int updated = updateBookmarksOrFolder(uri, values, selection, selectionArgs);
if (updated > 0) {
return updated;
}
- // Transaction already begun by updateBookmarks.
- if (0 <= insertBookmark(uri, values)) {
+ // Transaction already begun by updateBookmarksOrFolder.
+ if (0 <= insertBookmarkOrFolder(uri, values)) {
// We 'updated' one row.
return 1;
}
// If something went wrong, then we updated zero rows.
return 0;
}
- private int updateBookmarks(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- trace("Updating bookmarks on URI: " + uri);
+ private int updateBookmarksOrFolder(Uri uri, ContentValues values, String selection,
+ String[] selectionArgs) {
+ trace("Updating bookmarks or folder on URI: " + uri);
final String[] bookmarksProjection = new String[] {
Bookmarks._ID, // 0
};
if (!values.containsKey(Bookmarks.DATE_MODIFIED)) {
values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
}
- trace("Querying bookmarks to update on URI: " + uri);
+ trace("Querying bookmarks or folder to update on URI: " + uri);
final SQLiteDatabase db = getWritableDatabase(uri);
// Compute matching IDs.
final Cursor cursor = db.query(TABLE_BOOKMARKS, bookmarksProjection,
selection, selectionArgs, null, null, null);
- // Now that we're done reading, open a transaction.
final String inClause;
try {
inClause = DBUtils.computeSQLInClauseFromLongs(cursor, Bookmarks._ID);
} finally {
cursor.close();
}
+ // Now that we're done reading, open a transaction.
beginWrite(db);
return db.update(TABLE_BOOKMARKS, values, inClause, null);
}
private long insertHistory(Uri uri, ContentValues values) {
final long now = System.currentTimeMillis();
values.put(History.DATE_CREATED, now);
values.put(History.DATE_MODIFIED, now);
@@ -2138,17 +2140,17 @@ public class BrowserProvider extends Sha
values.putNull(Bookmarks.KEYWORD);
values.putNull(Bookmarks.TAGS);
values.putNull(Bookmarks.FAVICON_ID);
// Doing this UPDATE (or the DELETE above) first ensures that the
// first operation within this transaction is a write.
// The cleanup call below will do a SELECT first, and thus would
// require the transaction to be upgraded from a reader to a writer.
- final int updated = updateBookmarks(uri, values, selection, selectionArgs);
+ final int updated = updateBookmarksOrFolder(uri, values, selection, selectionArgs);
try {
cleanUpSomeDeletedRecords(uri, TABLE_BOOKMARKS);
} catch (Exception e) {
// We don't care.
Log.e(LOGTAG, "Unable to clean up deleted bookmark records: ", e);
}
return updated;
}
--- a/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
+++ b/mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
@@ -978,24 +978,114 @@ public class LocalBrowserDB extends Brow
final Cursor[] arr = (Cursor[]) Array.newInstance(Cursor.class, cursorsToMerge.size());
return new MergeCursor(cursorsToMerge.toArray(arr));
} else {
return c;
}
}
@Override
+ public String getBookmarkFolderName(ContentResolver cr, long folderId) {
+ final Cursor c = cr.query(mBookmarksUriWithProfile,
+ new String[] { Bookmarks.TITLE },
+ Bookmarks._ID + " = ?",
+ new String[] { String.valueOf(folderId) },
+ null);
+ try {
+ final int col = c.getColumnIndexOrThrow(Bookmarks.TITLE);
+ if (!c.moveToFirst() || c.isNull(col)) {
+ return null;
+ }
+
+ return c.getString(col);
+ } finally {
+ c.close();
+ }
+ }
+
+ @Override
public int getBookmarkCountForFolder(ContentResolver cr, long folderID) {
if (folderID == Bookmarks.FAKE_READINGLIST_SMARTFOLDER_ID) {
return getUrlAnnotations().getAnnotationCount(cr, BrowserContract.UrlAnnotations.Key.READER_VIEW);
} else {
throw new IllegalArgumentException("Retrieving bookmark count for folder with ID=" + folderID + " not supported yet");
}
}
+ @Override
+ public Long getIDForFolder(ContentResolver cr, String folder, long parentId) {
+ final Cursor c = cr.query(bookmarksUriWithLimit(1),
+ new String[] { Bookmarks._ID },
+ Bookmarks.TITLE + " = ? AND " + Bookmarks.PARENT + " = ?", // TODO do i need to check IS_DELETED here?
+ new String[] { folder, String.valueOf(parentId) },
+ null);
+
+ if (c == null) {
+ return null;
+ }
+
+ try {
+ final int col = c.getColumnIndexOrThrow(Bookmarks._ID);
+ if (!c.moveToFirst()) {
+ return null;
+ }
+
+ return c.getLong(col);
+ } finally {
+ c.close();
+ }
+ }
+
+ private void addBookmarkFolderItem(ContentResolver cr, String folderName, long parentId) {
+ final long now = System.currentTimeMillis();
+
+ ContentValues values = new ContentValues();
+ values.put(Bookmarks.TITLE, folderName);
+ values.put(Bookmarks.TYPE, Bookmarks.TYPE_FOLDER);
+ values.put(Bookmarks.PARENT, parentId);
+ values.put(Bookmarks.DATE_MODIFIED, now);
+ // Restore a deleted record if possible.
+ values.put(Bookmarks.IS_DELETED, 0);
+
+ final Uri bookmarksWithInsert = mBookmarksUriWithProfile.buildUpon()
+ .appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true")
+ .build();
+
+ cr.update(bookmarksWithInsert,
+ values,
+ Bookmarks.TYPE + " = " + Bookmarks.TYPE_FOLDER + " AND " +
+ Bookmarks.PARENT + " = " + parentId + " AND " +
+ Bookmarks.TITLE + " = ?",
+ new String[] { folderName });
+
+ bumpFolderModifiedTime(cr, parentId, now);
+ }
+
+ private void bumpFolderModifiedTime(ContentResolver cr, long folderId, long time) {
+ debug("Bumping parent modified time for addition to: " + folderId);
+ final String where = Bookmarks._ID + " = ?";
+ final String[] args = new String[] { String.valueOf(folderId) };
+
+ ContentValues bumped = new ContentValues();
+ bumped.put(Bookmarks.DATE_MODIFIED, time);
+
+ final int updated = cr.update(mBookmarksUriWithProfile, bumped, where, args);
+ debug("Updated " + updated + " rows to new modified time.");
+ }
+
+ @Override
+ public void addBookmarkFolder(ContentResolver cr, String folder, long parentId) {
+ final Long folderId = getIDForFolder(cr, folder, parentId);
+ if (folderId != null) {
+ return;
+ }
+
+ addBookmarkFolderItem(cr, folder, parentId);
+ }
+
@CheckResult
private ArrayList<Cursor> getSpecialFoldersCursorList(final boolean addDesktopFolder,
final boolean addScreenshotsFolder, final boolean addReadingListFolder) {
if (addDesktopFolder || addScreenshotsFolder || addReadingListFolder) {
// Avoid calling this twice.
assertDefaultBookmarkColumnOrdering();
}
@@ -1171,26 +1261,17 @@ public class LocalBrowserDB extends Brow
.appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true")
.build();
cr.update(bookmarksWithInsert,
values,
Bookmarks.URL + " = ? AND " +
Bookmarks.PARENT + " = " + folderId,
new String[] { uri });
- // Bump parent modified time using its ID.
- debug("Bumping parent modified time for addition to: " + folderId);
- final String where = Bookmarks._ID + " = ?";
- final String[] args = new String[] { String.valueOf(folderId) };
-
- ContentValues bumped = new ContentValues();
- bumped.put(Bookmarks.DATE_MODIFIED, now);
-
- final int updated = cr.update(mBookmarksUriWithProfile, bumped, where, args);
- debug("Updated " + updated + " rows to new modified time.");
+ bumpFolderModifiedTime(cr, folderId, now);
}
@Override
@RobocopTarget
public boolean addBookmark(ContentResolver cr, String title, String uri) {
long folderId = getFolderIdFromGuid(cr, Bookmarks.MOBILE_FOLDER_GUID);
if (isBookmarkForUrlInFolder(cr, uri, folderId)) {
// Bookmark added already.
@@ -1250,29 +1331,44 @@ public class LocalBrowserDB extends Brow
@Override
public void registerBookmarkObserver(ContentResolver cr, ContentObserver observer) {
cr.registerContentObserver(mBookmarksUriWithProfile, false, observer);
}
@Override
@RobocopTarget
public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword) {
- ContentValues values = new ContentValues();
+ final ContentValues values = new ContentValues();
values.put(Bookmarks.TITLE, title);
values.put(Bookmarks.URL, uri);
values.put(Bookmarks.KEYWORD, keyword);
values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
cr.update(mBookmarksUriWithProfile,
values,
Bookmarks._ID + " = ?",
new String[] { String.valueOf(id) });
}
@Override
+ public void updateBookmark(ContentResolver cr, int id, String uri, String title, String keyword, long folderId) {
+ final ContentValues values = new ContentValues();
+ values.put(Bookmarks.TITLE, title);
+ values.put(Bookmarks.URL, uri);
+ values.put(Bookmarks.KEYWORD, keyword);
+ values.put(Bookmarks.PARENT, folderId);
+ values.put(Bookmarks.DATE_MODIFIED, System.currentTimeMillis());
+
+ cr.update(mBookmarksUriWithProfile,
+ values,
+ Bookmarks._ID + " = ?",
+ new String[] { String.valueOf(id) });
+ }
+
+ @Override
public boolean hasBookmarkWithGuid(ContentResolver cr, String guid) {
Cursor c = cr.query(bookmarksUriWithLimit(1),
new String[] { Bookmarks.GUID },
Bookmarks.GUID + " = ?",
new String[] { guid },
null);
try {
@@ -1794,16 +1890,17 @@ public class LocalBrowserDB extends Brow
@Override
@RobocopTarget
public Cursor getBookmarkForUrl(ContentResolver cr, String url) {
Cursor c = cr.query(bookmarksUriWithLimit(1),
new String[] { Bookmarks._ID,
Bookmarks.URL,
Bookmarks.TITLE,
+ Bookmarks.PARENT,
Bookmarks.KEYWORD },
Bookmarks.URL + " = ?",
new String[] { url },
null);
if (c != null && c.getCount() == 0) {
c.close();
c = null;