[WIP] Bug 1232439 (972193) - Add more database folder methods for bookmark edit dialogs. draft
authorTom Klein <twointofive@gmail.com>
Mon, 19 Dec 2016 20:21:37 -0600
changeset 453953 9f72271a7109c0be70f338df61b9bcbe2b2294da
parent 453952 28d57b8b4ec6f8972181294c8ad7738dbfbbca83
child 453954 2a7c251816cd42ae8eb877649fc9ac5532b2c6f3
push id39776
push userbmo:twointofive@gmail.com
push dateMon, 26 Dec 2016 21:15:51 +0000
bugs1232439, 972193
milestone53.0a1
[WIP] Bug 1232439 (972193) - Add more database folder methods for bookmark edit dialogs. MozReview-Commit-ID: 83QDxrMd6SP
mobile/android/base/java/org/mozilla/gecko/db/BrowserDB.java
mobile/android/base/java/org/mozilla/gecko/db/BrowserProvider.java
mobile/android/base/java/org/mozilla/gecko/db/LocalBrowserDB.java
--- 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;