diff --git a/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadManager.java b/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadManager.java index 545785d2c..67e2254f9 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadManager.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadManager.java @@ -42,11 +42,11 @@ public class DownloadManager { private PublishSubject downloadsQueueSubject; private BehaviorSubject threadsNumber; + private BehaviorSubject runningSubject; private Subscription downloadsSubscription; private Subscription threadsNumberSubscription; private DownloadQueue queue; - private volatile boolean isQueuePaused; private volatile boolean isRunning; public static final String PAGE_LIST_FILE = "index.json"; @@ -61,9 +61,10 @@ public class DownloadManager { downloadsQueueSubject = PublishSubject.create(); threadsNumber = BehaviorSubject.create(); + runningSubject = BehaviorSubject.create(); } - public void initializeSubscriptions() { + private void initializeSubscriptions() { if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) downloadsSubscription.unsubscribe(); @@ -71,8 +72,6 @@ public class DownloadManager { threadsNumberSubscription.unsubscribe(); threadsNumberSubscription = preferences.getDownloadTheadsObservable() - .filter(n -> !isQueuePaused) - .doOnNext(n -> isQueuePaused = (n == 0)) .subscribe(threadsNumber::onNext); downloadsSubscription = downloadsQueueSubject @@ -86,11 +85,17 @@ public class DownloadManager { } }, e -> Timber.e(e.getCause(), e.getMessage())); - isRunning = true; + if (!isRunning) { + isRunning = true; + runningSubject.onNext(true); + } } public void destroySubscriptions() { - isRunning = false; + if (isRunning) { + isRunning = false; + runningSubject.onNext(false); + } if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) { downloadsSubscription.unsubscribe(); @@ -131,7 +136,7 @@ public class DownloadManager { // Prepare the download. Returns true if the chapter is already downloaded private boolean prepareDownload(Download download) { // If the chapter is already queued, don't add it again - for (Download queuedDownload : queue.get()) { + for (Download queuedDownload : queue) { if (download.chapter.id.equals(queuedDownload.chapter.id)) return true; } @@ -376,28 +381,22 @@ public class DownloadManager { } public boolean areAllDownloadsFinished() { - for (Download download : queue.get()) { + for (Download download : queue) { if (download.getStatus() <= Download.DOWNLOADING) return false; } return true; } - public void resumeDownloads() { - isQueuePaused = false; - threadsNumber.onNext(preferences.getDownloadThreads()); - } - - public void pauseDownloads() { - threadsNumber.onNext(0); - } - public boolean startDownloads() { + if (queue.isEmpty()) + return false; + boolean hasPendingDownloads = false; if (downloadsSubscription == null || threadsNumberSubscription == null) initializeSubscriptions(); - for (Download download : queue.get()) { + for (Download download : queue) { if (download.getStatus() != Download.DOWNLOADED) { if (download.getStatus() != Download.QUEUE) download.setStatus(Download.QUEUE); if (!hasPendingDownloads) hasPendingDownloads = true; @@ -409,15 +408,15 @@ public class DownloadManager { public void stopDownloads() { destroySubscriptions(); - for (Download download : queue.get()) { + for (Download download : queue) { if (download.getStatus() == Download.DOWNLOADING) { download.setStatus(Download.ERROR); } } } - public boolean isRunning() { - return isRunning; + public BehaviorSubject getRunningSubject() { + return runningSubject; } } diff --git a/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadService.java b/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadService.java index 2b648a845..b65a9fe2b 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadService.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/download/DownloadService.java @@ -24,6 +24,7 @@ public class DownloadService extends Service { private PowerManager.WakeLock wakeLock; private Subscription networkChangeSubscription; + private Subscription queueRunningSubscription; public static void start(Context context) { context.startService(new Intent(context, DownloadService.class)); @@ -40,6 +41,7 @@ public class DownloadService extends Service { createWakeLock(); + listenQueueRunningChanges(); EventBus.getDefault().registerSticky(this); listenNetworkChanges(); } @@ -52,6 +54,7 @@ public class DownloadService extends Service { @Override public void onDestroy() { EventBus.getDefault().unregister(this); + queueRunningSubscription.unsubscribe(); networkChangeSubscription.unsubscribe(); downloadManager.destroySubscriptions(); destroyWakeLock(); @@ -67,8 +70,6 @@ public class DownloadService extends Service { public void onEvent(DownloadChaptersEvent event) { EventBus.getDefault().removeStickyEvent(event); downloadManager.onDownloadChaptersEvent(event); - if (downloadManager.isRunning()) - acquireWakeLock(); } private void listenNetworkChanges() { @@ -79,15 +80,22 @@ public class DownloadService extends Service { // If there are no remaining downloads, destroy the service if (!downloadManager.startDownloads()) stopSelf(); - else - acquireWakeLock(); } else { downloadManager.stopDownloads(); - releaseWakeLock(); } }); } + private void listenQueueRunningChanges() { + queueRunningSubscription = downloadManager.getRunningSubject() + .subscribe(running -> { + if (running) + acquireWakeLock(); + else + releaseWakeLock(); + }); + } + private void createWakeLock() { wakeLock = ((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock( PowerManager.PARTIAL_WAKE_LOCK, "DownloadService:WakeLock"); diff --git a/app/src/main/java/eu/kanade/mangafeed/data/download/model/DownloadQueue.java b/app/src/main/java/eu/kanade/mangafeed/data/download/model/DownloadQueue.java index 0407c589a..06b0df460 100644 --- a/app/src/main/java/eu/kanade/mangafeed/data/download/model/DownloadQueue.java +++ b/app/src/main/java/eu/kanade/mangafeed/data/download/model/DownloadQueue.java @@ -8,29 +8,28 @@ import eu.kanade.mangafeed.data.source.model.Page; import rx.Observable; import rx.subjects.PublishSubject; -public class DownloadQueue { +public class DownloadQueue extends ArrayList { - private List queue; private PublishSubject statusSubject; public DownloadQueue() { - queue = new ArrayList<>(); + super(); statusSubject = PublishSubject.create(); } - public void add(Download download) { + public boolean add(Download download) { download.setStatusSubject(statusSubject); download.setStatus(Download.QUEUE); - queue.add(download); + return super.add(download); } public void remove(Download download) { - queue.remove(download); + super.remove(download); download.setStatusSubject(null); } public void remove(Chapter chapter) { - for (Download download : queue) { + for (Download download : this) { if (download.chapter.id.equals(chapter.id)) { remove(download); break; @@ -38,12 +37,8 @@ public class DownloadQueue { } } - public List get() { - return queue; - } - public Observable getActiveDownloads() { - return Observable.from(queue) + return Observable.from(this) .filter(download -> download.getStatus() == Download.DOWNLOADING); } diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadFragment.java b/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadFragment.java index 9c3c7db78..b78cfa780 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadFragment.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadFragment.java @@ -5,6 +5,9 @@ import android.support.annotation.Nullable; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -13,9 +16,11 @@ import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; import eu.kanade.mangafeed.R; +import eu.kanade.mangafeed.data.download.DownloadService; import eu.kanade.mangafeed.data.download.model.Download; import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment; import nucleus.factory.RequiresPresenter; +import rx.Subscription; @RequiresPresenter(DownloadPresenter.class) public class DownloadFragment extends BaseRxFragment { @@ -23,10 +28,22 @@ public class DownloadFragment extends BaseRxFragment { @Bind(R.id.download_list) RecyclerView recyclerView; private DownloadAdapter adapter; + private MenuItem startButton; + private MenuItem stopButton; + + private Subscription queueStatusSubscription; + private boolean isRunning; + public static DownloadFragment newInstance() { return new DownloadFragment(); } + @Override + public void onCreate(Bundle bundle) { + super.onCreate(bundle); + setHasOptionsMenu(true); + } + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -43,6 +60,51 @@ public class DownloadFragment extends BaseRxFragment { return view; } + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.download_queue, menu); + startButton = menu.findItem(R.id.start_queue); + stopButton = menu.findItem(R.id.stop_queue); + + // Menu seems to be inflated after onResume in fragments, so we initialize them here + startButton.setVisible(!isRunning && !getPresenter().downloadManager.getQueue().isEmpty()); + stopButton.setVisible(isRunning); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case R.id.start_queue: + DownloadService.start(getActivity()); + break; + case R.id.stop_queue: + DownloadService.stop(getActivity()); + break; + } + return super.onOptionsItemSelected(item); + } + + @Override + public void onResume() { + super.onResume(); + queueStatusSubscription = getPresenter().downloadManager.getRunningSubject() + .subscribe(this::onRunningChange); + } + + @Override + public void onPause() { + queueStatusSubscription.unsubscribe(); + super.onPause(); + } + + private void onRunningChange(boolean running) { + isRunning = running; + if (startButton != null) + startButton.setVisible(!running && !getPresenter().downloadManager.getQueue().isEmpty()); + if (stopButton != null) + stopButton.setVisible(running); + } + private void createAdapter() { adapter = new DownloadAdapter(getActivity()); recyclerView.setAdapter(adapter); diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadPresenter.java b/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadPresenter.java index 4d4954c6b..2b48da411 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadPresenter.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/download/DownloadPresenter.java @@ -37,7 +37,7 @@ public class DownloadPresenter extends BasePresenter { progressSubscriptions = new HashMap<>(); restartableLatestCache(GET_DOWNLOAD_QUEUE, - () -> Observable.just(downloadQueue.get()), + () -> Observable.just(downloadQueue), DownloadFragment::onNextDownloads, (view, error) -> Timber.e(error.getMessage())); diff --git a/app/src/main/java/eu/kanade/mangafeed/ui/manga/chapter/ChaptersPresenter.java b/app/src/main/java/eu/kanade/mangafeed/ui/manga/chapter/ChaptersPresenter.java index fc0831c25..093e2144f 100644 --- a/app/src/main/java/eu/kanade/mangafeed/ui/manga/chapter/ChaptersPresenter.java +++ b/app/src/main/java/eu/kanade/mangafeed/ui/manga/chapter/ChaptersPresenter.java @@ -148,7 +148,7 @@ public class ChaptersPresenter extends BasePresenter { } private void setChapterStatus(Chapter chapter) { - for (Download download : downloadManager.getQueue().get()) { + for (Download download : downloadManager.getQueue()) { if (chapter.id.equals(download.chapter.id)) { chapter.status = download.getStatus(); return; diff --git a/app/src/main/res/drawable-hdpi/ic_play_arrow.png b/app/src/main/res/drawable-hdpi/ic_play_arrow.png new file mode 100644 index 000000000..2c9651862 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_play_arrow.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_stop.png b/app/src/main/res/drawable-hdpi/ic_stop.png new file mode 100644 index 000000000..a692d9d1c Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_stop.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_play_arrow.png b/app/src/main/res/drawable-ldpi/ic_play_arrow.png new file mode 100644 index 000000000..b83fa2514 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_play_arrow.png differ diff --git a/app/src/main/res/drawable-ldpi/ic_stop.png b/app/src/main/res/drawable-ldpi/ic_stop.png new file mode 100644 index 000000000..0d3e6f1a8 Binary files /dev/null and b/app/src/main/res/drawable-ldpi/ic_stop.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_play_arrow.png b/app/src/main/res/drawable-mdpi/ic_play_arrow.png new file mode 100644 index 000000000..2b0e3baca Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_play_arrow.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_stop.png b/app/src/main/res/drawable-mdpi/ic_stop.png new file mode 100644 index 000000000..8af466941 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_stop.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_play_arrow.png b/app/src/main/res/drawable-xhdpi/ic_play_arrow.png new file mode 100644 index 000000000..c369bafda Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_play_arrow.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_stop.png b/app/src/main/res/drawable-xhdpi/ic_stop.png new file mode 100644 index 000000000..8b3507383 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_stop.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_play_arrow.png b/app/src/main/res/drawable-xxhdpi/ic_play_arrow.png new file mode 100644 index 000000000..41061c1c8 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_play_arrow.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_stop.png b/app/src/main/res/drawable-xxhdpi/ic_stop.png new file mode 100644 index 000000000..f9307dec0 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_stop.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_play_arrow.png b/app/src/main/res/drawable-xxxhdpi/ic_play_arrow.png new file mode 100644 index 000000000..6b8b55f78 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_play_arrow.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_stop.png b/app/src/main/res/drawable-xxxhdpi/ic_stop.png new file mode 100644 index 000000000..630cb2e63 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_stop.png differ diff --git a/app/src/main/res/menu/download_queue.xml b/app/src/main/res/menu/download_queue.xml new file mode 100644 index 000000000..e15e208e0 --- /dev/null +++ b/app/src/main/res/menu/download_queue.xml @@ -0,0 +1,17 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 70ba9dbf8..cb0c10093 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -24,6 +24,8 @@ Unread Downloaded Next unread + Start + Stop OK