package net.bible.service.history; import android.app.Activity; import android.util.Log; import net.bible.android.control.ApplicationScope; import net.bible.android.control.event.ABEventBus; import net.bible.android.control.event.passage.BeforeCurrentPageChangeEvent; import net.bible.android.control.page.CurrentPage; import net.bible.android.control.page.window.Window; import net.bible.android.control.page.window.WindowControl; import net.bible.android.view.activity.base.AndBibleActivity; import net.bible.android.view.activity.base.CurrentActivityHolder; import net.bible.android.view.activity.page.MainBibleActivity; import org.crosswire.jsword.book.Book; import org.crosswire.jsword.passage.Key; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Stack; import javax.inject.Inject; /** * Application managed History List. * The HistoryManager keeps a different history list for each window. * * @see gnu.lgpl.License for license details.<br> * The copyright to this program is held by it's authors. * @author Martin Denham [mjdenham at gmail dot com] */ @ApplicationScope public class HistoryManager { private static int MAX_HISTORY = 80; private final Map<Window, Stack<HistoryItem>> screenHistoryStackMap = new HashMap<>(); private boolean isGoingBack = false; private final WindowControl windowControl; private static final String TAG = "HistoryManager"; @Inject public HistoryManager(WindowControl windowControl) { this.windowControl = windowControl; // register for BeforePageChangeEvent Log.i(TAG, "Registering HistoryManager with EventBus"); ABEventBus.getDefault().safelyRegister(this); } /** allow current page to save any settings or data before being changed */ public void onEvent(BeforeCurrentPageChangeEvent event) { beforePageChange(); } public boolean canGoBack() { return getHistoryStack().size()>0; } /** * called when a verse is changed to allow current Activity to be saved in History list */ public void beforePageChange() { // if we cause the change by requesting Back then ignore it if (!isGoingBack) { HistoryItem item = createHistoryItem(); add(getHistoryStack(), item); } } private HistoryItem createHistoryItem() { HistoryItem historyItem = null; Activity currentActivity = CurrentActivityHolder.getInstance().getCurrentActivity(); if (currentActivity instanceof MainBibleActivity) { CurrentPage currentPage = windowControl.getActiveWindowPageManager().getCurrentPage(); Book doc = currentPage.getCurrentDocument(); if (currentPage.getKey()==null) { return null; } Key key = currentPage.getSingleKey(); float yOffsetRatio = currentPage.getCurrentYOffsetRatio(); historyItem = new KeyHistoryItem(doc, key, yOffsetRatio, windowControl.getActiveWindow()); } else if (currentActivity instanceof AndBibleActivity) { AndBibleActivity andBibleActivity = (AndBibleActivity)currentActivity; if (andBibleActivity.isIntegrateWithHistoryManager()) { historyItem = new IntentHistoryItem(currentActivity.getTitle(), ((AndBibleActivity) currentActivity).getIntentForHistoryList(), windowControl.getActiveWindow()); } } return historyItem; } public void goBack() { if (getHistoryStack().size()>0) { try { Log.d(TAG, "History size:"+getHistoryStack().size()); isGoingBack = true; // pop the previous item HistoryItem previousItem = getHistoryStack().pop(); if (previousItem!=null) { Log.d(TAG, "Going back to:"+previousItem); previousItem.revertTo(); // finish current activity if not the Main screen Activity currentActivity = CurrentActivityHolder.getInstance().getCurrentActivity(); if (!(currentActivity instanceof MainBibleActivity)) { currentActivity.finish(); } } } finally { isGoingBack = false; } } } public List<HistoryItem> getHistory() { List<HistoryItem> allHistory = new ArrayList<>(getHistoryStack()); // reverse so most recent items are at top rather than end Collections.reverse(allHistory); return allHistory; } /** * Add item and check size of stack */ private synchronized void add(Stack<HistoryItem> stack, HistoryItem item) { if (item!=null) { if (stack.isEmpty() || !item.equals(stack.peek())) { Log.d(TAG, "Adding "+item+" to history"); Log.d(TAG, "Stack size:"+stack.size()); stack.push(item); while (stack.size()>MAX_HISTORY) { Log.d(TAG, "Shrinking large stack"); stack.remove(0); } } } } private Stack<HistoryItem> getHistoryStack() { Window window = windowControl.getActiveWindow(); Stack<HistoryItem> historyStack = screenHistoryStackMap.get(window); if (historyStack==null) { synchronized(screenHistoryStackMap) { historyStack = screenHistoryStackMap.get(window); if (historyStack==null) { historyStack = new Stack<>(); screenHistoryStackMap.put(window, historyStack); } } } return historyStack; } }