root/chrome/android/java/src/org/chromium/chrome/browser/BookmarksBridge.java

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CalledByNative
  2. onBookmarksAvailable
  3. CalledByNative
  4. onBookmarksFolderHierarchyAvailable
  5. bookmarkNodeMoved
  6. bookmarkNodeAdded
  7. bookmarkNodeRemoved
  8. bookmarkNodeChanged
  9. bookmarkNodeChildrenReordered
  10. extensiveBookmarkChangesBeginning
  11. extensiveBookmarkChangesEnded
  12. bookmarkModelChanged
  13. destroy
  14. addObserver
  15. removeObserver
  16. getBookmarksForFolder
  17. getCurrentFolderHierarchy
  18. deleteBookmark
  19. moveBookmark
  20. isEditBookmarksEnabled
  21. bookmarkModelLoaded
  22. bookmarkModelDeleted
  23. bookmarkNodeMoved
  24. bookmarkNodeAdded
  25. bookmarkNodeRemoved
  26. bookmarkNodeChanged
  27. bookmarkNodeChildrenReordered
  28. extensiveBookmarkChangesBeginning
  29. extensiveBookmarkChangesEnded
  30. bookmarkModelChanged
  31. createBookmarkItem
  32. addToList
  33. createBookmarkId
  34. nativeGetBookmarksForFolder
  35. nativeGetCurrentFolderHierarchy
  36. nativeDeleteBookmark
  37. nativeMoveBookmark
  38. nativeInit
  39. nativeDestroy
  40. nativeIsEditBookmarksEnabled
  41. getBookmarkTypeFromChar
  42. isValidBookmarkTypeFromChar
  43. getBookmarkIdFromString
  44. CalledByNative
  45. getId
  46. CalledByNative
  47. getType
  48. getBookmarkTypeString
  49. toString
  50. equals
  51. hashCode
  52. getTitle
  53. getUrl
  54. getId
  55. isFolder
  56. getParentId
  57. isEditable
  58. callCallbackMethod

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.browser;

import android.text.TextUtils;
import android.util.Log;

import org.chromium.base.CalledByNative;
import org.chromium.base.ObserverList;
import org.chromium.chrome.browser.profiles.Profile;

import java.util.ArrayList;
import java.util.List;

/**
 * Provides the communication channel for Android to fetch and manipulate the
 * bookmark model stored in native.
 */
public class BookmarksBridge {

    // Should mirror constants in chrome/browser/android/bookmarks/bookmarks_bridge.cc
    public static final int BOOKMARK_TYPE_NORMAL = 0;
    public static final int BOOKMARK_TYPE_MANAGED = 1;
    public static final int BOOKMARK_TYPE_PARTNER = 2;

    public static final int INVALID_FOLDER_ID = -2;
    public static final int ROOT_FOLDER_ID = -1;

    private final Profile mProfile;
    private long mNativeBookmarksBridge;
    private boolean mIsNativeBookmarkModelLoaded;
    private final List<DelayedBookmarkCallback> mDelayedBookmarkCallbacks =
            new ArrayList<DelayedBookmarkCallback>();
    private final ObserverList<BookmarkModelObserver> mObservers =
            new ObserverList<BookmarkModelObserver>();

    /**
     * Interface for callback object for fetching bookmarks and folder hierarchy.
     */
    public interface BookmarksCallback {
        /**
         * Callback method for fetching bookmarks for a folder and the folder hierarchy.
         * @param folderId The folder id to which the bookmarks belong.
         * @param bookmarksList List holding the fetched bookmarks and details.
         */
        @CalledByNative("BookmarksCallback")
        void onBookmarksAvailable(BookmarkId folderId, List<BookmarkItem> bookmarksList);

        /**
         * Callback method for fetching the folder hierarchy.
         * @param folderId The folder id to which the bookmarks belong.
         * @param bookmarksList List holding the fetched folder details.
         */
        @CalledByNative("BookmarksCallback")
        void onBookmarksFolderHierarchyAvailable(BookmarkId folderId,
                List<BookmarkItem> bookmarksList);
    }

    /**
     * Interface that provides listeners to be notified of changes to the bookmark model.
     */
    public interface BookmarkModelObserver {
        /**
         * Invoked when a node has moved.
         * @param oldParent The parent before the move.
         * @param oldIndex The index of the node in the old parent.
         * @param newParent The parent after the move.
         * @param newIndex The index of the node in the new parent.
         */
        void bookmarkNodeMoved(
                BookmarkItem oldParent, int oldIndex, BookmarkItem newParent, int newIndex);

        /**
         * Invoked when a node has been added.
         * @param parent The parent of the node being added.
         * @param index The index of the added node.
         */
        void bookmarkNodeAdded(BookmarkItem parent, int index);

