mirror of
https://github.com/MinimalBible/MinimalBible
synced 2025-06-30 21:36:10 -04:00
Download search indexes alongside the book
This commit is contained in:
@ -25,6 +25,7 @@ import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.functions.Action1;
|
||||
import rx.functions.Func1;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
/**
|
||||
* Created by bspeice on 5/20/14.
|
||||
@ -47,6 +48,9 @@ public class BookItemHolder {
|
||||
BookManager bookManager;
|
||||
@Inject @Named("DownloadActivityContext")
|
||||
Context ctx;
|
||||
@Inject
|
||||
PublishSubject<DLProgressEvent> downloadProgressEvents;
|
||||
|
||||
private Subscription subscription;
|
||||
|
||||
// TODO: Factory style?
|
||||
@ -61,12 +65,13 @@ public class BookItemHolder {
|
||||
itemName.setText(b.getName());
|
||||
DLProgressEvent dlProgressEvent = bookManager.getDownloadProgress(b);
|
||||
if (dlProgressEvent != null) {
|
||||
displayProgress((int) dlProgressEvent.toCircular());
|
||||
displayProgress(dlProgressEvent.toCircular());
|
||||
} else if (bookManager.isInstalled(b)) {
|
||||
displayInstalled();
|
||||
}
|
||||
|
||||
//TODO: Refactor
|
||||
subscription = bookManager.getDownloadEvents()
|
||||
subscription = downloadProgressEvents
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.filter(new Func1<DLProgressEvent, Boolean>() {
|
||||
@Override
|
||||
@ -98,7 +103,7 @@ public class BookItemHolder {
|
||||
, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
} else {
|
||||
bookManager.installBook(this.b);
|
||||
bookManager.downloadBook(this.b);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import android.net.ConnectivityManager;
|
||||
import org.bspeice.minimalbible.Injector;
|
||||
import org.bspeice.minimalbible.MinimalBibleModules;
|
||||
import org.bspeice.minimalbible.activity.downloader.manager.BookManager;
|
||||
import org.bspeice.minimalbible.activity.downloader.manager.DLProgressEvent;
|
||||
import org.bspeice.minimalbible.activity.downloader.manager.LocaleManager;
|
||||
import org.bspeice.minimalbible.activity.downloader.manager.RefreshManager;
|
||||
import org.crosswire.jsword.book.BookCategory;
|
||||
@ -23,6 +24,7 @@ import javax.inject.Singleton;
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import de.devland.esperandro.Esperandro;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
/**
|
||||
* Module mappings for the classes under the Download Activity
|
||||
@ -79,8 +81,15 @@ public class DownloadActivityModules {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
BookManager provideBookDownloadManager(Books installedBooks, RefreshManager rm) {
|
||||
return new BookManager(installedBooks, rm);
|
||||
PublishSubject<DLProgressEvent> dlProgressEventPublisher() {
|
||||
return PublishSubject.create();
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
BookManager provideBookDownloadManager(Books installedBooks, RefreshManager rm,
|
||||
PublishSubject<DLProgressEvent> progressEvents) {
|
||||
return new BookManager(installedBooks, rm, progressEvents);
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
@ -12,33 +12,41 @@ import rx.Observable;
|
||||
import rx.schedulers.Schedulers;
|
||||
import rx.subjects.PublishSubject;
|
||||
import org.crosswire.jsword.book.BookException
|
||||
import org.crosswire.jsword.util.IndexDownloader
|
||||
import org.crosswire.common.progress.Progress
|
||||
|
||||
/**
|
||||
* Single point of authority for what is being downloaded and its progress
|
||||
* Please note that you should never be modifying installedBooks,
|
||||
* only operate on installedBooksList
|
||||
*/
|
||||
//TODO: Install indexes for Bibles
|
||||
//TODO: Figure out how to get Robolectric to mock the Log, rather than removing the calls
|
||||
class BookManager(private val installedBooks: Books, val rM: RefreshManager) :
|
||||
class BookManager(private val installedBooks: Books,
|
||||
val rM: RefreshManager,
|
||||
val downloadEvents: PublishSubject<DLProgressEvent>) :
|
||||
WorkListener, BooksListener {
|
||||
|
||||
private val bookJobNamePrefix = Progress.INSTALL_BOOK.substringBeforeLast("%s")
|
||||
private val indexJobNamePrefix = Progress.DOWNLOAD_SEARCH_INDEX.substringBeforeLast("%s")
|
||||
|
||||
/**
|
||||
* List of jobs currently active by their job name
|
||||
*/
|
||||
val inProgressJobNames: MutableMap<String, Book> = hashMapOf()
|
||||
|
||||
/**
|
||||
* Cached copy of downloads in progress so views displaying this info can get it quickly.
|
||||
*/
|
||||
// TODO: Combine to one map
|
||||
val bookMappings: MutableMap<String, Book> = hashMapOf()
|
||||
val inProgressDownloads: MutableMap<Book, DLProgressEvent> = hashMapOf()
|
||||
|
||||
/**
|
||||
* A list of books that is locally maintained - installedBooks isn't always up-to-date
|
||||
*/
|
||||
val installedBooksList: MutableList<Book> = installedBooks.getBooks() ?: linkedListOf()
|
||||
val downloadEvents: PublishSubject<DLProgressEvent> = PublishSubject.create();
|
||||
val installedBooksList: MutableList<Book> = installedBooks.getBooks() ?: linkedListOf();
|
||||
|
||||
{
|
||||
JobManager.addWorkListener(this)
|
||||
installedBooks.addBooksListener(this)
|
||||
downloadEvents.subscribe { this.inProgressDownloads[it.b] = it }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -48,25 +56,35 @@ class BookManager(private val installedBooks: Books, val rM: RefreshManager) :
|
||||
* @param b The book to predict the download job name of
|
||||
* @return The name of the job that will/is download/ing this book
|
||||
*/
|
||||
fun getJobId(b: Book) = "INSTALL_BOOK-${b.getInitials()}"
|
||||
|
||||
fun installBook(b: Book) {
|
||||
downloadBook(b)
|
||||
addJob(getJobId(b), b)
|
||||
downloadEvents onNext DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING, b)
|
||||
}
|
||||
|
||||
fun addJob(jobId: String, b: Book) {
|
||||
bookMappings.put(jobId, b)
|
||||
}
|
||||
fun getJobNames(b: Book) = listOf("${bookJobNamePrefix}${b.getInitials()}",
|
||||
"${indexJobNamePrefix}${b.getInitials()}")
|
||||
|
||||
fun downloadBook(b: Book) {
|
||||
// First, look up where the Book came from
|
||||
Observable.just(rM installerFromBook b)
|
||||
.observeOn(Schedulers.io())
|
||||
.subscribe { it subscribe { it install b } }
|
||||
val installerObs = Observable.just(rM installerFromBook b)
|
||||
|
||||
downloadEvents onNext DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING, b)
|
||||
// And subscribe on two different threads for the download
|
||||
// Not sure why we need two threads, guessing it's because the
|
||||
// thread is closed when the install event is done
|
||||
installerObs
|
||||
.observeOn(Schedulers.newThread())
|
||||
.subscribe {
|
||||
// Download the actual book
|
||||
it subscribe { it install b }
|
||||
}
|
||||
|
||||
installerObs
|
||||
.observeOn(Schedulers.newThread())
|
||||
.subscribe {
|
||||
// Download the book index
|
||||
it subscribe { IndexDownloader.downloadIndex(b, it) }
|
||||
}
|
||||
|
||||
// Then notify everyone that we're starting
|
||||
downloadEvents onNext DLProgressEvent.beginningEvent(b)
|
||||
|
||||
// Finally register the jobs in progress
|
||||
getJobNames(b).forEach { this.inProgressJobNames[it] = b }
|
||||
}
|
||||
|
||||
/**
|
||||
@ -100,6 +118,7 @@ class BookManager(private val installedBooks: Books, val rM: RefreshManager) :
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the status of a book download in progress.
|
||||
* @param b The book to get the current progress of
|
||||
@ -112,20 +131,24 @@ class BookManager(private val installedBooks: Books, val rM: RefreshManager) :
|
||||
// TODO: I have a strange feeling I can simplify this further...
|
||||
override fun workProgressed(ev: WorkEvent) {
|
||||
val job = ev.getJob()
|
||||
bookMappings.filter { it.getKey() == job.getJobID() }
|
||||
.map {
|
||||
// We multiply by 100 first to avoid integer truncation
|
||||
// Also avoids roundoff error. Neat trick, but I'm spending just as much time
|
||||
// documenting it as implementing the floating point would take
|
||||
val event = DLProgressEvent(job.getWorkDone() * 100 / job.getTotalWork(), it.getValue())
|
||||
downloadEvents onNext event
|
||||
|
||||
if (job.getWorkDone() == job.getTotalWork()) {
|
||||
inProgressDownloads remove bookMappings[job.getJobID()]
|
||||
bookMappings remove job.getJobID()
|
||||
} else
|
||||
inProgressDownloads.put(it.getValue(), event)
|
||||
}
|
||||
val book = inProgressJobNames[job.getJobID()] as Book
|
||||
val oldEvent = inProgressDownloads[book] ?: DLProgressEvent.beginningEvent(book)
|
||||
|
||||
var newEvent: DLProgressEvent
|
||||
if (job.getJobID().contains(bookJobNamePrefix))
|
||||
newEvent = oldEvent.copy(bookProgress = job.getWork())
|
||||
else
|
||||
newEvent = oldEvent.copy(indexProgress = job.getWork())
|
||||
|
||||
downloadEvents onNext newEvent
|
||||
|
||||
if (newEvent.averageProgress == DLProgressEvent.PROGRESS_COMPLETE) {
|
||||
inProgressDownloads remove inProgressJobNames[job.getJobID()]
|
||||
inProgressJobNames remove job.getJobID()
|
||||
} else
|
||||
inProgressDownloads.put(book, newEvent)
|
||||
|
||||
}
|
||||
|
||||
override fun workStateChanged(ev: WorkEvent) {
|
||||
@ -133,18 +156,8 @@ class BookManager(private val installedBooks: Books, val rM: RefreshManager) :
|
||||
}
|
||||
|
||||
override fun bookAdded(booksEvent: BooksEvent) {
|
||||
// It's possible the install finished before we received a progress event for it,
|
||||
// we handle that case here.
|
||||
val b = booksEvent.getBook()
|
||||
// Log.d("BookDownloadManager", "Book added: ${b.getName()}")
|
||||
inProgressDownloads remove b
|
||||
|
||||
// Not sure why, but the inProgressDownloads might not have our book,
|
||||
// so we always trigger the PROGRESS_COMPLETE event.
|
||||
downloadEvents onNext DLProgressEvent(DLProgressEvent.PROGRESS_COMPLETE, b)
|
||||
|
||||
// And update the locally available list
|
||||
installedBooksList add b
|
||||
// Update the local list of available books
|
||||
installedBooksList add booksEvent.getBook()
|
||||
}
|
||||
|
||||
override fun bookRemoved(booksEvent: BooksEvent) {
|
||||
|
@ -5,12 +5,23 @@ import org.crosswire.jsword.book.Book
|
||||
/**
|
||||
* Created by bspeice on 11/11/14.
|
||||
*/
|
||||
|
||||
class DLProgressEvent(val progress: Int, val b: Book) {
|
||||
data class DLProgressEvent(val bookProgress: Int,
|
||||
val indexProgress: Int,
|
||||
val b: Book) {
|
||||
class object {
|
||||
val PROGRESS_COMPLETE = 100
|
||||
val PROGRESS_BEGINNING = 0
|
||||
|
||||
/**
|
||||
* Build a DLProgressEvent that is just beginning
|
||||
* Mostly just a nice shorthand
|
||||
*/
|
||||
fun beginningEvent(b: Book) = DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING,
|
||||
DLProgressEvent.PROGRESS_BEGINNING, b)
|
||||
}
|
||||
|
||||
fun toCircular() = (progress.toFloat() * 360 / 100).toInt()
|
||||
val averageProgress: Int
|
||||
get() = (bookProgress + indexProgress) / 2
|
||||
|
||||
fun toCircular() = (averageProgress.toFloat() * 360 / 100).toInt()
|
||||
}
|
||||
|
Reference in New Issue
Block a user