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

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: Some fix 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 11 matching lines...) Expand all
41 40
42 /** 41 /**
43 * Interface that the {@link Adapter} uses to interact with the items it man ages. 42 * Interface that the {@link Adapter} uses to interact with the items it man ages.
44 */ 43 */
45 public abstract static class TimedItem { 44 public abstract static class TimedItem {
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
Theresa 2017/02/03 22:36:45 It looks like you need to sync and rebase this CL.
shaktisahu 2017/02/04 18:57:43 Done.
52 /** See {@link #mPosition}. */ 51 /** See {@link #mPosition}. */
53 private final void setPosition(int position) { 52 public final void setPosition(int position) {
54 mPosition = position; 53 mPosition = position;
55 } 54 }
56 55
57 /** See {@link #mPosition}. */ 56 /** See {@link #mPosition}. */
58 public final int getPosition() { 57 public final int getPosition() {
59 return mPosition; 58 return mPosition;
60 } 59 }
61 60
62 /** @return The timestamp for this item. */ 61 /** @return The timestamp for this item. */
63 public abstract long getTimestamp(); 62 public abstract long getTimestamp();
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
108 107
109 protected static class BasicViewHolder extends RecyclerView.ViewHolder { 108 protected static class BasicViewHolder extends RecyclerView.ViewHolder {
110 public BasicViewHolder(View itemView) { 109 public BasicViewHolder(View itemView) {
111 super(itemView); 110 super(itemView);
112 } 111 }
113 } 112 }
114 113
115 /** 114 /**
116 * A bucket of items with the same date. 115 * A bucket of items with the same date.
117 */ 116 */
118 protected static class ItemGroup { 117 public static class ItemGroup {
119 private final Date mDate; 118 private final Date mDate;
120 private final List<TimedItem> mItems = new ArrayList<>(); 119 protected final List<TimedItem> mItems = new ArrayList<>();
121 120
122 /** Index of the header, relative to the full list. Must be set only on ce.*/ 121 /** Index of the header, relative to the full list. Must be set only on ce.*/
123 private int mIndex; 122 private int mIndex;
124 123
125 private boolean mIsSorted; 124 private boolean mIsSorted;
126 private boolean mIsListHeaderOrFooter; 125 private boolean mIsListHeaderOrFooter;
127 126
128 public ItemGroup(long timestamp) { 127 public ItemGroup(long timestamp) {
129 mDate = new Date(timestamp); 128 mDate = new Date(timestamp);
130 mIsSorted = true; 129 mIsSorted = true;
131 } 130 }
132 131
133 public void addItem(TimedItem item) { 132 public void addItem(TimedItem item) {
134 mItems.add(item); 133 mItems.add(item);
135 mIsSorted = mItems.size() == 1; 134 mIsSorted = mItems.size() == 1;
136 } 135 }
137 136
138 public void removeItem(TimedItem item) { 137 public void removeItem(TimedItem item) {
139 mItems.remove(item); 138 mItems.remove(item);
140 } 139 }
141 140
142 /** Records the position of all the TimedItems in this group, relative t o the full list. */ 141 /** Records the position of all the TimedItems in this group, relative t o the full list. */
143 public void setPosition(int index) { 142 public void setPosition(int index) {
144 assert mIndex == 0 || mIndex == TimedItem.INVALID_POSITION; 143 assert mIndex == 0 || mIndex == TimedItem.INVALID_POSITION;
145 mIndex = index; 144 mIndex = index;
146 145
147 sortIfNeeded(); 146 sortIfNeeded();
147 setPositionForItems(index + 1);
148 }
149
150 protected void setPositionForItems(int startIndex) {
151 int index = startIndex;
148 for (TimedItem item : mItems) { 152 for (TimedItem item : mItems) {
153 item.setPosition(index);
149 index += 1; 154 index += 1;
150 item.setPosition(index);
151 } 155 }
152 } 156 }
153 157
154 /** Unsets the position of all TimedItems in this group. */ 158 /** Unsets the position of all TimedItems in this group. */
155 public void resetPosition() { 159 public void resetPosition() {
156 mIndex = TimedItem.INVALID_POSITION; 160 mIndex = TimedItem.INVALID_POSITION;
157 for (TimedItem item : mItems) item.setPosition(TimedItem.INVALID_POS ITION); 161 for (TimedItem item : mItems) item.setPosition(TimedItem.INVALID_POS ITION);
158 } 162 }
159 163
160 /** 164 /**
161 * @return Whether the given date happens in the same day as the items i n this group. 165 * @return Whether the given date happens in the same day as the items i n this group.
162 */ 166 */
163 public boolean isSameDay(Date otherDate) { 167 public boolean isSameDay(Date otherDate) {
164 return compareDate(mDate, otherDate) == 0; 168 return compareDate(mDate, otherDate) == 0;
165 } 169 }
166 170
171 protected boolean isListHeaderOrFooter() {
Theresa 2017/02/03 19:53:06 Where is this called?
shaktisahu 2017/02/04 18:57:43 Done. Removed. Thanks!
172 return mIsListHeaderOrFooter;
173 }
174
175 protected Date getDate() {
176 return mDate;
177 }
178
167 /** 179 /**
168 * @return The size of this group. 180 * @return The size of this group.
169 */ 181 */
170 public int size() { 182 public int size() {
171 if (mIsListHeaderOrFooter) return 1; 183 if (mIsListHeaderOrFooter) return 1;
172 184
173 // Plus 1 to account for the date header. 185 // Plus 1 to account for the date header.
174 return mItems.size() + 1; 186 return mItems.size() + 1;
175 } 187 }
176 188
177 public TimedItem getItemAt(int index) { 189 public TimedItem getItemAt(int index) {
178 // 0 is allocated to the date header. The list header has no items. 190 // 0 is allocated to the date header. The list header has no items.
179 if (index == 0 || mIsListHeaderOrFooter) return null; 191 if (index == 0 || mIsListHeaderOrFooter) return null;
180 192
181 sortIfNeeded(); 193 sortIfNeeded();
182 return mItems.get(index - 1); 194 return mItems.get(index - 1);
183 } 195 }
184 196
185 /** 197 /**
186 * Rather than sorting the list each time a new item is added, the list is sorted when 198 * Rather than sorting the list each time a new item is added, the list is sorted when
187 * something requires a correct ordering of the items. 199 * something requires a correct ordering of the items.
188 */ 200 */
189 private void sortIfNeeded() { 201 protected void sortIfNeeded() {
190 if (mIsSorted) return; 202 if (mIsSorted) return;
191 mIsSorted = true; 203 mIsSorted = true;
192 204
193 Collections.sort(mItems, new Comparator<TimedItem>() { 205 Collections.sort(mItems, new Comparator<TimedItem>() {
194 @Override 206 @Override
195 public int compare(TimedItem lhs, TimedItem rhs) { 207 public int compare(TimedItem lhs, TimedItem rhs) {
196 // More recent items are listed first. Ideally we'd use Lon g.compare, but that 208 return compareItem(lhs, rhs);
197 // is an API level 19 call for some inexplicable reason.
198 long timeDelta = lhs.getTimestamp() - rhs.getTimestamp();
199 if (timeDelta > 0) {
200 return -1;
201 } else if (timeDelta == 0) {
202 return 0;
203 } else {
204 return 1;
205 }
206 } 209 }
207 }); 210 });
208 } 211 }
212
213 protected int compareItem(TimedItem lhs, TimedItem rhs) {
214 // More recent items are listed first. Ideally we'd use Long.compar e, but that
215 // is an API level 19 call for some inexplicable reason.
216 long timeDelta = lhs.getTimestamp() - rhs.getTimestamp();
217 if (timeDelta > 0) {
218 return -1;
219 } else if (timeDelta == 0) {
220 return 0;
221 } else {
222 return 1;
223 }
224 }
225
226 public int getItemViewType(int position) {
227 return TYPE_NORMAL;
228 }
209 } 229 }
210 230
211 // Cached async tasks to get the two Calendar objects, which are used when c omparing dates. 231 // Cached async tasks to get the two Calendar objects, which are used when c omparing dates.
212 private static final AsyncTask<Void, Void, Calendar> sCal1 = createCalendar( ); 232 private static final AsyncTask<Void, Void, Calendar> sCal1 = createCalendar( );
213 private static final AsyncTask<Void, Void, Calendar> sCal2 = createCalendar( ); 233 private static final AsyncTask<Void, Void, Calendar> sCal2 = createCalendar( );
214 234
215 public static final int TYPE_FOOTER = -2; 235 public static final int TYPE_FOOTER = -2;
216 public static final int TYPE_HEADER = -1; 236 public static final int TYPE_HEADER = -1;
217 public static final int TYPE_DATE = 0; 237 public static final int TYPE_DATE = 0;
218 public static final int TYPE_NORMAL = 1; 238 public static final int TYPE_NORMAL = 1;
239 public static final int TYPE_SUGGESTED_OFFLINE_PAGES_HEADER = 2;
Theresa 2017/02/03 19:53:06 DateDividedAdapter shouldn't know about offline pa
shaktisahu 2017/02/04 18:57:43 Yes, I changed this to TYPE_SUBSECTION which can b
219 240
220 private int mSize; 241 private int mSize;
221 private boolean mHasListHeader; 242 private boolean mHasListHeader;
222 private boolean mHasListFooter; 243 private boolean mHasListFooter;
223 244
224 private SortedSet<ItemGroup> mGroups = new TreeSet<>(new Comparator<ItemGrou p>() { 245 private SortedSet<ItemGroup> mGroups = new TreeSet<>(new Comparator<ItemGrou p>() {
225 @Override 246 @Override
226 public int compare(ItemGroup lhs, ItemGroup rhs) { 247 public int compare(ItemGroup lhs, ItemGroup rhs) {
227 return compareDate(lhs.mDate, rhs.mDate); 248 return compareDate(lhs.mDate, rhs.mDate);
228 } 249 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
267 * {@link #clear(boolean)} to remove previous items. 288 * {@link #clear(boolean)} to remove previous items.
268 */ 289 */
269 public void loadItems(List<? extends TimedItem> timedItems) { 290 public void loadItems(List<? extends TimedItem> timedItems) {
270 for (TimedItem timedItem : timedItems) { 291 for (TimedItem timedItem : timedItems) {
271 Date date = new Date(timedItem.getTimestamp()); 292 Date date = new Date(timedItem.getTimestamp());
272 boolean found = false; 293 boolean found = false;
273 for (ItemGroup group : mGroups) { 294 for (ItemGroup group : mGroups) {
274 if (group.isSameDay(date)) { 295 if (group.isSameDay(date)) {
275 found = true; 296 found = true;
276 group.addItem(timedItem); 297 group.addItem(timedItem);
277 mSize++;
278 break; 298 break;
279 } 299 }
280 } 300 }
281 if (!found) { 301 if (!found) {
282 // Create a new ItemGroup with the date for the new item. This i ncreases the 302 // Create a new ItemGroup with the date for the new item. This i ncreases the
283 // size by two because we add new views for the date and the ite m itself. 303 // size by two because we add new views for the date and the ite m itself.
284 ItemGroup newGroup = new ItemGroup(timedItem.getTimestamp()); 304 ItemGroup newGroup = createGroup(timedItem.getTimestamp());
285 newGroup.addItem(timedItem); 305 newGroup.addItem(timedItem);
286 mGroups.add(newGroup); 306 mGroups.add(newGroup);
287 mSize += 2;
288 } 307 }
289 } 308 }
290 309
310 computeItemCount();
291 setGroupPositions(); 311 setGroupPositions();
292 notifyDataSetChanged(); 312 notifyDataSetChanged();
293 } 313 }
294 314
315 protected ItemGroup createGroup(long timeStamp) {
316 ItemGroup group = new ItemGroup(timeStamp);
317 return group;
318 }
319
295 /** 320 /**
296 * Tells each group where they start in the list. 321 * Tells each group where they start in the list.
297 */ 322 */
298 private void setGroupPositions() { 323 protected void setGroupPositions() {
299 int startIndex = 0; 324 int startIndex = 0;
300 for (ItemGroup group : mGroups) { 325 for (ItemGroup group : mGroups) {
301 group.resetPosition(); 326 group.resetPosition();
302 group.setPosition(startIndex); 327 group.setPosition(startIndex);
303 startIndex += group.size(); 328 startIndex += group.size();
304 } 329 }
305 } 330 }
306 331
307 /** 332 /**
308 * Adds a header as the first group in this adapter. 333 * Adds a header as the first group in this adapter.
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after
399 } 424 }
400 425
401 @Override 426 @Override
402 public final int getItemViewType(int position) { 427 public final int getItemViewType(int position) {
403 Pair<ItemGroup, Integer> pair = getGroupAt(position); 428 Pair<ItemGroup, Integer> pair = getGroupAt(position);
404 if (pair.second == TYPE_HEADER) { 429 if (pair.second == TYPE_HEADER) {
405 return TYPE_HEADER; 430 return TYPE_HEADER;
406 } else if (pair.second == TYPE_FOOTER) { 431 } else if (pair.second == TYPE_FOOTER) {
407 return TYPE_FOOTER; 432 return TYPE_FOOTER;
408 } else if (pair.second == 0) { 433 } else if (pair.second == 0) {
409 return TYPE_DATE; 434 return TYPE_DATE;
Theresa 2017/02/03 19:53:06 If we're moving the responsibility of knowing the
shaktisahu 2017/02/04 18:57:43 Done.
410 } else { 435 } else {
411 return TYPE_NORMAL; 436 ItemGroup group = pair.first;
437 return group.getItemViewType(pair.second);
412 } 438 }
413 } 439 }
414 440
415 @Override 441 @Override
416 public final RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, in t viewType) { 442 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int view Type) {
417 if (viewType == TYPE_DATE) { 443 if (viewType == TYPE_DATE) {
418 return new DateViewHolder(LayoutInflater.from(parent.getContext()).i nflate( 444 return new DateViewHolder(LayoutInflater.from(parent.getContext()).i nflate(
419 getTimedItemViewResId(), parent, false)); 445 getTimedItemViewResId(), parent, false));
420 } else if (viewType == TYPE_NORMAL) { 446 } else if (viewType == TYPE_NORMAL) {
421 return createViewHolder(parent); 447 return createViewHolder(parent);
422 } else if (viewType == TYPE_HEADER) { 448 } else if (viewType == TYPE_HEADER) {
423 return createHeader(parent); 449 return createHeader(parent);
424 } else if (viewType == TYPE_FOOTER) { 450 } else if (viewType == TYPE_FOOTER) {
425 return createFooter(parent); 451 return createFooter(parent);
426 } 452 }
427 assert false; 453 assert false;
428 return null; 454 return null;
429 } 455 }
430 456
431 @Override 457 @Override
432 public final void onBindViewHolder(RecyclerView.ViewHolder holder, int posit ion) { 458 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
433 Pair<Date, TimedItem> pair = getItemAt(position); 459 Pair<Date, TimedItem> pair = getItemAt(position);
434 if (holder instanceof DateViewHolder) { 460 if (holder instanceof DateViewHolder) {
435 ((DateViewHolder) holder).setDate(pair.first); 461 ((DateViewHolder) holder).setDate(pair.first);
436 } else if (!(holder instanceof BasicViewHolder)) { 462 } else if (!(holder instanceof BasicViewHolder)) {
437 bindViewHolderForTimedItem(holder, pair.second); 463 bindViewHolderForTimedItem(holder, pair.second);
438 } 464 }
439 } 465 }
440 466
441 @Override 467 @Override
442 public final int getItemCount() { 468 public final int getItemCount() {
(...skipping 24 matching lines...) Expand all
467 assert false; 493 assert false;
468 return null; 494 return null;
469 } 495 }
470 496
471 /** 497 /**
472 * @param item The item to remove from the adapter. 498 * @param item The item to remove from the adapter.
473 */ 499 */
474 protected void removeItem(TimedItem item) { 500 protected void removeItem(TimedItem item) {
475 ItemGroup group = getGroupAt(item.getPosition()).first; 501 ItemGroup group = getGroupAt(item.getPosition()).first;
476 group.removeItem(item); 502 group.removeItem(item);
477 mSize--;
478 503
479 // Remove the group if only the date header is left. 504 // Remove the group if only the date header is left.
480 if (group.size() == 1) { 505 if (group.size() == 1) {
481 mGroups.remove(group); 506 mGroups.remove(group);
482 mSize--;
483 } 507 }
484 508
509 computeItemCount();
485 setGroupPositions(); 510 setGroupPositions();
486 notifyDataSetChanged(); 511 notifyDataSetChanged();
487 } 512 }
488 513
514 protected void computeItemCount() {
515 mSize = 0;
516 for (ItemGroup group : mGroups) {
517 mSize += group.size();
518 }
519 }
520
489 /** 521 /**
490 * Creates a long ID that identifies a particular day in history. 522 * Creates a long ID that identifies a particular day in history.
491 * @param date Date to process. 523 * @param date Date to process.
492 * @return Long that has the day of the year (1-365) in the lowest 16 bits a nd the year in the 524 * @return Long that has the day of the year (1-365) in the lowest 16 bits a nd the year in the
493 * next 16 bits over. 525 * next 16 bits over.
494 */ 526 */
495 private static long getStableIdFromDate(Date date) { 527 private static long getStableIdFromDate(Date date) {
496 Pair<Calendar, Calendar> pair = getCachedCalendars(); 528 Pair<Calendar, Calendar> pair = getCachedCalendars();
497 Calendar calendar = pair.first; 529 Calendar calendar = pair.first;
498 calendar.setTime(date); 530 calendar.setTime(date);
499 long dayOfYear = calendar.get(Calendar.DAY_OF_YEAR); 531 long dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
500 long year = calendar.get(Calendar.YEAR); 532 long year = calendar.get(Calendar.YEAR);
501 return (year << 16) + dayOfYear; 533 return (year << 16) + dayOfYear;
502 } 534 }
503 535
504 /** 536 /**
505 * Compares two {@link Date}s. Note if you already have two {@link Calendar} objects, use 537 * Compares two {@link Date}s. Note if you already have two {@link Calendar} objects, use
506 * {@link #compareCalendar(Calendar, Calendar)} instead. 538 * {@link #compareCalendar(Calendar, Calendar)} instead.
507 * @return 0 if date1 and date2 are in the same day; 1 if date1 is before da te2; -1 otherwise. 539 * @return 0 if date1 and date2 are in the same day; 1 if date1 is before da te2; -1 otherwise.
508 */ 540 */
509 private static int compareDate(Date date1, Date date2) { 541 protected static int compareDate(Date date1, Date date2) {
510 Pair<Calendar, Calendar> pair = getCachedCalendars(); 542 Pair<Calendar, Calendar> pair = getCachedCalendars();
511 Calendar cal1 = pair.first, cal2 = pair.second; 543 Calendar cal1 = pair.first, cal2 = pair.second;
512 cal1.setTime(date1); 544 cal1.setTime(date1);
513 cal2.setTime(date2); 545 cal2.setTime(date2);
514 return compareCalendar(cal1, cal2); 546 return compareCalendar(cal1, cal2);
515 } 547 }
516 548
517 /** 549 /**
518 * @return 0 if cal1 and cal2 are in the same day; 1 if cal1 happens before cal2; -1 otherwise. 550 * @return 0 if cal1 and cal2 are in the same day; 1 if cal1 happens before cal2; -1 otherwise.
519 */ 551 */
(...skipping 30 matching lines...) Expand all
550 */ 582 */
551 private static AsyncTask<Void, Void, Calendar> createCalendar() { 583 private static AsyncTask<Void, Void, Calendar> createCalendar() {
552 return new AsyncTask<Void, Void, Calendar>() { 584 return new AsyncTask<Void, Void, Calendar>() {
553 @Override 585 @Override
554 protected Calendar doInBackground(Void... unused) { 586 protected Calendar doInBackground(Void... unused) {
555 return Calendar.getInstance(); 587 return Calendar.getInstance();
556 } 588 }
557 }.execute(); 589 }.execute();
558 } 590 }
559 } 591 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698