package net.bible.android.control.page; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import net.bible.android.activity.R; import net.bible.android.control.PassageChangeMediator; import net.bible.service.common.ParseException; import net.bible.service.format.HtmlMessageFormatter; import net.bible.service.format.Note; import net.bible.service.sword.SwordContentFacade; import net.bible.service.sword.SwordDocumentFacade; import org.apache.commons.lang3.StringUtils; import org.crosswire.common.activate.Activator; import org.crosswire.jsword.book.Book; import org.crosswire.jsword.passage.Key; import org.json.JSONException; import org.json.JSONObject; import java.util.List; /** Common functionality for different document page types * * @author Martin Denham [mjdenham at gmail dot com] * @see gnu.lgpl.License for license details.<br> * The copyright to this program is held by it's author. */ abstract class CurrentPageBase implements CurrentPage { private Book currentDocument; private boolean inhibitChangeNotifications; private float currentYOffsetRatio; private Key keyWhenYOffsetRatioSet; private Book docWhenYOffsetRatioSet; private static final String TAG = "CurrentPage"; // all bibles and commentaries share the same key private boolean shareKeyBetweenDocs = false; private final SwordContentFacade swordContentFacade; private SwordDocumentFacade swordDocumentFacade; public abstract void doSetKey(Key key); protected CurrentPageBase(boolean shareKeyBetweenDocs, SwordContentFacade swordContentFacade, SwordDocumentFacade swordDocumentFacade) { super(); this.shareKeyBetweenDocs = shareKeyBetweenDocs; this.swordContentFacade = swordContentFacade; this.swordDocumentFacade = swordDocumentFacade; } /** notify mediator that page has changed and a lot of things need to update themselves */ protected void beforePageChange() { if (!isInhibitChangeNotifications()) { PassageChangeMediator.getInstance().onBeforeCurrentPageChanged(); } } /** notify mediator that page has changed and a lot of things need to update themselves */ protected void pageChange() { if (!isInhibitChangeNotifications()) { PassageChangeMediator.getInstance().onCurrentPageChanged(); } } @Override public Key getSingleKey() { // by default do not handle single key requirement - it is really just for bibles return getKey(); } @Override public void setKey(Key key) { beforePageChange(); doSetKey(key); pageChange(); } @Override public void next() { } @Override public void previous() { } /** add or subtract a number of pages from the current position and return Page * default is one key per page - all except bible use this default */ public Key getPagePlus(int num) { // If 1 key per page then same as getKeyPlus return getKeyPlus(num); } @Override public boolean isSingleKey() { return false; } @Override public String getCurrentPageContent() throws ParseException { String htmlText = swordContentFacade.readHtmlText(getCurrentDocument(), getKey()); if (StringUtils.isEmpty(htmlText)) { htmlText = HtmlMessageFormatter.format(R.string.error_no_content); } return htmlText; } @Override public List<Note> getCurrentPageFootnotesAndReferences() throws ParseException { List<Note> footnotes = swordContentFacade.readFootnotesAndReferences(getCurrentDocument(), getKey()); return footnotes; } public boolean checkCurrentDocumentStillInstalled() { if (currentDocument!=null) { Log.d(TAG, "checkCurrentDocumentStillInstalled:"+currentDocument); // this sets currentDoc to null if it does not exist currentDocument = swordDocumentFacade.getDocumentByInitials(currentDocument.getInitials()); } return currentDocument!=null; } /* (non-Javadoc) * @see net.bible.android.control.CurrentPage#getCurrentDocument() */ @Override public Book getCurrentDocument() { if (currentDocument==null) { List<Book> books = swordDocumentFacade.getBooks(getBookCategory()); if (books.size()>0) { currentDocument = books.get(0); } } return currentDocument; } /* Set new doc and if possible show new doc * @see net.bible.android.control.CurrentPage#setCurrentDocument(org.crosswire.jsword.book.Book) */ @Override public void setCurrentDocument(Book doc) { Log.d(TAG, "Set current doc to "+doc); Book prevDoc = currentDocument; if (!doc.equals(currentDocument) && !shareKeyBetweenDocs && getKey()!=null && !doc.contains(getKey())) { doSetKey(null); } localSetCurrentDocument(doc); // try to clear memory to prevent OutOfMemory errors if (!currentDocument.equals(prevDoc)) { Activator.deactivate(prevDoc); } } /* Set new doc and if possible show new doc * @see net.bible.android.control.CurrentPage#setCurrentDocument(org.crosswire.jsword.book.Book) */ @Override public void setCurrentDocumentAndKey(Book doc, Key key) { doSetKey(key); localSetCurrentDocument(doc); } protected void localSetCurrentDocument(Book doc) { this.currentDocument = doc; } @Override public void updateOptionsMenu(Menu menu) { // these are fine for Bible and commentary MenuItem menuItem = menu.findItem(R.id.searchButton); if (menuItem!=null) { menuItem.setEnabled(isSearchable()); } menuItem = menu.findItem(R.id.bookmarksButton); if (menuItem!=null) { menuItem.setEnabled(true); } menuItem = menu.findItem(R.id.speakButton); if (menuItem!=null) { menuItem.setEnabled(isSpeakable()); } } @Override public boolean isInhibitChangeNotifications() { return inhibitChangeNotifications; } @Override public void setInhibitChangeNotifications(boolean inhibitChangeNotifications) { this.inhibitChangeNotifications = inhibitChangeNotifications; } @Override public void restoreState(JSONObject jsonObject) throws JSONException { if (jsonObject!=null) { Log.d(TAG, "Restoring page state"); if (jsonObject.has("document")) { String document = jsonObject.getString("document"); if (StringUtils.isNotEmpty(document)) { Log.d(TAG, "State document:"+document); Book book = swordDocumentFacade.getDocumentByInitials(document); if (book!=null) { Log.d(TAG, "Restored document:"+book.getName()); // bypass setter to avoid automatic notifications localSetCurrentDocument(book); try { if (jsonObject.has("key")) { String keyName = jsonObject.getString("key"); if (StringUtils.isNotEmpty(keyName)) { doSetKey(book.getKey(keyName)); Log.d(TAG, "Restored key:"+keyName); } } } catch (Exception e) { Log.e(TAG, "Error restoring key for document category:"+getBookCategory().getName()); } } } } } } /** called during app close down to save state */ @Override public JSONObject getStateJson() throws JSONException { JSONObject object = new JSONObject(); if (currentDocument!=null) { Log.d(TAG, "Getting json state for "+getBookCategory().getName()); object.put("document", getCurrentDocument().getInitials()); if (this.getKey()!=null) { object.put("key", getKey().getName()); } } return object; } public boolean isShareKeyBetweenDocs() { return shareKeyBetweenDocs; } /** can we enable the main menu Speak button */ @Override public boolean isSpeakable() { return true; } /** how far down the page was the user - allows Back to go to correct line on non-Bible pages (Bibles use verse number for positioning) */ public float getCurrentYOffsetRatio() { try { // if key has changed then offsetRatio must be reset because user has changed page if (getKey()==null || !getKey().equals(keyWhenYOffsetRatioSet) || !getCurrentDocument().equals(docWhenYOffsetRatioSet)) { currentYOffsetRatio = 0; } } catch (Exception e) { // cope with occasional NPE thrown by above if statement // just pretend we are at the top of the page if error occurs currentYOffsetRatio = 0; Log.w(TAG, "NPE getting currentYOffsetRatio"); } return currentYOffsetRatio; } public void setCurrentYOffsetRatio(float currentYOffsetRatio) { this.docWhenYOffsetRatioSet = getCurrentDocument(); this.keyWhenYOffsetRatioSet = getKey(); this.currentYOffsetRatio = currentYOffsetRatio; } public SwordDocumentFacade getSwordDocumentFacade() { return swordDocumentFacade; } }