mirror of
https://github.com/MinimalBible/MinimalBible
synced 2024-11-04 23:28:19 -05:00
Refactor BookDownloadManager to Kotlin
I don't like that I had to make one static method a class method, but I like how much cleaner everything else is!
This commit is contained in:
parent
86adeb4951
commit
7cfe273cb6
@ -91,7 +91,7 @@ public class BookDownloadManagerTest extends MBTestCase implements Injector {
|
|||||||
|
|
||||||
public void testJobIdMatch() {
|
public void testJobIdMatch() {
|
||||||
final Book toInstall = installableBooks().toBlocking().first();
|
final Book toInstall = installableBooks().toBlocking().first();
|
||||||
final String jobName = BookDownloadManager.getJobId(toInstall);
|
final String jobName = bookDownloadManager.getJobId(toInstall);
|
||||||
final AtomicBoolean jobNameMatch = new AtomicBoolean(false);
|
final AtomicBoolean jobNameMatch = new AtomicBoolean(false);
|
||||||
|
|
||||||
JobManager.addWorkListener(new WorkListener() {
|
JobManager.addWorkListener(new WorkListener() {
|
||||||
@ -138,12 +138,6 @@ public class BookDownloadManagerTest extends MBTestCase implements Injector {
|
|||||||
prefs = mock(DownloadPrefs.class);
|
prefs = mock(DownloadPrefs.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
|
||||||
@Singleton
|
|
||||||
Injector provideInjector() {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
Books provideBooks() {
|
Books provideBooks() {
|
||||||
@ -170,5 +164,11 @@ public class BookDownloadManagerTest extends MBTestCase implements Injector {
|
|||||||
return new RefreshManager(installers,
|
return new RefreshManager(installers,
|
||||||
prefs, manager);
|
prefs, manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
BookDownloadManager bookDownloadManager(Books installed, RefreshManager rm) {
|
||||||
|
return new BookDownloadManager(installed, rm);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -35,6 +35,7 @@ public class BookItemHolder {
|
|||||||
// TODO: The holder should register and unregister itself for DownloadProgress events
|
// TODO: The holder should register and unregister itself for DownloadProgress events
|
||||||
// so that we can display live updates.
|
// so that we can display live updates.
|
||||||
|
|
||||||
|
private final Book b;
|
||||||
@InjectView(R.id.download_txt_item_acronym)
|
@InjectView(R.id.download_txt_item_acronym)
|
||||||
TextView acronym;
|
TextView acronym;
|
||||||
@InjectView(R.id.txt_download_item_name)
|
@InjectView(R.id.txt_download_item_name)
|
||||||
@ -43,15 +44,12 @@ public class BookItemHolder {
|
|||||||
ImageButton isDownloaded;
|
ImageButton isDownloaded;
|
||||||
@InjectView(R.id.download_prg_download)
|
@InjectView(R.id.download_prg_download)
|
||||||
ProgressWheel downloadProgress;
|
ProgressWheel downloadProgress;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
BookDownloadManager bookDownloadManager;
|
BookDownloadManager bookDownloadManager;
|
||||||
@Inject
|
@Inject
|
||||||
InstalledManager installedManager;
|
InstalledManager installedManager;
|
||||||
@Inject @Named("DownloadActivityContext")
|
@Inject @Named("DownloadActivityContext")
|
||||||
Context ctx;
|
Context ctx;
|
||||||
|
|
||||||
private final Book b;
|
|
||||||
private Subscription subscription;
|
private Subscription subscription;
|
||||||
|
|
||||||
// TODO: Factory style?
|
// TODO: Factory style?
|
||||||
@ -64,7 +62,7 @@ public class BookItemHolder {
|
|||||||
public void bindHolder() {
|
public void bindHolder() {
|
||||||
acronym.setText(b.getInitials());
|
acronym.setText(b.getInitials());
|
||||||
itemName.setText(b.getName());
|
itemName.setText(b.getName());
|
||||||
DLProgressEvent dlProgressEvent = bookDownloadManager.getInProgressDownloadProgress(b);
|
DLProgressEvent dlProgressEvent = bookDownloadManager.getDownloadProgress(b);
|
||||||
if (dlProgressEvent != null) {
|
if (dlProgressEvent != null) {
|
||||||
displayProgress((int) dlProgressEvent.toCircular());
|
displayProgress((int) dlProgressEvent.toCircular());
|
||||||
} else if (installedManager.isInstalled(b)) {
|
} else if (installedManager.isInstalled(b)) {
|
||||||
|
@ -77,8 +77,8 @@ public class DownloadActivityModules {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides @Singleton
|
@Provides @Singleton
|
||||||
BookDownloadManager provideBookDownloadManager() {
|
BookDownloadManager provideBookDownloadManager(Books installedBooks, RefreshManager rm) {
|
||||||
return new BookDownloadManager(activity);
|
return new BookDownloadManager(installedBooks, rm);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Provides @Singleton
|
@Provides @Singleton
|
||||||
@ -94,7 +94,7 @@ public class DownloadActivityModules {
|
|||||||
|
|
||||||
//TODO: Move this to a true async
|
//TODO: Move this to a true async
|
||||||
@Provides @Singleton
|
@Provides @Singleton
|
||||||
Books provideInstalledBooksManager() {
|
Books provideInstalledBooks() {
|
||||||
return Books.installed();
|
return Books.installed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,163 +0,0 @@
|
|||||||
package org.bspeice.minimalbible.activity.downloader.manager;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.bspeice.minimalbible.Injector;
|
|
||||||
import org.crosswire.common.progress.JobManager;
|
|
||||||
import org.crosswire.common.progress.Progress;
|
|
||||||
import org.crosswire.common.progress.WorkEvent;
|
|
||||||
import org.crosswire.common.progress.WorkListener;
|
|
||||||
import org.crosswire.jsword.book.Book;
|
|
||||||
import org.crosswire.jsword.book.Books;
|
|
||||||
import org.crosswire.jsword.book.BooksEvent;
|
|
||||||
import org.crosswire.jsword.book.BooksListener;
|
|
||||||
import org.crosswire.jsword.book.install.InstallException;
|
|
||||||
import org.crosswire.jsword.book.install.Installer;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.functions.Action1;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
import rx.subjects.PublishSubject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to convert JSword progress events to MinimalBible EventBus-based
|
|
||||||
*/
|
|
||||||
//TODO: Make sure that jobs have the correct name
|
|
||||||
//TODO: Install indexes for Bibles
|
|
||||||
@Singleton
|
|
||||||
public class BookDownloadManager implements WorkListener, BooksListener {
|
|
||||||
/**
|
|
||||||
* Mapping of Job ID to the EventBus we should trigger progress on
|
|
||||||
*/
|
|
||||||
private final Map<String, Book> bookMappings;
|
|
||||||
/**
|
|
||||||
* Cached copy of downloads in progress so views displaying this info can get it quickly.
|
|
||||||
*/
|
|
||||||
private final Map<Book, DLProgressEvent> inProgressDownloads;
|
|
||||||
private final PublishSubject<DLProgressEvent> downloadEvents = PublishSubject.create();
|
|
||||||
@Inject Books installedBooks;
|
|
||||||
@Inject RefreshManager refreshManager;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public BookDownloadManager(Injector injector) {
|
|
||||||
bookMappings = new HashMap<String, Book>();
|
|
||||||
inProgressDownloads = new HashMap<Book, DLProgressEvent>();
|
|
||||||
JobManager.addWorkListener(this);
|
|
||||||
injector.inject(this);
|
|
||||||
installedBooks.addBooksListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build what the installer creates the job name as.
|
|
||||||
* Likely prone to be brittle.
|
|
||||||
*
|
|
||||||
* @param b The book to predict the download job name of
|
|
||||||
* @return The name of the job that will/is download/ing this book
|
|
||||||
*/
|
|
||||||
|
|
||||||
public static String getJobId(Book b) {
|
|
||||||
return "INSTALL_BOOK-" + b.getInitials();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void installBook(Book b) {
|
|
||||||
downloadBook(b);
|
|
||||||
addJob(getJobId(b), b);
|
|
||||||
downloadEvents.onNext(new DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addJob(String jobId, Book b) {
|
|
||||||
bookMappings.put(jobId, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void downloadBook(final Book b) {
|
|
||||||
// So, the JobManager can't be injected, but we'll make do
|
|
||||||
|
|
||||||
// First, look up where the Book came from
|
|
||||||
Observable.just(refreshManager.installerFromBook(b))
|
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.subscribe(new Action1<Observable<Installer>>() {
|
|
||||||
@Override
|
|
||||||
public void call(Observable<Installer> installerObservable) {
|
|
||||||
try {
|
|
||||||
installerObservable.toBlocking().first().install(b);
|
|
||||||
} catch (InstallException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
getDownloadEvents()
|
|
||||||
.onNext(new DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void workProgressed(WorkEvent ev) {
|
|
||||||
Progress job = ev.getJob();
|
|
||||||
Log.d("BookDownloadManager", "Download in progress: " + job.getJobID() + " - " + job.getJobName() + " " + job.getWorkDone() + "/" + job.getTotalWork());
|
|
||||||
if (bookMappings.containsKey(job.getJobID())) {
|
|
||||||
Book b = bookMappings.get(job.getJobID());
|
|
||||||
|
|
||||||
if (job.getWorkDone() == job.getTotalWork()) {
|
|
||||||
// Download is complete
|
|
||||||
inProgressDownloads.remove(bookMappings.get(job.getJobID()));
|
|
||||||
bookMappings.remove(job.getJobID());
|
|
||||||
downloadEvents.onNext(new DLProgressEvent(DLProgressEvent.PROGRESS_COMPLETE, b));
|
|
||||||
} else {
|
|
||||||
// Track the ongoing download
|
|
||||||
DLProgressEvent event = new DLProgressEvent(
|
|
||||||
(job.getWorkDone() / job.getTotalWork()) * 100,
|
|
||||||
b);
|
|
||||||
inProgressDownloads.put(b, event);
|
|
||||||
downloadEvents.onNext(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the status of a book download in progress.
|
|
||||||
* @param b The book to get the current progress of
|
|
||||||
* @return The most recent DownloadProgressEvent for the book, or null if not downloading
|
|
||||||
*/
|
|
||||||
public DLProgressEvent getInProgressDownloadProgress(Book b) {
|
|
||||||
if (inProgressDownloads.containsKey(b)) {
|
|
||||||
return inProgressDownloads.get(b);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublishSubject<DLProgressEvent> getDownloadEvents() {
|
|
||||||
return downloadEvents;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void workStateChanged(WorkEvent ev) {
|
|
||||||
Log.d("BookDownloadManager", ev.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bookAdded(BooksEvent booksEvent) {
|
|
||||||
// It's possible the install finished before we received a progress event for it,
|
|
||||||
// we handle that case here.
|
|
||||||
Book b = booksEvent.getBook();
|
|
||||||
Log.d("BookDownloadManager", "Book added: " + b.getName());
|
|
||||||
if (inProgressDownloads.containsKey(b)) {
|
|
||||||
inProgressDownloads.remove(b);
|
|
||||||
}
|
|
||||||
// Not sure why, but the inProgressDownloads might not have our book,
|
|
||||||
// so we always trigger the PROGRESS_COMPLETE event.
|
|
||||||
// TODO: Make sure all books get to the inProgressDownloads
|
|
||||||
downloadEvents.onNext(new DLProgressEvent(DLProgressEvent.PROGRESS_COMPLETE, b));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bookRemoved(BooksEvent booksEvent) {
|
|
||||||
// Not too worried about this just yet.
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,108 @@
|
|||||||
|
package org.bspeice.minimalbible.activity.downloader.manager
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.crosswire.common.progress.JobManager;
|
||||||
|
import org.crosswire.common.progress.WorkEvent;
|
||||||
|
import org.crosswire.common.progress.WorkListener;
|
||||||
|
import org.crosswire.jsword.book.Book;
|
||||||
|
import org.crosswire.jsword.book.Books;
|
||||||
|
import org.crosswire.jsword.book.BooksEvent;
|
||||||
|
import org.crosswire.jsword.book.BooksListener;
|
||||||
|
|
||||||
|
import rx.Observable;
|
||||||
|
import rx.schedulers.Schedulers;
|
||||||
|
import rx.subjects.PublishSubject;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single point of authority for what is being downloaded and its progress
|
||||||
|
*/
|
||||||
|
//TODO: Install indexes for Bibles
|
||||||
|
class BookDownloadManager(val installedBooks: Books, val rM: RefreshManager) :
|
||||||
|
WorkListener, BooksListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached copy of downloads in progress so views displaying this info can get it quickly.
|
||||||
|
*/
|
||||||
|
// TODO: Combine to one map
|
||||||
|
var bookMappings: MutableMap<String, Book> = hashMapOf()
|
||||||
|
var inProgressDownloads: MutableMap<Book, DLProgressEvent> = hashMapOf()
|
||||||
|
val downloadEvents: PublishSubject<DLProgressEvent> = PublishSubject.create();
|
||||||
|
|
||||||
|
{
|
||||||
|
JobManager.addWorkListener(this)
|
||||||
|
installedBooks.addBooksListener(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build what the installer creates the job name as.
|
||||||
|
* This technically could be static, but Kotlin won't have none of that.
|
||||||
|
*
|
||||||
|
* @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 downloadBook(b: Book) {
|
||||||
|
// First, look up where the Book came from
|
||||||
|
Observable.just(rM installerFromBook b)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.subscribe { it.toBlocking().first().install(b) }
|
||||||
|
|
||||||
|
downloadEvents onNext DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
val event = DLProgressEvent(job.getWorkDone() / job.getTotalWork() * 100,
|
||||||
|
it.getValue())
|
||||||
|
downloadEvents onNext event
|
||||||
|
|
||||||
|
if (job.getWorkDone() == job.getTotalWork()) {
|
||||||
|
inProgressDownloads remove bookMappings.get(job.getJobID())
|
||||||
|
bookMappings remove job.getJobID()
|
||||||
|
} else
|
||||||
|
inProgressDownloads.put(it.getValue(), event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the status of a book download in progress.
|
||||||
|
* @param b The book to get the current progress of
|
||||||
|
* @return The most recent DownloadProgressEvent for the book, or null if not downloading
|
||||||
|
*/
|
||||||
|
fun getDownloadProgress(b: Book) = inProgressDownloads.get(b)
|
||||||
|
|
||||||
|
override fun workStateChanged(ev: WorkEvent) {
|
||||||
|
Log.d("BookDownloadManager", ev.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun bookRemoved(booksEvent: BooksEvent) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user