OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package org.chromium.chrome.browser.enhancedbookmarks; |
| 6 |
| 7 import android.content.Context; |
| 8 import android.support.v7.widget.RecyclerView; |
| 9 import android.support.v7.widget.RecyclerView.ViewHolder; |
| 10 import android.view.LayoutInflater; |
| 11 import android.view.View; |
| 12 import android.view.View.OnClickListener; |
| 13 import android.view.View.OnLongClickListener; |
| 14 import android.view.ViewGroup; |
| 15 import android.widget.Checkable; |
| 16 |
| 17 import com.google.android.apps.chrome.R; |
| 18 |
| 19 import org.chromium.base.annotations.SuppressFBWarnings; |
| 20 import org.chromium.chrome.browser.BookmarksBridge.BookmarkItem; |
| 21 import org.chromium.chrome.browser.BookmarksBridge.BookmarkModelObserver; |
| 22 import org.chromium.chrome.browser.enhanced_bookmarks.EnhancedBookmarksBridge.Fi
ltersObserver; |
| 23 import org.chromium.chrome.browser.enhancedbookmarks.EnhancedBookmarkPromoHeader
.PromoHeaderShowingChangeListener; |
| 24 import org.chromium.chrome.browser.util.MathUtils; |
| 25 import org.chromium.components.bookmarks.BookmarkId; |
| 26 |
| 27 import java.util.ArrayList; |
| 28 import java.util.List; |
| 29 |
| 30 /** |
| 31 * BaseAdapter for EnhancedBookmarkItemsContainer. It manages bookmarks to list
there. |
| 32 */ |
| 33 class EnhancedBookmarkItemsAdapter extends RecyclerView.Adapter<RecyclerView.Vie
wHolder> implements |
| 34 EnhancedBookmarkUIObserver, PromoHeaderShowingChangeListener { |
| 35 |
| 36 /** |
| 37 * An abstraction for the common functionalities for {@link EnhancedBookmark
Folder} and |
| 38 * {@link EnhancedBookmarkItem} |
| 39 */ |
| 40 public interface BookmarkGrid extends OnClickListener, Checkable { |
| 41 /** |
| 42 * Sets the bookmarkId the object is holding. Corresponding UI changes m
ight occur. |
| 43 */ |
| 44 public void setBookmarkId(BookmarkId id); |
| 45 |
| 46 /** |
| 47 * @return The bookmark that the object is holding. |
| 48 */ |
| 49 public BookmarkId getBookmarkId(); |
| 50 } |
| 51 |
| 52 public static final int PROMO_HEADER_GRID = 0; |
| 53 public static final int FOLDER_VIEW_GRID = 1; |
| 54 public static final int PADDING_VIEW = 2; |
| 55 public static final int BOOKMARK_VIEW_GRID = 3; |
| 56 |
| 57 // Have different view types for list and grid modes, in order to force the
recycler not reuse |
| 58 // the same tiles across the two modes. |
| 59 public static final int PROMO_HEADER_LIST = 4; |
| 60 public static final int FOLDER_VIEW_LIST = 5; |
| 61 public static final int DIVIDER_LIST = 6; |
| 62 public static final int BOOKMARK_VIEW_LIST = 7; |
| 63 |
| 64 private EnhancedBookmarkDelegate mDelegate; |
| 65 private Context mContext; |
| 66 private EnhancedBookmarkPromoHeader mPromoHeaderManager; |
| 67 private ItemFactory mItemFactory; |
| 68 private List<BookmarkId> mFolders = new ArrayList<BookmarkId>(); |
| 69 private List<BookmarkId> mBookmarks = new ArrayList<BookmarkId>(); |
| 70 |
| 71 // These are used to track placeholder invisible view between folder items a
nd bookmark items, |
| 72 // to align the beginning of the bookmark items to be the first column. |
| 73 // In list mode, this variable represents the presence of the divider, eithe
r 1 or 0. |
| 74 private int mEmptyViewCount; |
| 75 private int mColumnCount = 1; |
| 76 |
| 77 private BookmarkModelObserver mBookmarkModelObserver = new BookmarkModelObse
rver() { |
| 78 @Override |
| 79 public void bookmarkNodeChanged(BookmarkItem node) { |
| 80 int position = getPositionForBookmark(node.getId()); |
| 81 if (position >= 0) notifyItemChanged(position); |
| 82 } |
| 83 |
| 84 @Override |
| 85 public void bookmarkNodeRemoved(BookmarkItem parent, int oldIndex, Bookm
arkItem node, |
| 86 boolean isDoingExtensiveChanges) { |
| 87 if (node.isFolder()) { |
| 88 mDelegate.notifyStateChange(EnhancedBookmarkItemsAdapter.this); |
| 89 } else { |
| 90 int deletedPosition = getPositionForBookmark(node.getId()); |
| 91 if (deletedPosition >= 0) { |
| 92 removeItem(deletedPosition); |
| 93 } |
| 94 } |
| 95 } |
| 96 |
| 97 @Override |
| 98 public void bookmarkModelChanged() { |
| 99 mDelegate.notifyStateChange(EnhancedBookmarkItemsAdapter.this); |
| 100 } |
| 101 }; |
| 102 |
| 103 private FiltersObserver mFiltersObserver = new FiltersObserver() { |
| 104 @Override |
| 105 public void onFiltersChanged() { |
| 106 if (mDelegate.getCurrentState() == EnhancedBookmarkDelegate.STATE_FI
LTER) { |
| 107 mDelegate.notifyStateChange(EnhancedBookmarkItemsAdapter.this); |
| 108 } |
| 109 } |
| 110 }; |
| 111 |
| 112 EnhancedBookmarkItemsAdapter(Context context) { |
| 113 mContext = context; |
| 114 } |
| 115 |
| 116 /** |
| 117 * @return Whether the first item is header. |
| 118 */ |
| 119 boolean isHeader(int position) { |
| 120 int type = getItemViewType(position); |
| 121 return type == PROMO_HEADER_GRID || type == PROMO_HEADER_LIST; |
| 122 } |
| 123 |
| 124 /** |
| 125 * Set folders and bookmarks to show. Will trigger animation provided by ite
m animator. |
| 126 * @param folders This can be null if there is no folders to show. |
| 127 */ |
| 128 void setBookmarks(List<BookmarkId> folders, List<BookmarkId> bookmarks) { |
| 129 if (folders == null) folders = new ArrayList<BookmarkId>(); |
| 130 notifyItemRangeRemoved(0, getItemCount()); |
| 131 mFolders = folders; |
| 132 mBookmarks = bookmarks; |
| 133 updateEmptyViewCount(); |
| 134 notifyItemRangeInserted(0, getItemCount()); |
| 135 } |
| 136 |
| 137 /** |
| 138 * Set number of columns. This information will be used to align the startin
g position of |
| 139 * bookmarks to be the beginning of the row. |
| 140 * |
| 141 * This is a hacky responsibility violation, since adapter shouldn't care ab
out the way items |
| 142 * are presented in the UI. However, there is no other easy way to do this c
orrectly with |
| 143 * GridView. |
| 144 */ |
| 145 void setNumColumns(int columnNumber) { |
| 146 if (mColumnCount == columnNumber) return; |
| 147 mColumnCount = columnNumber; |
| 148 updateDataset(); |
| 149 } |
| 150 |
| 151 /** |
| 152 * If called, recycler view will be brutally reset and no animation will be
shown. |
| 153 */ |
| 154 void updateDataset() { |
| 155 updateEmptyViewCount(); |
| 156 notifyDataSetChanged(); |
| 157 } |
| 158 |
| 159 private void updateEmptyViewCount() { |
| 160 if (mDelegate.isListModeEnabled()) mEmptyViewCount = mFolders.size() > 0
? 1 : 0; |
| 161 else mEmptyViewCount = MathUtils.positiveModulo(-mFolders.size(), mColum
nCount); |
| 162 } |
| 163 |
| 164 private int getHeaderItemsCount() { |
| 165 // In listview a promo header carries a divider below it. |
| 166 if (mPromoHeaderManager.isShowing()) { |
| 167 return mDelegate.isListModeEnabled() ? 2 : 1; |
| 168 } |
| 169 return 0; |
| 170 } |
| 171 |
| 172 @Override |
| 173 public int getItemCount() { |
| 174 return getHeaderItemsCount() + mFolders.size() + mEmptyViewCount + mBook
marks.size(); |
| 175 } |
| 176 |
| 177 /** |
| 178 * @return The position of the given bookmark in adapter. Will return -1 if
not found. |
| 179 */ |
| 180 int getPositionForBookmark(BookmarkId bookmark) { |
| 181 assert bookmark != null; |
| 182 int position = -1; |
| 183 for (int i = 0; i < getItemCount(); i++) { |
| 184 if (bookmark.equals(getItem(i))) { |
| 185 position = i; |
| 186 break; |
| 187 } |
| 188 } |
| 189 return position; |
| 190 } |
| 191 |
| 192 BookmarkId getItem(int position) { |
| 193 switch(getItemViewType(position)) { |
| 194 case PROMO_HEADER_GRID: |
| 195 case PROMO_HEADER_LIST: |
| 196 return null; |
| 197 case FOLDER_VIEW_GRID: |
| 198 case FOLDER_VIEW_LIST: |
| 199 return mFolders.get(position - getHeaderItemsCount()); |
| 200 case PADDING_VIEW: |
| 201 case DIVIDER_LIST: |
| 202 return null; |
| 203 case BOOKMARK_VIEW_GRID: |
| 204 case BOOKMARK_VIEW_LIST: |
| 205 return mBookmarks.get( |
| 206 position - getHeaderItemsCount() - mFolders.size() - mEm
ptyViewCount); |
| 207 default: |
| 208 assert false; |
| 209 return null; |
| 210 } |
| 211 } |
| 212 |
| 213 void removeItem(int position) { |
| 214 switch(getItemViewType(position)) { |
| 215 case PROMO_HEADER_GRID: |
| 216 case PROMO_HEADER_LIST: |
| 217 assert false : "Promo header remove should be handled in PromoHe
aderManager."; |
| 218 break; |
| 219 case FOLDER_VIEW_GRID: |
| 220 case FOLDER_VIEW_LIST: |
| 221 mFolders.remove(position - getHeaderItemsCount()); |
| 222 notifyItemRemoved(position); |
| 223 break; |
| 224 case PADDING_VIEW: |
| 225 case DIVIDER_LIST: |
| 226 assert false : "Cannot remove a padding view or a divider"; |
| 227 break; |
| 228 case BOOKMARK_VIEW_GRID: |
| 229 case BOOKMARK_VIEW_LIST: |
| 230 mBookmarks.remove( |
| 231 position |
| 232 - getHeaderItemsCount() |
| 233 - mFolders.size() |
| 234 - mEmptyViewCount); |
| 235 notifyItemRemoved(position); |
| 236 break; |
| 237 default: |
| 238 assert false; |
| 239 } |
| 240 } |
| 241 |
| 242 @Override |
| 243 public int getItemViewType(int position) { |
| 244 int i = position; |
| 245 |
| 246 if (mPromoHeaderManager.isShowing()) { |
| 247 if (mDelegate.isListModeEnabled()) { |
| 248 if (i == 0) return PROMO_HEADER_LIST; |
| 249 if (i == 1) return DIVIDER_LIST; |
| 250 } else { |
| 251 if (i == 0) return PROMO_HEADER_GRID; |
| 252 } |
| 253 } |
| 254 i -= getHeaderItemsCount(); |
| 255 |
| 256 if (i < mFolders.size()) { |
| 257 return mDelegate.isListModeEnabled() ? FOLDER_VIEW_LIST : FOLDER_VIE
W_GRID; |
| 258 } |
| 259 i -= mFolders.size(); |
| 260 |
| 261 if (i < mEmptyViewCount) { |
| 262 if (mDelegate.isListModeEnabled()) return DIVIDER_LIST; |
| 263 else return PADDING_VIEW; |
| 264 } |
| 265 i -= mEmptyViewCount; |
| 266 |
| 267 if (i < mBookmarks.size()) { |
| 268 return mDelegate.isListModeEnabled() ? BOOKMARK_VIEW_LIST : BOOKMARK
_VIEW_GRID; |
| 269 } |
| 270 |
| 271 assert false : "Invalid position requested"; |
| 272 return -1; |
| 273 } |
| 274 |
| 275 @Override |
| 276 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { |
| 277 switch (viewType) { |
| 278 case PROMO_HEADER_GRID: |
| 279 case PROMO_HEADER_LIST: |
| 280 return mPromoHeaderManager.createHolder(parent, mDelegate.isList
ModeEnabled()); |
| 281 case FOLDER_VIEW_GRID: |
| 282 case FOLDER_VIEW_LIST: |
| 283 EnhancedBookmarkFolder folder = mItemFactory.createBookmarkFolde
r(parent); |
| 284 folder.setDelegate(mDelegate); |
| 285 return new ItemViewHolder(folder, mDelegate); |
| 286 case PADDING_VIEW: |
| 287 // Padding views are empty place holders that will be set invisi
ble. |
| 288 return new ViewHolder(new View(parent.getContext())) {}; |
| 289 case DIVIDER_LIST: |
| 290 return new ViewHolder(LayoutInflater.from(parent.getContext()).i
nflate( |
| 291 R.layout.eb_list_divider, parent, false)) {}; |
| 292 case BOOKMARK_VIEW_GRID: |
| 293 case BOOKMARK_VIEW_LIST: |
| 294 EnhancedBookmarkItem item = mItemFactory.createBookmarkItem(pare
nt); |
| 295 item.onEnhancedBookmarkDelegateInitialized(mDelegate); |
| 296 return new ItemViewHolder(item, mDelegate); |
| 297 default: |
| 298 assert false; |
| 299 return null; |
| 300 } |
| 301 } |
| 302 |
| 303 @SuppressFBWarnings("BC_UNCONFIRMED_CAST") |
| 304 @Override |
| 305 public void onBindViewHolder(ViewHolder holder, int position) { |
| 306 BookmarkId id = getItem(position); |
| 307 switch (getItemViewType(position)) { |
| 308 case PROMO_HEADER_GRID: |
| 309 case PROMO_HEADER_LIST: |
| 310 break; |
| 311 case FOLDER_VIEW_GRID: |
| 312 case FOLDER_VIEW_LIST: |
| 313 ((ItemViewHolder) holder).setBookmarkId(id); |
| 314 break; |
| 315 case PADDING_VIEW: |
| 316 case DIVIDER_LIST: |
| 317 break; |
| 318 case BOOKMARK_VIEW_GRID: |
| 319 case BOOKMARK_VIEW_LIST: |
| 320 ((ItemViewHolder) holder).setBookmarkId(id); |
| 321 break; |
| 322 default: |
| 323 assert false : "View type not supported!"; |
| 324 } |
| 325 } |
| 326 |
| 327 // PromoHeaderShowingChangeListener implementation. |
| 328 |
| 329 @Override |
| 330 public void onPromoHeaderShowingChanged(boolean isShowing) { |
| 331 if (isShowing) { |
| 332 notifyItemInserted(0); |
| 333 } else { |
| 334 // TODO(kkimlabs): Ideally we want to animate by |notifyItemRemoved|
but the default |
| 335 // animation looks broken for this promo header for some reason. |
| 336 notifyDataSetChanged(); |
| 337 } |
| 338 } |
| 339 |
| 340 // EnhancedBookmarkUIObserver implementations. |
| 341 |
| 342 @Override |
| 343 public void onEnhancedBookmarkDelegateInitialized(EnhancedBookmarkDelegate d
elegate) { |
| 344 mDelegate = delegate; |
| 345 mDelegate.addUIObserver(this); |
| 346 mDelegate.getModel().addModelObserver(mBookmarkModelObserver); |
| 347 mDelegate.getModel().addFiltersObserver(mFiltersObserver); |
| 348 |
| 349 mItemFactory = mDelegate.isListModeEnabled() |
| 350 ? new ListItemFactory() : new GridItemFactory(); |
| 351 |
| 352 mPromoHeaderManager = new EnhancedBookmarkPromoHeader(mContext, this); |
| 353 } |
| 354 |
| 355 @Override |
| 356 public void onDestroy() { |
| 357 mDelegate.removeUIObserver(this); |
| 358 mDelegate.getModel().removeModelObserver(mBookmarkModelObserver); |
| 359 mDelegate.getModel().removeFiltersObserver(mFiltersObserver); |
| 360 |
| 361 mPromoHeaderManager.destroy(); |
| 362 } |
| 363 |
| 364 @Override |
| 365 public void onAllBookmarksStateSet() { |
| 366 setBookmarks(null, mDelegate.getModel().getAllBookmarkIDsOrderedByCreati
onDate()); |
| 367 } |
| 368 |
| 369 @Override |
| 370 public void onFolderStateSet(BookmarkId folder) { |
| 371 setBookmarks(mDelegate.getModel().getChildIDs(folder, true, false), |
| 372 mDelegate.getModel().getChildIDs(folder, false, true)); |
| 373 } |
| 374 |
| 375 @Override |
| 376 public void onFilterStateSet(String filter) { |
| 377 setBookmarks(null, mDelegate.getModel().getBookmarksForFilter(filter)); |
| 378 } |
| 379 |
| 380 @Override |
| 381 public void onSelectionStateChange(List<BookmarkId> selectedBookmarks) {} |
| 382 |
| 383 @Override |
| 384 public void onListModeChange(boolean isListModeEnabled) { |
| 385 if (isListModeEnabled) { |
| 386 setNumColumns(1); |
| 387 mItemFactory = new EnhancedBookmarkItemsAdapter.ListItemFactory(); |
| 388 } else { |
| 389 mItemFactory = new EnhancedBookmarkItemsAdapter.GridItemFactory(); |
| 390 } |
| 391 updateDataset(); |
| 392 } |
| 393 |
| 394 private static class ItemViewHolder extends RecyclerView.ViewHolder implemen
ts OnClickListener, |
| 395 OnLongClickListener { |
| 396 private EnhancedBookmarkDelegate mDelegate; |
| 397 private BookmarkGrid mGrid; |
| 398 |
| 399 public ItemViewHolder(View view, EnhancedBookmarkDelegate delegate) { |
| 400 super(view); |
| 401 mGrid = (BookmarkGrid) view; |
| 402 mDelegate = delegate; |
| 403 view.setOnClickListener(this); |
| 404 view.setOnLongClickListener(this); |
| 405 } |
| 406 |
| 407 public void setBookmarkId(BookmarkId id) { |
| 408 mGrid.setBookmarkId(id); |
| 409 mGrid.setChecked(mDelegate.isBookmarkSelected(mGrid.getBookmarkId())
); |
| 410 } |
| 411 |
| 412 @Override |
| 413 public boolean onLongClick(View v) { |
| 414 mGrid.setChecked(mDelegate.toggleSelectionForBookmark(mGrid.getBookm
arkId())); |
| 415 return true; |
| 416 } |
| 417 |
| 418 @Override |
| 419 public void onClick(View v) { |
| 420 if (mDelegate.isSelectionEnabled()) onLongClick(v); |
| 421 else mGrid.onClick(itemView); |
| 422 } |
| 423 } |
| 424 |
| 425 /** |
| 426 * Factory interface to get the correct views to show. |
| 427 */ |
| 428 private interface ItemFactory { |
| 429 EnhancedBookmarkItem createBookmarkItem(ViewGroup parent); |
| 430 EnhancedBookmarkFolder createBookmarkFolder(ViewGroup parent); |
| 431 } |
| 432 |
| 433 private static class GridItemFactory implements ItemFactory { |
| 434 @Override |
| 435 public EnhancedBookmarkItem createBookmarkItem(ViewGroup parent) { |
| 436 return (EnhancedBookmarkItem) LayoutInflater.from(parent.getContext(
)).inflate( |
| 437 R.layout.eb_grid_item, parent, false); |
| 438 } |
| 439 |
| 440 @Override |
| 441 public EnhancedBookmarkFolder createBookmarkFolder(ViewGroup parent) { |
| 442 return (EnhancedBookmarkFolder) LayoutInflater.from( |
| 443 parent.getContext()).inflate(R.layout.eb_grid_folder, parent
, false); |
| 444 } |
| 445 } |
| 446 |
| 447 private static class ListItemFactory implements ItemFactory { |
| 448 @Override |
| 449 public EnhancedBookmarkItem createBookmarkItem(ViewGroup parent) { |
| 450 return (EnhancedBookmarkItem) LayoutInflater.from(parent.getContext(
)).inflate( |
| 451 R.layout.eb_list_item, parent, false); |
| 452 } |
| 453 |
| 454 @Override |
| 455 public EnhancedBookmarkFolder createBookmarkFolder(ViewGroup parent) { |
| 456 return (EnhancedBookmarkFolder) LayoutInflater.from( |
| 457 parent.getContext()).inflate(R.layout.eb_list_folder, parent
, false); |
| 458 } |
| 459 } |
| 460 } |
OLD | NEW |