// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.download.home.list.mutator;
import android.util.Pair;
import org.chromium.chrome.browser.download.home.list.ListItem;
import org.chromium.chrome.browser.download.home.list.ListItem.CardDividerListItem;
import org.chromium.chrome.browser.download.home.list.ListUtils;
import org.chromium.chrome.browser.download.home.list.UiUtils;
import org.chromium.components.browser_ui.util.date.CalendarUtils;
import org.chromium.components.offline_items_collection.OfflineItem;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* Given a sorted list of offline items, generates a list of {@link ListItem} that has card header
* and footer items for group cards.
*/
public class GroupCardLabelAdder implements ListConsumer {
private static final long CARD_DIVIDER_MIDDLE_HASH_CODE_OFFSET = 200000;
private ListConsumer mListConsumer;
private CardPaginator mCardPaginator;
private long mDividerIndexId;
/** Constructor. */
public GroupCardLabelAdder(CardPaginator paginator) {
mCardPaginator = paginator;
}
@Override
public ListConsumer setListConsumer(ListConsumer consumer) {
mListConsumer = consumer;
return mListConsumer;
}
@Override
public void onListUpdated(List<ListItem> inputList) {
if (mListConsumer == null) return;
mListConsumer.onListUpdated(addLabels(inputList));
}
private List<ListItem> addLabels(List<ListItem> sortedList) {
mDividerIndexId = CARD_DIVIDER_MIDDLE_HASH_CODE_OFFSET;
List<ListItem> outList = new ArrayList<>();
List<ListItem> candidateCardItems = new ArrayList<>();
ListItem previousItem = null;
for (ListItem listItem : sortedList) {
if (listItem instanceof ListItem.OfflineItemListItem) {
((ListItem.OfflineItemListItem) listItem).isGrouped = false;
}
boolean addToExistingGroup =
ListUtils.canGroup(listItem)
&& ListUtils.canGroup(previousItem)
&& getDateAndDomainForItem(listItem)
.equals(getDateAndDomainForItem(previousItem));
if (addToExistingGroup) {
candidateCardItems.add(listItem);
} else {
// Add the grouped items that we have seen so far but didn't add into a card.
flushCandidateCardItemsToList(candidateCardItems, outList);
candidateCardItems.clear();
if (ListUtils.canGroup(listItem)) {
// The item is content indexed with a different domain. Start a new group.
candidateCardItems.add(listItem);
} else {
// The item is not content indexed. Just add it to the list.
outList.add(listItem);
}
}
previousItem = listItem;
}
flushCandidateCardItemsToList(candidateCardItems, outList);
return outList;
}
/**
* Flushes the candidate card items into the list. If the item count is greater than minimum
* threshold, creates a group card out of the items, otherwise adds them to the flat list. Adds
* the header and footer cards for the group card.
* @param candidateCardItems The items to be considered for creating a group card. They must
* have same domain.
* @param outList The output list that would be shown in the UI.
*/
private void flushCandidateCardItemsToList(
List<ListItem> candidateCardItems, List<ListItem> outList) {
if (candidateCardItems.isEmpty()) return;
if (candidateCardItems.size() < mCardPaginator.minItemCountPerCard()) {
// We don't have enough items to build a card. Just add them to the flat list.
outList.addAll(candidateCardItems);
return;
}
Pair<Date, String> dateAndDomain = getDateAndDomainForItem(candidateCardItems.get(0));
String url = ((ListItem.OfflineItemListItem) candidateCardItems.get(0)).item.url.getSpec();
mCardPaginator.initializeEntry(dateAndDomain);
// Add the card header, and the divider above it.
outList.add(createDivider(CardDividerListItem.Position.TOP));
outList.add(new ListItem.CardHeaderListItem(dateAndDomain, url));
int itemsBeforePagination = mCardPaginator.getItemCountForCard(dateAndDomain);
int numItemsToShow = Math.min(itemsBeforePagination, candidateCardItems.size());
// Add the list items and the associated dividers.
for (int i = 0; i < numItemsToShow; i++) {
ListItem.OfflineItemListItem listItem =
(ListItem.OfflineItemListItem) candidateCardItems.get(i);
listItem.isGrouped = true;
outList.add(listItem);
if (i < numItemsToShow - 1) {
outList.add(createDivider(CardDividerListItem.Position.MIDDLE));
}
}
if (candidateCardItems.size() > itemsBeforePagination) {
// Add the card footer and the divider below.
outList.add(createDivider(CardDividerListItem.Position.MIDDLE));
outList.add(new ListItem.CardFooterListItem(dateAndDomain));
}
outList.add(createDivider(CardDividerListItem.Position.BOTTOM));
}
private ListItem createDivider(CardDividerListItem.Position position) {
return new CardDividerListItem(mDividerIndexId++, position);
}
private static Pair<Date, String> getDateAndDomainForItem(ListItem listItem) {
assert ListUtils.canGroup(listItem);
OfflineItem offlineItem = ((ListItem.OfflineItemListItem) listItem).item;
Date date = CalendarUtils.getStartOfDay(offlineItem.creationTimeMs).getTime();
String domain = UiUtils.getDomainForItem(offlineItem);
return Pair.create(date, domain);
}
}