OLD | NEW |
---|---|
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chrome.browser.widget; | 5 package org.chromium.chrome.browser.widget; |
6 | 6 |
7 import android.os.AsyncTask; | 7 import android.os.AsyncTask; |
8 import android.support.v7.widget.RecyclerView; | 8 import android.support.v7.widget.RecyclerView; |
9 import android.support.v7.widget.RecyclerView.Adapter; | 9 import android.support.v7.widget.RecyclerView.Adapter; |
10 import android.support.v7.widget.RecyclerView.ViewHolder; | 10 import android.support.v7.widget.RecyclerView.ViewHolder; |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
142 mTextView.setText(builder); | 142 mTextView.setText(builder); |
143 } | 143 } |
144 } | 144 } |
145 | 145 |
146 protected static class BasicViewHolder extends RecyclerView.ViewHolder { | 146 protected static class BasicViewHolder extends RecyclerView.ViewHolder { |
147 public BasicViewHolder(View itemView) { | 147 public BasicViewHolder(View itemView) { |
148 super(itemView); | 148 super(itemView); |
149 } | 149 } |
150 } | 150 } |
151 | 151 |
152 protected static class SubsectionHeaderViewHolder extends RecyclerView.ViewH older { | |
153 private View mView; | |
154 | |
155 public SubsectionHeaderViewHolder(View itemView) { | |
156 super(itemView); | |
157 mView = itemView; | |
158 } | |
159 | |
160 public View getView() { | |
161 return mView; | |
162 } | |
163 } | |
164 | |
152 /** | 165 /** |
153 * A bucket of items with the same date. | 166 * A bucket of items with the same date. |
154 */ | 167 */ |
155 protected static class ItemGroup { | 168 public static class ItemGroup { |
gone
2017/02/15 23:58:10
Make this protected again.
shaktisahu
2017/02/16 06:07:07
I am subclassing this from DownloadItemGroup which
| |
156 private final Date mDate; | 169 private final Date mDate; |
157 private final List<TimedItem> mItems = new ArrayList<>(); | 170 private final List<TimedItem> mItems = new ArrayList<>(); |
158 | 171 |
159 /** Index of the header, relative to the full list. Must be set only on ce.*/ | 172 /** Index of the header, relative to the full list. Must be set only on ce.*/ |
160 private int mIndex; | 173 private int mIndex; |
161 | 174 |
162 private boolean mIsSorted; | 175 private boolean mIsSorted; |
163 private boolean mIsListHeader; | 176 private boolean mIsListHeader; |
164 private boolean mIsListFooter; | 177 private boolean mIsListFooter; |
165 | 178 |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
220 if (index == 0 || mIsListHeader || mIsListFooter) return null; | 233 if (index == 0 || mIsListHeader || mIsListFooter) return null; |
221 | 234 |
222 sortIfNeeded(); | 235 sortIfNeeded(); |
223 return mItems.get(index - 1); | 236 return mItems.get(index - 1); |
224 } | 237 } |
225 | 238 |
226 /** | 239 /** |
227 * Rather than sorting the list each time a new item is added, the list is sorted when | 240 * Rather than sorting the list each time a new item is added, the list is sorted when |
228 * something requires a correct ordering of the items. | 241 * something requires a correct ordering of the items. |
229 */ | 242 */ |
230 private void sortIfNeeded() { | 243 protected void sortIfNeeded() { |
231 if (mIsSorted) return; | 244 if (mIsSorted) return; |
232 mIsSorted = true; | 245 mIsSorted = true; |
233 | 246 |
234 Collections.sort(mItems, new Comparator<TimedItem>() { | 247 Collections.sort(mItems, new Comparator<TimedItem>() { |
235 @Override | 248 @Override |
236 public int compare(TimedItem lhs, TimedItem rhs) { | 249 public int compare(TimedItem lhs, TimedItem rhs) { |
237 // More recent items are listed first. Ideally we'd use Lon g.compare, but that | 250 return compareItem(lhs, rhs); |
238 // is an API level 19 call for some inexplicable reason. | |
239 long timeDelta = lhs.getTimestamp() - rhs.getTimestamp(); | |
240 if (timeDelta > 0) { | |
241 return -1; | |
242 } else if (timeDelta == 0) { | |
243 return 0; | |
244 } else { | |
245 return 1; | |
246 } | |
247 } | 251 } |
248 }); | 252 }); |
249 } | 253 } |
254 | |
255 protected int compareItem(TimedItem lhs, TimedItem rhs) { | |
256 // More recent items are listed first. Ideally we'd use Long.compar e, but that | |
257 // is an API level 19 call for some inexplicable reason. | |
258 long timeDelta = lhs.getTimestamp() - rhs.getTimestamp(); | |
259 if (timeDelta > 0) { | |
260 return -1; | |
261 } else if (timeDelta == 0) { | |
262 return 0; | |
263 } else { | |
264 return 1; | |
265 } | |
266 } | |
250 } | 267 } |
251 | 268 |
252 // Cached async tasks to get the two Calendar objects, which are used when c omparing dates. | 269 // Cached async tasks to get the two Calendar objects, which are used when c omparing dates. |
253 private static final AsyncTask<Void, Void, Calendar> sCal1 = createCalendar( ); | 270 private static final AsyncTask<Void, Void, Calendar> sCal1 = createCalendar( ); |
254 private static final AsyncTask<Void, Void, Calendar> sCal2 = createCalendar( ); | 271 private static final AsyncTask<Void, Void, Calendar> sCal2 = createCalendar( ); |
255 | 272 |
256 public static final int TYPE_FOOTER = -2; | 273 public static final int TYPE_FOOTER = -2; |
257 public static final int TYPE_HEADER = -1; | 274 public static final int TYPE_HEADER = -1; |
258 public static final int TYPE_DATE = 0; | 275 public static final int TYPE_DATE = 0; |
259 public static final int TYPE_NORMAL = 1; | 276 public static final int TYPE_NORMAL = 1; |
277 public static final int TYPE_SUBSECTION_HEADER = 2; | |
260 | 278 |
261 private int mSize; | 279 private int mSize; |
262 private boolean mHasListHeader; | 280 private boolean mHasListHeader; |
263 private boolean mHasListFooter; | 281 private boolean mHasListFooter; |
264 | 282 |
265 private SortedSet<ItemGroup> mGroups = new TreeSet<>(new Comparator<ItemGrou p>() { | 283 private SortedSet<ItemGroup> mGroups = new TreeSet<>(new Comparator<ItemGrou p>() { |
266 @Override | 284 @Override |
267 public int compare(ItemGroup lhs, ItemGroup rhs) { | 285 public int compare(ItemGroup lhs, ItemGroup rhs) { |
268 if (lhs == rhs) return 0; | 286 if (lhs == rhs) return 0; |
269 | 287 |
270 // There should only be at most one list header and one list footer in the SortedSet. | 288 // There should only be at most one list header and one list footer in the SortedSet. |
271 if (lhs.mIsListHeader || rhs.mIsListFooter) return -1; | 289 if (lhs.mIsListHeader || rhs.mIsListFooter) return -1; |
272 if (lhs.mIsListFooter || rhs.mIsListHeader) return 1; | 290 if (lhs.mIsListFooter || rhs.mIsListHeader) return 1; |
273 | 291 |
274 return compareDate(lhs.mDate, rhs.mDate); | 292 return compareDate(lhs.mDate, rhs.mDate); |
275 } | 293 } |
276 }); | 294 }); |
277 | 295 |
278 /** | 296 /** |
279 * Creates a {@link ViewHolder} in the given view parent. | 297 * Creates a {@link ViewHolder} in the given view parent. |
280 * @see #onCreateViewHolder(ViewGroup, int) | 298 * @see #onCreateViewHolder(ViewGroup, int) |
281 */ | 299 */ |
282 protected abstract ViewHolder createViewHolder(ViewGroup parent); | 300 protected abstract ViewHolder createViewHolder(ViewGroup parent); |
283 | 301 |
284 /** | 302 /** |
303 * Creates a {@link ViewHolder} for a subsection in the given view parent. | |
304 * @see #onCreateViewHolder(ViewGroup, int) | |
305 */ | |
306 protected SubsectionHeaderViewHolder createSubsectionHeader(ViewGroup parent ) { | |
gone
2017/02/15 23:58:10
@Nullable?
shaktisahu
2017/02/16 06:07:07
Not sure if it adds any value. Each of the followi
gone
2017/02/17 19:21:16
Yeah, they really should be.
shaktisahu
2017/02/17 19:55:10
Done.
| |
307 return null; | |
308 } | |
309 | |
310 /** | |
285 * Creates a {@link BasicViewHolder} in the given view parent for the header . | 311 * Creates a {@link BasicViewHolder} in the given view parent for the header . |
286 * @see #onCreateViewHolder(ViewGroup, int) | 312 * @see #onCreateViewHolder(ViewGroup, int) |
287 */ | 313 */ |
288 protected BasicViewHolder createHeader(ViewGroup parent) { | 314 protected BasicViewHolder createHeader(ViewGroup parent) { |
289 return null; | 315 return null; |
290 } | 316 } |
291 | 317 |
292 /** | 318 /** |
293 * Creates a {@link BasicViewHolder} in the given view parent for the footer . | 319 * Creates a {@link BasicViewHolder} in the given view parent for the footer . |
294 * See {@link #onCreateViewHolder(ViewGroup, int)}. | 320 * See {@link #onCreateViewHolder(ViewGroup, int)}. |
(...skipping 11 matching lines...) Expand all Loading... | |
306 getTimedItemViewResId(), parent, false)); | 332 getTimedItemViewResId(), parent, false)); |
307 } | 333 } |
308 | 334 |
309 /** | 335 /** |
310 * Binds the {@link ViewHolder} with the given {@link TimedItem}. | 336 * Binds the {@link ViewHolder} with the given {@link TimedItem}. |
311 * @see #onBindViewHolder(ViewHolder, int) | 337 * @see #onBindViewHolder(ViewHolder, int) |
312 */ | 338 */ |
313 protected abstract void bindViewHolderForTimedItem(ViewHolder viewHolder, Ti medItem item); | 339 protected abstract void bindViewHolderForTimedItem(ViewHolder viewHolder, Ti medItem item); |
314 | 340 |
315 /** | 341 /** |
342 * Binds the {@link SubsectionHeaderViewHolder} with the given {@link TimedI tem}. | |
343 * @see #onBindViewHolder(ViewHolder, int) | |
344 */ | |
345 protected void bindViewHolderForSubsectionHeader( | |
346 SubsectionHeaderViewHolder holder, TimedItem timedItem) {} | |
347 | |
348 /** | |
316 * Gets the resource id of the view showing the date header. | 349 * Gets the resource id of the view showing the date header. |
317 * Contract for subclasses: this view should be a {@link TextView}. | 350 * Contract for subclasses: this view should be a {@link TextView}. |
318 */ | 351 */ |
319 protected abstract int getTimedItemViewResId(); | 352 protected abstract int getTimedItemViewResId(); |
320 | 353 |
354 protected boolean isSubsectionHeader(TimedItem timedItem) { | |
355 return false; | |
356 } | |
gone
2017/02/15 23:58:10
Group your subsection header-related functions tog
shaktisahu
2017/02/16 06:07:07
Done.
| |
357 | |
321 /** | 358 /** |
322 * Loads a list of {@link TimedItem}s to this adapter. Previous data will no t be removed. Call | 359 * Loads a list of {@link TimedItem}s to this adapter. Previous data will no t be removed. Call |
323 * {@link #clear(boolean)} to remove previous items. | 360 * {@link #clear(boolean)} to remove previous items. |
324 */ | 361 */ |
325 public void loadItems(List<? extends TimedItem> timedItems) { | 362 public void loadItems(List<? extends TimedItem> timedItems) { |
326 for (TimedItem timedItem : timedItems) { | 363 for (TimedItem timedItem : timedItems) { |
327 Date date = new Date(timedItem.getTimestamp()); | 364 Date date = new Date(timedItem.getTimestamp()); |
328 boolean found = false; | 365 boolean found = false; |
329 for (ItemGroup group : mGroups) { | 366 for (ItemGroup group : mGroups) { |
330 if (group.isSameDay(date)) { | 367 if (group.isSameDay(date)) { |
331 found = true; | 368 found = true; |
332 group.addItem(timedItem); | 369 group.addItem(timedItem); |
333 mSize++; | 370 mSize++; |
334 break; | 371 break; |
335 } | 372 } |
336 } | 373 } |
337 if (!found) { | 374 if (!found) { |
338 // Create a new ItemGroup with the date for the new item. This i ncreases the | 375 // Create a new ItemGroup with the date for the new item. This i ncreases the |
339 // size by two because we add new views for the date and the ite m itself. | 376 // size by two because we add new views for the date and the ite m itself. |
340 ItemGroup newGroup = new ItemGroup(timedItem.getTimestamp()); | 377 ItemGroup newGroup = createGroup(timedItem.getTimestamp()); |
341 newGroup.addItem(timedItem); | 378 newGroup.addItem(timedItem); |
342 mGroups.add(newGroup); | 379 mGroups.add(newGroup); |
343 mSize += 2; | 380 mSize += 2; |
344 } | 381 } |
345 } | 382 } |
346 | 383 |
347 setGroupPositions(); | 384 setGroupPositions(); |
348 notifyDataSetChanged(); | 385 notifyDataSetChanged(); |
349 } | 386 } |
350 | 387 |
388 protected ItemGroup createGroup(long timestamp) { | |
gone
2017/02/15 23:58:10
Javadoc for protected & public methods
shaktisahu
2017/02/16 06:07:06
Done.
| |
389 return new ItemGroup(timestamp); | |
390 } | |
391 | |
351 /** | 392 /** |
352 * Tells each group where they start in the list. | 393 * Tells each group where they start in the list. |
353 */ | 394 */ |
354 private void setGroupPositions() { | 395 private void setGroupPositions() { |
355 int startIndex = 0; | 396 int startIndex = 0; |
356 for (ItemGroup group : mGroups) { | 397 for (ItemGroup group : mGroups) { |
357 group.resetPosition(); | 398 group.resetPosition(); |
358 group.setPosition(startIndex); | 399 group.setPosition(startIndex); |
359 startIndex += group.size(); | 400 startIndex += group.size(); |
360 } | 401 } |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
450 */ | 491 */ |
451 public Pair<Date, TimedItem> getItemAt(int position) { | 492 public Pair<Date, TimedItem> getItemAt(int position) { |
452 Pair<ItemGroup, Integer> pair = getGroupAt(position); | 493 Pair<ItemGroup, Integer> pair = getGroupAt(position); |
453 ItemGroup group = pair.first; | 494 ItemGroup group = pair.first; |
454 return new Pair<>(group.mDate, group.getItemAt(pair.second)); | 495 return new Pair<>(group.mDate, group.getItemAt(pair.second)); |
455 } | 496 } |
456 | 497 |
457 @Override | 498 @Override |
458 public final int getItemViewType(int position) { | 499 public final int getItemViewType(int position) { |
459 Pair<ItemGroup, Integer> pair = getGroupAt(position); | 500 Pair<ItemGroup, Integer> pair = getGroupAt(position); |
501 ItemGroup group = pair.first; | |
460 if (pair.second == TYPE_HEADER) { | 502 if (pair.second == TYPE_HEADER) { |
461 return TYPE_HEADER; | 503 return TYPE_HEADER; |
462 } else if (pair.second == TYPE_FOOTER) { | 504 } else if (pair.second == TYPE_FOOTER) { |
463 return TYPE_FOOTER; | 505 return TYPE_FOOTER; |
464 } else if (pair.second == 0) { | 506 } else if (pair.second == 0) { |
465 return TYPE_DATE; | 507 return TYPE_DATE; |
508 } else if (isSubsectionHeader(group.getItemAt(pair.second))) { | |
509 return TYPE_SUBSECTION_HEADER; | |
466 } else { | 510 } else { |
467 return TYPE_NORMAL; | 511 return TYPE_NORMAL; |
468 } | 512 } |
469 } | 513 } |
470 | 514 |
471 @Override | 515 @Override |
472 public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, in t viewType) { | 516 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int view Type) { |
gone
2017/02/15 23:58:10
Make this function final again
shaktisahu
2017/02/16 06:07:06
Done.
| |
473 if (viewType == TYPE_DATE) { | 517 if (viewType == TYPE_DATE) { |
474 return createDateViewHolder(parent); | 518 return createDateViewHolder(parent); |
475 } else if (viewType == TYPE_NORMAL) { | 519 } else if (viewType == TYPE_NORMAL) { |
476 return createViewHolder(parent); | 520 return createViewHolder(parent); |
477 } else if (viewType == TYPE_HEADER) { | 521 } else if (viewType == TYPE_HEADER) { |
478 return createHeader(parent); | 522 return createHeader(parent); |
479 } else if (viewType == TYPE_FOOTER) { | 523 } else if (viewType == TYPE_FOOTER) { |
480 return createFooter(parent); | 524 return createFooter(parent); |
525 } else if (viewType == TYPE_SUBSECTION_HEADER) { | |
526 return createSubsectionHeader(parent); | |
481 } | 527 } |
482 assert false; | 528 assert false; |
483 return null; | 529 return null; |
484 } | 530 } |
485 | 531 |
486 @Override | 532 @Override |
487 public final void onBindViewHolder(RecyclerView.ViewHolder holder, int posit ion) { | 533 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { |
gone
2017/02/15 23:58:10
Make this function final again
shaktisahu
2017/02/16 06:07:07
Done.
| |
488 Pair<Date, TimedItem> pair = getItemAt(position); | 534 Pair<Date, TimedItem> pair = getItemAt(position); |
489 if (holder instanceof DateViewHolder) { | 535 if (holder instanceof DateViewHolder) { |
490 ((DateViewHolder) holder).setDate(pair.first); | 536 ((DateViewHolder) holder).setDate(pair.first); |
537 } else if (holder instanceof SubsectionHeaderViewHolder) { | |
538 bindViewHolderForSubsectionHeader((SubsectionHeaderViewHolder) holde r, pair.second); | |
491 } else if (!(holder instanceof BasicViewHolder)) { | 539 } else if (!(holder instanceof BasicViewHolder)) { |
492 bindViewHolderForTimedItem(holder, pair.second); | 540 bindViewHolderForTimedItem(holder, pair.second); |
493 } | 541 } |
494 } | 542 } |
495 | 543 |
496 @Override | 544 @Override |
497 public final int getItemCount() { | 545 public final int getItemCount() { |
498 return mSize; | 546 return mSize; |
499 } | 547 } |
500 | 548 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
556 long dayOfYear = calendar.get(Calendar.DAY_OF_YEAR); | 604 long dayOfYear = calendar.get(Calendar.DAY_OF_YEAR); |
557 long year = calendar.get(Calendar.YEAR); | 605 long year = calendar.get(Calendar.YEAR); |
558 return (year << 16) + dayOfYear; | 606 return (year << 16) + dayOfYear; |
559 } | 607 } |
560 | 608 |
561 /** | 609 /** |
562 * Compares two {@link Date}s. Note if you already have two {@link Calendar} objects, use | 610 * Compares two {@link Date}s. Note if you already have two {@link Calendar} objects, use |
563 * {@link #compareCalendar(Calendar, Calendar)} instead. | 611 * {@link #compareCalendar(Calendar, Calendar)} instead. |
564 * @return 0 if date1 and date2 are in the same day; 1 if date1 is before da te2; -1 otherwise. | 612 * @return 0 if date1 and date2 are in the same day; 1 if date1 is before da te2; -1 otherwise. |
565 */ | 613 */ |
566 private static int compareDate(Date date1, Date date2) { | 614 protected static int compareDate(Date date1, Date date2) { |
567 Pair<Calendar, Calendar> pair = getCachedCalendars(); | 615 Pair<Calendar, Calendar> pair = getCachedCalendars(); |
568 Calendar cal1 = pair.first, cal2 = pair.second; | 616 Calendar cal1 = pair.first, cal2 = pair.second; |
569 cal1.setTime(date1); | 617 cal1.setTime(date1); |
570 cal2.setTime(date2); | 618 cal2.setTime(date2); |
571 return compareCalendar(cal1, cal2); | 619 return compareCalendar(cal1, cal2); |
572 } | 620 } |
573 | 621 |
574 /** | 622 /** |
575 * @return 0 if cal1 and cal2 are in the same day; 1 if cal1 happens before cal2; -1 otherwise. | 623 * @return 0 if cal1 and cal2 are in the same day; 1 if cal1 happens before cal2; -1 otherwise. |
576 */ | 624 */ |
(...skipping 30 matching lines...) Expand all Loading... | |
607 */ | 655 */ |
608 private static AsyncTask<Void, Void, Calendar> createCalendar() { | 656 private static AsyncTask<Void, Void, Calendar> createCalendar() { |
609 return new AsyncTask<Void, Void, Calendar>() { | 657 return new AsyncTask<Void, Void, Calendar>() { |
610 @Override | 658 @Override |
611 protected Calendar doInBackground(Void... unused) { | 659 protected Calendar doInBackground(Void... unused) { |
612 return Calendar.getInstance(); | 660 return Calendar.getInstance(); |
613 } | 661 } |
614 }.execute(); | 662 }.execute(); |
615 } | 663 } |
616 } | 664 } |
OLD | NEW |