Commit 4015677e authored by Lijin Shen's avatar Lijin Shen Committed by Commit Bot

Fix clear selection of bookmarks after sync

Currently, the selected bookmarks are all cleared whenever there is a
change on another device. This behavior does hurt user experience and is
also different from that in iOS version. This CL only clears bookmarks
which are removed on other devices and keeps the rest of selected ones.

Bug: 999269
Change-Id: Ic86a8e6f76b0160118caf712966697c9b3af136c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1808014
Commit-Queue: Lijin Shen <lazzzis@google.com>
Reviewed-by: default avatarTheresa  <twellington@chromium.org>
Cr-Commit-Position: refs/heads/master@{#698968}
parent 716ed3af
...@@ -15,7 +15,6 @@ import org.chromium.base.metrics.RecordHistogram; ...@@ -15,7 +15,6 @@ import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction; import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.R; import org.chromium.chrome.R;
import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem; import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkItem;
import org.chromium.chrome.browser.bookmarks.BookmarkBridge.BookmarkModelObserver;
import org.chromium.chrome.browser.preferences.PrefServiceBridge; import org.chromium.chrome.browser.preferences.PrefServiceBridge;
import org.chromium.chrome.browser.tabmodel.TabLaunchType; import org.chromium.chrome.browser.tabmodel.TabLaunchType;
import org.chromium.chrome.browser.tabmodel.document.TabDelegate; import org.chromium.chrome.browser.tabmodel.document.TabDelegate;
...@@ -38,13 +37,6 @@ public class BookmarkActionBar extends SelectableListToolbar<BookmarkId> ...@@ -38,13 +37,6 @@ public class BookmarkActionBar extends SelectableListToolbar<BookmarkId>
private BookmarkItem mCurrentFolder; private BookmarkItem mCurrentFolder;
private BookmarkDelegate mDelegate; private BookmarkDelegate mDelegate;
private BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() {
@Override
public void bookmarkModelChanged() {
onSelectionStateChange(mDelegate.getSelectionDelegate().getSelectedItemsAsList());
}
};
public BookmarkActionBar(Context context, AttributeSet attrs) { public BookmarkActionBar(Context context, AttributeSet attrs) {
super(context, attrs); super(context, attrs);
setNavigationOnClickListener(this); setNavigationOnClickListener(this);
...@@ -163,7 +155,6 @@ public class BookmarkActionBar extends SelectableListToolbar<BookmarkId> ...@@ -163,7 +155,6 @@ public class BookmarkActionBar extends SelectableListToolbar<BookmarkId>
mDelegate = delegate; mDelegate = delegate;
mDelegate.addUIObserver(this); mDelegate.addUIObserver(this);
if (!delegate.isDialogUi()) getMenu().removeItem(R.id.close_menu_id); if (!delegate.isDialogUi()) getMenu().removeItem(R.id.close_menu_id);
delegate.getModel().addObserver(mBookmarkModelObserver);
getMenu().setGroupEnabled(R.id.selection_mode_menu_group, true); getMenu().setGroupEnabled(R.id.selection_mode_menu_group, true);
} }
...@@ -175,7 +166,6 @@ public class BookmarkActionBar extends SelectableListToolbar<BookmarkId> ...@@ -175,7 +166,6 @@ public class BookmarkActionBar extends SelectableListToolbar<BookmarkId>
if (mDelegate == null) return; if (mDelegate == null) return;
mDelegate.removeUIObserver(this); mDelegate.removeUIObserver(this);
mDelegate.getModel().removeObserver(mBookmarkModelObserver);
} }
@Override @Override
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package org.chromium.chrome.browser.bookmarks; package org.chromium.chrome.browser.bookmarks;
import android.os.SystemClock; import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Pair; import android.util.Pair;
...@@ -361,6 +362,7 @@ public class BookmarkBridge { ...@@ -361,6 +362,7 @@ public class BookmarkBridge {
* @return A BookmarkItem instance for the given BookmarkId. * @return A BookmarkItem instance for the given BookmarkId.
* <code>null</code> if it doesn't exist. * <code>null</code> if it doesn't exist.
*/ */
@Nullable
public BookmarkItem getBookmarkById(BookmarkId id) { public BookmarkItem getBookmarkById(BookmarkId id) {
assert mIsNativeBookmarkModelLoaded; assert mIsNativeBookmarkModelLoaded;
return BookmarkBridgeJni.get().getBookmarkByID( return BookmarkBridgeJni.get().getBookmarkByID(
...@@ -754,7 +756,8 @@ public class BookmarkBridge { ...@@ -754,7 +756,8 @@ public class BookmarkBridge {
/** /**
* Notifies the observer that bookmark model has been loaded. * Notifies the observer that bookmark model has been loaded.
*/ */
protected void notifyBookmarkModelLoaded() { @VisibleForTesting
public void notifyBookmarkModelLoaded() {
// Call isBookmarkModelLoaded() to do the check since it could be overridden by the child // Call isBookmarkModelLoaded() to do the check since it could be overridden by the child
// class to add the addition logic. // class to add the addition logic.
if (isBookmarkModelLoaded()) { if (isBookmarkModelLoaded()) {
......
...@@ -146,7 +146,8 @@ class BookmarkItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> ...@@ -146,7 +146,8 @@ class BookmarkItemsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
/** /**
* @return The position of the given bookmark in adapter. Will return -1 if not found. * @return The position of the given bookmark in adapter. Will return -1 if not found.
*/ */
private int getPositionForBookmark(BookmarkId bookmark) { @Override
public int getPositionForBookmark(BookmarkId bookmark) {
assert bookmark != null; assert bookmark != null;
int position = -1; int position = -1;
for (int i = 0; i < getItemCount(); i++) { for (int i = 0; i < getItemCount(); i++) {
......
...@@ -8,6 +8,7 @@ import android.app.Activity; ...@@ -8,6 +8,7 @@ import android.app.Activity;
import android.app.ActivityManager; import android.app.ActivityManager;
import android.content.Context; import android.content.Context;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.AdapterDataObserver;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
...@@ -76,6 +77,7 @@ public class BookmarkManager ...@@ -76,6 +77,7 @@ public class BookmarkManager
// TODO(crbug.com/160194): Clean up after bookmark reordering launches. // TODO(crbug.com/160194): Clean up after bookmark reordering launches.
private ItemsAdapter mAdapter; private ItemsAdapter mAdapter;
private BookmarkDragStateDelegate mDragStateDelegate; private BookmarkDragStateDelegate mDragStateDelegate;
private AdapterDataObserver mAdapterDataObserver;
/** /**
* An adapter responsible for managing bookmark items. * An adapter responsible for managing bookmark items.
...@@ -85,11 +87,14 @@ public class BookmarkManager ...@@ -85,11 +87,14 @@ public class BookmarkManager
void notifyDataSetChanged(); void notifyDataSetChanged();
void onBookmarkDelegateInitialized(BookmarkDelegate bookmarkDelegate); void onBookmarkDelegateInitialized(BookmarkDelegate bookmarkDelegate);
void search(String query); void search(String query);
void registerAdapterDataObserver(AdapterDataObserver observer);
void unregisterAdapterDataObserver(AdapterDataObserver observer);
void moveUpOne(BookmarkId bookmarkId); void moveUpOne(BookmarkId bookmarkId);
void moveDownOne(BookmarkId bookmarkId); void moveDownOne(BookmarkId bookmarkId);
void highlightBookmark(BookmarkId bookmarkId); void highlightBookmark(BookmarkId bookmarkId);
int getPositionForBookmark(BookmarkId bookmarkId);
} }
private final BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() { private final BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObserver() {
...@@ -112,7 +117,6 @@ public class BookmarkManager ...@@ -112,7 +117,6 @@ public class BookmarkManager
openFolder(parent.getId()); openFolder(parent.getId());
} }
} }
mSelectionDelegate.clearSelection();
// This is necessary as long as we rely on RecyclerView.ItemDecorations to apply padding // This is necessary as long as we rely on RecyclerView.ItemDecorations to apply padding
// at the bottom of the bookmarks list to avoid the bottom navigation menu. This ensures // at the bottom of the bookmarks list to avoid the bottom navigation menu. This ensures
...@@ -121,12 +125,6 @@ public class BookmarkManager ...@@ -121,12 +125,6 @@ public class BookmarkManager
mAdapter.notifyDataSetChanged(); mAdapter.notifyDataSetChanged();
} }
@Override
public void bookmarkNodeMoved(BookmarkItem oldParent, int oldIndex, BookmarkItem newParent,
int newIndex) {
mSelectionDelegate.clearSelection();
}
@Override @Override
public void bookmarkModelChanged() { public void bookmarkModelChanged() {
// If the folder no longer exists in folder mode, we need to fall back. Relying on the // If the folder no longer exists in folder mode, we need to fall back. Relying on the
...@@ -134,7 +132,6 @@ public class BookmarkManager ...@@ -134,7 +132,6 @@ public class BookmarkManager
if (getCurrentState() == BookmarkUIState.STATE_FOLDER) { if (getCurrentState() == BookmarkUIState.STATE_FOLDER) {
setState(mStateStack.peek()); setState(mStateStack.peek());
} }
mSelectionDelegate.clearSelection();
} }
}; };
...@@ -203,7 +200,10 @@ public class BookmarkManager ...@@ -203,7 +200,10 @@ public class BookmarkManager
mSelectionDelegate = new SelectionDelegate<BookmarkId>() { mSelectionDelegate = new SelectionDelegate<BookmarkId>() {
@Override @Override
public boolean toggleSelectionForItem(BookmarkId bookmark) { public boolean toggleSelectionForItem(BookmarkId bookmark) {
if (!mBookmarkModel.getBookmarkById(bookmark).isEditable()) return false; if (mBookmarkModel.getBookmarkById(bookmark) != null
&& !mBookmarkModel.getBookmarkById(bookmark).isEditable()) {
return false;
}
return super.toggleSelectionForItem(bookmark); return super.toggleSelectionForItem(bookmark);
} }
}; };
...@@ -227,6 +227,18 @@ public class BookmarkManager ...@@ -227,6 +227,18 @@ public class BookmarkManager
} else { } else {
mAdapter = new BookmarkItemsAdapter(activity); mAdapter = new BookmarkItemsAdapter(activity);
} }
mAdapterDataObserver = new AdapterDataObserver() {
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
syncAdapterAndSelectionDelegate();
}
@Override
public void onChanged() {
syncAdapterAndSelectionDelegate();
}
};
mAdapter.registerAdapterDataObserver(mAdapterDataObserver);
mRecyclerView = mSelectableListLayout.initializeRecyclerView( mRecyclerView = mSelectableListLayout.initializeRecyclerView(
(RecyclerView.Adapter<RecyclerView.ViewHolder>) mAdapter); (RecyclerView.Adapter<RecyclerView.ViewHolder>) mAdapter);
...@@ -296,6 +308,7 @@ public class BookmarkManager ...@@ -296,6 +308,7 @@ public class BookmarkManager
* Destroys and cleans up itself. This must be called after done using this class. * Destroys and cleans up itself. This must be called after done using this class.
*/ */
public void onDestroyed() { public void onDestroyed() {
mAdapter.unregisterAdapterDataObserver(mAdapterDataObserver);
mIsDestroyed = true; mIsDestroyed = true;
RecordUserAction.record("MobileBookmarkManagerClose"); RecordUserAction.record("MobileBookmarkManagerClose");
mSelectableListLayout.onDestroyed(); mSelectableListLayout.onDestroyed();
...@@ -446,13 +459,25 @@ public class BookmarkManager ...@@ -446,13 +459,25 @@ public class BookmarkManager
} }
} }
mSelectionDelegate.clearSelection();
for (BookmarkUIObserver observer : mUIObservers) { for (BookmarkUIObserver observer : mUIObservers) {
notifyStateChange(observer); notifyStateChange(observer);
} }
} }
// TODO(lazzzis): This method can be moved to adapter after bookmark reordering launches.
/**
* Some bookmarks may be moved to another folder or removed in another devices. However, it may
* still be stored by {@link #mSelectionDelegate}, which causes incorrect selection counting.
*/
private void syncAdapterAndSelectionDelegate() {
for (BookmarkId node : mSelectionDelegate.getSelectedItemsAsList()) {
if (mSelectionDelegate.isItemSelected(node)
&& mAdapter.getPositionForBookmark(node) == -1) {
mSelectionDelegate.toggleSelectionForItem(node);
}
}
}
@Override @Override
public void moveDownOne(BookmarkId bookmarkId) { public void moveDownOne(BookmarkId bookmarkId) {
mAdapter.moveDownOne(bookmarkId); mAdapter.moveDownOne(bookmarkId);
......
...@@ -265,7 +265,8 @@ abstract class BookmarkRow extends SelectableItemView<BookmarkId> ...@@ -265,7 +265,8 @@ abstract class BookmarkRow extends SelectableItemView<BookmarkId>
@Override @Override
public void onSearchStateSet() {} public void onSearchStateSet() {}
boolean isItemSelected() { @VisibleForTesting
public boolean isItemSelected() {
return mDelegate.getSelectionDelegate().isItemSelected(mBookmarkId); return mDelegate.getSelectionDelegate().isItemSelected(mBookmarkId);
} }
......
...@@ -127,7 +127,8 @@ class ReorderBookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkIte ...@@ -127,7 +127,8 @@ class ReorderBookmarkItemsAdapter extends DragReorderableListAdapter<BookmarkIte
/** /**
* @return The position of the given bookmark in adapter. Will return -1 if not found. * @return The position of the given bookmark in adapter. Will return -1 if not found.
*/ */
private int getPositionForBookmark(BookmarkId bookmark) { @Override
public int getPositionForBookmark(BookmarkId bookmark) {
assert bookmark != null; assert bookmark != null;
int position = -1; int position = -1;
for (int i = 0; i < getItemCount(); i++) { for (int i = 0; i < getItemCount(); i++) {
......
...@@ -32,6 +32,7 @@ import android.widget.TextView; ...@@ -32,6 +32,7 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener; import android.widget.TextView.OnEditorActionListener;
import androidx.annotation.CallSuper; import androidx.annotation.CallSuper;
import androidx.annotation.IntDef;
import androidx.annotation.StringRes; import androidx.annotation.StringRes;
import org.chromium.base.ApiCompatibilityUtils; import org.chromium.base.ApiCompatibilityUtils;
...@@ -51,6 +52,8 @@ import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionO ...@@ -51,6 +52,8 @@ import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionO
import org.chromium.ui.KeyboardVisibilityDelegate; import org.chromium.ui.KeyboardVisibilityDelegate;
import org.chromium.ui.UiUtils; import org.chromium.ui.UiUtils;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List; import java.util.List;
/** /**
...@@ -79,6 +82,14 @@ public class SelectableListToolbar<E> ...@@ -79,6 +82,14 @@ public class SelectableListToolbar<E>
void onEndSearch(); void onEndSearch();
} }
@IntDef({ViewType.NORMAL_VIEW, ViewType.SELECTION_VIEW, ViewType.SEARCH_VIEW})
@Retention(RetentionPolicy.SOURCE)
public @interface ViewType {
int NORMAL_VIEW = 0;
int SELECTION_VIEW = 1;
int SEARCH_VIEW = 2;
}
/** No navigation button is displayed. **/ /** No navigation button is displayed. **/
public static final int NAVIGATION_BUTTON_NONE = 0; public static final int NAVIGATION_BUTTON_NONE = 0;
/** Button to navigate back. This calls {@link #onNavigationBack()}. **/ /** Button to navigate back. This calls {@link #onNavigationBack()}. **/
...@@ -133,6 +144,9 @@ public class SelectableListToolbar<E> ...@@ -133,6 +144,9 @@ public class SelectableListToolbar<E>
private int mHideInfoStringId; private int mHideInfoStringId;
private int mExtraMenuItemId; private int mExtraMenuItemId;
// current view type that SelectableListToolbar is showing
private int mViewType;
/** /**
* Constructor for inflating from XML. * Constructor for inflating from XML.
*/ */
...@@ -527,6 +541,10 @@ public class SelectableListToolbar<E> ...@@ -527,6 +541,10 @@ public class SelectableListToolbar<E>
} }
protected void showNormalView() { protected void showNormalView() {
// hide overflow menu explicitly: crbug.com/999269
hideOverflowMenu();
mViewType = ViewType.NORMAL_VIEW;
getMenu().setGroupVisible(mNormalGroupResId, true); getMenu().setGroupVisible(mNormalGroupResId, true);
getMenu().setGroupVisible(mSelectedGroupResId, false); getMenu().setGroupVisible(mSelectedGroupResId, false);
if (mHasSearchView) { if (mHasSearchView) {
...@@ -546,6 +564,8 @@ public class SelectableListToolbar<E> ...@@ -546,6 +564,8 @@ public class SelectableListToolbar<E>
} }
protected void showSelectionView(List<E> selectedItems, boolean wasSelectionEnabled) { protected void showSelectionView(List<E> selectedItems, boolean wasSelectionEnabled) {
mViewType = ViewType.SELECTION_VIEW;
getMenu().setGroupVisible(mNormalGroupResId, false); getMenu().setGroupVisible(mNormalGroupResId, false);
getMenu().setGroupVisible(mSelectedGroupResId, true); getMenu().setGroupVisible(mSelectedGroupResId, true);
getMenu().setGroupEnabled(mSelectedGroupResId, !selectedItems.isEmpty()); getMenu().setGroupEnabled(mSelectedGroupResId, !selectedItems.isEmpty());
...@@ -563,6 +583,8 @@ public class SelectableListToolbar<E> ...@@ -563,6 +583,8 @@ public class SelectableListToolbar<E>
} }
private void showSearchViewInternal() { private void showSearchViewInternal() {
mViewType = ViewType.SEARCH_VIEW;
getMenu().setGroupVisible(mNormalGroupResId, false); getMenu().setGroupVisible(mNormalGroupResId, false);
getMenu().setGroupVisible(mSelectedGroupResId, false); getMenu().setGroupVisible(mSelectedGroupResId, false);
mNumberRollView.setVisibility(View.GONE); mNumberRollView.setVisibility(View.GONE);
...@@ -757,4 +779,9 @@ public class SelectableListToolbar<E> ...@@ -757,4 +779,9 @@ public class SelectableListToolbar<E>
child.setFocusableInTouchMode(true); child.setFocusableInTouchMode(true);
} }
} }
@VisibleForTesting
public @ViewType int getCurrentViewType() {
return mViewType;
}
} }
...@@ -161,7 +161,7 @@ public class BookmarkTest { ...@@ -161,7 +161,7 @@ public class BookmarkTest {
} }
} }
private boolean isItemPresentInBookmarkList(final String expectedTitle) { protected boolean isItemPresentInBookmarkList(final String expectedTitle) {
return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() { return TestThreadUtils.runOnUiThreadBlockingNoException(new Callable<Boolean>() {
@Override @Override
public Boolean call() throws Exception { public Boolean call() throws Exception {
...@@ -531,7 +531,7 @@ public class BookmarkTest { ...@@ -531,7 +531,7 @@ public class BookmarkTest {
() -> mBookmarkModel.addFolder(mBookmarkModel.getDefaultFolder(), 0, title)); () -> mBookmarkModel.addFolder(mBookmarkModel.getDefaultFolder(), 0, title));
} }
private void removeBookmark(final BookmarkId bookmarkId) { protected void removeBookmark(final BookmarkId bookmarkId) {
TestThreadUtils.runOnUiThreadBlocking(() -> mBookmarkModel.deleteBookmark(bookmarkId)); TestThreadUtils.runOnUiThreadBlocking(() -> mBookmarkModel.deleteBookmark(bookmarkId));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment