Download queue's UI in Kotlin
This commit is contained in:
parent
b95d0e2848
commit
f73f0cc341
@ -1,50 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.download;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
|
||||||
import eu.kanade.tachiyomi.R;
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
|
||||||
|
|
||||||
public class DownloadAdapter extends FlexibleAdapter<DownloadHolder, Download> {
|
|
||||||
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
public DownloadAdapter(Context context) {
|
|
||||||
this.context = context;
|
|
||||||
mItems = new ArrayList<>();
|
|
||||||
setHasStableIds(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public DownloadHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
|
||||||
View v = LayoutInflater.from(context).inflate(R.layout.item_download, parent, false);
|
|
||||||
return new DownloadHolder(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(DownloadHolder holder, int position) {
|
|
||||||
final Download download = getItem(position);
|
|
||||||
holder.onSetValues(download);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public long getItemId(int position) {
|
|
||||||
return getItem(position).chapter.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItems(List<Download> downloads) {
|
|
||||||
mItems = downloads;
|
|
||||||
notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateDataSet(String param) {}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,70 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.download
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||||
|
import eu.kanade.tachiyomi.R
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
import eu.kanade.tachiyomi.util.inflate
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter storing a list of downloads.
|
||||||
|
*
|
||||||
|
* @param context the context of the fragment containing this adapter.
|
||||||
|
*/
|
||||||
|
class DownloadAdapter(private val context: Context) : FlexibleAdapter<DownloadHolder, Download>() {
|
||||||
|
|
||||||
|
init {
|
||||||
|
setHasStableIds(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a list of downloads in the adapter.
|
||||||
|
*
|
||||||
|
* @param downloads the list to set.
|
||||||
|
*/
|
||||||
|
fun setItems(downloads: List<Download>) {
|
||||||
|
mItems = downloads
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the identifier for a download.
|
||||||
|
*
|
||||||
|
* @param position the position in the adapter.
|
||||||
|
* @return an identifier for the item.
|
||||||
|
*/
|
||||||
|
override fun getItemId(position: Int): Long {
|
||||||
|
return getItem(position).chapter.id
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new view holder.
|
||||||
|
*
|
||||||
|
* @param parent the parent view.
|
||||||
|
* @param viewType the type of the holder.
|
||||||
|
* @return a new view holder for a manga.
|
||||||
|
*/
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DownloadHolder {
|
||||||
|
val view = parent.inflate(R.layout.item_download)
|
||||||
|
return DownloadHolder(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a holder with a new position.
|
||||||
|
*
|
||||||
|
* @param holder the holder to bind.
|
||||||
|
* @param position the position to bind.
|
||||||
|
*/
|
||||||
|
override fun onBindViewHolder(holder: DownloadHolder, position: Int) {
|
||||||
|
val download = getItem(position)
|
||||||
|
holder.onSetValues(download)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to filter the list. Not used.
|
||||||
|
*/
|
||||||
|
override fun updateDataSet(param: String) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,147 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.download;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
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;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import butterknife.Bind;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import eu.kanade.tachiyomi.R;
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadService;
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
|
||||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
|
||||||
import nucleus.factory.RequiresPresenter;
|
|
||||||
import rx.Subscription;
|
|
||||||
|
|
||||||
@RequiresPresenter(DownloadPresenter.class)
|
|
||||||
public class DownloadFragment extends BaseRxFragment<DownloadPresenter> {
|
|
||||||
|
|
||||||
@Bind(R.id.download_list) RecyclerView recyclerView;
|
|
||||||
private DownloadAdapter adapter;
|
|
||||||
|
|
||||||
private MenuItem startButton;
|
|
||||||
private MenuItem pauseButton;
|
|
||||||
private MenuItem clearButton;
|
|
||||||
|
|
||||||
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) {
|
|
||||||
// Inflate the layout for this fragment
|
|
||||||
View view = inflater.inflate(R.layout.fragment_download_queue, container, false);
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
|
|
||||||
setToolbarTitle(R.string.label_download_queue);
|
|
||||||
|
|
||||||
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
|
||||||
recyclerView.setHasFixedSize(true);
|
|
||||||
createAdapter();
|
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
|
||||||
inflater.inflate(R.menu.download_queue, menu);
|
|
||||||
startButton = menu.findItem(R.id.start_queue);
|
|
||||||
pauseButton = menu.findItem(R.id.pause_queue);
|
|
||||||
clearButton = menu.findItem(R.id.clear_queue);
|
|
||||||
|
|
||||||
if(adapter.getItemCount() > 0) {
|
|
||||||
clearButton.setVisible(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Menu seems to be inflated after onResume in fragments, so we initialize them here
|
|
||||||
startButton.setVisible(!isRunning && !getPresenter().downloadManager.getQueue().isEmpty());
|
|
||||||
pauseButton.setVisible(isRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
|
||||||
switch (item.getItemId()) {
|
|
||||||
case R.id.start_queue:
|
|
||||||
DownloadService.start(getActivity());
|
|
||||||
break;
|
|
||||||
case R.id.pause_queue:
|
|
||||||
DownloadService.stop(getActivity());
|
|
||||||
break;
|
|
||||||
case R.id.clear_queue:
|
|
||||||
DownloadService.stop(getActivity());
|
|
||||||
getPresenter().clearQueue();
|
|
||||||
clearButton.setVisible(false);
|
|
||||||
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 (pauseButton != null)
|
|
||||||
pauseButton.setVisible(running);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createAdapter() {
|
|
||||||
adapter = new DownloadAdapter(getActivity());
|
|
||||||
recyclerView.setAdapter(adapter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onNextDownloads(List<Download> downloads) {
|
|
||||||
adapter.setItems(downloads);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateProgress(Download download) {
|
|
||||||
DownloadHolder holder = getHolder(download);
|
|
||||||
if (holder != null) {
|
|
||||||
holder.setDownloadProgress(download);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void updateDownloadedPages(Download download) {
|
|
||||||
DownloadHolder holder = getHolder(download);
|
|
||||||
if (holder != null) {
|
|
||||||
holder.setDownloadedPages(download);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private DownloadHolder getHolder(Download download) {
|
|
||||||
return (DownloadHolder) recyclerView.findViewHolderForItemId(download.chapter.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,179 @@
|
|||||||
|
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.ui.base.fragment.BaseRxFragment
|
||||||
|
import kotlinx.android.synthetic.main.fragment_download_queue.*
|
||||||
|
import nucleus.factory.RequiresPresenter
|
||||||
|
import rx.Subscription
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment that shows the currently active downloads.
|
||||||
|
* Uses R.layout.fragment_download_queue.
|
||||||
|
*/
|
||||||
|
@RequiresPresenter(DownloadPresenter::class)
|
||||||
|
class DownloadFragment : BaseRxFragment<DownloadPresenter>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter containing the active downloads.
|
||||||
|
*/
|
||||||
|
private lateinit var adapter: DownloadAdapter
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu item to start the queue.
|
||||||
|
*/
|
||||||
|
private var startButton: MenuItem? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu item to pause the queue.
|
||||||
|
*/
|
||||||
|
private var pauseButton: MenuItem? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Menu item to clear the queue.
|
||||||
|
*/
|
||||||
|
private var clearButton: MenuItem? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscription to know if the download queue is running.
|
||||||
|
*/
|
||||||
|
private var queueStatusSubscription: Subscription? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the download queue is running or not.
|
||||||
|
*/
|
||||||
|
private var isRunning: Boolean = false
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Creates a new instance of this fragment.
|
||||||
|
*
|
||||||
|
* @return a new instance of [DownloadFragment].
|
||||||
|
*/
|
||||||
|
@JvmStatic
|
||||||
|
fun newInstance(): DownloadFragment {
|
||||||
|
return DownloadFragment()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreate(bundle: Bundle?) {
|
||||||
|
super.onCreate(bundle)
|
||||||
|
setHasOptionsMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
|
||||||
|
savedState: Bundle?): View? {
|
||||||
|
return inflater.inflate(R.layout.fragment_download_queue, container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||||
|
setToolbarTitle(R.string.label_download_queue)
|
||||||
|
|
||||||
|
// Initialize adapter.
|
||||||
|
adapter = DownloadAdapter(activity)
|
||||||
|
recycler.adapter = adapter
|
||||||
|
|
||||||
|
// Set the layout manager for the recycler and fixed size.
|
||||||
|
recycler.layoutManager = LinearLayoutManager(activity)
|
||||||
|
recycler.setHasFixedSize(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||||
|
inflater.inflate(R.menu.download_queue, menu)
|
||||||
|
|
||||||
|
// Set start button visibility.
|
||||||
|
startButton = menu.findItem(R.id.start_queue).apply {
|
||||||
|
isVisible = !isRunning && !presenter.downloadQueue.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set pause button visibility.
|
||||||
|
pauseButton = menu.findItem(R.id.pause_queue).apply {
|
||||||
|
isVisible = isRunning
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set clear button visibility.
|
||||||
|
clearButton = menu.findItem(R.id.clear_queue).apply {
|
||||||
|
if (adapter.itemCount > 0) {
|
||||||
|
isVisible = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
|
when (item.itemId) {
|
||||||
|
R.id.start_queue -> DownloadService.start(activity)
|
||||||
|
R.id.pause_queue -> DownloadService.stop(activity)
|
||||||
|
R.id.clear_queue -> {
|
||||||
|
DownloadService.stop(activity)
|
||||||
|
presenter.clearQueue()
|
||||||
|
clearButton?.isVisible = false
|
||||||
|
}
|
||||||
|
else -> return super.onOptionsItemSelected(item)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
queueStatusSubscription = presenter.downloadManager.runningSubject
|
||||||
|
.subscribe { onQueueStatusChange(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
queueStatusSubscription?.unsubscribe()
|
||||||
|
super.onPause()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
startButton?.isVisible = !running && !presenter.downloadQueue.isEmpty()
|
||||||
|
pauseButton?.isVisible = running
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from the presenter to assign the downloads for the adapter.
|
||||||
|
*
|
||||||
|
* @param downloads the downloads from the queue.
|
||||||
|
*/
|
||||||
|
fun onNextDownloads(downloads: List<Download>) {
|
||||||
|
adapter.setItems(downloads)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from the presenter when the status of a download changes.
|
||||||
|
*
|
||||||
|
* @param download the download whose status has changed.
|
||||||
|
*/
|
||||||
|
fun onUpdateProgress(download: Download) {
|
||||||
|
getHolder(download)?.notifyProgress()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called from the presenter 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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,49 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.download;
|
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.ProgressBar;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import butterknife.Bind;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import eu.kanade.tachiyomi.R;
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
|
||||||
|
|
||||||
public class DownloadHolder extends RecyclerView.ViewHolder {
|
|
||||||
|
|
||||||
@Bind(R.id.download_title) TextView downloadTitle;
|
|
||||||
@Bind(R.id.download_progress) ProgressBar downloadProgress;
|
|
||||||
@Bind(R.id.download_progress_text) TextView downloadProgressText;
|
|
||||||
|
|
||||||
public DownloadHolder(View view) {
|
|
||||||
super(view);
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onSetValues(Download download) {
|
|
||||||
downloadTitle.setText(download.chapter.name);
|
|
||||||
|
|
||||||
if (download.pages == null) {
|
|
||||||
downloadProgress.setProgress(0);
|
|
||||||
downloadProgress.setMax(1);
|
|
||||||
downloadProgressText.setText("");
|
|
||||||
} else {
|
|
||||||
downloadProgress.setMax(download.pages.size() * 100);
|
|
||||||
setDownloadProgress(download);
|
|
||||||
setDownloadedPages(download);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDownloadedPages(Download download) {
|
|
||||||
String progressText = download.downloadedImages + "/" + download.pages.size();
|
|
||||||
downloadProgressText.setText(progressText);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDownloadProgress(Download download) {
|
|
||||||
if (downloadProgress.getMax() == 1)
|
|
||||||
downloadProgress.setMax(download.pages.size() * 100);
|
|
||||||
downloadProgress.setProgress(download.totalProgress);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -0,0 +1,60 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.download
|
||||||
|
|
||||||
|
import android.support.v7.widget.RecyclerView
|
||||||
|
import android.view.View
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
import kotlinx.android.synthetic.main.item_download.view.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used to hold the data of a download.
|
||||||
|
* All the elements from the layout file "item_download" are available in this class.
|
||||||
|
*
|
||||||
|
* @param view the inflated view for this holder.
|
||||||
|
* @constructor creates a new library holder.
|
||||||
|
*/
|
||||||
|
class DownloadHolder(private val view: View) : RecyclerView.ViewHolder(view) {
|
||||||
|
|
||||||
|
private lateinit var download: Download
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method called from [DownloadAdapter.onBindViewHolder]. It updates the data for this
|
||||||
|
* holder with the given download.
|
||||||
|
*
|
||||||
|
* @param download the download to bind.
|
||||||
|
*/
|
||||||
|
fun onSetValues(download: Download) {
|
||||||
|
this.download = download
|
||||||
|
|
||||||
|
// Update the chapter name.
|
||||||
|
view.download_title.text = download.chapter.name
|
||||||
|
|
||||||
|
// Update the progress bar and the number of downloaded pages
|
||||||
|
if (download.pages == null) {
|
||||||
|
view.download_progress.progress = 0
|
||||||
|
view.download_progress.max = 1
|
||||||
|
view.download_progress_text.text = ""
|
||||||
|
} else {
|
||||||
|
view.download_progress.max = download.pages.size * 100
|
||||||
|
notifyProgress()
|
||||||
|
notifyDownloadedPages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the progress bar of the download.
|
||||||
|
*/
|
||||||
|
fun notifyProgress() {
|
||||||
|
if (view.download_progress.max == 1) {
|
||||||
|
view.download_progress.max = download.pages.size * 100
|
||||||
|
}
|
||||||
|
view.download_progress.progress = download.totalProgress
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the text field of the number of downloaded pages.
|
||||||
|
*/
|
||||||
|
fun notifyDownloadedPages() {
|
||||||
|
view.download_progress_text.text = "${download.downloadedImages}/${download.pages.size}"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,128 +0,0 @@
|
|||||||
package eu.kanade.tachiyomi.ui.download;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager;
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
|
||||||
import eu.kanade.tachiyomi.data.download.model.DownloadQueue;
|
|
||||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.Subscription;
|
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
import timber.log.Timber;
|
|
||||||
|
|
||||||
public class DownloadPresenter extends BasePresenter<DownloadFragment> {
|
|
||||||
|
|
||||||
public final static int GET_DOWNLOAD_QUEUE = 1;
|
|
||||||
@Inject DownloadManager downloadManager;
|
|
||||||
private DownloadQueue downloadQueue;
|
|
||||||
private Subscription statusSubscription;
|
|
||||||
private Subscription pageProgressSubscription;
|
|
||||||
private HashMap<Download, Subscription> progressSubscriptions;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedState) {
|
|
||||||
super.onCreate(savedState);
|
|
||||||
|
|
||||||
downloadQueue = downloadManager.getQueue();
|
|
||||||
progressSubscriptions = new HashMap<>();
|
|
||||||
|
|
||||||
restartableLatestCache(GET_DOWNLOAD_QUEUE,
|
|
||||||
() -> Observable.just(downloadQueue),
|
|
||||||
DownloadFragment::onNextDownloads,
|
|
||||||
(view, error) -> Timber.e(error.getMessage()));
|
|
||||||
|
|
||||||
if (savedState == null)
|
|
||||||
start(GET_DOWNLOAD_QUEUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTakeView(DownloadFragment view) {
|
|
||||||
super.onTakeView(view);
|
|
||||||
|
|
||||||
add(statusSubscription = downloadQueue.getStatusObservable()
|
|
||||||
.startWith(downloadQueue.getActiveDownloads())
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(download -> {
|
|
||||||
processStatus(download, view);
|
|
||||||
}));
|
|
||||||
|
|
||||||
add(pageProgressSubscription = downloadQueue.getProgressObservable()
|
|
||||||
.onBackpressureBuffer()
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(view::updateDownloadedPages));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDropView() {
|
|
||||||
destroySubscriptions();
|
|
||||||
super.onDropView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void processStatus(Download download, DownloadFragment view) {
|
|
||||||
switch (download.getStatus()) {
|
|
||||||
case Download.DOWNLOADING:
|
|
||||||
observeProgress(download, view);
|
|
||||||
// Initial update of the downloaded pages
|
|
||||||
view.updateDownloadedPages(download);
|
|
||||||
break;
|
|
||||||
case Download.DOWNLOADED:
|
|
||||||
unsubscribeProgress(download);
|
|
||||||
view.updateProgress(download);
|
|
||||||
view.updateDownloadedPages(download);
|
|
||||||
break;
|
|
||||||
case Download.ERROR:
|
|
||||||
unsubscribeProgress(download);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void observeProgress(Download download, DownloadFragment view) {
|
|
||||||
Subscription subscription = Observable.interval(50, TimeUnit.MILLISECONDS, Schedulers.newThread())
|
|
||||||
.flatMap(tick -> Observable.from(download.pages)
|
|
||||||
.map(Page::getProgress)
|
|
||||||
.reduce((x, y) -> x + y))
|
|
||||||
.onBackpressureLatest()
|
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.subscribe(progress -> {
|
|
||||||
if (download.totalProgress != progress) {
|
|
||||||
download.totalProgress = progress;
|
|
||||||
view.updateProgress(download);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Avoid leaking subscriptions
|
|
||||||
Subscription oldSubscription = progressSubscriptions.remove(download);
|
|
||||||
if (oldSubscription != null) oldSubscription.unsubscribe();
|
|
||||||
|
|
||||||
progressSubscriptions.put(download, subscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unsubscribeProgress(Download download) {
|
|
||||||
Subscription subscription = progressSubscriptions.remove(download);
|
|
||||||
if (subscription != null)
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void destroySubscriptions() {
|
|
||||||
for (Subscription subscription : progressSubscriptions.values()) {
|
|
||||||
subscription.unsubscribe();
|
|
||||||
}
|
|
||||||
progressSubscriptions.clear();
|
|
||||||
|
|
||||||
remove(pageProgressSubscription);
|
|
||||||
remove(statusSubscription);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearQueue() {
|
|
||||||
downloadQueue.clear();
|
|
||||||
start(GET_DOWNLOAD_QUEUE);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,174 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.download
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
import eu.kanade.tachiyomi.data.download.model.DownloadQueue
|
||||||
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||||
|
import rx.Observable
|
||||||
|
import rx.Subscription
|
||||||
|
import rx.android.schedulers.AndroidSchedulers
|
||||||
|
import rx.schedulers.Schedulers
|
||||||
|
import timber.log.Timber
|
||||||
|
import java.util.*
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Presenter of [DownloadFragment].
|
||||||
|
*/
|
||||||
|
class DownloadPresenter : BasePresenter<DownloadFragment>() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Download manager.
|
||||||
|
*/
|
||||||
|
@Inject lateinit var downloadManager: DownloadManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Property to get the queue from the download manager.
|
||||||
|
*/
|
||||||
|
val downloadQueue: DownloadQueue
|
||||||
|
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?) {
|
||||||
|
super.onCreate(savedState)
|
||||||
|
|
||||||
|
restartableLatestCache(GET_DOWNLOAD_QUEUE,
|
||||||
|
{ Observable.just(downloadQueue) },
|
||||||
|
{ view, downloads -> view.onNextDownloads(downloads) },
|
||||||
|
{ view, error -> Timber.e(error.message) })
|
||||||
|
|
||||||
|
if (savedState == null) {
|
||||||
|
start(GET_DOWNLOAD_QUEUE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTakeView(view: DownloadFragment) {
|
||||||
|
super.onTakeView(view)
|
||||||
|
|
||||||
|
statusSubscription = downloadQueue.statusObservable
|
||||||
|
.startWith(downloadQueue.activeDownloads)
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe { processStatus(it, view) }
|
||||||
|
|
||||||
|
add(statusSubscription)
|
||||||
|
|
||||||
|
pageProgressSubscription = downloadQueue.progressObservable
|
||||||
|
.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)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears the download queue.
|
||||||
|
*/
|
||||||
|
fun clearQueue() {
|
||||||
|
downloadQueue.clear()
|
||||||
|
start(GET_DOWNLOAD_QUEUE)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -79,11 +79,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
|
|||||||
*/
|
*/
|
||||||
const val REQUEST_IMAGE_OPEN = 101
|
const val REQUEST_IMAGE_OPEN = 101
|
||||||
|
|
||||||
/**
|
|
||||||
* Key to add a manga to an [Intent].
|
|
||||||
*/
|
|
||||||
const val MANGA_EXTRA = "manga_extra"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key to save and restore [query] from a [Bundle].
|
* Key to save and restore [query] from a [Bundle].
|
||||||
*/
|
*/
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
<android.support.v7.widget.RecyclerView
|
<android.support.v7.widget.RecyclerView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/download_list">
|
android:id="@+id/recycler">
|
||||||
|
|
||||||
</android.support.v7.widget.RecyclerView>
|
</android.support.v7.widget.RecyclerView>
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user