diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.java b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.java
deleted file mode 100644
index f10ba392a..000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.java
+++ /dev/null
@@ -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) {}
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt
new file mode 100644
index 000000000..0919e7ccf
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadAdapter.kt
@@ -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) {
+    }
+
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadFragment.java b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadFragment.java
deleted file mode 100644
index eb6bb4a10..000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadFragment.java
+++ /dev/null
@@ -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);
-    }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadFragment.kt
new file mode 100644
index 000000000..5e3a13db0
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadFragment.kt
@@ -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
+    }
+
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.java b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.java
deleted file mode 100644
index c7fa0b324..000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.java
+++ /dev/null
@@ -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);
-    }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt
new file mode 100644
index 000000000..75ab822ca
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadHolder.kt
@@ -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}"
+    }
+
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.java b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.java
deleted file mode 100644
index b6c19a950..000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.java
+++ /dev/null
@@ -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);
-    }
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt
new file mode 100644
index 000000000..e4cb1d29f
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/download/DownloadPresenter.kt
@@ -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)
+    }
+
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt
index 16a3ef22c..81eb726da 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryFragment.kt
@@ -79,11 +79,6 @@ class LibraryFragment : BaseRxFragment<LibraryPresenter>(), ActionMode.Callback
          */
         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].
          */
diff --git a/app/src/main/res/layout/fragment_download_queue.xml b/app/src/main/res/layout/fragment_download_queue.xml
index 915697712..5bec7ea12 100644
--- a/app/src/main/res/layout/fragment_download_queue.xml
+++ b/app/src/main/res/layout/fragment_download_queue.xml
@@ -6,7 +6,7 @@
     <android.support.v7.widget.RecyclerView
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:id="@+id/download_list">
+        android:id="@+id/recycler">
 
     </android.support.v7.widget.RecyclerView>