Allow to start/stop queue from download queue fragment. DownloadQueue now extends from ArrayList.

This commit is contained in:
inorichi 2015-12-19 14:58:36 +01:00
parent 6412ec1d9b
commit 3c87b4cba9
20 changed files with 123 additions and 40 deletions

View File

@ -42,11 +42,11 @@ public class DownloadManager {
private PublishSubject<Download> downloadsQueueSubject; private PublishSubject<Download> downloadsQueueSubject;
private BehaviorSubject<Integer> threadsNumber; private BehaviorSubject<Integer> threadsNumber;
private BehaviorSubject<Boolean> runningSubject;
private Subscription downloadsSubscription; private Subscription downloadsSubscription;
private Subscription threadsNumberSubscription; private Subscription threadsNumberSubscription;
private DownloadQueue queue; private DownloadQueue queue;
private volatile boolean isQueuePaused;
private volatile boolean isRunning; private volatile boolean isRunning;
public static final String PAGE_LIST_FILE = "index.json"; public static final String PAGE_LIST_FILE = "index.json";
@ -61,9 +61,10 @@ public class DownloadManager {
downloadsQueueSubject = PublishSubject.create(); downloadsQueueSubject = PublishSubject.create();
threadsNumber = BehaviorSubject.create(); threadsNumber = BehaviorSubject.create();
runningSubject = BehaviorSubject.create();
} }
public void initializeSubscriptions() { private void initializeSubscriptions() {
if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed())
downloadsSubscription.unsubscribe(); downloadsSubscription.unsubscribe();
@ -71,8 +72,6 @@ public class DownloadManager {
threadsNumberSubscription.unsubscribe(); threadsNumberSubscription.unsubscribe();
threadsNumberSubscription = preferences.getDownloadTheadsObservable() threadsNumberSubscription = preferences.getDownloadTheadsObservable()
.filter(n -> !isQueuePaused)
.doOnNext(n -> isQueuePaused = (n == 0))
.subscribe(threadsNumber::onNext); .subscribe(threadsNumber::onNext);
downloadsSubscription = downloadsQueueSubject downloadsSubscription = downloadsQueueSubject
@ -86,11 +85,17 @@ public class DownloadManager {
} }
}, e -> Timber.e(e.getCause(), e.getMessage())); }, e -> Timber.e(e.getCause(), e.getMessage()));
isRunning = true; if (!isRunning) {
isRunning = true;
runningSubject.onNext(true);
}
} }
public void destroySubscriptions() { public void destroySubscriptions() {
isRunning = false; if (isRunning) {
isRunning = false;
runningSubject.onNext(false);
}
if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) { if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed()) {
downloadsSubscription.unsubscribe(); downloadsSubscription.unsubscribe();
@ -131,7 +136,7 @@ public class DownloadManager {
// Prepare the download. Returns true if the chapter is already downloaded // Prepare the download. Returns true if the chapter is already downloaded
private boolean prepareDownload(Download download) { private boolean prepareDownload(Download download) {
// If the chapter is already queued, don't add it again // 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)) if (download.chapter.id.equals(queuedDownload.chapter.id))
return true; return true;
} }
@ -376,28 +381,22 @@ public class DownloadManager {
} }
public boolean areAllDownloadsFinished() { public boolean areAllDownloadsFinished() {
for (Download download : queue.get()) { for (Download download : queue) {
if (download.getStatus() <= Download.DOWNLOADING) if (download.getStatus() <= Download.DOWNLOADING)
return false; return false;
} }
return true; return true;
} }
public void resumeDownloads() {
isQueuePaused = false;
threadsNumber.onNext(preferences.getDownloadThreads());
}
public void pauseDownloads() {
threadsNumber.onNext(0);
}
public boolean startDownloads() { public boolean startDownloads() {
if (queue.isEmpty())
return false;
boolean hasPendingDownloads = false; boolean hasPendingDownloads = false;
if (downloadsSubscription == null || threadsNumberSubscription == null) if (downloadsSubscription == null || threadsNumberSubscription == null)
initializeSubscriptions(); initializeSubscriptions();
for (Download download : queue.get()) { for (Download download : queue) {
if (download.getStatus() != Download.DOWNLOADED) { if (download.getStatus() != Download.DOWNLOADED) {
if (download.getStatus() != Download.QUEUE) download.setStatus(Download.QUEUE); if (download.getStatus() != Download.QUEUE) download.setStatus(Download.QUEUE);
if (!hasPendingDownloads) hasPendingDownloads = true; if (!hasPendingDownloads) hasPendingDownloads = true;
@ -409,15 +408,15 @@ public class DownloadManager {
public void stopDownloads() { public void stopDownloads() {
destroySubscriptions(); destroySubscriptions();
for (Download download : queue.get()) { for (Download download : queue) {
if (download.getStatus() == Download.DOWNLOADING) { if (download.getStatus() == Download.DOWNLOADING) {
download.setStatus(Download.ERROR); download.setStatus(Download.ERROR);
} }
} }
} }
public boolean isRunning() { public BehaviorSubject<Boolean> getRunningSubject() {
return isRunning; return runningSubject;
} }
} }

View File

@ -24,6 +24,7 @@ public class DownloadService extends Service {
private PowerManager.WakeLock wakeLock; private PowerManager.WakeLock wakeLock;
private Subscription networkChangeSubscription; private Subscription networkChangeSubscription;
private Subscription queueRunningSubscription;
public static void start(Context context) { public static void start(Context context) {
context.startService(new Intent(context, DownloadService.class)); context.startService(new Intent(context, DownloadService.class));
@ -40,6 +41,7 @@ public class DownloadService extends Service {
createWakeLock(); createWakeLock();
listenQueueRunningChanges();
EventBus.getDefault().registerSticky(this); EventBus.getDefault().registerSticky(this);
listenNetworkChanges(); listenNetworkChanges();
} }
@ -52,6 +54,7 @@ public class DownloadService extends Service {
@Override @Override
public void onDestroy() { public void onDestroy() {
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
queueRunningSubscription.unsubscribe();
networkChangeSubscription.unsubscribe(); networkChangeSubscription.unsubscribe();
downloadManager.destroySubscriptions(); downloadManager.destroySubscriptions();
destroyWakeLock(); destroyWakeLock();
@ -67,8 +70,6 @@ public class DownloadService extends Service {
public void onEvent(DownloadChaptersEvent event) { public void onEvent(DownloadChaptersEvent event) {
EventBus.getDefault().removeStickyEvent(event); EventBus.getDefault().removeStickyEvent(event);
downloadManager.onDownloadChaptersEvent(event); downloadManager.onDownloadChaptersEvent(event);
if (downloadManager.isRunning())
acquireWakeLock();
} }
private void listenNetworkChanges() { private void listenNetworkChanges() {
@ -79,15 +80,22 @@ public class DownloadService extends Service {
// If there are no remaining downloads, destroy the service // If there are no remaining downloads, destroy the service
if (!downloadManager.startDownloads()) if (!downloadManager.startDownloads())
stopSelf(); stopSelf();
else
acquireWakeLock();
} else { } else {
downloadManager.stopDownloads(); downloadManager.stopDownloads();
releaseWakeLock();
} }
}); });
} }
private void listenQueueRunningChanges() {
queueRunningSubscription = downloadManager.getRunningSubject()
.subscribe(running -> {
if (running)
acquireWakeLock();
else
releaseWakeLock();
});
}
private void createWakeLock() { private void createWakeLock() {
wakeLock = ((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock( wakeLock = ((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, "DownloadService:WakeLock"); PowerManager.PARTIAL_WAKE_LOCK, "DownloadService:WakeLock");

View File

@ -8,29 +8,28 @@ import eu.kanade.mangafeed.data.source.model.Page;
import rx.Observable; import rx.Observable;
import rx.subjects.PublishSubject; import rx.subjects.PublishSubject;
public class DownloadQueue { public class DownloadQueue extends ArrayList<Download> {
private List<Download> queue;
private PublishSubject<Download> statusSubject; private PublishSubject<Download> statusSubject;
public DownloadQueue() { public DownloadQueue() {
queue = new ArrayList<>(); super();
statusSubject = PublishSubject.create(); statusSubject = PublishSubject.create();
} }
public void add(Download download) { public boolean add(Download download) {
download.setStatusSubject(statusSubject); download.setStatusSubject(statusSubject);
download.setStatus(Download.QUEUE); download.setStatus(Download.QUEUE);
queue.add(download); return super.add(download);
} }
public void remove(Download download) { public void remove(Download download) {
queue.remove(download); super.remove(download);
download.setStatusSubject(null); download.setStatusSubject(null);
} }
public void remove(Chapter chapter) { public void remove(Chapter chapter) {
for (Download download : queue) { for (Download download : this) {
if (download.chapter.id.equals(chapter.id)) { if (download.chapter.id.equals(chapter.id)) {
remove(download); remove(download);
break; break;
@ -38,12 +37,8 @@ public class DownloadQueue {
} }
} }
public List<Download> get() {
return queue;
}
public Observable<Download> getActiveDownloads() { public Observable<Download> getActiveDownloads() {
return Observable.from(queue) return Observable.from(this)
.filter(download -> download.getStatus() == Download.DOWNLOADING); .filter(download -> download.getStatus() == Download.DOWNLOADING);
} }

View File

@ -5,6 +5,9 @@ import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@ -13,9 +16,11 @@ import java.util.List;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.kanade.mangafeed.R; import eu.kanade.mangafeed.R;
import eu.kanade.mangafeed.data.download.DownloadService;
import eu.kanade.mangafeed.data.download.model.Download; import eu.kanade.mangafeed.data.download.model.Download;
import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment; import eu.kanade.mangafeed.ui.base.fragment.BaseRxFragment;
import nucleus.factory.RequiresPresenter; import nucleus.factory.RequiresPresenter;
import rx.Subscription;
@RequiresPresenter(DownloadPresenter.class) @RequiresPresenter(DownloadPresenter.class)
public class DownloadFragment extends BaseRxFragment<DownloadPresenter> { public class DownloadFragment extends BaseRxFragment<DownloadPresenter> {
@ -23,10 +28,22 @@ public class DownloadFragment extends BaseRxFragment<DownloadPresenter> {
@Bind(R.id.download_list) RecyclerView recyclerView; @Bind(R.id.download_list) RecyclerView recyclerView;
private DownloadAdapter adapter; private DownloadAdapter adapter;
private MenuItem startButton;
private MenuItem stopButton;
private Subscription queueStatusSubscription;
private boolean isRunning;
public static DownloadFragment newInstance() { public static DownloadFragment newInstance() {
return new DownloadFragment(); return new DownloadFragment();
} }
@Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setHasOptionsMenu(true);
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
@ -43,6 +60,51 @@ public class DownloadFragment extends BaseRxFragment<DownloadPresenter> {
return view; 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() { private void createAdapter() {
adapter = new DownloadAdapter(getActivity()); adapter = new DownloadAdapter(getActivity());
recyclerView.setAdapter(adapter); recyclerView.setAdapter(adapter);

View File

@ -37,7 +37,7 @@ public class DownloadPresenter extends BasePresenter<DownloadFragment> {
progressSubscriptions = new HashMap<>(); progressSubscriptions = new HashMap<>();
restartableLatestCache(GET_DOWNLOAD_QUEUE, restartableLatestCache(GET_DOWNLOAD_QUEUE,
() -> Observable.just(downloadQueue.get()), () -> Observable.just(downloadQueue),
DownloadFragment::onNextDownloads, DownloadFragment::onNextDownloads,
(view, error) -> Timber.e(error.getMessage())); (view, error) -> Timber.e(error.getMessage()));

View File

@ -148,7 +148,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
} }
private void setChapterStatus(Chapter chapter) { private void setChapterStatus(Chapter chapter) {
for (Download download : downloadManager.getQueue().get()) { for (Download download : downloadManager.getQueue()) {
if (chapter.id.equals(download.chapter.id)) { if (chapter.id.equals(download.chapter.id)) {
chapter.status = download.getStatus(); chapter.status = download.getStatus();
return; return;

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 B

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/action_start"
android:id="@+id/start_queue"
android:icon="@drawable/ic_play_arrow"
android:visible="false"
app:showAsAction="ifRoom"/>
<item android:title="@string/action_stop"
android:id="@+id/stop_queue"
android:icon="@drawable/ic_stop"
android:visible="false"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -24,6 +24,8 @@
<string name="action_show_unread">Unread</string> <string name="action_show_unread">Unread</string>
<string name="action_show_downloaded">Downloaded</string> <string name="action_show_downloaded">Downloaded</string>
<string name="action_next_unread">Next unread</string> <string name="action_next_unread">Next unread</string>
<string name="action_start">Start</string>
<string name="action_stop">Stop</string>
<!-- Buttons --> <!-- Buttons -->
<string name="button_ok">OK</string> <string name="button_ok">OK</string>