Refactor the tag parsing system

Much cleaner, I like this a whole lot more.
This commit is contained in:
Bradlee Speice
2014-12-01 11:51:13 -05:00
parent 7f221ed863
commit caf2227555
37 changed files with 117 additions and 2604 deletions

View File

@ -11,21 +11,17 @@ import org.crosswire.jsword.versification.getBooks
import org.crosswire.jsword.versification.BibleBook
import org.bspeice.minimalbible.activity.viewer.BookAdapter.ChapterInfo
import rx.subjects.PublishSubject
import org.bspeice.minimalbible.service.lookup.VerseLookup
import android.text.SpannableStringBuilder
import android.text.style.StyleSpan
import android.graphics.Typeface
import android.text.style.SuperscriptSpan
import android.text.style.RelativeSizeSpan
import android.util.TypedValue
import org.bspeice.minimalbible.service.format.osisparser.OsisParser
import android.util.Log
/**
* Adapter used for displaying a book
* Displays one chapter at a time,
* as each TextView widget is it's own line break
*/
class BookAdapter(val b: Book, val lookup: VerseLookup,
val prefs: BibleViewerPreferences)
class BookAdapter(val b: Book, val prefs: BibleViewerPreferences)
: RecyclerView.Adapter<PassageView>() {
val versification = b.getVersification()
@ -81,7 +77,7 @@ class BookAdapter(val b: Book, val lookup: VerseLookup,
// TODO: Prefs object for handling this?
emptyView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
val passage = PassageView(emptyView, b, lookup)
val passage = PassageView(emptyView, b)
return passage
}
@ -114,85 +110,20 @@ class BookAdapter(val b: Book, val lookup: VerseLookup,
}
}
class PassageView(val v: TextView, val b: Book, val lookup: VerseLookup)
class PassageView(val v: TextView, val b: Book)
: RecyclerView.ViewHolder(v) {
// Span to be applied to an individual verse - doesn't know about the sizes
// of other verses so that's why start and end are relative
/**
* A holder object that knows how apply itself to a SpannableStringBuilder
* Since we don't know ahead of time where this verse will end up relative to the
* entire TextView (since there is one chapter per TextView) we use a start and end
* relative to the verse text itself. That is, rStart of 0 indicates verse text start,
* and rEnd of (text.length - 1) indicates the end of verse text
* @param span The span object we should apply
* @param rStart When the span should begin, relative to the verse
* @param rEnd When the span should end, relative to the verse
*/
data class SpanHolder(val span: Any?, val rStart: Int, val rEnd: Int) {
// TODO: Is there a more case-class like way of doing this?
class object {
val EMPTY = SpanHolder(null, 0, 0)
}
/**
* Apply this span object to the specified builder
* Tries to be as close to immutable as possible. The offset is used to calculate
* the absolute position of when this span should start and end, since
* rStart and rEnd are relative to the verse text, and know nothing about the
* rest of the text in the TextView
*/
fun apply(builder: SpannableStringBuilder, offset: Int): SpannableStringBuilder {
if (span != null)
builder.setSpan(span, rStart + offset, rEnd + offset, 0)
return builder
}
fun buildOrdinal(verse: Int, info: ChapterInfo) =
b.getVersification().decodeOrdinal(verse + info.vOffset)
fun getAllVerses(verses: Progression<Int>, info: ChapterInfo): SpannableStringBuilder {
val builder = SpannableStringBuilder()
verses.forEach { OsisParser(builder).appendVerse(b, buildOrdinal(it, info)) }
return builder
}
// TODO: getRawVerse shouldn't know how to decode ints
fun getRawVerse(verse: Int): String =
lookup.getText(b.getVersification().decodeOrdinal(verse))
// TODO: This code is nasty, not sure how to refactor, but it needs doing
fun getProcessedVerse(verseOrdinal: Int, info: ChapterInfo): Pair<String, List<SpanHolder>> {
val rawText = getRawVerse(verseOrdinal)
// To be honest, I have no idea why I need to subtract one. But I do.
val relativeVerse = verseOrdinal - info.vOffset - 1
val processedText = when (relativeVerse) {
0 -> ""
1 -> "${info.chapter} $rawText"
else -> "$relativeVerse$rawText"
}
val spans: List<SpanHolder> = listOf(
when (relativeVerse) {
0 -> SpanHolder.EMPTY
1 -> SpanHolder(StyleSpan(Typeface.BOLD), 0, info.chapter.toString().length)
else -> SpanHolder(SuperscriptSpan(), 0, relativeVerse.toString().length)
}
)
val secondSpan =
if (relativeVerse > 1)
// TODO: No magic numbers!
spans plus SpanHolder(RelativeSizeSpan(0.6f), 0, relativeVerse.toString().length)
else
spans
return Pair(processedText, secondSpan)
}
fun getAllVerses(verses: Progression<Int>, info: ChapterInfo) =
verses.map { getProcessedVerse(it + info.vOffset, info) }
// For each verse, get the text
.fold(SpannableStringBuilder(), { initialBuilder, versePair ->
val offset = initialBuilder.length()
val builderWithText = initialBuilder append versePair.first
// And apply all spans
versePair.second.fold(builderWithText, { postBuilder, span ->
span.apply(postBuilder, offset)
})
})
fun bind(info: ChapterInfo) {
Log.d("PassageView", "Binding chapter ${info.chapter}")
v setText getAllVerses(info.vStart..info.vEnd, info)
}
}