        /**
         * Invoked when a node has been removed, the item may still be starred though.
         * @param parent The parent of the node that was removed.
         * @param oldIndex The index of the removed node in the parent before it was removed.
         * @param node The node that was removed.
         */
        void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, BookmarkItem node);

        /**
         * Invoked when the title or url of a node changes.
         * @param node The node being changed.
         */
        void bookmarkNodeChanged(BookmarkItem node);

        /**
         * Invoked when the children (just direct children, not descendants) of a node have been
         * reordered in some way, such as sorted.
         * @param node The node whose children are being reordered.
         */
        void bookmarkNodeChildrenReordered(BookmarkItem node);

        /**
         * Invoked before an extensive set of model changes is about to begin.  This tells UI
         * intensive observers to wait until the updates finish to update themselves. These methods
         * should only be used for imports and sync. Observers should still respond to
         * BookmarkNodeRemoved immediately, to avoid holding onto stale node references.
         */
        void extensiveBookmarkChangesBeginning();

        /**
         * Invoked after an extensive set of model changes has ended.  This tells observers to
         * update themselves if they were waiting for the update to finish.
         */
        void extensiveBookmarkChangesEnded();

        /**
         *  Called when there are changes to the bookmark model that don't trigger any of the other
         *  callback methods. For example, this is called when managed or partner bookmarks change.
         */
        void bookmarkModelChanged();
    }

    /**
     * Handler to fetch the bookmarks, titles, urls and folder hierarchy.
     * @param profile Profile instance corresponding to the active profile.
     */
    public BookmarksBridge(Profile profile) {
        mProfile = profile;
        mNativeBookmarksBridge = nativeInit(profile);
    }

    /**
     * Destroys this instance so no further calls can be executed.
     */
    public void destroy() {
        if (mNativeBookmarksBridge != 0) {
            nativeDestroy(mNativeBookmarksBridge);
            mNativeBookmarksBridge = 0;
            mIsNativeBookmarkModelLoaded = false;
            mDelayedBookmarkCallbacks.clear();
        }
        mObservers.clear();
    }

    /**
     * Add an observer to bookmark model changes.
     * @param observer The observer to be added.
     */
    public void addObserver(BookmarkModelObserver observer) {
        mObservers.addObserver(observer);
    }

    /**
     * Remove an observer of bookmark model changes.
     * @param observer The observer to be removed.
     */
    public void removeObserver(BookmarkModelObserver observer) {
        mObservers.removeObserver(observer);
    }

    /**
     * Fetches the bookmarks of the current folder. Callback will be
     * synchronous if the bookmark model is already loaded and async if it is loaded in the
     * background.
     * @param folderId The current folder id.
     * @param callback Instance of a callback object.
     */
    public void getBookmarksForFolder(BookmarkId folderId, BookmarksCallback callback) {
        if (mIsNativeBookmarkModelLoaded) {
            nativeGetBookmarksForFolder(mNativeBookmarksBridge, folderId, callback,
                    new ArrayList<BookmarkItem>());
        } else {
            mDelayedBookmarkCallbacks.add(new DelayedBookmarkCallback(folderId, callback,
                    DelayedBookmarkCallback.GET_BOOKMARKS_FOR_FOLDER, this));
        }
    }

    /**
     * Fetches the folder hierarchy of the given folder. Callback will be
     * synchronous if the bookmark model is already loaded and async if it is loaded in the
     * background.
     * @param folderId The current folder id.
     * @param callback Instance of a callback object.
     */
    public void getCurrentFolderHierarchy(BookmarkId folderId, BookmarksCallback callback) {
        if (mIsNativeBookmarkModelLoaded) {
            nativeGetCurrentFolderHierarchy(mNativeBookmarksBridge, folderId, callback,
                    new ArrayList<BookmarkItem>());
        } else {
            mDelayedBookmarkCallbacks.add(new DelayedBookmarkCallback(folderId, callback,
                    DelayedBookmarkCallback.GET_CURRENT_FOLDER_HIERARCHY, this));
        }
    }

    /**
     * Deletes a specified bookmark node.
     * @param bookmarkId The ID of the bookmark to be deleted.
     */
    public void deleteBookmark(BookmarkId bookmarkId) {
        nativeDeleteBookmark(mNativeBookmarksBridge, bookmarkId);
    }

    /**
     * Move the bookmark to the new index within same folder or to a different folder.
     * @param bookmarkId The id of the bookmark that is being moved.
     * @param newParentId The parent folder id.
     * @param index The new index for the bookmark.
     */
    public void moveBookmark(BookmarkId bookmarkId, BookmarkId newParentId, int index) {
        nativeMoveBookmark(mNativeBookmarksBridge, bookmarkId, newParentId, index);
    }

    public static boolean isEditBookmarksEnabled() {
        return nativeIsEditBookmarksEnabled();
    }

    @CalledByNative
    private void bookmarkModelLoaded() {
        mIsNativeBookmarkModelLoaded = true;
        if (!mDelayedBookmarkCallbacks.isEmpty()) {
            for (int i = 0; i < mDelayedBookmarkCallbacks.size(); i++) {
                mDelayedBookmarkCallbacks.get(i).callCallbackMethod();
            }
            mDelayedBookmarkCallbacks.clear();
        }
    }

    @CalledByNative
    private void bookmarkModelDeleted() {
        destroy();
    }

    @CalledByNative
    private void bookmarkNodeMoved(
            BookmarkItem oldParent, int oldIndex, BookmarkItem newParent, int newIndex) {
        for (BookmarkModelObserver observer : mObservers) {
            observer.bookmarkNodeMoved(oldParent, oldIndex, newParent, newIndex);
        }
    }

    @CalledByNative
    private void bookmarkNodeAdded(BookmarkItem parent, int index) {
        for (BookmarkModelObserver observer : mObservers) {
            observer.bookmarkNodeAdded(parent, index);
        }
    }

    @CalledByNative
    private void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, BookmarkItem node) {
        for (BookmarkModelObserver observer : mObservers) {
            observer.bookmarkNodeRemoved(parent, oldIndex, node);
        }
    }

    @CalledByNative
    private void bookmarkNodeChanged(BookmarkItem node) {
        for (BookmarkModelObserver observer : mObservers) {
            observer.bookmarkNodeChanged(node);
        }
    }

    @CalledByNative
    private void bookmarkNodeChildrenReordered(BookmarkItem node) {
        for (BookmarkModelObserver observer : mObservers) {
            observer.bookmarkNodeChildrenReordered(node);
        }
    }

    @CalledByNative
    private void extensiveBookmarkChangesBeginning() {
        for (BookmarkModelObserver observer : mObservers) {
            observer.extensiveBookmarkChangesBeginning();
        }
    }

    @CalledByNative
    private void extensiveBookmarkChangesEnded() {
        for (BookmarkModelObserver observer : mObservers) {
            observer.extensiveBookmarkChangesEnded();
        }
    }

    @CalledByNative
    private void bookmarkModelChanged() {
        for (BookmarkModelObserver observer : mObservers) {
            observer.bookmarkModelChanged();
        }
    }

    @CalledByNative
    private static BookmarkItem createBookmarkItem(long id, int type, String title, String url,
            boolean isFolder, long parentId, int parentIdType, boolean isEditable) {
        return new BookmarkItem(new BookmarkId(id, type), title, url, isFolder,
                new BookmarkId(parentId, parentIdType), isEditable);
    }

    @CalledByNative
    private static void addToList(List<BookmarkItem> bookmarksList, BookmarkItem bookmark) {
        bookmarksList.add(bookmark);
    }

    @CalledByNative
    private static BookmarkId createBookmarkId(long id, int type) {
        return new BookmarkId(id, type);
    }

    private native void nativeGetBookmarksForFolder(long nativeBookmarksBridge,
            BookmarkId folderId, BookmarksCallback callback,
            List<BookmarkItem> bookmarksList);
    private native void nativeGetCurrentFolderHierarchy(long nativeBookmarksBridge,
            BookmarkId folderId, BookmarksCallback callback,
            List<BookmarkItem> bookmarksList);
    private native void nativeDeleteBookmark(long nativeBookmarksBridge, BookmarkId bookmarkId);
    private native void nativeMoveBookmark(long nativeBookmarksBridge, BookmarkId bookmarkId,
            BookmarkId newParentId, int index);
    private native long nativeInit(Profile profile);
    private native void nativeDestroy(long nativeBookmarksBridge);
    private static native boolean nativeIsEditBookmarksEnabled();

    /**
     * Simple object representing the bookmark id.
     */
    public static class BookmarkId {
        private static final String LOG_TAG = "BookmarkId";
        private static final char TYPE_MANAGED = 'm';
        private static final char TYPE_PARTNER = 'p';

        private final long mId;
        private final int mType;

        public BookmarkId(long id, int type) {
            mId = id;
            mType = type;
        }

        /**
         * @param c The char representing the type.
         * @return The Bookmark type from a char representing the type.
         */
        private static int getBookmarkTypeFromChar(char c) {
            switch (c) {
                case TYPE_MANAGED:
                    return BOOKMARK_TYPE_MANAGED;
                case TYPE_PARTNER:
                    return BOOKMARK_TYPE_PARTNER;
                default:
                    return BOOKMARK_TYPE_NORMAL;
            }
        }

        /**
         * @param c The char representing the bookmark type.
         * @return Whether the char representing the bookmark type is a valid type.
         */
        private static boolean isValidBookmarkTypeFromChar(char c) {
            return (c == TYPE_MANAGED || c == TYPE_PARTNER);
        }

        /**
         * @param s The bookmark id string (Eg: m1 for managed bookmark id 1).
         * @return the Bookmark id from the string which is a concatenation of bookmark type and
         *         the bookmark id.
         */
        public static BookmarkId getBookmarkIdFromString(String s) {
            long id = ROOT_FOLDER_ID;
            int type = BOOKMARK_TYPE_NORMAL;
            if (TextUtils.isEmpty(s)) return new BookmarkId(id, type);
            char folderTypeChar = s.charAt(0);
            if (isValidBookmarkTypeFromChar(folderTypeChar)) {
                type = getBookmarkTypeFromChar(folderTypeChar);
                s = s.substring(1);
            }
            try {
                id = Long.parseLong(s);
            } catch (NumberFormatException exception) {
                Log.e(LOG_TAG, "Error parsing url to extract the bookmark folder id.", exception);
            }
            return new BookmarkId(id, type);
        }

        /**
         * @return The id of the bookmark.
         */
        @CalledByNative("BookmarkId")
        public long getId() {
            return mId;
        }

        /**
         * @return The bookmark type.
         */
        @CalledByNative("BookmarkId")
        public int getType() {
            return mType;
        }

        private String getBookmarkTypeString() {
            switch (mType) {
                case BOOKMARK_TYPE_MANAGED:
                    return String.valueOf(TYPE_MANAGED);
                case BOOKMARK_TYPE_PARTNER:
                    return String.valueOf(TYPE_PARTNER);
                case BOOKMARK_TYPE_NORMAL:
                default:
                    return "";
            }
        }

        @Override
        public String toString() {
            return getBookmarkTypeString() + mId;
        }

        @Override
        public boolean equals(Object o) {
            if (!(o instanceof BookmarkId)) return false;
            BookmarkId item = (BookmarkId) o;
            return (item.mId == mId && item.mType == mType);
        }

        @Override
        public int hashCode() {
            return toString().hashCode();
        }
    }

    /**
     * Simple object representing the bookmark item.
     */
    public static class BookmarkItem {

        private final String mTitle;
        private final String mUrl;
        private final BookmarkId mId;
        private final boolean mIsFolder;
        private final BookmarkId mParentId;
        private final boolean mIsEditable;


        private BookmarkItem(BookmarkId id, String title, String url, boolean isFolder,
                BookmarkId parentId, boolean isEditable) {
            mId = id;
            mTitle = title;
            mUrl = url;
            mIsFolder = isFolder;
            mParentId = parentId;
            mIsEditable = isEditable;
        }

        /** @return Title of the bookmark item. */
        public String getTitle() {
            return mTitle;
        }

        /** @return Url of the bookmark item. */
        public String getUrl() {
            return mUrl;
        }

        /** @return Id of the bookmark item. */
        public BookmarkId getId() {
            return mId;
        }

        /** @return Whether item is a folder or a bookmark. */
        public boolean isFolder() {
            return mIsFolder;
        }

        /** @return Parent id of the bookmark item. */
        public BookmarkId getParentId() {
            return mParentId;
        }

        /** @return Whether this bookmark can be edited. */
        public boolean isEditable() {
            return mIsEditable;
        }
    }

    /**
     * Details about callbacks that need to be called once the bookmark model has loaded.
     */
    private static class DelayedBookmarkCallback {

        private static final int GET_BOOKMARKS_FOR_FOLDER = 0;
        private static final int GET_CURRENT_FOLDER_HIERARCHY = 1;

        private final BookmarksCallback mCallback;
        private final BookmarkId mFolderId;
        private final int mCallbackMethod;
        private final BookmarksBridge mHandler;

        private DelayedBookmarkCallback(BookmarkId folderId, BookmarksCallback callback,
                int method, BookmarksBridge handler) {
            mFolderId = folderId;
            mCallback = callback;
            mCallbackMethod = method;
            mHandler = handler;
        }

        /**
         * Invoke the callback method.
         */
        private void callCallbackMethod() {
            switch (mCallbackMethod) {
                case GET_BOOKMARKS_FOR_FOLDER:
                    mHandler.getBookmarksForFolder(mFolderId, mCallback);
                    break;
                case GET_CURRENT_FOLDER_HIERARCHY:
                    mHandler.getCurrentFolderHierarchy(mFolderId, mCallback);
                    break;
                default:
                    assert false;
                    break;
            }
        }
    }

}


/* [<][>][^][v][top][bottom][index][help] */