Downloading now shows progress!

Nasty bug where you can double register to the bus because it's not actually destroyed, need to fix that.
This commit is contained in:
Bradlee Speice 2014-05-24 22:13:23 -04:00
parent 6afb9b6f28
commit 6191943d3f
8 changed files with 163 additions and 160 deletions

View File

@ -5,8 +5,11 @@ import org.bspeice.minimalbible.activities.downloader.manager.BookDownloadManage
import org.bspeice.minimalbible.activities.downloader.manager.BookDownloadThread; import org.bspeice.minimalbible.activities.downloader.manager.BookDownloadThread;
import org.bspeice.minimalbible.activities.downloader.manager.BookRefreshTask; import org.bspeice.minimalbible.activities.downloader.manager.BookRefreshTask;
import org.bspeice.minimalbible.activities.downloader.manager.DownloadManager; import org.bspeice.minimalbible.activities.downloader.manager.DownloadManager;
import org.bspeice.minimalbible.activities.downloader.manager.RefreshManager;
import org.crosswire.common.progress.JobManager; import org.crosswire.common.progress.JobManager;
import java.sql.Ref;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
@ -24,22 +27,12 @@ import de.greenrobot.event.EventBus;
BookRefreshTask.class, BookRefreshTask.class,
BookItemHolder.class, BookItemHolder.class,
BookDownloadManager.class, BookDownloadManager.class,
BookDownloadThread.class BookDownloadThread.class,
RefreshManager.class
} }
) )
public class ActivityDownloaderModule { public class ActivityDownloaderModule {
/**
* Provide a Singleton DownloadManager for injection
* Note that we need to annotate Singleton here, only annotating on the
* DownloadManager itself is not enough.
* @return Global DownloadManager instance
*/
@Provides @Singleton
DownloadManager provideDownloadManager() {
return new DownloadManager();
}
@Provides @Provides
EventBus provideBus() { EventBus provideBus() {
return new EventBus(); return new EventBus();

View File

@ -1,5 +1,6 @@
package org.bspeice.minimalbible.activities.downloader; package org.bspeice.minimalbible.activities.downloader;
import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.RelativeLayout; import android.widget.RelativeLayout;
@ -9,8 +10,9 @@ import com.todddavies.components.progressbar.ProgressWheel;
import org.bspeice.minimalbible.MinimalBible; import org.bspeice.minimalbible.MinimalBible;
import org.bspeice.minimalbible.R; import org.bspeice.minimalbible.R;
import org.bspeice.minimalbible.activities.downloader.manager.BookDownloadManager;
import org.bspeice.minimalbible.activities.downloader.manager.DLProgressEvent;
import org.bspeice.minimalbible.activities.downloader.manager.DownloadManager; import org.bspeice.minimalbible.activities.downloader.manager.DownloadManager;
import org.bspeice.minimalbible.activities.downloader.manager.DownloadProgressEvent;
import org.crosswire.jsword.book.Book; import org.crosswire.jsword.book.Book;
import javax.inject.Inject; import javax.inject.Inject;
@ -18,7 +20,6 @@ import javax.inject.Inject;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.InjectView; import butterknife.InjectView;
import butterknife.OnClick; import butterknife.OnClick;
import de.greenrobot.event.EventBus;
/** /**
* Created by bspeice on 5/20/14. * Created by bspeice on 5/20/14.
@ -34,6 +35,7 @@ public class BookItemHolder {
@InjectView(R.id.download_prg_download) ProgressWheel downloadProgress; @InjectView(R.id.download_prg_download) ProgressWheel downloadProgress;
@Inject DownloadManager downloadManager; @Inject DownloadManager downloadManager;
@Inject BookDownloadManager bookDownloadManager;
Book b; Book b;
@ -46,21 +48,21 @@ 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());
DownloadProgressEvent downloadProgressEvent = downloadManager.getInProgressDownloadProgress(b); DLProgressEvent dlProgressEvent = bookDownloadManager.getInProgressDownloadProgress(b);
if (downloadProgressEvent != null) { if (dlProgressEvent != null) {
displayProgress((int) downloadProgressEvent.toCircular()); displayProgress((int) dlProgressEvent.toCircular());
} }
// TODO: Display a remove icon if the book has been downloaded. // TODO: Display a remove icon if the book has been downloaded.
} }
@OnClick(R.id.download_ibtn_download) @OnClick(R.id.download_ibtn_download)
public void onDownloadItem(View v) { public void onDownloadItem(View v) {
downloadManager.getRefreshBus().register(this); downloadManager.getDownloadBus().register(this);
downloadManager.installBook(this.b); bookDownloadManager.installBook(this.b);
} }
public void onEventMainThread(DownloadProgressEvent event) { public void onEventMainThread(DLProgressEvent event) {
if (event.getB().equals(b)) { if (event.getB().getName().equals(b.getName())) {
displayProgress((int) event.toCircular()); displayProgress((int) event.toCircular());
} }
} }
@ -72,7 +74,7 @@ public class BookItemHolder {
private void displayProgress(int progress) { private void displayProgress(int progress) {
if (progress == DownloadProgressEvent.PROGRESS_BEGINNING) { if (progress == DLProgressEvent.PROGRESS_BEGINNING) {
// Download starting // Download starting
RelativeLayout.LayoutParams acronymParams = RelativeLayout.LayoutParams acronymParams =
(RelativeLayout.LayoutParams)acronym.getLayoutParams(); (RelativeLayout.LayoutParams)acronym.getLayoutParams();
@ -99,6 +101,7 @@ public class BookItemHolder {
isDownloaded.setVisibility(View.GONE); isDownloaded.setVisibility(View.GONE);
downloadProgress.setVisibility(View.VISIBLE); downloadProgress.setVisibility(View.VISIBLE);
downloadProgress.stopSpinning();
downloadProgress.setProgress(progress); downloadProgress.setProgress(progress);
} else { } else {
// Download complete // Download complete

View File

@ -17,6 +17,7 @@ import org.bspeice.minimalbible.R;
import org.bspeice.minimalbible.activities.BaseFragment; import org.bspeice.minimalbible.activities.BaseFragment;
import org.bspeice.minimalbible.activities.downloader.manager.DownloadManager; import org.bspeice.minimalbible.activities.downloader.manager.DownloadManager;
import org.bspeice.minimalbible.activities.downloader.manager.EventBookList; import org.bspeice.minimalbible.activities.downloader.manager.EventBookList;
import org.bspeice.minimalbible.activities.downloader.manager.RefreshManager;
import org.crosswire.jsword.book.Book; import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookCategory; import org.crosswire.jsword.book.BookCategory;
import org.crosswire.jsword.book.BookComparators; import org.crosswire.jsword.book.BookComparators;
@ -48,6 +49,7 @@ public class BookListFragment extends BaseFragment {
ListView downloadsAvailable; ListView downloadsAvailable;
@Inject DownloadManager downloadManager; @Inject DownloadManager downloadManager;
@Inject RefreshManager refreshManager;
@Inject DownloadPrefs downloadPrefs; @Inject DownloadPrefs downloadPrefs;
@ -116,10 +118,10 @@ public class BookListFragment extends BaseFragment {
*/ */
private void refreshModules() { private void refreshModules() {
// Check if the downloadManager has already refreshed everything // Check if the downloadManager has already refreshed everything
List<Book> bookList = downloadManager.getBookList(); List<Book> bookList = refreshManager.getBookList();
if (bookList == null) { if (bookList == null) {
// downloadManager is in progress of refreshing // downloadManager is in progress of refreshing
downloadManager.getRefreshBus().register(this); downloadManager.getDownloadBus().register(this);
refreshDialog = new ProgressDialog(getActivity()); refreshDialog = new ProgressDialog(getActivity());
refreshDialog.setMessage("Refreshing available modules..."); refreshDialog.setMessage("Refreshing available modules...");
refreshDialog.setCancelable(false); refreshDialog.setCancelable(false);

View File

@ -29,21 +29,24 @@ public class BookDownloadManager implements WorkListener{
*/ */
private Map<String, Book> bookMappings; private Map<String, Book> bookMappings;
/**
* Cached copy of downloads in progress so views displaying this info can get it quickly.
*/
private Map<Book, DLProgressEvent> inProgressDownloads;
@Inject @Inject
Provider<BookDownloadThread> dlThreadProvider; Provider<BookDownloadThread> dlThreadProvider;
/* Going to fix this in the next commit, right now it's circular @Inject DownloadManager downloadManager;
@Inject
*/
DownloadManager downloadManager;
public BookDownloadManager() { public BookDownloadManager() {
bookMappings = new HashMap<String, Book>(); bookMappings = new HashMap<String, Book>();
inProgressDownloads = new HashMap<Book, DLProgressEvent>();
JobManager.addWorkListener(this); JobManager.addWorkListener(this);
MinimalBible.getApplication().inject(this); MinimalBible.getApplication().inject(this);
} }
public void downloadBook(Book b) { public void installBook(Book b) {
BookDownloadThread dlThread = dlThreadProvider.get(); BookDownloadThread dlThread = dlThreadProvider.get();
dlThread.downloadBook(b); dlThread.downloadBook(b);
addJob(BookDownloadThread.getJobId(b), b); addJob(BookDownloadThread.getJobId(b), b);
@ -55,12 +58,35 @@ public class BookDownloadManager implements WorkListener{
@Override @Override
public void workProgressed(WorkEvent ev) { public void workProgressed(WorkEvent ev) {
Log.d("BookDownloadManager", ev.toString());
Progress job = ev.getJob(); Progress job = ev.getJob();
EventBus downloadBus = downloadManager.getDownloadBus();
if (bookMappings.containsKey(job.getJobID())) { if (bookMappings.containsKey(job.getJobID())) {
downloadManager.getRefreshBus() Book b = bookMappings.get(job.getJobID());
.post(new DownloadProgressEvent(job.getTotalWork(), job.getWorkDone(),
bookMappings.get(job.getJobID()))); if (job.getWorkDone() == job.getTotalWork()) {
// Download is complete
inProgressDownloads.remove(bookMappings.get(job.getJobID()));
downloadBus.post(new DLProgressEvent(DLProgressEvent.PROGRESS_COMPLETE, b));
} else {
// Track the ongoing download
DLProgressEvent event = new DLProgressEvent(job.getWorkDone(),
job.getTotalWork(), b);
inProgressDownloads.put(b, event);
downloadBus.post(event);
}
}
}
/**
* Check the status of a book download in progress.
* @param b
* @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;
} }
} }
@ -68,5 +94,4 @@ public class BookDownloadManager implements WorkListener{
public void workStateChanged(WorkEvent ev) { public void workStateChanged(WorkEvent ev) {
Log.d("BookDownloadManager", ev.toString()); Log.d("BookDownloadManager", ev.toString());
} }
} }

View File

@ -4,18 +4,12 @@ import android.util.Log;
import org.bspeice.minimalbible.MinimalBible; import org.bspeice.minimalbible.MinimalBible;
import org.crosswire.common.progress.JobManager; 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.Book;
import org.crosswire.jsword.book.install.InstallException; import org.crosswire.jsword.book.install.InstallException;
import org.crosswire.jsword.book.install.Installer; import org.crosswire.jsword.book.install.Installer;
import java.util.UUID;
import javax.inject.Inject; import javax.inject.Inject;
import de.greenrobot.event.EventBus;
/** /**
* Created by bspeice on 5/12/14. * Created by bspeice on 5/12/14.
*/ */
@ -25,6 +19,7 @@ public class BookDownloadThread {
@Inject @Inject
DownloadManager downloadManager; DownloadManager downloadManager;
@Inject RefreshManager refreshManager;
public BookDownloadThread() { public BookDownloadThread() {
MinimalBible.getApplication().inject(this); MinimalBible.getApplication().inject(this);
@ -34,7 +29,7 @@ public class BookDownloadThread {
// So, the JobManager can't be injected, but we'll make do // So, the JobManager can't be injected, but we'll make do
// First, look up where the Book came from // First, look up where the Book came from
final Installer i = downloadManager.installerFromBook(b); final Installer i = refreshManager.installerFromBook(b);
final Thread worker = new Thread() { final Thread worker = new Thread() {
@Override @Override
@ -47,17 +42,19 @@ public class BookDownloadThread {
} }
}; };
worker.start(); worker.start();
// The worker automatically communicates with the JobManager for its progress.
JobManager.createJob(getJobId(b), b.getName(), worker); downloadManager.getDownloadBus().post(new DLProgressEvent(DLProgressEvent.PROGRESS_BEGINNING, b));
downloadManager.getRefreshBus().post(new DownloadProgressEvent(DownloadProgressEvent.PROGRESS_BEGINNING, b));
} }
/** /**
* Return a job ID for a given book. Must be consistent per book. * Build what the installer creates the job name as.
* Likely prone to be brittle.
* TODO: Make sure to test that this is an accurate job name
* @param b * @param b
* @return A string representing this job IDs * @return
*/ */
public static String getJobId(Book b) { public static String getJobId(Book b) {
return b.toString(); return "INSTALL_BOOK-" + b.getInitials().toUpperCase();
} }
} }

View File

@ -5,19 +5,23 @@ import org.crosswire.jsword.book.Book;
/** /**
* Used for notifying that a book's download progress is ongoing * Used for notifying that a book's download progress is ongoing
*/ */
public class DownloadProgressEvent { public class DLProgressEvent {
private int progress; private int progress;
private Book b; private Book b;
public static final int PROGRESS_COMPLETE = 100; public static final int PROGRESS_COMPLETE = 100;
public static final int PROGRESS_BEGINNING = 0; public static final int PROGRESS_BEGINNING = 0;
public DownloadProgressEvent(int workDone, int totalWork, Book b) { public DLProgressEvent(int workDone, int totalWork, Book b) {
this.progress = workDone / totalWork; if (totalWork == 0) {
this.progress = 0;
} else {
this.progress = (int)((float) workDone / totalWork * 100);
}
this.b = b; this.b = b;
} }
public DownloadProgressEvent(int workDone, Book b) { public DLProgressEvent(int workDone, Book b) {
this.progress = workDone; this.progress = workDone;
this.b = b; this.b = b;
} }

View File

@ -3,17 +3,12 @@ package org.bspeice.minimalbible.activities.downloader.manager;
import android.util.Log; import android.util.Log;
import org.bspeice.minimalbible.MinimalBible; import org.bspeice.minimalbible.MinimalBible;
import org.crosswire.common.util.CWProject;
import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.BookCategory; import org.crosswire.jsword.book.BookCategory;
import org.crosswire.jsword.book.install.InstallManager; import org.crosswire.jsword.book.install.InstallManager;
import org.crosswire.jsword.book.install.Installer; import org.crosswire.jsword.book.install.Installer;
import org.crosswire.jsword.book.sword.SwordBookPath; import org.crosswire.jsword.book.sword.SwordBookPath;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
@ -27,20 +22,8 @@ public class DownloadManager {
private final String TAG = "DownloadManager"; private final String TAG = "DownloadManager";
/**
* Cached copy of modules that are available so we don't refresh for everyone who requests it.
*/
private Map<Installer, List<Book>> availableModules;
/**
* Cached copy of downloads in progress so views displaying this info can get it quickly.
*/
private Map<Book, DownloadProgressEvent> inProgressDownloads;
@Inject @Inject
protected EventBus refreshBus; protected EventBus downloadBus;
@Inject BookDownloadManager bookDownloadManager;
public static final BookCategory[] VALID_CATEGORIES = { BookCategory.BIBLE, public static final BookCategory[] VALID_CATEGORIES = { BookCategory.BIBLE,
BookCategory.COMMENTARY, BookCategory.DICTIONARY, BookCategory.COMMENTARY, BookCategory.DICTIONARY,
@ -52,11 +35,6 @@ public class DownloadManager {
public DownloadManager() { public DownloadManager() {
MinimalBible.getApplication().inject(this); MinimalBible.getApplication().inject(this);
setDownloadDir(); setDownloadDir();
availableModules = new HashMap<Installer, List<Book>>();
refreshModules();
inProgressDownloads = new HashMap<Book, DownloadProgressEvent>();
} }
/** /**
@ -93,93 +71,10 @@ public class DownloadManager {
} }
/** /**
* Do the work of kicking off the AsyncTask to refresh books, and make sure we know * Get the current download bus
* when it's done. * Used to broker refresh events, and ongoing download events
*/ */
private void refreshModules() { public EventBus getDownloadBus() {
refreshBus.register(this); return this.downloadBus;
new BookRefreshTask(refreshBus).execute(getInstallersArray());
}
/**
* When book refresh is done, cache the list so we can give that to someone else
* @param event A POJO wrapper around the Book list
*/
@SuppressWarnings("unused")
public void onEvent(EventBookList event) {
this.availableModules = event.getInstallerMapping();
}
/**
* Get the cached book list
* @return The cached book list, or null
*/
public List<Book> getBookList() {
if (availableModules.values().size() == 0) {
return null;
} else {
List<Book> bookList = new ArrayList<Book>();
for (List<Book> l : availableModules.values()) {
bookList.addAll(l);
}
return bookList;
} }
} }
/**
* Get the current download bus if you want to know when refresh is done.
* Please note that you will not be notified if the book refresh has already
* been completed, make sure to check {@link #getBookList()} first.
* @return The EventBus the DownloadManager is using
*/
public EventBus getRefreshBus() {
return this.refreshBus;
}
// TODO: All code below should be migrated to BookDownloadManager
/**
* Handle a book download progress event.
* Mostly responsible for caching the in progress status to check on it easily
* @param event
*/
public void onEvent(DownloadProgressEvent event) {
if (event.isComplete() && inProgressDownloads.containsKey(event.getB())) {
inProgressDownloads.remove(event.getB());
} else {
inProgressDownloads.put(event.getB(), event);
}
}
/**
* Check the status of a book download in progress.
* @param b
* @return The most recent DownloadProgressEvent for the book, or null if not downloading
*/
public DownloadProgressEvent getInProgressDownloadProgress(Book b) {
if (inProgressDownloads.containsKey(b)) {
return inProgressDownloads.get(b);
} else {
return null;
}
}
/**
* Find the installer that a Book comes from.
* @param b The book to search for
* @return The Installer that should be used for this book.
*/
public Installer installerFromBook(Book b) {
for (Map.Entry<Installer, List<Book>> entry : availableModules.entrySet()) {
if (entry.getValue().contains(b)) {
return entry.getKey();
}
}
return null;
}
public void installBook(Book b) {
bookDownloadManager.downloadBook(b);
}
}

View File

@ -0,0 +1,84 @@
package org.bspeice.minimalbible.activities.downloader.manager;
import org.bspeice.minimalbible.MinimalBible;
import org.crosswire.jsword.book.Book;
import org.crosswire.jsword.book.install.Installer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import de.greenrobot.event.EventBus;
/**
* Created by bspeice on 5/24/14.
*/
@Singleton
public class RefreshManager {
@Inject DownloadManager downloadManager;
/**
* Cached copy of modules that are available so we don't refresh for everyone who requests it.
*/
private Map<Installer, List<Book>> availableModules;
public RefreshManager() {
MinimalBible.getApplication().inject(this);
availableModules = new HashMap<Installer, List<Book>>();
refreshModules();
}
/**
* Do the work of kicking off the AsyncTask to refresh books, and make sure we know
* when it's done.
*/
private void refreshModules() {
EventBus refreshBus = downloadManager.getDownloadBus();
refreshBus.register(this);
new BookRefreshTask(refreshBus).execute(downloadManager.getInstallersArray());
}
/**
* When book refresh is done, cache the list so we can give that to someone else
* @param event A POJO wrapper around the Book list
*/
@SuppressWarnings("unused")
public void onEvent(EventBookList event) {
this.availableModules = event.getInstallerMapping();
}
/**
* Get the cached book list
* @return The cached book list, or null
*/
public List<Book> getBookList() {
if (availableModules.values().size() == 0) {
return null;
} else {
List<Book> bookList = new ArrayList<Book>();
for (List<Book> l : availableModules.values()) {
bookList.addAll(l);
}
return bookList;
}
}
/**
* Find the installer that a Book comes from.
* @param b The book to search for
* @return The Installer that should be used for this book.
*/
public Installer installerFromBook(Book b) {
for (Map.Entry<Installer, List<Book>> entry : availableModules.entrySet()) {
if (entry.getValue().contains(b)) {
return entry.getKey();
}
}
return null;
}
}