/* * Copyright (c) 2015 yvolk (Yuri Volkov), http://yurivolkov.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.andstatus.app.msg; import android.support.annotation.NonNull; import android.support.v4.util.Pair; import org.andstatus.app.WhichPage; import org.andstatus.app.list.ListData; import org.andstatus.app.util.MyLog; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; /** * @author yvolk@yurivolkov.com */ public class TimelineData extends ListData { private static final int MAX_PAGES_COUNT = 5; final List<TimelinePage> pages; final long updatedAt = MyLog.uniqueCurrentTimeMS(); final TimelineListParameters params; final boolean isSameTimeline; public TimelineData(TimelineData oldData, @NonNull TimelinePage thisPage) { super(oldData); this.params = thisPage.params; isSameTimeline = oldData != null && params.getContentUri().equals(oldData.params.getContentUri()); this.pages = isSameTimeline ? copyPages(oldData.pages) : new ArrayList<TimelinePage>(); addThisPage(thisPage); collapseDuplicates(isCollapseDuplicates(), 0); dropExcessivePage(thisPage); } private List<TimelinePage> copyPages(List<TimelinePage> pages) { ArrayList<TimelinePage> copiedPages = new ArrayList<>(); for (TimelinePage page : pages) { copiedPages.add(page); } return copiedPages; } private void dropExcessivePage(TimelinePage lastLoadedPage) { if (pages.size() > MAX_PAGES_COUNT) { if (lastLoadedPage.params.whichPage == WhichPage.YOUNGER) { pages.remove(pages.size() - 1); } else { pages.remove(0); } } } private void addThisPage(TimelinePage page) { switch (page.params.whichPage) { case YOUNGEST: if (mayHaveYoungerPage()) { pages.clear(); pages.add(page); } else { removeDuplicatesWithOlder(page, 1); pages.remove(0); pages.add(0, page); } break; case CURRENT: case TOP: pages.clear(); pages.add(page); break; case OLDER: removeDuplicatesWithYounger(page, pages.size() - 1); pages.add(page); break; case YOUNGER: removeDuplicatesWithOlder(page, 0); pages.add(0, page); break; default: if (pages.size() < 2) { pages.clear(); pages.add(page); } else { int found = -1; for (int ind = 0; ind < pages.size(); ind++) { TimelinePage p = pages.get(ind); if (p.params.maxSentDate == page.params.maxSentDate && p.params.minSentDate == page.params.minSentDate) { found = ind; break; } } if (found >= 0) { removeDuplicatesWithYounger(page, found - 1); removeDuplicatesWithOlder(page, found + 1); pages.remove(found); pages.add(found, page); } else { pages.add(page); } } break; } } private void removeDuplicatesWithYounger(TimelinePage page, int indExistingPage) { if (indExistingPage < 0 || indExistingPage >= pages.size() || pages.get(indExistingPage).items.isEmpty() || page.items.isEmpty()) { return; } TimelinePage ePage = pages.get(indExistingPage); if (ePage.params.maxSentDate > 0 && page.params.maxSentDate >= ePage.params.maxSentDate) { MyLog.v(this, "Previous younger page removed"); pages.remove(indExistingPage); return; } long edgeDate = ePage.params.minSentDateLoaded; List<TimelineViewItem> toRemove = new ArrayList<>(); for (int ind = 0; ind < page.items.size(); ind++) { TimelineViewItem item = page.items.get(ind); if (item.sentDate < edgeDate) { break; } else if (item.sentDate > edgeDate) { MyLog.e(this, "This page has an item younger than on a younger page: " + item); toRemove.add(item); } else { for (int eInd = ePage.items.size() - 1; eInd >= 0; eInd--) { TimelineViewItem eItem = ePage.items.get(eInd); if (eItem.sentDate > item.sentDate) { break; } if (eItem.getMsgId() == item.getMsgId()) { mergeWithExisting(item, eItem); toRemove.add(item); break; } } } } page.items.removeAll(toRemove); } private void mergeWithExisting(TimelineViewItem newItem, TimelineViewItem existingItem) { // TODO: Merge something... } private void removeDuplicatesWithOlder(TimelinePage page, int indExistingPage) { if (indExistingPage < 0 || indExistingPage >= pages.size() || pages.get(indExistingPage).items.isEmpty() || page.items.isEmpty()) { return; } TimelinePage ePage = pages.get(indExistingPage); if (page.params.minSentDate <= ePage.params.minSentDate) { MyLog.v(this, "Previous older page removed"); pages.remove(indExistingPage); return; } long edgeDate = ePage.params.maxSentDateLoaded; List<TimelineViewItem> toRemove = new ArrayList<>(); for (int ind = page.items.size() - 1; ind >= 0; ind--) { TimelineViewItem item = page.items.get(ind); if ( item.sentDate > edgeDate) { break; } else if (item.sentDate < edgeDate) { MyLog.e(this, "This page has an item older than on an older page: " + item); toRemove.add(item); } else { for (int eInd = 0; eInd < ePage.items.size(); eInd++) { TimelineViewItem eItem = ePage.items.get(eInd); if (eItem.sentDate < item.sentDate) { break; } if (eItem.getMsgId() == item.getMsgId()) { mergeWithExisting(item, eItem); toRemove.add(item); break; } } } } page.items.removeAll(toRemove); } @Override public int size() { int count = 0; for (TimelinePage page : pages) { count += page.items.size(); } return count; } @Override public TimelineViewItem getItem(int position) { int firstPosition = 0; for (TimelinePage page : pages) { if (position < firstPosition) { break; } if (position < firstPosition + page.items.size()) { return page.items.get(position - firstPosition); } firstPosition += page.items.size(); } return TimelineViewItem.getEmpty(); } public TimelineViewItem getById(long itemId) { for (TimelinePage page : pages) { for (TimelineViewItem item : page.items) { if (item.getMsgId() == itemId) { return item; } } } return TimelineViewItem.getEmpty(); } public boolean mayHaveYoungerPage() { return pages.size() == 0 || pages.get(0).params.mayHaveYoungerPage(); } public boolean mayHaveOlderPage() { return pages.size() == 0 || pages.get(pages.size() - 1).params.mayHaveOlderPage(); } @Override public String toString() { String s = "pages:" + pages.size() + ", total items:" + size() + ","; for (TimelinePage page : pages) { s += "\nPage size:" + page.items.size() + ", params: " + page.params + ","; } return MyLog.formatKeyValue(this, s ); } /** For all or for only one item */ @Override public void collapseDuplicates(boolean collapse, long itemId) { super.collapseDuplicates(collapse, itemId); if (collapse) { collapseDuplicates(itemId); } else { showDuplicates(itemId); } } private void collapseDuplicates(long itemId) { Set<Pair<TimelinePage, TimelineViewItem>> toCollapse = new HashSet<>(); innerCollapseDuplicates(itemId, toCollapse); for (Pair<TimelinePage, TimelineViewItem> pair : toCollapse) { pair.first.items.remove(pair.second); } } private void innerCollapseDuplicates(long itemId, Collection<Pair<TimelinePage, TimelineViewItem>> toCollapse) { Pair<TimelinePage, TimelineViewItem> parent = new Pair<>(null, null); Set<Pair<TimelinePage, TimelineViewItem>> group = new HashSet<>(); for (TimelinePage page : pages) { for (TimelineViewItem item : page.items) { Pair<TimelinePage, TimelineViewItem> itemPair =new Pair<>(page, item); switch (item.duplicates(parent.second)) { case DUPLICATES: break; case IS_DUPLICATED: parent = itemPair; break; default: if (collapseThisGroup(itemId, parent, group, toCollapse)) { return; } group.clear(); parent = itemPair; break; } group.add(itemPair); } } collapseThisGroup(itemId, parent, group, toCollapse); } private boolean collapseThisGroup(long itemId, Pair<TimelinePage, TimelineViewItem> parent, Set<Pair<TimelinePage, TimelineViewItem>> group, Collection<Pair<TimelinePage, TimelineViewItem>> toCollapse) { if (group.isEmpty()) { return false; } boolean groupOfSelectedItem = false; if (itemId != 0) { for (Pair<TimelinePage, TimelineViewItem> itemPair : group) { if (itemId == itemPair.second.getMsgId()) { groupOfSelectedItem = true; break; } } } if (groupOfSelectedItem) { for (Pair<TimelinePage, TimelineViewItem> itemPair : group) { setIndividualCollapsedStatus(true, itemPair.second.getMsgId()); } } boolean hasIndividualCollapseState = false; if (!groupOfSelectedItem && !individualCollapsedStateIds.isEmpty()) { for (Pair<TimelinePage, TimelineViewItem> itemPair : group) { if (individualCollapsedStateIds.contains(itemPair.second.getMsgId())) { hasIndividualCollapseState = true; break; } } } if (!hasIndividualCollapseState) { for (Pair<TimelinePage, TimelineViewItem> itemPair : group) { if (!parent.equals(itemPair)) { parent.second.collapse(itemPair.second); toCollapse.add(itemPair); } } } return groupOfSelectedItem; } private void showDuplicates(long itemId) { for (TimelinePage page : pages) { for (int ind = page.items.size() - 1; ind >= 0; ind--) { if (page.items.get(ind).isCollapsed()) { if (showDuplicatesOfOneItem(itemId, page, ind)) { return; } } } } } private boolean showDuplicatesOfOneItem(long itemId, TimelinePage page, int ind) { TimelineViewItem item = page.items.get(ind); boolean groupOfSelectedItem = itemId == item.getMsgId(); if (itemId != 0 && !groupOfSelectedItem) { for (TimelineViewItem child : item.getChildren()) { if (itemId == child.getMsgId()) { groupOfSelectedItem = true; break; } } } if (groupOfSelectedItem) { setIndividualCollapsedStatus(false, item.getMsgId()); for (TimelineViewItem child : item.getChildren()) { setIndividualCollapsedStatus(false, child.getMsgId()); } } boolean hasIndividualCollapseState = false; if (!groupOfSelectedItem && !individualCollapsedStateIds.isEmpty()) { for (TimelineViewItem child : item.getChildren()) { if (individualCollapsedStateIds.contains(child.getMsgId())) { hasIndividualCollapseState = true; break; } } } if (!hasIndividualCollapseState && (itemId == 0 || groupOfSelectedItem)) { int ind2 = ind + 1; for (TimelineViewItem child : item.getChildren()) { page.items.add(ind2++, child); } item.getChildren().clear(); } return groupOfSelectedItem; } }