package org.wikipedia.feed; import android.content.Context; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import org.wikipedia.dataclient.WikiSite; import org.wikipedia.feed.dataclient.FeedClient; import org.wikipedia.feed.model.Card; import org.wikipedia.feed.offline.OfflineCard; import org.wikipedia.feed.progress.ProgressCard; import org.wikipedia.settings.Prefs; import org.wikipedia.util.ThrowableUtil; import org.wikipedia.util.log.L; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; public abstract class FeedCoordinatorBase { private static final int MAX_HIDDEN_CARDS = 100; public interface FeedUpdateListener { void insert(Card card, int pos); void swap(Card card, int pos); void remove(Card card, int pos); } @NonNull private Context context; @Nullable private WikiSite wiki; @Nullable private FeedUpdateListener updateListener; @NonNull private final List<Card> cards = new ArrayList<>(); private int currentAge; private List<FeedClient> pendingClients = new ArrayList<>(); private FeedClient.Callback callback = new ClientRequestCallback(); private Card progressCard = new ProgressCard(); private Set<String> hiddenCards = Collections.newSetFromMap(new LinkedHashMap<String, Boolean>() { @Override public boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) { return size() > MAX_HIDDEN_CARDS; } }); public FeedCoordinatorBase(@NonNull Context context) { this.context = context; hiddenCards.addAll(Prefs.getHiddenCards()); } @NonNull public List<Card> getCards() { return cards; } public void setFeedUpdateListener(@Nullable FeedUpdateListener listener) { updateListener = listener; } public void reset() { wiki = null; currentAge = 0; for (FeedClient client : pendingClients) { client.cancel(); } pendingClients.clear(); cards.clear(); } public void more(@NonNull WikiSite wiki) { this.wiki = wiki; if (cards.size() == 0) { insertCard(progressCard, 0); } if (cards.size() > 1) { currentAge++; } buildScript(currentAge); requestNextCard(wiki); } public boolean finished() { return pendingClients.isEmpty(); } public int getAge() { return currentAge; } public int dismissCard(@NonNull Card card) { int position = cards.indexOf(card); addHiddenCard(card); removeCard(card, position); return position; } public void undoDismissCard(@NonNull Card card, int position) { unHideCard(card); insertCard(card, position); } void retryFromOffline(@NonNull WikiSite wiki) { Card card = cards.get(lastIndex()); if (card instanceof OfflineCard) { swapCard(progressCard, lastIndex()); } currentAge--; more(wiki); } protected abstract void buildScript(int age); protected void addPendingClient(FeedClient client) { pendingClients.add(client); } private int lastIndex() { return cards.size() - 1; } private void requestNextCard(@NonNull WikiSite wiki) { if (pendingClients.isEmpty()) { removeProgressCard(); return; } pendingClients.remove(0).request(context, wiki, currentAge, callback); } private void removeProgressCard() { int pos = cards.indexOf(progressCard); if (pos < 0) { return; } removeCard(progressCard, pos); } private class ClientRequestCallback implements FeedClient.Callback { @Override public void success(@NonNull List<? extends Card> cardList) { for (Card card : cardList) { if (!isCardHidden(card)) { appendCard(card); } } //noinspection ConstantConditions requestNextCard(wiki); } @Override public void error(@NonNull Throwable caught) { if (ThrowableUtil.isOffline(caught)) { appendCard(new OfflineCard()); removeProgressCard(); } else { //noinspection ConstantConditions requestNextCard(wiki); L.w(caught); } } } private void appendCard(@NonNull Card card) { int progressPos = cards.indexOf(progressCard); insertCard(card, progressPos >= 0 ? progressPos : cards.size()); } private void insertCard(@NonNull Card card, int position) { cards.add(position, card); if (updateListener != null) { updateListener.insert(card, position); } } private void swapCard(@NonNull Card card, int position) { cards.remove(cards.get(position)); cards.add(position, card); if (updateListener != null) { updateListener.swap(card, position); } } private void removeCard(@NonNull Card card, int position) { cards.remove(card); if (updateListener != null) { updateListener.remove(card, position); } } private void addHiddenCard(@NonNull Card card) { hiddenCards.add(card.getHideKey()); Prefs.setHiddenCards(hiddenCards); } private boolean isCardHidden(@NonNull Card card) { return hiddenCards.contains(card.getHideKey()); } private void unHideCard(@NonNull Card card) { hiddenCards.remove(card.getHideKey()); Prefs.setHiddenCards(hiddenCards); } }