Minor changes to fix a possible crash in the downloads view
This commit is contained in:
parent
4befcf3819
commit
bfb12bc7c1
@ -10,7 +10,13 @@ import eu.kanade.tachiyomi.ui.main.MainActivity
|
|||||||
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
|
import eu.kanade.tachiyomi.widget.NpaLinearLayoutManager
|
||||||
import kotlinx.android.synthetic.main.fragment_download_queue.*
|
import kotlinx.android.synthetic.main.fragment_download_queue.*
|
||||||
import nucleus.factory.RequiresPresenter
|
import nucleus.factory.RequiresPresenter
|
||||||
|
import rx.Observable
|
||||||
import rx.Subscription
|
import rx.Subscription
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import rx.schedulers.Schedulers
|
||||||
|
import rx.subscriptions.CompositeSubscription
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fragment that shows the currently active downloads.
|
* Fragment that shows the currently active downloads.
|
||||||
@ -40,9 +46,14 @@ class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
|
|||||||
private var clearButton: MenuItem? = null
|
private var clearButton: MenuItem? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Subscription to know if the download queue is running.
|
* Subscription list to be cleared during [onPause].
|
||||||
*/
|
*/
|
||||||
private var queueStatusSubscription: Subscription? = null
|
private val resumeSubscriptions by lazy { CompositeSubscription() }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map of subscriptions for active downloads.
|
||||||
|
*/
|
||||||
|
private val progressSubscriptions by lazy { HashMap<Download, Subscription>() }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether the download queue is running or not.
|
* Whether the download queue is running or not.
|
||||||
@ -66,8 +77,7 @@ class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
|
|||||||
setHasOptionsMenu(true)
|
setHasOptionsMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View {
|
||||||
savedState: Bundle?): View? {
|
|
||||||
return inflater.inflate(R.layout.fragment_download_queue, container, false)
|
return inflater.inflate(R.layout.fragment_download_queue, container, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,15 +133,91 @@ class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
queueStatusSubscription = presenter.downloadManager.runningSubject
|
presenter.downloadManager.runningSubject
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe { onQueueStatusChange(it) }
|
.subscribe { onQueueStatusChange(it) }
|
||||||
|
.apply { resumeSubscriptions.add(this) }
|
||||||
|
|
||||||
|
presenter.getStatusObservable()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { onStatusChange(it) }
|
||||||
|
.apply { resumeSubscriptions.add(this) }
|
||||||
|
|
||||||
|
presenter.getProgressObservable()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { onUpdateDownloadedPages(it) }
|
||||||
|
.apply { resumeSubscriptions.add(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
queueStatusSubscription?.unsubscribe()
|
for (subscription in progressSubscriptions.values) {
|
||||||
|
subscription.unsubscribe()
|
||||||
|
}
|
||||||
|
progressSubscriptions.clear()
|
||||||
|
resumeSubscriptions.clear()
|
||||||
super.onPause()
|
super.onPause()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the status of a download changes.
|
||||||
|
*
|
||||||
|
* @param download the download whose status has changed.
|
||||||
|
*/
|
||||||
|
private fun onStatusChange(download: Download) {
|
||||||
|
when (download.status) {
|
||||||
|
Download.DOWNLOADING -> {
|
||||||
|
observeProgress(download)
|
||||||
|
// Initial update of the downloaded pages
|
||||||
|
onUpdateDownloadedPages(download)
|
||||||
|
}
|
||||||
|
Download.DOWNLOADED -> {
|
||||||
|
unsubscribeProgress(download)
|
||||||
|
onUpdateProgress(download)
|
||||||
|
onUpdateDownloadedPages(download)
|
||||||
|
}
|
||||||
|
Download.ERROR -> unsubscribeProgress(download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observe the progress of a download and notify the view.
|
||||||
|
*
|
||||||
|
* @param download the download to observe its progress.
|
||||||
|
*/
|
||||||
|
private fun observeProgress(download: Download) {
|
||||||
|
val subscription = Observable.interval(50, TimeUnit.MILLISECONDS, Schedulers.newThread())
|
||||||
|
// Get the sum of percentages for all the pages.
|
||||||
|
.flatMap {
|
||||||
|
Observable.from(download.pages)
|
||||||
|
.map { it.progress }
|
||||||
|
.reduce { x, y -> x + y }
|
||||||
|
}
|
||||||
|
// Keep only the latest emission to avoid backpressure.
|
||||||
|
.onBackpressureLatest()
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { progress ->
|
||||||
|
// Update the view only if the progress has changed.
|
||||||
|
if (download.totalProgress != progress) {
|
||||||
|
download.totalProgress = progress
|
||||||
|
onUpdateProgress(download)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid leaking subscriptions
|
||||||
|
progressSubscriptions.remove(download)?.unsubscribe()
|
||||||
|
|
||||||
|
progressSubscriptions.put(download, subscription)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsubscribes the given download from the progress subscriptions.
|
||||||
|
*
|
||||||
|
* @param download the download to unsubscribe.
|
||||||
|
*/
|
||||||
|
private fun unsubscribeProgress(download: Download) {
|
||||||
|
progressSubscriptions.remove(download)?.unsubscribe()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the queue's status has changed. Updates the visibility of the buttons.
|
* Called when the queue's status has changed. Updates the visibility of the buttons.
|
||||||
*
|
*
|
||||||
@ -157,16 +243,16 @@ class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the presenter when the status of a download changes.
|
* Called when the progress of a download changes.
|
||||||
*
|
*
|
||||||
* @param download the download whose status has changed.
|
* @param download the download whose progress has changed.
|
||||||
*/
|
*/
|
||||||
fun onUpdateProgress(download: Download) {
|
fun onUpdateProgress(download: Download) {
|
||||||
getHolder(download)?.notifyProgress()
|
getHolder(download)?.notifyProgress()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called from the presenter when a page of a download is downloaded.
|
* Called when a page of a download is downloaded.
|
||||||
*
|
*
|
||||||
* @param download the download whose page has been downloaded.
|
* @param download the download whose page has been downloaded.
|
||||||
*/
|
*/
|
||||||
|
@ -10,7 +10,7 @@ import kotlinx.android.synthetic.main.item_download.view.*
|
|||||||
* All the elements from the layout file "item_download" are available in this class.
|
* All the elements from the layout file "item_download" are available in this class.
|
||||||
*
|
*
|
||||||
* @param view the inflated view for this holder.
|
* @param view the inflated view for this holder.
|
||||||
* @constructor creates a new library holder.
|
* @constructor creates a new download holder.
|
||||||
*/
|
*/
|
||||||
class DownloadHolder(private val view: View) : RecyclerView.ViewHolder(view) {
|
class DownloadHolder(private val view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
|
||||||
|
@ -6,12 +6,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
|||||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import rx.Subscription
|
|
||||||
import rx.android.schedulers.AndroidSchedulers
|
|
||||||
import rx.schedulers.Schedulers
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -19,6 +14,13 @@ import javax.inject.Inject
|
|||||||
*/
|
*/
|
||||||
class DownloadPresenter : BasePresenter<DownloadFragment>() {
|
class DownloadPresenter : BasePresenter<DownloadFragment>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Id of the restartable that returns the download queue.
|
||||||
|
*/
|
||||||
|
const val GET_DOWNLOAD_QUEUE = 1
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Download manager.
|
* Download manager.
|
||||||
*/
|
*/
|
||||||
@ -30,28 +32,6 @@ class DownloadPresenter : BasePresenter<DownloadFragment>() {
|
|||||||
val downloadQueue: DownloadQueue
|
val downloadQueue: DownloadQueue
|
||||||
get() = downloadManager.queue
|
get() = downloadManager.queue
|
||||||
|
|
||||||
/**
|
|
||||||
* Map of subscriptions for active downloads.
|
|
||||||
*/
|
|
||||||
private val progressSubscriptions by lazy { HashMap<Download, Subscription>() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription for status changes on downloads.
|
|
||||||
*/
|
|
||||||
private var statusSubscription: Subscription? = null
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscription for downloaded pages for active downloads.
|
|
||||||
*/
|
|
||||||
private var pageProgressSubscription: Subscription? = null
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Id of the restartable that returns the download queue.
|
|
||||||
*/
|
|
||||||
const val GET_DOWNLOAD_QUEUE = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate(savedState: Bundle?) {
|
override fun onCreate(savedState: Bundle?) {
|
||||||
super.onCreate(savedState)
|
super.onCreate(savedState)
|
||||||
|
|
||||||
@ -65,102 +45,14 @@ class DownloadPresenter : BasePresenter<DownloadFragment>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTakeView(view: DownloadFragment) {
|
fun getStatusObservable(): Observable<Download> {
|
||||||
super.onTakeView(view)
|
return downloadQueue.getStatusObservable()
|
||||||
|
|
||||||
statusSubscription = downloadQueue.getStatusObservable()
|
|
||||||
.startWith(downloadQueue.getActiveDownloads())
|
.startWith(downloadQueue.getActiveDownloads())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
}
|
||||||
.subscribe { processStatus(it, view) }
|
|
||||||
|
|
||||||
add(statusSubscription)
|
fun getProgressObservable(): Observable<Download> {
|
||||||
|
return downloadQueue.getProgressObservable()
|
||||||
pageProgressSubscription = downloadQueue.getProgressObservable()
|
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe { view.onUpdateDownloadedPages(it) }
|
|
||||||
|
|
||||||
add(pageProgressSubscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDropView() {
|
|
||||||
destroySubscriptions()
|
|
||||||
super.onDropView()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Process the status of a download when its status has changed and notify the view.
|
|
||||||
*
|
|
||||||
* @param download the download whose status has changed.
|
|
||||||
* @param view the view.
|
|
||||||
*/
|
|
||||||
private fun processStatus(download: Download, view: DownloadFragment) {
|
|
||||||
when (download.status) {
|
|
||||||
Download.DOWNLOADING -> {
|
|
||||||
observeProgress(download, view)
|
|
||||||
// Initial update of the downloaded pages
|
|
||||||
view.onUpdateDownloadedPages(download)
|
|
||||||
}
|
|
||||||
Download.DOWNLOADED -> {
|
|
||||||
unsubscribeProgress(download)
|
|
||||||
view.onUpdateProgress(download)
|
|
||||||
view.onUpdateDownloadedPages(download)
|
|
||||||
}
|
|
||||||
Download.ERROR -> unsubscribeProgress(download)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observe the progress of a download and notify the view.
|
|
||||||
*
|
|
||||||
* @param download the download to observe its progress.
|
|
||||||
* @param view the view.
|
|
||||||
*/
|
|
||||||
private fun observeProgress(download: Download, view: DownloadFragment) {
|
|
||||||
val subscription = Observable.interval(50, TimeUnit.MILLISECONDS, Schedulers.newThread())
|
|
||||||
// Get the sum of percentages for all the pages.
|
|
||||||
.flatMap {
|
|
||||||
Observable.from(download.pages)
|
|
||||||
.map { it.progress }
|
|
||||||
.reduce { x, y -> x + y }
|
|
||||||
}
|
|
||||||
// Keep only the latest emission to avoid backpressure.
|
|
||||||
.onBackpressureLatest()
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe { progress ->
|
|
||||||
// Update the view only if the progress has changed.
|
|
||||||
if (download.totalProgress != progress) {
|
|
||||||
download.totalProgress = progress
|
|
||||||
view.onUpdateProgress(download)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid leaking subscriptions
|
|
||||||
progressSubscriptions.remove(download)?.unsubscribe()
|
|
||||||
|
|
||||||
progressSubscriptions.put(download, subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unsubscribes the given download from the progress subscriptions.
|
|
||||||
*
|
|
||||||
* @param download the download to unsubscribe.
|
|
||||||
*/
|
|
||||||
private fun unsubscribeProgress(download: Download) {
|
|
||||||
progressSubscriptions.remove(download)?.unsubscribe()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys all the subscriptions of the presenter.
|
|
||||||
*/
|
|
||||||
private fun destroySubscriptions() {
|
|
||||||
for (subscription in progressSubscriptions.values) {
|
|
||||||
subscription.unsubscribe()
|
|
||||||
}
|
|
||||||
progressSubscriptions.clear()
|
|
||||||
|
|
||||||
remove(pageProgressSubscription)
|
|
||||||
remove(statusSubscription)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user