Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(300)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/widget/DateDividedAdapter.java

Issue 2670083002: [Download Home] Displaying offline page bundle per day (Closed)
Patch Set: Theresa's comments Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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;
11 import android.text.format.DateUtils; 11 import android.text.format.DateUtils;
12 import android.util.Pair; 12 import android.util.Pair;
13 import android.view.LayoutInflater; 13 import android.view.LayoutInflater;
14 import android.view.View; 14 import android.view.View;
15 import android.view.ViewGroup; 15 import android.view.ViewGroup;
16 import android.widget.TextView; 16 import android.widget.TextView;
17 17
18 import org.chromium.chrome.R; 18 import org.chromium.chrome.R;
19 import org.chromium.chrome.browser.widget.DateDividedAdapter.ItemGroup;
20 19
21 import java.util.ArrayList; 20 import java.util.ArrayList;
22 import java.util.Calendar; 21 import java.util.Calendar;
23 import java.util.Collections; 22 import java.util.Collections;
24 import java.util.Comparator; 23 import java.util.Comparator;
25 import java.util.Date; 24 import java.util.Date;
26 import java.util.List; 25 import java.util.List;
27 import java.util.SortedSet; 26 import java.util.SortedSet;
28 import java.util.TreeSet; 27 import java.util.TreeSet;
29 import java.util.concurrent.ExecutionException; 28 import java.util.concurrent.ExecutionException;
(...skipping 16 matching lines...) Expand all
46 /** Value indicating that a TimedItem is not currently being displayed. */ 45 /** Value indicating that a TimedItem is not currently being displayed. */
47 public static final int INVALID_POSITION = -1; 46 public static final int INVALID_POSITION = -1;
48 47
49 /** Position of the TimedItem in the list, or {@link #INVALID_POSITION} if not shown. */ 48 /** Position of the TimedItem in the list, or {@link #INVALID_POSITION} if not shown. */
50 private int mPosition = INVALID_POSITION; 49 private int mPosition = INVALID_POSITION;
51 50
52 private boolean mIsFirstInGroup; 51 private boolean mIsFirstInGroup;
53 private boolean mIsLastInGroup; 52 private boolean mIsLastInGroup;
54 53
55 /** See {@link #mPosition}. */ 54 /** See {@link #mPosition}. */
56 private final void setPosition(int position) { 55 public final void setPosition(int position) {
57 mPosition = position; 56 mPosition = position;
58 } 57 }
59 58
60 /** See {@link #mPosition}. */ 59 /** See {@link #mPosition}. */
61 public final int getPosition() { 60 public final int getPosition() {
62 return mPosition; 61 return mPosition;
63 } 62 }
64 63
65 /** 64 /**
66 * @param isFirst Whether this item is the first in its group. 65 * @param isFirst Whether this item is the first in its group.
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
145 144
146 protected static class BasicViewHolder extends RecyclerView.ViewHolder { 145 protected static class BasicViewHolder extends RecyclerView.ViewHolder {
147 public BasicViewHolder(View itemView) { 146 public BasicViewHolder(View itemView) {
148 super(itemView); 147 super(itemView);
149 } 148 }
150 } 149 }
151 150
152 /** 151 /**
153 * A bucket of items with the same date. 152 * A bucket of items with the same date.
154 */ 153 */
155 protected static class ItemGroup { 154 public static class ItemGroup {
156 private final Date mDate; 155 private final Date mDate;
157 private final List<TimedItem> mItems = new ArrayList<>(); 156 protected final List<TimedItem> mItems = new ArrayList<>();
158 157
159 /** Index of the header, relative to the full list. Must be set only on ce.*/ 158 /** Index of the header, relative to the full list. Must be set only on ce.*/
160 private int mIndex; 159 private int mIndex;
161 160
162 private boolean mIsSorted; 161 private boolean mIsSorted;
163 private boolean mIsListHeaderOrFooter; 162 private boolean mIsListHeaderOrFooter;
164 163
165 public ItemGroup(long timestamp) { 164 public ItemGroup(long timestamp) {
166 mDate = new Date(timestamp); 165 mDate = new Date(timestamp);
167 mIsSorted = true; 166 mIsSorted = true;
168 } 167 }
169 168
170 public void addItem(TimedItem item) { 169 public void addItem(TimedItem item) {
171 mItems.add(item); 170 mItems.add(item);
172 mIsSorted = mItems.size() == 1; 171 mIsSorted = mItems.size() == 1;
173 } 172 }
174 173
175 public void removeItem(TimedItem item) { 174 public void removeItem(TimedItem item) {
176 mItems.remove(item); 175 mItems.remove(item);
177 } 176 }
178 177
179 /** Records the position of all the TimedItems in this group, relative t o the full list. */ 178 /** Records the position of all the TimedItems in this group, relative t o the full list. */
180 public void setPosition(int index) { 179 public void setPosition(int index) {
181 assert mIndex == 0 || mIndex == TimedItem.INVALID_POSITION; 180 assert mIndex == 0 || mIndex == TimedItem.INVALID_POSITION;
182 mIndex = index; 181 mIndex = index;
183 182
184 sortIfNeeded(); 183 sortIfNeeded();
184 setPositionForItems(index + 1);
185 setPositionForFirstAndLastInGroup();
186 }
187
188 protected void setPositionForItems(int startIndex) {
189 int index = startIndex;
185 for (int i = 0; i < mItems.size(); i++) { 190 for (int i = 0; i < mItems.size(); i++) {
186 index += 1;
187 TimedItem item = mItems.get(i); 191 TimedItem item = mItems.get(i);
188 item.setPosition(index); 192 item.setPosition(index);
189 item.setIsFirstInGroup(i == 0); 193 index += 1;
190 item.setIsLastInGroup(i == mItems.size() - 1);
191 } 194 }
192 } 195 }
193 196
197 protected void setPositionForFirstAndLastInGroup() {
gone 2017/02/10 02:31:45 This isn't setting positions; call it "identifyFir
shaktisahu 2017/02/10 21:30:25 Done.
198 TimedItem first = mItems.get(0);
199 TimedItem last = mItems.get(mItems.size() - 1);
200 first.setIsFirstInGroup(true);
201 last.setIsLastInGroup(true);
202 }
203
194 /** Unsets the position of all TimedItems in this group. */ 204 /** Unsets the position of all TimedItems in this group. */
195 public void resetPosition() { 205 public void resetPosition() {
196 mIndex = TimedItem.INVALID_POSITION; 206 mIndex = TimedItem.INVALID_POSITION;
197 for (TimedItem item : mItems) item.setPosition(TimedItem.INVALID_POS ITION); 207 for (TimedItem item : mItems) item.setPosition(TimedItem.INVALID_POS ITION);
198 } 208 }
199 209
200 /** 210 /**
201 * @return Whether the given date happens in the same day as the items i n this group. 211 * @return Whether the given date happens in the same day as the items i n this group.
202 */ 212 */
203 public boolean isSameDay(Date otherDate) { 213 public boolean isSameDay(Date otherDate) {
204 return compareDate(mDate, otherDate) == 0; 214 return compareDate(mDate, otherDate) == 0;
205 } 215 }
206 216
217 protected Date getDate() {
218 return mDate;
Theresa 2017/02/10 00:55:12 For consistency, we should either have a getter th
shaktisahu 2017/02/10 21:30:25 Done. I would make mDate as protected and remove t
219 }
220
207 /** 221 /**
208 * @return The size of this group. 222 * @return The size of this group.
209 */ 223 */
210 public int size() { 224 public int size() {
211 if (mIsListHeaderOrFooter) return 1; 225 if (mIsListHeaderOrFooter) return 1;
212 226
213 // Plus 1 to account for the date header. 227 // Plus 1 to account for the date header.
214 return mItems.size() + 1; 228 return mItems.size() + 1;
215 } 229 }
216 230
217 public TimedItem getItemAt(int index) { 231 public TimedItem getItemAt(int index) {
218 // 0 is allocated to the date header. The list header has no items. 232 // 0 is allocated to the date header. The list header has no items.
219 if (index == 0 || mIsListHeaderOrFooter) return null; 233 if (index == 0 || mIsListHeaderOrFooter) return null;
220 234
221 sortIfNeeded(); 235 sortIfNeeded();
236 return getItemAtInner(index);
237 }
238
239 protected TimedItem getItemAtInner(int index) {
Theresa 2017/02/10 00:55:12 nit: getItemInternal() is more consistent with nam
shaktisahu 2017/02/10 21:30:25 Done. Renamed it to getItemAtInternal
222 return mItems.get(index - 1); 240 return mItems.get(index - 1);
223 } 241 }
224 242
225 /** 243 /**
226 * Rather than sorting the list each time a new item is added, the list is sorted when 244 * Rather than sorting the list each time a new item is added, the list is sorted when
227 * something requires a correct ordering of the items. 245 * something requires a correct ordering of the items.
228 */ 246 */
229 private void sortIfNeeded() { 247 protected void sortIfNeeded() {
230 if (mIsSorted) return; 248 if (mIsSorted) return;
231 mIsSorted = true; 249 mIsSorted = true;
232 250
233 Collections.sort(mItems, new Comparator<TimedItem>() { 251 Collections.sort(mItems, new Comparator<TimedItem>() {
234 @Override 252 @Override
235 public int compare(TimedItem lhs, TimedItem rhs) { 253 public int compare(TimedItem lhs, TimedItem rhs) {
236 // More recent items are listed first. Ideally we'd use Lon g.compare, but that 254 return compareItem(lhs, rhs);
237 // is an API level 19 call for some inexplicable reason.
238 long timeDelta = lhs.getTimestamp() - rhs.getTimestamp();
239 if (timeDelta > 0) {
240 return -1;
241 } else if (timeDelta == 0) {
242 return 0;
243 } else {
244 return 1;
245 }
246 } 255 }
247 }); 256 });
248 } 257 }
258
259 protected int compareItem(TimedItem lhs, TimedItem rhs) {
260 // More recent items are listed first. Ideally we'd use Long.compar e, but that
261 // is an API level 19 call for some inexplicable reason.
262 long timeDelta = lhs.getTimestamp() - rhs.getTimestamp();
263 if (timeDelta > 0) {
264 return -1;
265 } else if (timeDelta == 0) {
266 return 0;
267 } else {
268 return 1;
269 }
270 }
271
272 public int getItemViewType(int position) {
273 return position == 0 ? TYPE_DATE : TYPE_NORMAL;
274 }
249 } 275 }
250 276
251 // Cached async tasks to get the two Calendar objects, which are used when c omparing dates. 277 // Cached async tasks to get the two Calendar objects, which are used when c omparing dates.
252 private static final AsyncTask<Void, Void, Calendar> sCal1 = createCalendar( ); 278 private static final AsyncTask<Void, Void, Calendar> sCal1 = createCalendar( );
253 private static final AsyncTask<Void, Void, Calendar> sCal2 = createCalendar( ); 279 private static final AsyncTask<Void, Void, Calendar> sCal2 = createCalendar( );
254 280
255 public static final int TYPE_FOOTER = -2; 281 public static final int TYPE_FOOTER = -2;
256 public static final int TYPE_HEADER = -1; 282 public static final int TYPE_HEADER = -1;
257 public static final int TYPE_DATE = 0; 283 public static final int TYPE_DATE = 0;
258 public static final int TYPE_NORMAL = 1; 284 public static final int TYPE_NORMAL = 1;
285 public static final int TYPE_SUBSECTION_HEADER = 2;
259 286
260 private int mSize; 287 private int mSize;
261 private boolean mHasListHeader; 288 private boolean mHasListHeader;
262 private boolean mHasListFooter; 289 private boolean mHasListFooter;
263 290
264 private SortedSet<ItemGroup> mGroups = new TreeSet<>(new Comparator<ItemGrou p>() { 291 private SortedSet<ItemGroup> mGroups = new TreeSet<>(new Comparator<ItemGrou p>() {
265 @Override 292 @Override
266 public int compare(ItemGroup lhs, ItemGroup rhs) { 293 public int compare(ItemGroup lhs, ItemGroup rhs) {
267 return compareDate(lhs.mDate, rhs.mDate); 294 return compareDate(lhs.mDate, rhs.mDate);
268 } 295 }
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 * {@link #clear(boolean)} to remove previous items. 343 * {@link #clear(boolean)} to remove previous items.
317 */ 344 */
318 public void loadItems(List<? extends TimedItem> timedItems) { 345 public void loadItems(List<? extends TimedItem> timedItems) {
319 for (TimedItem timedItem : timedItems) { 346 for (TimedItem timedItem : timedItems) {
320 Date date = new Date(timedItem.getTimestamp()); 347 Date date = new Date(timedItem.getTimestamp());
321 boolean found = false; 348 boolean found = false;
322 for (ItemGroup group : mGroups) { 349 for (ItemGroup group : mGroups) {
323 if (group.isSameDay(date)) { 350 if (group.isSameDay(date)) {
324 found = true; 351 found = true;
325 group.addItem(timedItem); 352 group.addItem(timedItem);
326 mSize++;
327 break; 353 break;
328 } 354 }
329 } 355 }
330 if (!found) { 356 if (!found) {
331 // Create a new ItemGroup with the date for the new item. This i ncreases the 357 // Create a new ItemGroup with the date for the new item. This i ncreases the
332 // size by two because we add new views for the date and the ite m itself. 358 // size by two because we add new views for the date and the ite m itself.
333 ItemGroup newGroup = new ItemGroup(timedItem.getTimestamp()); 359 ItemGroup newGroup = createGroup(timedItem.getTimestamp());
334 newGroup.addItem(timedItem); 360 newGroup.addItem(timedItem);
335 mGroups.add(newGroup); 361 mGroups.add(newGroup);
336 mSize += 2;
337 } 362 }
338 } 363 }
339 364
365 computeItemCount();
340 setGroupPositions(); 366 setGroupPositions();
341 notifyDataSetChanged(); 367 notifyDataSetChanged();
342 } 368 }
343 369
370 protected ItemGroup createGroup(long timeStamp) {
Theresa 2017/02/10 00:55:12 nit: s/timeStamp/timestamp
shaktisahu 2017/02/10 21:30:25 Done.
371 ItemGroup group = new ItemGroup(timeStamp);
372 return group;
gone 2017/02/10 02:31:45 return new ItemGroup(timestamp);
shaktisahu 2017/02/10 21:30:25 Done.
373 }
374
344 /** 375 /**
345 * Tells each group where they start in the list. 376 * Tells each group where they start in the list.
346 */ 377 */
347 private void setGroupPositions() { 378 protected void setGroupPositions() {
348 int startIndex = 0; 379 int startIndex = 0;
349 for (ItemGroup group : mGroups) { 380 for (ItemGroup group : mGroups) {
350 group.resetPosition(); 381 group.resetPosition();
351 group.setPosition(startIndex); 382 group.setPosition(startIndex);
352 startIndex += group.size(); 383 startIndex += group.size();
353 } 384 }
354 } 385 }
355 386
356 /** 387 /**
357 * Adds a header as the first group in this adapter. 388 * Adds a header as the first group in this adapter.
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
447 return new Pair<>(group.mDate, group.getItemAt(pair.second)); 478 return new Pair<>(group.mDate, group.getItemAt(pair.second));
448 } 479 }
449 480
450 @Override 481 @Override
451 public final int getItemViewType(int position) { 482 public final int getItemViewType(int position) {
452 Pair<ItemGroup, Integer> pair = getGroupAt(position); 483 Pair<ItemGroup, Integer> pair = getGroupAt(position);
453 if (pair.second == TYPE_HEADER) { 484 if (pair.second == TYPE_HEADER) {
454 return TYPE_HEADER; 485 return TYPE_HEADER;
455 } else if (pair.second == TYPE_FOOTER) { 486 } else if (pair.second == TYPE_FOOTER) {
456 return TYPE_FOOTER; 487 return TYPE_FOOTER;
457 } else if (pair.second == 0) {
458 return TYPE_DATE;
459 } else { 488 } else {
460 return TYPE_NORMAL; 489 ItemGroup group = pair.first;
490 return group.getItemViewType(pair.second);
Theresa 2017/02/10 00:55:12 nit: This could be condensed to one line: pair.fir
shaktisahu 2017/02/10 21:30:25 hmm.. I think this is more readable.
461 } 491 }
462 } 492 }
463 493
464 @Override 494 @Override
465 public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, in t viewType) { 495 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int view Type) {
466 if (viewType == TYPE_DATE) { 496 if (viewType == TYPE_DATE) {
467 return createDateViewHolder(parent); 497 return createDateViewHolder(parent);
468 } else if (viewType == TYPE_NORMAL) { 498 } else if (viewType == TYPE_NORMAL) {
469 return createViewHolder(parent); 499 return createViewHolder(parent);
470 } else if (viewType == TYPE_HEADER) { 500 } else if (viewType == TYPE_HEADER) {
471 return createHeader(parent); 501 return createHeader(parent);
472 } else if (viewType == TYPE_FOOTER) { 502 } else if (viewType == TYPE_FOOTER) {
473 return createFooter(parent); 503 return createFooter(parent);
474 } 504 }
475 assert false; 505 assert false;
476 return null; 506 return null;
477 } 507 }
478 508
479 @Override 509 @Override
480 public final void onBindViewHolder(RecyclerView.ViewHolder holder, int posit ion) { 510 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
481 Pair<Date, TimedItem> pair = getItemAt(position); 511 Pair<Date, TimedItem> pair = getItemAt(position);
482 if (holder instanceof DateViewHolder) { 512 if (holder instanceof DateViewHolder) {
483 ((DateViewHolder) holder).setDate(pair.first); 513 ((DateViewHolder) holder).setDate(pair.first);
484 } else if (!(holder instanceof BasicViewHolder)) { 514 } else if (!(holder instanceof BasicViewHolder)) {
485 bindViewHolderForTimedItem(holder, pair.second); 515 bindViewHolderForTimedItem(holder, pair.second);
486 } 516 }
487 } 517 }
488 518
489 @Override 519 @Override
490 public final int getItemCount() { 520 public final int getItemCount() {
(...skipping 24 matching lines...) Expand all
515 assert false; 545 assert false;
516 return null; 546 return null;
517 } 547 }
518 548
519 /** 549 /**
520 * @param item The item to remove from the adapter. 550 * @param item The item to remove from the adapter.
521 */ 551 */
522 protected void removeItem(TimedItem item) { 552 protected void removeItem(TimedItem item) {
523 ItemGroup group = getGroupAt(item.getPosition()).first; 553 ItemGroup group = getGroupAt(item.getPosition()).first;
524 group.removeItem(item); 554 group.removeItem(item);
525 mSize--;
526 555
527 // Remove the group if only the date header is left. 556 // Remove the group if only the date header is left.
528 if (group.size() == 1) { 557 if (group.size() == 1) {
529 mGroups.remove(group); 558 mGroups.remove(group);
530 mSize--;
531 } 559 }
532 560
561 computeItemCount();
Theresa 2017/02/10 00:55:12 If there are a lot of item groups, this is less ef
gone 2017/02/10 02:31:45 +1 again. I asked about this in Patch Set 1.
shaktisahu 2017/02/10 21:30:25 I thought this makes it cleaner (also used when ad
gone 2017/02/10 22:49:52 Cleanliness < efficiency sometimes, and depending
shaktisahu 2017/02/11 00:39:06 Done.
533 setGroupPositions(); 562 setGroupPositions();
534 notifyDataSetChanged(); 563 notifyDataSetChanged();
535 } 564 }
536 565
566 protected void computeItemCount() {
567 mSize = 0;
568 for (ItemGroup group : mGroups) {
569 mSize += group.size();
570 }
571 }
572
537 /** 573 /**
538 * Creates a long ID that identifies a particular day in history. 574 * Creates a long ID that identifies a particular day in history.
539 * @param date Date to process. 575 * @param date Date to process.
540 * @return Long that has the day of the year (1-365) in the lowest 16 bits a nd the year in the 576 * @return Long that has the day of the year (1-365) in the lowest 16 bits a nd the year in the
541 * next 16 bits over. 577 * next 16 bits over.
542 */ 578 */
543 private static long getStableIdFromDate(Date date) { 579 private static long getStableIdFromDate(Date date) {
544 Pair<Calendar, Calendar> pair = getCachedCalendars(); 580 Pair<Calendar, Calendar> pair = getCachedCalendars();
545 Calendar calendar = pair.first; 581 Calendar calendar = pair.first;
546 calendar.setTime(date); 582 calendar.setTime(date);
547 long dayOfYear = calendar.get(Calendar.DAY_OF_YEAR); 583 long dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
548 long year = calendar.get(Calendar.YEAR); 584 long year = calendar.get(Calendar.YEAR);
549 return (year << 16) + dayOfYear; 585 return (year << 16) + dayOfYear;
550 } 586 }
551 587
552 /** 588 /**
553 * Compares two {@link Date}s. Note if you already have two {@link Calendar} objects, use 589 * Compares two {@link Date}s. Note if you already have two {@link Calendar} objects, use
554 * {@link #compareCalendar(Calendar, Calendar)} instead. 590 * {@link #compareCalendar(Calendar, Calendar)} instead.
555 * @return 0 if date1 and date2 are in the same day; 1 if date1 is before da te2; -1 otherwise. 591 * @return 0 if date1 and date2 are in the same day; 1 if date1 is before da te2; -1 otherwise.
556 */ 592 */
557 private static int compareDate(Date date1, Date date2) { 593 protected static int compareDate(Date date1, Date date2) {
558 Pair<Calendar, Calendar> pair = getCachedCalendars(); 594 Pair<Calendar, Calendar> pair = getCachedCalendars();
559 Calendar cal1 = pair.first, cal2 = pair.second; 595 Calendar cal1 = pair.first, cal2 = pair.second;
560 cal1.setTime(date1); 596 cal1.setTime(date1);
561 cal2.setTime(date2); 597 cal2.setTime(date2);
562 return compareCalendar(cal1, cal2); 598 return compareCalendar(cal1, cal2);
563 } 599 }
564 600
565 /** 601 /**
566 * @return 0 if cal1 and cal2 are in the same day; 1 if cal1 happens before cal2; -1 otherwise. 602 * @return 0 if cal1 and cal2 are in the same day; 1 if cal1 happens before cal2; -1 otherwise.
567 */ 603 */
(...skipping 30 matching lines...) Expand all
598 */ 634 */
599 private static AsyncTask<Void, Void, Calendar> createCalendar() { 635 private static AsyncTask<Void, Void, Calendar> createCalendar() {
600 return new AsyncTask<Void, Void, Calendar>() { 636 return new AsyncTask<Void, Void, Calendar>() {
601 @Override 637 @Override
602 protected Calendar doInBackground(Void... unused) { 638 protected Calendar doInBackground(Void... unused) {
603 return Calendar.getInstance(); 639 return Calendar.getInstance();
604 } 640 }
605 }.execute(); 641 }.execute();
606 } 642 }
607 } 643 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698