Downloads with conductor. Remove flexible adapter 4 dependency and unused classes.
This commit is contained in:
parent
f521622d4d
commit
72ea256906
10 changed files with 306 additions and 397 deletions
|
@ -100,17 +100,6 @@ android {
|
|||
|
||||
dependencies {
|
||||
|
||||
compile "com.bluelinelabs:conductor:2.1.3"
|
||||
compile 'com.github.inorichi:conductor-support-preference:master-SNAPSHOT'
|
||||
|
||||
final rxbindings_version = '1.0.1'
|
||||
compile "com.jakewharton.rxbinding:rxbinding-kotlin:$rxbindings_version"
|
||||
compile "com.jakewharton.rxbinding:rxbinding-appcompat-v7-kotlin:$rxbindings_version"
|
||||
compile "com.jakewharton.rxbinding:rxbinding-support-v4-kotlin:$rxbindings_version"
|
||||
compile "com.jakewharton.rxbinding:rxbinding-recyclerview-v7-kotlin:$rxbindings_version"
|
||||
|
||||
compile 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.0.4'
|
||||
|
||||
// Modified dependencies
|
||||
compile 'com.github.inorichi:subsampling-scale-image-view:01e5385'
|
||||
compile 'com.github.inorichi:junrar-android:634c1f5'
|
||||
|
@ -201,12 +190,23 @@ dependencies {
|
|||
compile 'com.dmitrymalkovich.android:material-design-dimens:1.4'
|
||||
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
||||
compile 'eu.davidea:flexible-adapter:5.0.0-rc1'
|
||||
compile 'com.github.inorichi:FlexibleAdapter:93985fe' // v4.2.0 to be removed
|
||||
compile 'com.nononsenseapps:filepicker:2.5.2'
|
||||
compile 'com.github.amulyakhare:TextDrawable:558677e'
|
||||
compile 'com.afollestad.material-dialogs:core:0.9.4.2'
|
||||
compile 'me.zhanghai.android.systemuihelper:library:1.0.0'
|
||||
compile 'de.hdodenhof:circleimageview:2.1.0'
|
||||
compile 'com.nightlynexus.viewstatepageradapter:viewstatepageradapter:1.0.4'
|
||||
|
||||
// Conductor
|
||||
compile "com.bluelinelabs:conductor:2.1.3"
|
||||
compile 'com.github.inorichi:conductor-support-preference:9e36460'
|
||||
|
||||
// RxBindings
|
||||
final rxbindings_version = '1.0.1'
|
||||
compile "com.jakewharton.rxbinding:rxbinding-kotlin:$rxbindings_version"
|
||||
compile "com.jakewharton.rxbinding:rxbinding-appcompat-v7-kotlin:$rxbindings_version"
|
||||
compile "com.jakewharton.rxbinding:rxbinding-support-v4-kotlin:$rxbindings_version"
|
||||
compile "com.jakewharton.rxbinding:rxbinding-recyclerview-v7-kotlin:$rxbindings_version"
|
||||
|
||||
// Tests
|
||||
testCompile 'junit:junit:4.12'
|
||||
|
|
|
@ -53,9 +53,6 @@
|
|||
android:scheme="tachiyomi" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.download.DownloadActivity"
|
||||
android:launchMode="singleTop" />
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
|
|
|
@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.data.notification
|
|||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadActivity
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.util.getUriCompat
|
||||
import java.io.File
|
||||
|
||||
|
@ -17,8 +17,9 @@ object NotificationHandler {
|
|||
* @param context context of application
|
||||
*/
|
||||
internal fun openDownloadManagerPendingActivity(context: Context): PendingIntent {
|
||||
val intent = Intent(context, DownloadActivity::class.java).apply {
|
||||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT
|
||||
action = MainActivity.SHORTCUT_DOWNLOADS
|
||||
}
|
||||
return PendingIntent.getActivity(context, 0, intent, 0)
|
||||
}
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.base.adapter
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.View
|
||||
|
||||
import eu.davidea.flexibleadapter4.FlexibleAdapter
|
||||
|
||||
abstract class FlexibleViewHolder(view: View,
|
||||
private val adapter: FlexibleAdapter<*, *>,
|
||||
private val itemClickListener: FlexibleViewHolder.OnListItemClickListener) :
|
||||
RecyclerView.ViewHolder(view), View.OnClickListener, View.OnLongClickListener {
|
||||
|
||||
init {
|
||||
view.setOnClickListener(this)
|
||||
view.setOnLongClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(view: View) {
|
||||
if (itemClickListener.onListItemClick(adapterPosition)) {
|
||||
toggleActivation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLongClick(view: View): Boolean {
|
||||
itemClickListener.onListItemLongClick(adapterPosition)
|
||||
toggleActivation()
|
||||
return true
|
||||
}
|
||||
|
||||
fun toggleActivation() {
|
||||
itemView.isActivated = adapter.isSelected(adapterPosition)
|
||||
}
|
||||
|
||||
interface OnListItemClickListener {
|
||||
fun onListItemClick(position: Int): Boolean
|
||||
fun onListItemLongClick(position: Int)
|
||||
}
|
||||
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package eu.kanade.tachiyomi.ui.base.adapter
|
||||
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v4.app.FragmentStatePagerAdapter
|
||||
import android.util.SparseArray
|
||||
import android.view.ViewGroup
|
||||
import java.util.*
|
||||
|
||||
abstract class SmartFragmentStatePagerAdapter(fragmentManager: FragmentManager) :
|
||||
FragmentStatePagerAdapter(fragmentManager) {
|
||||
// Sparse array to keep track of registered fragments in memory
|
||||
private val registeredFragments = SparseArray<Fragment>()
|
||||
|
||||
// Register the fragment when the item is instantiated
|
||||
override fun instantiateItem(container: ViewGroup, position: Int): Any {
|
||||
val fragment = super.instantiateItem(container, position) as Fragment
|
||||
registeredFragments.put(position, fragment)
|
||||
return fragment
|
||||
}
|
||||
|
||||
// Unregister when the item is inactive
|
||||
override fun destroyItem(container: ViewGroup?, position: Int, `object`: Any) {
|
||||
registeredFragments.remove(position)
|
||||
super.destroyItem(container, position, `object`)
|
||||
}
|
||||
|
||||
// Returns the fragment for the position (if instantiated)
|
||||
fun getRegisteredFragment(position: Int): Fragment {
|
||||
return registeredFragments.get(position)
|
||||
}
|
||||
|
||||
fun getRegisteredFragments(): List<Fragment> {
|
||||
val fragments = ArrayList<Fragment>()
|
||||
for (i in 0..registeredFragments.size() - 1) {
|
||||
fragments.add(registeredFragments.valueAt(i))
|
||||
}
|
||||
return fragments
|
||||
}
|
||||
|
||||
}
|
|
@ -1,8 +1,7 @@
|
|||
package eu.kanade.tachiyomi.ui.download
|
||||
|
||||
import android.content.Context
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.view.ViewGroup
|
||||
import eu.davidea.flexibleadapter4.FlexibleAdapter
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.util.inflate
|
||||
|
@ -12,7 +11,9 @@ import eu.kanade.tachiyomi.util.inflate
|
|||
*
|
||||
* @param context the context of the fragment containing this adapter.
|
||||
*/
|
||||
class DownloadAdapter(private val context: Context) : FlexibleAdapter<DownloadHolder, Download>() {
|
||||
class DownloadAdapter : RecyclerView.Adapter<DownloadHolder>() {
|
||||
|
||||
private var items = emptyList<Download>()
|
||||
|
||||
init {
|
||||
setHasStableIds(true)
|
||||
|
@ -24,10 +25,17 @@ class DownloadAdapter(private val context: Context) : FlexibleAdapter<DownloadHo
|
|||
* @param downloads the list to set.
|
||||
*/
|
||||
fun setItems(downloads: List<Download>) {
|
||||
mItems = downloads
|
||||
items = downloads
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of downloads in the adapter
|
||||
*/
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier for a download.
|
||||
*
|
||||
|
@ -35,7 +43,7 @@ class DownloadAdapter(private val context: Context) : FlexibleAdapter<DownloadHo
|
|||
* @return an identifier for the item.
|
||||
*/
|
||||
override fun getItemId(position: Int): Long {
|
||||
return getItem(position).chapter.id!!
|
||||
return items[position].chapter.id!!
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -57,14 +65,8 @@ class DownloadAdapter(private val context: Context) : FlexibleAdapter<DownloadHo
|
|||
* @param position the position to bind.
|
||||
*/
|
||||
override fun onBindViewHolder(holder: DownloadHolder, position: Int) {
|
||||
val download = getItem(position)
|
||||
val download = items[position]
|
||||
holder.onSetValues(download)
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to filter the list. Not used.
|
||||
*/
|
||||
override fun updateDataSet(param: String) {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,246 +1,252 @@
|
|||
package eu.kanade.tachiyomi.ui.download
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
|
||||
import eu.kanade.tachiyomi.util.plusAssign
|
||||
import kotlinx.android.synthetic.main.fragment_download_queue.*
|
||||
import kotlinx.android.synthetic.main.toolbar.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.subscriptions.CompositeSubscription
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Activity that shows the currently active downloads.
|
||||
* Uses R.layout.fragment_download_queue.
|
||||
*/
|
||||
@RequiresPresenter(DownloadPresenter::class)
|
||||
class DownloadActivity : BaseRxActivity<DownloadPresenter>() {
|
||||
/**
|
||||
* Adapter containing the active downloads.
|
||||
*/
|
||||
private lateinit var adapter: DownloadAdapter
|
||||
|
||||
/**
|
||||
* Subscription list to be cleared during [onDestroy].
|
||||
*/
|
||||
private val subscriptions 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.
|
||||
*/
|
||||
private var isRunning: Boolean = false
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
setAppTheme()
|
||||
super.onCreate(savedState)
|
||||
setContentView(R.layout.activity_download_manager)
|
||||
setupToolbar(toolbar)
|
||||
setToolbarTitle(R.string.label_download_queue)
|
||||
|
||||
// Check if download queue is empty and update information accordingly.
|
||||
setInformationView()
|
||||
|
||||
// Initialize adapter.
|
||||
adapter = DownloadAdapter(this)
|
||||
recycler.adapter = adapter
|
||||
|
||||
// Set the layout manager for the recycler and fixed size.
|
||||
recycler.layoutManager = LinearLayoutManager(this)
|
||||
recycler.setHasFixedSize(true)
|
||||
|
||||
// Suscribe to changes
|
||||
subscriptions += DownloadService.runningRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { onQueueStatusChange(it) }
|
||||
|
||||
subscriptions += presenter.getDownloadStatusObservable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { onStatusChange(it) }
|
||||
|
||||
subscriptions += presenter.getDownloadProgressObservable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe { onUpdateDownloadedPages(it) }
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
for (subscription in progressSubscriptions.values) {
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
progressSubscriptions.clear()
|
||||
subscriptions.clear()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.download_queue, menu)
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
// Set start button visibility.
|
||||
menu.findItem(R.id.start_queue).isVisible = !isRunning && !presenter.downloadQueue.isEmpty()
|
||||
|
||||
// Set pause button visibility.
|
||||
menu.findItem(R.id.pause_queue).isVisible = isRunning
|
||||
|
||||
// Set clear button visibility.
|
||||
menu.findItem(R.id.clear_queue).isVisible = !presenter.downloadQueue.isEmpty()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.start_queue -> DownloadService.start(this)
|
||||
R.id.pause_queue -> {
|
||||
DownloadService.stop(this)
|
||||
presenter.pauseDownloads()
|
||||
}
|
||||
R.id.clear_queue -> {
|
||||
DownloadService.stop(this)
|
||||
presenter.clearQueue()
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
// Get the sum of percentages for all the pages.
|
||||
.flatMap {
|
||||
Observable.from(download.pages)
|
||||
.map(Page::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.
|
||||
*
|
||||
* @param running whether the queue is now running or not.
|
||||
*/
|
||||
private fun onQueueStatusChange(running: Boolean) {
|
||||
isRunning = running
|
||||
supportInvalidateOptionsMenu()
|
||||
|
||||
// Check if download queue is empty and update information accordingly.
|
||||
setInformationView()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter to assign the downloads for the adapter.
|
||||
*
|
||||
* @param downloads the downloads from the queue.
|
||||
*/
|
||||
fun onNextDownloads(downloads: List<Download>) {
|
||||
supportInvalidateOptionsMenu()
|
||||
setInformationView()
|
||||
adapter.setItems(downloads)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the progress of a download changes.
|
||||
*
|
||||
* @param download the download whose progress has changed.
|
||||
*/
|
||||
fun onUpdateProgress(download: Download) {
|
||||
getHolder(download)?.notifyProgress()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a page of a download is downloaded.
|
||||
*
|
||||
* @param download the download whose page has been downloaded.
|
||||
*/
|
||||
fun onUpdateDownloadedPages(download: Download) {
|
||||
getHolder(download)?.notifyDownloadedPages()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the holder for the given download.
|
||||
*
|
||||
* @param download the download to find.
|
||||
* @return the holder of the download or null if it's not bound.
|
||||
*/
|
||||
private fun getHolder(download: Download): DownloadHolder? {
|
||||
return recycler.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set information view when queue is empty
|
||||
*/
|
||||
private fun setInformationView() {
|
||||
updateEmptyView(presenter.downloadQueue.isEmpty(),
|
||||
R.string.information_no_downloads, R.drawable.ic_file_download_black_128dp)
|
||||
}
|
||||
|
||||
fun updateEmptyView(show: Boolean, textResource: Int, drawable: Int) {
|
||||
// if (show) empty_view.show(drawable, textResource) else empty_view.hide()
|
||||
}
|
||||
}
|
||||
package eu.kanade.tachiyomi.ui.download
|
||||
|
||||
import android.os.Bundle
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import android.view.*
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.download.DownloadService
|
||||
import eu.kanade.tachiyomi.data.download.model.Download
|
||||
import eu.kanade.tachiyomi.source.model.Page
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import kotlinx.android.synthetic.main.activity_download_manager.view.*
|
||||
import rx.Observable
|
||||
import rx.Subscription
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* Controller that shows the currently active downloads.
|
||||
* Uses R.layout.fragment_download_queue.
|
||||
*/
|
||||
class DownloadController : NucleusController<DownloadPresenter>() {
|
||||
|
||||
/**
|
||||
* Adapter containing the active downloads.
|
||||
*/
|
||||
private var adapter: DownloadAdapter? = null
|
||||
|
||||
/**
|
||||
* Map of subscriptions for active downloads.
|
||||
*/
|
||||
private val progressSubscriptions by lazy { HashMap<Download, Subscription>() }
|
||||
|
||||
/**
|
||||
* Whether the download queue is running or not.
|
||||
*/
|
||||
private var isRunning: Boolean = false
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
|
||||
return inflater.inflate(R.layout.activity_download_manager, container, false)
|
||||
}
|
||||
|
||||
override fun createPresenter(): DownloadPresenter {
|
||||
return DownloadPresenter()
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return resources?.getString(R.string.label_download_queue)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedViewState: Bundle?) {
|
||||
super.onViewCreated(view, savedViewState)
|
||||
|
||||
// Check if download queue is empty and update information accordingly.
|
||||
setInformationView()
|
||||
|
||||
// Initialize adapter.
|
||||
adapter = DownloadAdapter()
|
||||
with(view) {
|
||||
recycler.adapter = adapter
|
||||
|
||||
// Set the layout manager for the recycler and fixed size.
|
||||
recycler.layoutManager = LinearLayoutManager(context)
|
||||
recycler.setHasFixedSize(true)
|
||||
}
|
||||
|
||||
// Suscribe to changes
|
||||
DownloadService.runningRelay
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy { onQueueStatusChange(it) }
|
||||
|
||||
presenter.getDownloadStatusObservable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy { onStatusChange(it) }
|
||||
|
||||
presenter.getDownloadProgressObservable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribeUntilDestroy { onUpdateDownloadedPages(it) }
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
for (subscription in progressSubscriptions.values) {
|
||||
subscription.unsubscribe()
|
||||
}
|
||||
progressSubscriptions.clear()
|
||||
adapter = null
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.download_queue, menu)
|
||||
}
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu) {
|
||||
// Set start button visibility.
|
||||
menu.findItem(R.id.start_queue).isVisible = !isRunning && !presenter.downloadQueue.isEmpty()
|
||||
|
||||
// Set pause button visibility.
|
||||
menu.findItem(R.id.pause_queue).isVisible = isRunning
|
||||
|
||||
// Set clear button visibility.
|
||||
menu.findItem(R.id.clear_queue).isVisible = !presenter.downloadQueue.isEmpty()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val context = applicationContext ?: return false
|
||||
when (item.itemId) {
|
||||
R.id.start_queue -> DownloadService.start(context)
|
||||
R.id.pause_queue -> {
|
||||
DownloadService.stop(context)
|
||||
presenter.pauseDownloads()
|
||||
}
|
||||
R.id.clear_queue -> {
|
||||
DownloadService.stop(context)
|
||||
presenter.clearQueue()
|
||||
}
|
||||
else -> return super.onOptionsItemSelected(item)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
// Get the sum of percentages for all the pages.
|
||||
.flatMap {
|
||||
Observable.from(download.pages)
|
||||
.map(Page::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.
|
||||
*
|
||||
* @param running whether the queue is now running or not.
|
||||
*/
|
||||
private fun onQueueStatusChange(running: Boolean) {
|
||||
isRunning = running
|
||||
activity?.invalidateOptionsMenu()
|
||||
|
||||
// Check if download queue is empty and update information accordingly.
|
||||
setInformationView()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter to assign the downloads for the adapter.
|
||||
*
|
||||
* @param downloads the downloads from the queue.
|
||||
*/
|
||||
fun onNextDownloads(downloads: List<Download>) {
|
||||
activity?.invalidateOptionsMenu()
|
||||
setInformationView()
|
||||
adapter?.setItems(downloads)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the progress of a download changes.
|
||||
*
|
||||
* @param download the download whose progress has changed.
|
||||
*/
|
||||
fun onUpdateProgress(download: Download) {
|
||||
getHolder(download)?.notifyProgress()
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a page of a download is downloaded.
|
||||
*
|
||||
* @param download the download whose page has been downloaded.
|
||||
*/
|
||||
fun onUpdateDownloadedPages(download: Download) {
|
||||
getHolder(download)?.notifyDownloadedPages()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the holder for the given download.
|
||||
*
|
||||
* @param download the download to find.
|
||||
* @return the holder of the download or null if it's not bound.
|
||||
*/
|
||||
private fun getHolder(download: Download): DownloadHolder? {
|
||||
val recycler = view?.recycler ?: return null
|
||||
return recycler.findViewHolderForItemId(download.chapter.id!!) as? DownloadHolder
|
||||
}
|
||||
|
||||
/**
|
||||
* Set information view when queue is empty
|
||||
*/
|
||||
private fun setInformationView() {
|
||||
val emptyView = view?.empty_view ?: return
|
||||
if (presenter.downloadQueue.isEmpty()) {
|
||||
emptyView.show(R.drawable.ic_file_download_black_128dp,
|
||||
R.string.information_no_downloads)
|
||||
} else {
|
||||
emptyView.hide()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -12,9 +12,9 @@ import uy.kohesive.injekt.injectLazy
|
|||
import java.util.*
|
||||
|
||||
/**
|
||||
* Presenter of [DownloadActivity].
|
||||
* Presenter of [DownloadController].
|
||||
*/
|
||||
class DownloadPresenter : BasePresenter<DownloadActivity>() {
|
||||
class DownloadPresenter : BasePresenter<DownloadController>() {
|
||||
|
||||
/**
|
||||
* Download manager.
|
||||
|
@ -33,7 +33,7 @@ class DownloadPresenter : BasePresenter<DownloadActivity>() {
|
|||
downloadQueue.getUpdatedObservable()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.map { ArrayList(it) }
|
||||
.subscribeLatestCache(DownloadActivity::onNextDownloads, { view, error ->
|
||||
.subscribeLatestCache(DownloadController::onNextDownloads, { view, error ->
|
||||
Timber.e(error)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package eu.kanade.tachiyomi.ui.main
|
||||
|
||||
import android.animation.ObjectAnimator
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.support.v4.view.GravityCompat
|
||||
|
@ -19,7 +18,7 @@ import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
|
|||
import eu.kanade.tachiyomi.ui.base.controller.SecondaryDrawerController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.TabbedController
|
||||
import eu.kanade.tachiyomi.ui.catalogue.CatalogueController
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadActivity
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadController
|
||||
import eu.kanade.tachiyomi.ui.latest_updates.LatestUpdatesController
|
||||
import eu.kanade.tachiyomi.ui.library.LibraryController
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
|
@ -85,7 +84,9 @@ class MainActivity : BaseActivity() {
|
|||
R.id.nav_drawer_catalogues -> setRoot(CatalogueController(), id)
|
||||
R.id.nav_drawer_latest_updates -> setRoot(LatestUpdatesController(), id)
|
||||
R.id.nav_drawer_downloads -> {
|
||||
startActivity(Intent(this, DownloadActivity::class.java))
|
||||
router.pushController(RouterTransaction.with(DownloadController())
|
||||
.pushChangeHandler(FadeChangeHandler())
|
||||
.popChangeHandler(FadeChangeHandler()))
|
||||
}
|
||||
R.id.nav_drawer_settings ->
|
||||
router.pushController(RouterTransaction.with(SettingsMainController())
|
||||
|
@ -109,6 +110,7 @@ class MainActivity : BaseActivity() {
|
|||
SHORTCUT_CATALOGUES -> setSelectedDrawerItem(R.id.nav_drawer_catalogues)
|
||||
SHORTCUT_MANGA -> router.setRoot(
|
||||
RouterTransaction.with(MangaController(intent.extras)))
|
||||
SHORTCUT_DOWNLOADS -> setSelectedDrawerItem(R.id.nav_drawer_downloads)
|
||||
else -> setSelectedDrawerItem(startScreenId)
|
||||
}
|
||||
}
|
||||
|
@ -225,6 +227,7 @@ class MainActivity : BaseActivity() {
|
|||
private const val SHORTCUT_RECENTLY_UPDATED = "eu.kanade.tachiyomi.SHOW_RECENTLY_UPDATED"
|
||||
private const val SHORTCUT_RECENTLY_READ = "eu.kanade.tachiyomi.SHOW_RECENTLY_READ"
|
||||
private const val SHORTCUT_CATALOGUES = "eu.kanade.tachiyomi.SHOW_CATALOGUES"
|
||||
const val SHORTCUT_DOWNLOADS = "eu.kanade.tachiyomi.SHOW_DOWNLOADS"
|
||||
const val SHORTCUT_MANGA = "eu.kanade.tachiyomi.SHOW_MANGA"
|
||||
}
|
||||
|
||||
|
|
|
@ -1,42 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:fitsSystemWindows="true">
|
||||
android:id="@+id/frame_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/recycler"
|
||||
tools:listitem="@layout/item_download"/>
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<include layout="@layout/toolbar"/>
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/frame_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/recycler"
|
||||
tools:listitem="@layout/item_download"/>
|
||||
|
||||
<eu.kanade.tachiyomi.widget.EmptyView
|
||||
android:id="@+id/empty_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"/>
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</FrameLayout>
|
||||
|
|
Reference in a new issue