mirror of
https://github.com/MinimalBible/MinimalBible
synced 2024-11-25 01:18:17 -05:00
Kotlin migration is done!
There will of course be refactoring and whatnot, but I consider this an accomplishment.
This commit is contained in:
parent
0e7680ca9e
commit
187a73cf92
@ -2,7 +2,8 @@ package org.bspeice.minimalbible.activity.viewer;
|
|||||||
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.bspeice.minimalbible.service.book.VerseLookupModules;
|
import org.bspeice.minimalbible.service.lookup.DefaultVerseLookup;
|
||||||
|
import org.bspeice.minimalbible.service.lookup.VerseLookup;
|
||||||
import org.bspeice.minimalbible.service.manager.BookManager;
|
import org.bspeice.minimalbible.service.manager.BookManager;
|
||||||
import org.crosswire.jsword.book.Book;
|
import org.crosswire.jsword.book.Book;
|
||||||
|
|
||||||
@ -26,8 +27,7 @@ import rx.functions.Func1;
|
|||||||
BibleViewer.class,
|
BibleViewer.class,
|
||||||
BookFragment.class,
|
BookFragment.class,
|
||||||
BookChapterNavFragment.class
|
BookChapterNavFragment.class
|
||||||
},
|
}
|
||||||
includes = VerseLookupModules.class
|
|
||||||
)
|
)
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
public class BibleViewerModules {
|
public class BibleViewerModules {
|
||||||
@ -96,4 +96,10 @@ public class BibleViewerModules {
|
|||||||
BookManager bookManager() {
|
BookManager bookManager() {
|
||||||
return new BookManager();
|
return new BookManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
VerseLookup verseLookup(@Named("MainBook") Book b) {
|
||||||
|
return new DefaultVerseLookup(b);
|
||||||
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ import android.webkit.WebView;
|
|||||||
import org.bspeice.minimalbible.Injector;
|
import org.bspeice.minimalbible.Injector;
|
||||||
import org.bspeice.minimalbible.R;
|
import org.bspeice.minimalbible.R;
|
||||||
import org.bspeice.minimalbible.activity.BaseFragment;
|
import org.bspeice.minimalbible.activity.BaseFragment;
|
||||||
import org.bspeice.minimalbible.service.book.VerseLookupService;
|
import org.bspeice.minimalbible.service.lookup.VerseLookup;
|
||||||
import org.crosswire.jsword.book.Book;
|
import org.crosswire.jsword.book.Book;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
@ -20,7 +20,6 @@ import javax.inject.Named;
|
|||||||
|
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
import butterknife.InjectView;
|
import butterknife.InjectView;
|
||||||
import dagger.Lazy;
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
import rx.functions.Action1;
|
import rx.functions.Action1;
|
||||||
import rx.subjects.PublishSubject;
|
import rx.subjects.PublishSubject;
|
||||||
@ -35,18 +34,15 @@ public class BookFragment extends BaseFragment {
|
|||||||
Injector i;
|
Injector i;
|
||||||
@Inject
|
@Inject
|
||||||
@Named("MainBook")
|
@Named("MainBook")
|
||||||
Lazy<Book> mBook;
|
Book mBook;
|
||||||
|
@Inject
|
||||||
|
VerseLookup verseLookup;
|
||||||
|
|
||||||
@InjectView(R.id.book_content)
|
@InjectView(R.id.book_content)
|
||||||
WebView mainContent;
|
WebView mainContent;
|
||||||
|
|
||||||
PublishSubject<String> titleReceiver = PublishSubject.create();
|
PublishSubject<String> titleReceiver = PublishSubject.create();
|
||||||
|
|
||||||
public BookFragment() {
|
|
||||||
// We can't initialize the lookupService here since the fragment hasn't been tied
|
|
||||||
// to the parent activity yet.
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new instance of this fragment for the given book.
|
* Returns a new instance of this fragment for the given book.
|
||||||
*/
|
*/
|
||||||
@ -76,7 +72,7 @@ public class BookFragment extends BaseFragment {
|
|||||||
|
|
||||||
// TODO: Load initial text from SharedPreferences, rather than getting the actual book.
|
// TODO: Load initial text from SharedPreferences, rather than getting the actual book.
|
||||||
|
|
||||||
displayBook(mBook.get());
|
displayBook(mBook);
|
||||||
|
|
||||||
return rootView;
|
return rootView;
|
||||||
}
|
}
|
||||||
@ -103,8 +99,7 @@ public class BookFragment extends BaseFragment {
|
|||||||
((BibleViewer)getActivity()).setActionBarTitle(b.getInitials());
|
((BibleViewer)getActivity()).setActionBarTitle(b.getInitials());
|
||||||
mainContent.loadUrl(getString(R.string.book_html));
|
mainContent.loadUrl(getString(R.string.book_html));
|
||||||
|
|
||||||
VerseLookupService lookupService = new VerseLookupService(i, mBook.get());
|
BibleViewClient client = new BibleViewClient(b, verseLookup, titleReceiver);
|
||||||
BibleViewClient client = new BibleViewClient(b, lookupService, titleReceiver);
|
|
||||||
titleReceiver
|
titleReceiver
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(new Action1<String>() {
|
.subscribe(new Action1<String>() {
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
package org.bspeice.minimalbible.exception;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Error to be thrown when no books are currently installed.
|
|
||||||
*/
|
|
||||||
public class NoBooksInstalledException extends Exception {
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package org.bspeice.minimalbible.service.book;
|
|
||||||
|
|
||||||
import android.support.v4.util.LruCache;
|
|
||||||
|
|
||||||
import dagger.Module;
|
|
||||||
import dagger.Provides;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Created by bspeice on 9/1/14.
|
|
||||||
*/
|
|
||||||
@Module(injects = VerseLookupService.class)
|
|
||||||
public class VerseLookupModules {
|
|
||||||
private static final int MAX_SIZE = 1000000; // 1MB
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new LruCache. We're free to create new ones since they're all backed by the file
|
|
||||||
* system anyways.
|
|
||||||
*
|
|
||||||
* @return The LruCache to use
|
|
||||||
*/
|
|
||||||
@Provides
|
|
||||||
LruCache<String, String> getLruCache() {
|
|
||||||
return new LruCache<String, String>(MAX_SIZE);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
package org.bspeice.minimalbible.service.book;
|
|
||||||
|
|
||||||
import android.support.v4.util.LruCache;
|
|
||||||
|
|
||||||
import org.bspeice.minimalbible.Injector;
|
|
||||||
import org.bspeice.minimalbible.service.format.osisparser.OsisParser;
|
|
||||||
import org.crosswire.common.xml.SAXEventProvider;
|
|
||||||
import org.crosswire.jsword.book.Book;
|
|
||||||
import org.crosswire.jsword.book.BookData;
|
|
||||||
import org.crosswire.jsword.book.BookException;
|
|
||||||
import org.crosswire.jsword.passage.Verse;
|
|
||||||
import org.xml.sax.SAXException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import rx.functions.Action1;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
import rx.subjects.PublishSubject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class has a simple purpose, but implements the dirty work needed to make it happen.
|
|
||||||
* The idea is this: someone wants the text for a verse, we look it up quickly.
|
|
||||||
* This means aggressive caching, cache prediction, and classes letting us know that some verses
|
|
||||||
* may be needed in the future (i.e. searching).
|
|
||||||
* <p/>
|
|
||||||
* There is one VerseLookupService per Book, but multiple VerseLookupServices can work with
|
|
||||||
* the same book. Because the actual caching mechanism is disk-based, we're safe.
|
|
||||||
* <p/>
|
|
||||||
* TODO: Statistics on cache hits/misses vs. verses cached
|
|
||||||
*/
|
|
||||||
public class VerseLookupService implements Action1<Verse> {
|
|
||||||
|
|
||||||
Book book;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
LruCache<String, String> cache;
|
|
||||||
/**
|
|
||||||
* The listener is responsible for delegating calls to cache verses.
|
|
||||||
* This way, @notifyVerse can just tell the listener what's what,
|
|
||||||
* and the listener can delegate to another thread.
|
|
||||||
*/
|
|
||||||
private PublishSubject<Verse> listener = PublishSubject.create();
|
|
||||||
|
|
||||||
public VerseLookupService(Injector i, Book b) {
|
|
||||||
listener.subscribeOn(Schedulers.io())
|
|
||||||
.subscribe(this);
|
|
||||||
this.book = b;
|
|
||||||
i.inject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the text for a corresponding verse
|
|
||||||
* First, check the cache. If that doesn't exist, manually get the verse.
|
|
||||||
* In all cases, notify that we're looking up a verse so we can get the surrounding ones.
|
|
||||||
*
|
|
||||||
* @param v The verse to look up
|
|
||||||
* @return The JSON object for this verse (\<p\/>)
|
|
||||||
*/
|
|
||||||
public String getJsonVerse(Verse v) {
|
|
||||||
if (contains(v)) {
|
|
||||||
return cache.get(getEntryName(v));
|
|
||||||
} else {
|
|
||||||
// The awkward method calls below are so notifyVerse doesn't
|
|
||||||
// call the same doVerseLookup
|
|
||||||
String verseContent = doVerseLookup(v);
|
|
||||||
notifyVerse(v);
|
|
||||||
return verseContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform the ugly work of getting the actual data for a verse
|
|
||||||
* Note that we build the verse object, JS should be left to determine how
|
|
||||||
* it is displayed.
|
|
||||||
*
|
|
||||||
* @param v The verse to look up
|
|
||||||
* @return The JSON content of this verse
|
|
||||||
*/
|
|
||||||
public String doVerseLookup(Verse v) {
|
|
||||||
BookData bookData = new BookData(book, v);
|
|
||||||
try {
|
|
||||||
SAXEventProvider provider = bookData.getSAXEventProvider();
|
|
||||||
OsisParser handler = new OsisParser();
|
|
||||||
handler.setVerse(v);
|
|
||||||
provider.provideSAXEvents(handler);
|
|
||||||
return handler.getVerseContent().toJson();
|
|
||||||
} catch (BookException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return "Unable to locate " + v.toString() + "!";
|
|
||||||
} catch (SAXException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not necessary, but helpful if you let us know ahead of time we should pre-cache a verse.
|
|
||||||
* For example, if something showed up in search results, it'd be helpful to start
|
|
||||||
* looking up some of the results.
|
|
||||||
*
|
|
||||||
* @param v The verse we should pre-cache
|
|
||||||
*/
|
|
||||||
public void notifyVerse(Verse v) {
|
|
||||||
listener.onNext(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Let someone know if the cache contains a verse we want
|
|
||||||
* Also provides a nice wrapper if the underlying cache isn't working properly.
|
|
||||||
*
|
|
||||||
* @param v The verse to check
|
|
||||||
* @return Whether we can retrieve the verse from our cache
|
|
||||||
*/
|
|
||||||
public boolean contains(Verse v) {
|
|
||||||
return cache.get(getEntryName(v)) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a verse, what should it's name in the cache be?
|
|
||||||
* Example: Matthew 7:7 becomes:
|
|
||||||
* MAT_7_7
|
|
||||||
*
|
|
||||||
* @param v The verse we need to generate a name for
|
|
||||||
* @return The name this verse should have in the cache
|
|
||||||
*/
|
|
||||||
private String getEntryName(Verse v) {
|
|
||||||
return v.getBook().toString() + "_" +
|
|
||||||
v.getChapter() + "_" +
|
|
||||||
v.getVerse();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------
|
|
||||||
IO Thread operations below
|
|
||||||
------------------------------------------------------------------------*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The listener has let us know that we need to look up a verse. So, look up
|
|
||||||
* that one first, and get its surrounding verses as well just in case.
|
|
||||||
* We can safely assume we are not on the main thread.
|
|
||||||
*
|
|
||||||
* @param verse The verse we need to look up
|
|
||||||
*/
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void call(Verse verse) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +1,34 @@
|
|||||||
package org.bspeice.minimalbible.activity.viewer
|
package org.bspeice.minimalbible.activity.viewer
|
||||||
|
|
||||||
import org.crosswire.jsword.passage.Verse
|
import org.crosswire.jsword.passage.Verse
|
||||||
import org.bspeice.minimalbible.service.book.VerseLookupService
|
|
||||||
import android.webkit.WebViewClient
|
import android.webkit.WebViewClient
|
||||||
import android.webkit.JavascriptInterface
|
import android.webkit.JavascriptInterface
|
||||||
import org.crosswire.jsword.book.Book
|
import org.crosswire.jsword.book.Book
|
||||||
import java.util.ArrayList
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import rx.subjects.PublishSubject
|
import rx.subjects.PublishSubject
|
||||||
import org.crosswire.jsword.book.getVersification
|
import org.crosswire.jsword.book.getVersification
|
||||||
|
import org.bspeice.minimalbible.service.lookup.VerseLookup
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Created by bspeice on 9/14/14.
|
* Created by bspeice on 9/14/14.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BibleViewClient(val b: Book, val lookup: VerseLookupService,
|
class BibleViewClient(val b: Book, val lookup: VerseLookup,
|
||||||
val subject: PublishSubject<String>?) : WebViewClient() {
|
val subject: PublishSubject<String>?) : WebViewClient() {
|
||||||
|
|
||||||
// We can receive and return only primitives and Strings. Still means we can use JSON :)
|
// We can receive and return only primitives and Strings. Still means we can use JSON :)
|
||||||
JavascriptInterface fun getVerse(ordinal: Int): String {
|
JavascriptInterface fun getVerse(ordinal: Int): String {
|
||||||
val v = Verse(b.getVersification(), ordinal)
|
val v = Verse(b.getVersification(), ordinal)
|
||||||
// TODO: WebView should notify us what verse it's on
|
// TODO: WebView should notify us what verse it's on
|
||||||
subject?.onNext(v.getBook().toString() + " " + v.getChapter() + ":" + v.getVerse())
|
subject?.onNext("${v.getBook()} ${v.getChapter()}:${v.getVerse()}")
|
||||||
return lookup.getJsonVerse(v)
|
return lookup getJson v
|
||||||
}
|
}
|
||||||
|
|
||||||
JavascriptInterface fun getVerses(first: Int, count: Int): String {
|
JavascriptInterface fun getVerses(first: Int, count: Int): String {
|
||||||
Log.e("getVerses", "First: " + first + " count: " + count)
|
Log.e("getVerses", "First: $first count: $count")
|
||||||
val verses: MutableList<String> = ArrayList<String>()
|
val verses: MutableList<String> = linkedListOf()
|
||||||
var trueCount: Int
|
val trueCount: Int
|
||||||
var trueFirst: Int
|
val trueFirst: Int
|
||||||
when {
|
when {
|
||||||
first < 0 - count -> return ""
|
first < 0 - count -> return ""
|
||||||
first < 0 -> {
|
first < 0 -> {
|
||||||
@ -45,7 +44,7 @@ class BibleViewClient(val b: Book, val lookup: VerseLookupService,
|
|||||||
for (i in trueFirst..trueFirst + trueCount - 1) {
|
for (i in trueFirst..trueFirst + trueCount - 1) {
|
||||||
verses.add(getVerse(i))
|
verses.add(getVerse(i))
|
||||||
}
|
}
|
||||||
Log.e("getVerses", "return verses size: " + verses.size.toString())
|
Log.e("getVerses", "return verses size: ${verses.size}")
|
||||||
return verses.toString()
|
return verses.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
package org.bspeice.minimalbible.exception
|
||||||
|
|
||||||
|
class NoBooksInstalledException() : Exception("No books currently installed!") {}
|
@ -22,6 +22,8 @@ class OsisParser() : DefaultHandler() {
|
|||||||
// TODO: Implement a stack to keep min API 8
|
// TODO: Implement a stack to keep min API 8
|
||||||
val doWrite = ArrayDeque<Boolean>()
|
val doWrite = ArrayDeque<Boolean>()
|
||||||
|
|
||||||
|
fun getJson() = verseContent.toJson()
|
||||||
|
|
||||||
override fun startElement(uri: String, localName: String,
|
override fun startElement(uri: String, localName: String,
|
||||||
qName: String, attributes: Attributes) {
|
qName: String, attributes: Attributes) {
|
||||||
when (localName) {
|
when (localName) {
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
package org.bspeice.minimalbible.service.lookup
|
||||||
|
|
||||||
|
import org.crosswire.jsword.book.Book
|
||||||
|
import android.support.v4.util.LruCache
|
||||||
|
import rx.functions.Action1
|
||||||
|
import org.crosswire.jsword.passage.Verse
|
||||||
|
import rx.subjects.PublishSubject
|
||||||
|
import rx.schedulers.Schedulers
|
||||||
|
import org.crosswire.jsword.book.BookData
|
||||||
|
import org.bspeice.minimalbible.service.format.osisparser.OsisParser
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by bspeice on 11/12/14.
|
||||||
|
*/
|
||||||
|
open class VerseLookup(val b: Book,
|
||||||
|
val cache: LruCache<Int, String> = LruCache(1000000)) : Action1<Verse> {
|
||||||
|
/**
|
||||||
|
* The listener servers to let other objects notify us we should pre-cache verses
|
||||||
|
*/
|
||||||
|
val listener: PublishSubject<Verse> = PublishSubject.create();
|
||||||
|
|
||||||
|
{
|
||||||
|
listener.observeOn(Schedulers.io())
|
||||||
|
.subscribe(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getVerseId(v: Verse) = v.getOrdinal()
|
||||||
|
|
||||||
|
fun getJson(v: Verse): String =
|
||||||
|
if (contains(v))
|
||||||
|
cache[getVerseId(v)]
|
||||||
|
else {
|
||||||
|
val content = doLookup(v)
|
||||||
|
notify(v)
|
||||||
|
content
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform the ugly work of getting the actual data for a verse
|
||||||
|
* Note that we build the verse object, JS should be left to determine how
|
||||||
|
* it is displayed.
|
||||||
|
*
|
||||||
|
* @param v The verse to look up
|
||||||
|
* @return The JSON content of this verse
|
||||||
|
*/
|
||||||
|
fun doLookup(v: Verse): String {
|
||||||
|
val data = BookData(b, v)
|
||||||
|
val provider = data.getSAXEventProvider()
|
||||||
|
val handler = OsisParser()
|
||||||
|
handler.verse = v
|
||||||
|
provider provideSAXEvents handler
|
||||||
|
return handler.getJson()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Not necessary, but helpful if you let us know ahead of time we should pre-cache a verse.
|
||||||
|
* For example, if something showed up in search results, it'd be helpful to start
|
||||||
|
* looking up some of the results.
|
||||||
|
*
|
||||||
|
* @param v The verse we should pre-cache
|
||||||
|
*/
|
||||||
|
fun notify(v: Verse) = listener onNext v
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let someone know if the cache contains a verse we want
|
||||||
|
* Also provides a nice wrapper if the underlying cache isn't working properly.
|
||||||
|
*
|
||||||
|
* @param v The verse to check
|
||||||
|
* @return Whether we can retrieve the verse from our cache
|
||||||
|
*/
|
||||||
|
fun contains(v: Verse) = cache[v.getOrdinal()] != null
|
||||||
|
|
||||||
|
// IO Thread operations begin here
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Someone was nice enough to let us know that a verse was recently called,
|
||||||
|
* we should probably cache its neighbors!
|
||||||
|
*/
|
||||||
|
override fun call(t1: Verse?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class DefaultVerseLookup(b: Book) : VerseLookup(b) {}
|
Loading…
Reference in New Issue
Block a user