package net.bible.android.control.page.window;
import java.util.List;
import net.bible.android.control.event.apptobackground.AppToBackgroundEvent;
import net.bible.android.control.event.passage.PassageChangedEvent;
import net.bible.android.control.event.window.ScrollSecondaryWindowEvent;
import net.bible.android.control.event.window.UpdateSecondaryWindowEvent;
import net.bible.android.control.page.CurrentPage;
import net.bible.android.control.page.UpdateTextTask;
import net.bible.service.device.ScreenSettings;
import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookCategory;
import org.crosswire.jsword.passage.Key;
import org.crosswire.jsword.passage.KeyUtil;
import org.crosswire.jsword.passage.Verse;
import org.crosswire.jsword.versification.Versification;
import de.greenrobot.event.EventBus;
public class WindowSync {
private boolean isFirstTimeInit = true;
private boolean resynchRequired = false;
private boolean screenPreferencesChanged = false;
private Key lastSynchdInactiveWindowKey;
private boolean lastSynchWasInNightMode;
private WindowRepository windowRepository;
public WindowSync(WindowRepository windowRepository) {
this.windowRepository = windowRepository;
// register for passage change and fore/background events
EventBus.getDefault().register(this);
}
public void onEvent(PassageChangedEvent event) {
synchronizeScreens();
}
/**
* Save/restore dynamic state that is not automatically saved as Preferences
*/
public void onEvent(AppToBackgroundEvent event) {
if (event.isMovedToBackground()) {
// ensure nonactive screen is initialised when returning from background
lastSynchdInactiveWindowKey = null;
} else {
lastSynchdInactiveWindowKey = null;
}
}
/** Synchronise the inactive key and inactive screen with the active key and screen if required
*/
public void synchronizeScreens() {
Window activeWindow = windowRepository.getActiveWindow();
CurrentPage activePage = activeWindow.getPageManager().getCurrentPage();
Key targetActiveWindowKey = activePage.getSingleKey();
List<Window> inactiveWindowList = windowRepository.getWindowsToSynchronise();
for (Window inactiveWindow : inactiveWindowList) {
CurrentPage inactivePage = inactiveWindow.getPageManager().getCurrentPage();
Key inactiveWindowKey = inactivePage.getSingleKey();
boolean inactiveUpdated = false;
boolean isTotalRefreshRequired = isFirstTimeInit || lastSynchWasInNightMode!=ScreenSettings.isNightMode() || screenPreferencesChanged || resynchRequired;
if (isSynchronizableVerseKey(activePage) && activeWindow.isSynchronised() && inactiveWindow.isSynchronised()) {
// inactive screen may not be displayed (e.g. if viewing a dict) but if switched to the key must be correct
// Only Bible and cmtry are synch'd and they share a Verse key
updateInactiveBibleKey(inactiveWindow, targetActiveWindowKey);
if (isSynchronizableVerseKey(inactivePage) && inactiveWindow.isVisible()) {
// re-get as it may have been mapped to the correct v11n
// this looks odd but the inactivePage key has already been updated to the activeScreenKey
targetActiveWindowKey = inactivePage.getSingleKey();
// prevent infinite loop as each screen update causes a synchronise by comparing last key
// only update pages if empty or synchronised
if (isFirstTimeInit || resynchRequired ||
(!targetActiveWindowKey.equals(lastSynchdInactiveWindowKey)) ) {
updateInactiveWindow(inactiveWindow, inactivePage, targetActiveWindowKey, lastSynchdInactiveWindowKey, isTotalRefreshRequired);
inactiveUpdated = true;
}
}
}
// force inactive screen to display something otherwise it may be initially blank
// or if nightMode has changed then force an update
if (!inactiveUpdated && isTotalRefreshRequired) {
// force an update of the inactive page to prevent blank screen
updateInactiveWindow(inactiveWindow, inactivePage, inactiveWindowKey, inactiveWindowKey, isTotalRefreshRequired);
inactiveUpdated = true;
}
}
lastSynchdInactiveWindowKey = targetActiveWindowKey;
lastSynchWasInNightMode = ScreenSettings.isNightMode();
screenPreferencesChanged = false;
resynchRequired = false;
isFirstTimeInit = false;
}
/** Only call if screens are synchronised. Update synch'd keys even if inactive page not shown so if it is shown then it is correct
*/
private void updateInactiveBibleKey(Window inactiveWindow, Key activeWindowKey) {
inactiveWindow.getPageManager().getCurrentBible().doSetKey(activeWindowKey);
}
/** refresh/synch inactive screen if required
*/
private void updateInactiveWindow(Window inactiveWindow, CurrentPage inactivePage, Key targetKey, Key inactiveWindowKey, boolean forceRefresh) {
// standard null checks
if (targetKey!=null && inactivePage!=null) {
// Not just bibles and commentaries get this far so NOT always fine to convert key to verse
Verse targetVerse = null;
Versification targetV11n = null;
if (targetKey instanceof Verse) {
targetVerse = KeyUtil.getVerse(targetKey);
targetV11n = targetVerse.getVersification();
}
Verse currentVerse = null;
if (inactiveWindowKey!=null && inactiveWindowKey instanceof Verse) {
currentVerse = KeyUtil.getVerse(inactiveWindowKey);
}
// update inactive screens as smoothly as possible i.e. just jump/scroll if verse is on current page
if (!forceRefresh &&
BookCategory.BIBLE.equals(inactivePage.getCurrentDocument().getBookCategory()) &&
currentVerse!=null && targetVerse!=null && targetV11n.isSameChapter(targetVerse, currentVerse)) {
EventBus.getDefault().post(new ScrollSecondaryWindowEvent(inactiveWindow, targetVerse.getVerse()));
} else {
new UpdateInactiveScreenTextTask().execute(inactiveWindow);
}
}
}
/** Only Bibles and commentaries have the same sort of key and can be synchronized
*/
private boolean isSynchronizableVerseKey(CurrentPage page) {
boolean result = false;
// various null checks then the test
if (page!=null) {
Book document = page.getCurrentDocument();
if (document!=null) {
BookCategory bookCategory = document.getBookCategory();
// The important part
result = BookCategory.BIBLE.equals(bookCategory) || BookCategory.COMMENTARY.equals(bookCategory);
}
}
return result;
}
private class UpdateInactiveScreenTextTask extends UpdateTextTask {
/** callback from base class when result is ready */
@Override
protected void showText(String text, Window window, int verseNo, float yOffsetRatio) {
EventBus.getDefault().post(new UpdateSecondaryWindowEvent(window, text, verseNo));
}
}
public void setResynchRequired(boolean resynchRequired) {
this.resynchRequired = resynchRequired;
}
public void setScreenPreferencesChanged(boolean screenPreferencesChanged) {
this.screenPreferencesChanged = screenPreferencesChanged;
}
}