Browse Source

Rewrote Recent to Kotlin

NoodleMage 9 years ago
parent
commit
a7e652f1f7

+ 0 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/main/MainActivity.java

@@ -81,7 +81,6 @@ public class MainActivity extends BaseActivity {
                         new PrimaryDrawerItem()
                                 .withName(R.string.label_catalogues)
                                 .withIdentifier(R.id.nav_drawer_catalogues)
-
                                 .withIcon(GoogleMaterial.Icon.gmd_explore),
                         new PrimaryDrawerItem()
                                 .withName(R.string.label_download_queue)

+ 0 - 140
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersAdapter.java

@@ -1,140 +0,0 @@
-package eu.kanade.tachiyomi.ui.recent;
-
-import android.support.v7.widget.RecyclerView;
-import android.text.format.DateUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.TextView;
-
-import java.util.Date;
-import java.util.List;
-
-import butterknife.Bind;
-import butterknife.ButterKnife;
-import eu.davidea.flexibleadapter.FlexibleAdapter;
-import eu.kanade.tachiyomi.R;
-import eu.kanade.tachiyomi.data.database.models.MangaChapter;
-
-/**
- * Adapter of RecentChaptersHolder.
- * Connection between Fragment and Holder
- * Holder updates should be called from here.
- */
-public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHolder, Object> {
-
-    /**
-     * Fragment of RecentChaptersFragment
-     */
-    private final RecentChaptersFragment fragment;
-
-    /**
-     * The id of the view type
-     */
-    private static final int VIEW_TYPE_CHAPTER = 0;
-
-    /**
-     * The id of the view type
-     */
-    private static final int VIEW_TYPE_SECTION = 1;
-
-    /**
-     * Constructor
-     *
-     * @param fragment fragment
-     */
-    public RecentChaptersAdapter(RecentChaptersFragment fragment) {
-        this.fragment = fragment;
-        setHasStableIds(true);
-    }
-
-    @Override
-    public long getItemId(int position) {
-        Object item = getItem(position);
-        if (item instanceof MangaChapter)
-            return ((MangaChapter) item).chapter.id;
-        else
-            return item.hashCode();
-    }
-
-    /**
-     * Update items
-     *
-     * @param items items
-     */
-    public void setItems(List<Object> items) {
-        mItems = items;
-        notifyDataSetChanged();
-    }
-
-    @Override
-    public void updateDataSet(String param) {
-
-    }
-
-    @Override
-    public int getItemViewType(int position) {
-        return getItem(position) instanceof MangaChapter ? VIEW_TYPE_CHAPTER : VIEW_TYPE_SECTION;
-    }
-
-    @Override
-    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
-        LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-        View v;
-
-        // Check which view type and set correct values.
-        switch (viewType) {
-            case VIEW_TYPE_CHAPTER:
-                v = inflater.inflate(R.layout.item_recent_chapter, parent, false);
-                return new RecentChaptersHolder(v, this, fragment);
-            case VIEW_TYPE_SECTION:
-                v = inflater.inflate(R.layout.item_recent_chapter_section, parent, false);
-                return new SectionViewHolder(v);
-        }
-        return null;
-    }
-
-    @Override
-    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
-        // Check which view type and set correct values.
-        switch (holder.getItemViewType()) {
-            case VIEW_TYPE_CHAPTER:
-                final MangaChapter chapter = (MangaChapter) getItem(position);
-                ((RecentChaptersHolder) holder).onSetValues(chapter);
-                break;
-            case VIEW_TYPE_SECTION:
-                final Date date = (Date) getItem(position);
-                ((SectionViewHolder) holder).onSetValues(date);
-                break;
-        }
-
-        //When user scrolls this bind the correct selection status
-        holder.itemView.setActivated(isSelected(position));
-    }
-
-    /**
-     * Returns fragment
-     * @return RecentChaptersFragment
-     */
-    public RecentChaptersFragment getFragment() {
-        return fragment;
-    }
-
-    public static class SectionViewHolder extends RecyclerView.ViewHolder {
-
-        @Bind(R.id.section_text) TextView section;
-
-        private final long now = new Date().getTime();
-
-        public SectionViewHolder(View view) {
-            super(view);
-            ButterKnife.bind(this, view);
-        }
-
-        public void onSetValues(Date date) {
-            CharSequence s = DateUtils.getRelativeTimeSpanString(
-                    date.getTime(), now, DateUtils.DAY_IN_MILLIS);
-            section.setText(s);
-        }
-    }
-}

+ 128 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersAdapter.kt

@@ -0,0 +1,128 @@
+package eu.kanade.tachiyomi.ui.recent
+
+import android.support.v7.widget.RecyclerView
+import android.view.View
+import android.view.ViewGroup
+import eu.davidea.flexibleadapter.FlexibleAdapter
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.MangaChapter
+import eu.kanade.tachiyomi.util.inflate
+import java.util.*
+
+/**
+ * Adapter of RecentChaptersHolder.
+ * Connection between Fragment and Holder
+ * Holder updates should be called from here.
+ *
+ * @param fragment a RecentChaptersFragment object
+ * @constructor creates an instance of the adapter.
+ */
+
+class RecentChaptersAdapter(val fragment: RecentChaptersFragment) : FlexibleAdapter<RecyclerView.ViewHolder, Any>() {
+    /**
+     * The id of the view type
+     */
+    private val VIEW_TYPE_CHAPTER = 0
+
+    /**
+     * The id of the view type
+     */
+    private val VIEW_TYPE_SECTION = 1
+
+    init {
+        // Let each each item in the data set be represented with a unique identifier.
+        setHasStableIds(true)
+    }
+
+    /**
+     * Called when ViewHolder is bind
+     *
+     * @param holder bind holder
+     * @param position position of holder
+     */
+    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+        // Check which view type and set correct values.
+        val item = getItem(position)
+        when (holder.itemViewType) {
+            VIEW_TYPE_CHAPTER -> {
+                if (item is MangaChapter) {
+                    (holder as RecentChaptersHolder).onSetValues(item)
+                }
+            }
+            VIEW_TYPE_SECTION -> {
+                if (item is Date) {
+                    (holder as SectionViewHolder).onSetValues(item)
+                }
+            }
+        }
+
+        //When user scrolls this bind the correct selection status
+        holder.itemView.isActivated = isSelected(position)
+    }
+
+    /**
+     * Called when ViewHolder is created
+     *
+     * @param parent parent View
+     * @param viewType int containing viewType
+     */
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder? {
+        val view: View
+
+        // Check which view type and set correct values.
+        when (viewType) {
+            VIEW_TYPE_CHAPTER -> {
+                view = parent.inflate(R.layout.item_recent_chapter)
+                return RecentChaptersHolder(view, this, fragment)
+            }
+            VIEW_TYPE_SECTION -> {
+                view = parent.inflate(R.layout.item_recent_chapter_section)
+                return SectionViewHolder(view)
+            }
+        }
+        return null
+    }
+
+    /**
+     * Returns the correct ViewType
+     *
+     * @param position position of item
+     */
+    override fun getItemViewType(position: Int): Int {
+        return if (getItem(position) is MangaChapter) VIEW_TYPE_CHAPTER else VIEW_TYPE_SECTION
+    }
+
+
+    /**
+     * Update items
+
+     * @param items items
+     */
+    fun setItems(items: List<Any>) {
+        mItems = items
+        notifyDataSetChanged()
+    }
+
+    /**
+     * Needed to determine holder id
+     *
+     * @param position position of holder item
+     */
+    override fun getItemId(position: Int): Long {
+        val item = getItem(position)
+        if (item is MangaChapter)
+            return item.chapter.id
+        else
+            return item.hashCode().toLong()
+    }
+
+    /**
+     * Abstract function (not needed).
+     *
+     * @param p0 a string.
+     */
+    override fun updateDataSet(p0: String) {
+        // Empty function.
+    }
+
+}

+ 0 - 200
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersFragment.java

@@ -1,200 +0,0 @@
-package eu.kanade.tachiyomi.ui.recent;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.afollestad.materialdialogs.MaterialDialog;
-
-import java.util.List;
-
-import butterknife.Bind;
-import butterknife.ButterKnife;
-import eu.kanade.tachiyomi.R;
-import eu.kanade.tachiyomi.data.database.models.Chapter;
-import eu.kanade.tachiyomi.data.database.models.Manga;
-import eu.kanade.tachiyomi.data.database.models.MangaChapter;
-import eu.kanade.tachiyomi.data.download.DownloadService;
-import eu.kanade.tachiyomi.data.download.model.Download;
-import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
-import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
-import eu.kanade.tachiyomi.ui.decoration.DividerItemDecoration;
-import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
-import nucleus.factory.RequiresPresenter;
-import rx.Observable;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
-
-/**
- * Fragment that shows recent chapters.
- * Uses R.layout.fragment_recent_chapters.
- * UI related actions should be called from here.
- */
-@RequiresPresenter(RecentChaptersPresenter.class)
-public class RecentChaptersFragment extends BaseRxFragment<RecentChaptersPresenter> implements FlexibleViewHolder.OnListItemClickListener {
-
-    @Bind(R.id.chapter_list) RecyclerView recyclerView;
-
-    private RecentChaptersAdapter adapter;
-
-    public static RecentChaptersFragment newInstance() {
-        return new RecentChaptersFragment();
-    }
-
-    @Override
-    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
-        // Inflate the layout for this fragment
-        View view = inflater.inflate(R.layout.fragment_recent_chapters, container, false);
-        ButterKnife.bind(this, view);
-
-        // Init RecyclerView and adapter
-        recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
-        recyclerView.addItemDecoration(new DividerItemDecoration(ContextCompat.getDrawable(
-                getContext(), R.drawable.line_divider)));
-        recyclerView.setHasFixedSize(true);
-        adapter = new RecentChaptersAdapter(this);
-        recyclerView.setAdapter(adapter);
-
-        setToolbarTitle(R.string.label_recent_updates);
-        return view;
-    }
-
-    /**
-     * Populate adapter with chapters
-     *
-     * @param chapters list of chapters
-     */
-    public void onNextMangaChapters(List<Object> chapters) {
-        adapter.setItems(chapters);
-    }
-
-    @Override
-    public boolean onListItemClick(int position) {
-        // Get item from position
-        Object item = adapter.getItem(position);
-        if (item instanceof MangaChapter) {
-            // Open chapter in reader
-            openChapter((MangaChapter) item);
-        }
-        return false;
-    }
-
-    @Override
-    public void onListItemLongClick(int position) {
-        // Empty function
-    }
-
-    /**
-     * Open chapter in reader
-     *
-     * @param chapter selected chapter
-     */
-    private void openChapter(MangaChapter chapter) {
-        getPresenter().onOpenChapter(chapter);
-        Intent intent = ReaderActivity.newIntent(getActivity());
-        startActivity(intent);
-    }
-
-    /**
-     * Update download status of chapter
-     *
-     * @param download download object containing download progress.
-     */
-    public void onChapterStatusChange(Download download) {
-        RecentChaptersHolder holder = getHolder(download.chapter);
-        if (holder != null)
-            holder.onStatusChange(download.getStatus());
-    }
-
-    @Nullable
-    private RecentChaptersHolder getHolder(Chapter chapter) {
-        return (RecentChaptersHolder) recyclerView.findViewHolderForItemId(chapter.id);
-    }
-
-    /**
-     * Start downloading chapter
-     *
-     * @param chapters selected chapters
-     * @param manga    manga that belongs to chapter
-     * @return true
-     */
-    @SuppressWarnings("SameReturnValue")
-    protected boolean onDownload(Observable<Chapter> chapters, Manga manga) {
-        // Start the download service.
-        DownloadService.start(getActivity());
-
-        // Refresh data on download competition.
-        Observable<Chapter> observable = chapters
-                .doOnCompleted(adapter::notifyDataSetChanged);
-
-        // Download chapter.
-        getPresenter().downloadChapter(observable, manga);
-        return true;
-    }
-
-    /**
-     * Start deleting chapter
-     * @param chapters selected chapters
-     * @param manga manga that belongs to chapter
-     * @return success of deletion.
-     */
-    protected boolean onDelete(Observable<Chapter> chapters, Manga manga) {
-        int size = adapter.getSelectedItemCount();
-
-        MaterialDialog dialog = new MaterialDialog.Builder(getActivity())
-                .title(R.string.deleting)
-                .progress(false, size, true)
-                .cancelable(false)
-                .show();
-
-        Observable<Chapter> observable = chapters
-                .concatMap(chapter -> {
-                    getPresenter().deleteChapter(chapter, manga);
-                    return Observable.just(chapter);
-                })
-                .subscribeOn(Schedulers.io())
-                .observeOn(AndroidSchedulers.mainThread())
-                .doOnNext(chapter -> {
-                    dialog.incrementProgress(1);
-                    chapter.status = Download.NOT_DOWNLOADED;
-                })
-                .doOnCompleted(adapter::notifyDataSetChanged)
-                .finallyDo(dialog::dismiss);
-
-        getPresenter().deleteChapters(observable);
-
-        return true;
-    }
-
-    /**
-     * Mark chapter as read
-     *
-     * @param chapters selected chapter
-     * @return true
-     */
-    @SuppressWarnings("SameReturnValue")
-    protected boolean onMarkAsRead(Observable<Chapter> chapters) {
-        getPresenter().markChaptersRead(chapters, true);
-        return true;
-    }
-
-    /**
-     * Mark chapter as unread
-     *
-     * @param chapters selected chapter
-     * @return true
-     */
-    @SuppressWarnings("SameReturnValue")
-    protected boolean onMarkAsUnread(Observable<Chapter> chapters) {
-        getPresenter().markChaptersRead(chapters, false);
-        return true;
-    }
-
-
-}

+ 221 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersFragment.kt

@@ -0,0 +1,221 @@
+package eu.kanade.tachiyomi.ui.recent
+
+import android.os.Bundle
+import android.support.v4.content.ContextCompat
+import android.support.v7.widget.LinearLayoutManager
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.Chapter
+import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.database.models.MangaChapter
+import eu.kanade.tachiyomi.data.download.DownloadService
+import eu.kanade.tachiyomi.data.download.model.Download
+import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
+import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
+import eu.kanade.tachiyomi.ui.decoration.DividerItemDecoration
+import eu.kanade.tachiyomi.ui.reader.ReaderActivity
+import kotlinx.android.synthetic.main.fragment_recent_chapters.*
+import nucleus.factory.RequiresPresenter
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+
+/**
+ * Fragment that shows recent chapters.
+ * Uses R.layout.fragment_recent_chapters.
+ * UI related actions should be called from here.
+ */
+@RequiresPresenter(RecentChaptersPresenter::class)
+class RecentChaptersFragment : BaseRxFragment<RecentChaptersPresenter>(), FlexibleViewHolder.OnListItemClickListener {
+    companion object {
+        /**
+         * Create new RecentChaptersFragment.
+         *
+         */
+        @JvmStatic
+        fun newInstance(): RecentChaptersFragment {
+            return RecentChaptersFragment()
+        }
+    }
+
+    /**
+     * Adapter containing the recent chapters.
+     */
+    lateinit var adapter: RecentChaptersAdapter
+        private set
+
+    /**
+     * Called when view gets created
+     *
+     * @param inflater layout inflater
+     * @param container view group
+     * @param savedState status of saved state
+     */
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View? {
+        // Inflate view
+        return inflater.inflate(R.layout.fragment_recent_chapters, container, false)
+    }
+
+    /**
+     * Called when view is created
+     *
+     * @param view created view
+     * @param savedInstanceState status of saved sate
+     */
+    override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+        // Init RecyclerView and adapter
+        recycler.layoutManager = LinearLayoutManager(activity)
+        recycler.addItemDecoration(DividerItemDecoration(ContextCompat.getDrawable(
+                context, R.drawable.line_divider)))
+        recycler.setHasFixedSize(true)
+        adapter = RecentChaptersAdapter(this)
+        recycler.adapter = adapter
+
+        // Update toolbar text
+        setToolbarTitle(R.string.label_recent_updates)
+    }
+
+    /**
+     * Called when item in list is clicked
+     *
+     * @param position position of clicked item
+     */
+    override fun onListItemClick(position: Int): Boolean {
+        // Get item from position
+        val item = adapter.getItem(position)
+        if (item is MangaChapter) {
+            // Open chapter in reader
+            openChapter(item)
+        }
+        return false
+    }
+
+    /**
+     * Called when item in list is long clicked
+     *
+     * @param position position of clicked item
+     */
+    override fun onListItemLongClick(position: Int) {
+        // Empty function
+    }
+
+    /**
+     * Open chapter in reader
+
+     * @param chapter selected chapter
+     */
+    private fun openChapter(chapter: MangaChapter) {
+        // Start reader event
+        presenter.onOpenChapter(chapter)
+
+        //Start reader intent
+        val intent = ReaderActivity.newIntent(activity)
+        startActivity(intent)
+    }
+
+    /**
+     * Populate adapter with chapters
+
+     * @param chapters list of chapters
+     */
+    fun onNextMangaChapters(chapters: List<Any>) {
+        adapter.setItems(chapters)
+    }
+
+    /**
+     * Update download status of chapter
+
+     * @param download download object containing download progress.
+     */
+    fun onChapterStatusChange(download: Download) {
+        getHolder(download)?.onStatusChange(download.status)
+
+    }
+
+    /**
+     * Returns holder belonging to chapter
+     *
+     * @param download download object containing download progress.
+     */
+    private fun getHolder(download: Download): RecentChaptersHolder? {
+        return recycler.findViewHolderForItemId(download.chapter.id) as? RecentChaptersHolder
+    }
+
+    /**
+     * Start downloading chapter
+
+     * @param chapters selected chapters
+     * @param manga    manga that belongs to chapter
+     * @return true
+     */
+    fun onDownload(chapters: Observable<Chapter>, manga: Manga): Boolean {
+        // Start the download service.
+        DownloadService.start(activity)
+
+        // Refresh data on download competition.
+        val observable = chapters
+                .doOnCompleted({
+                    adapter.notifyDataSetChanged()
+                    presenter.start(presenter.CHAPTER_STATUS_CHANGES)
+                })
+
+        // Download chapter.
+        presenter.downloadChapter(observable, manga)
+        return true
+    }
+
+    /**
+     * Start deleting chapter
+     *
+     * @param chapters selected chapters
+     * @param manga manga that belongs to chapter
+     * @return success of deletion.
+     */
+    fun onDelete(chapters: Observable<Chapter>, manga: Manga): Boolean {
+        //Create observable
+        val observable = chapters
+                .concatMap { chapter ->
+                    presenter.deleteChapter(chapter, manga)
+                    Observable.just(chapter)
+                }
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .doOnNext { chapter ->
+                    chapter.status = Download.NOT_DOWNLOADED
+                }
+                .doOnCompleted { adapter.notifyDataSetChanged() }
+
+        // Delete chapters with observable
+        presenter.deleteChapters(observable)
+
+        return true
+    }
+
+    /**
+     * Mark chapter as read
+
+     * @param chapters selected chapter
+     * @return true
+     */
+    fun onMarkAsRead(chapters: Observable<Chapter>): Boolean {
+        // Set marked as read
+        presenter.markChaptersRead(chapters, true)
+        return true
+    }
+
+    /**
+     * Mark chapter as unread
+
+     * @param chapters selected chapter
+     * @return true
+     */
+    fun onMarkAsUnread(chapters: Observable<Chapter>): Boolean {
+        // Set marked as unread
+        presenter.markChaptersRead(chapters, false)
+        return true
+    }
+
+
+}

+ 0 - 187
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersHolder.java

@@ -1,187 +0,0 @@
-package eu.kanade.tachiyomi.ui.recent;
-
-import android.support.v4.content.ContextCompat;
-import android.view.Menu;
-import android.view.View;
-import android.widget.PopupMenu;
-import android.widget.RelativeLayout;
-import android.widget.TextView;
-
-import butterknife.Bind;
-import butterknife.ButterKnife;
-import eu.kanade.tachiyomi.R;
-import eu.kanade.tachiyomi.data.database.models.Chapter;
-import eu.kanade.tachiyomi.data.database.models.MangaChapter;
-import eu.kanade.tachiyomi.data.download.model.Download;
-import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
-import rx.Observable;
-
-/**
- * Holder that contains chapter item
- * Uses R.layout.item_recent_chapter.
- * UI related actions should be called from here.
- */
-public class RecentChaptersHolder extends FlexibleViewHolder {
-
-    /**
-     * Adapter for recent chapters
-     */
-    private final RecentChaptersAdapter adapter;
-
-    /**
-     * TextView containing chapter title
-     */
-    @Bind(R.id.chapter_title) TextView chapterTitle;
-
-    /**
-     * TextView containing manga name
-     */
-    @Bind(R.id.manga_title) TextView mangaTitle;
-
-    /**
-     * TextView containing download status
-     */
-    @Bind(R.id.download_text) TextView downloadText;
-
-    /**
-     * RelativeLayout containing popup menu with download options
-     */
-    @Bind(R.id.chapter_menu) RelativeLayout chapterMenu;
-
-    /**
-     * Color of read chapter
-     */
-    private final int readColor;
-
-    /**
-     * Color of unread chapter
-     */
-    private final int unreadColor;
-
-    /**
-     * Object containing chapter information
-     */
-    private MangaChapter mangaChapter;
-
-    /**
-     * Constructor of RecentChaptersHolder
-     * @param view view of ChapterHolder
-     * @param adapter adapter of ChapterHolder
-     * @param onListItemClickListener ClickListener
-     */
-    public RecentChaptersHolder(View view, RecentChaptersAdapter adapter, OnListItemClickListener onListItemClickListener) {
-        super(view, adapter, onListItemClickListener);
-        this.adapter = adapter;
-        ButterKnife.bind(this, view);
-
-        // Set colors.
-        readColor = ContextCompat.getColor(view.getContext(), R.color.hint_text);
-        unreadColor = ContextCompat.getColor(view.getContext(), R.color.primary_text);
-
-        //Set OnClickListener for download menu
-        chapterMenu.setOnClickListener(v -> v.post(() -> showPopupMenu(v)));
-    }
-
-    /**
-     * Set values of view
-     *
-     * @param item item containing chapter information
-     */
-    public void onSetValues(MangaChapter item) {
-        this.mangaChapter = item;
-
-        // Set chapter title
-        chapterTitle.setText(item.chapter.name);
-
-        // Set manga title
-        mangaTitle.setText(item.manga.title);
-
-        // Check if chapter is read and set correct color
-        if (item.chapter.read) {
-            chapterTitle.setTextColor(readColor);
-            mangaTitle.setTextColor(readColor);
-        } else {
-            chapterTitle.setTextColor(unreadColor);
-            mangaTitle.setTextColor(unreadColor);
-        }
-
-        // Set chapter status
-        onStatusChange(item.chapter.status);
-    }
-
-    /**
-     * Updates chapter status in view.
-     *
-     * @param status download status
-     */
-    public void onStatusChange(int status) {
-        switch (status) {
-            case Download.QUEUE:
-                downloadText.setText(R.string.chapter_queued);
-                break;
-            case Download.DOWNLOADING:
-                downloadText.setText(R.string.chapter_downloading);
-                break;
-            case Download.DOWNLOADED:
-                downloadText.setText(R.string.chapter_downloaded);
-                break;
-            case Download.ERROR:
-                downloadText.setText(R.string.chapter_error);
-                break;
-            default:
-                downloadText.setText("");
-                break;
-        }
-    }
-
-    /**
-     * Show pop up menu
-     * @param view view containing popup menu.
-     */
-    private void showPopupMenu(View view) {
-        // Create a PopupMenu, giving it the clicked view for an anchor
-        PopupMenu popup = new PopupMenu(adapter.getFragment().getActivity(), view);
-
-        // Inflate our menu resource into the PopupMenu's Menu
-        popup.getMenuInflater().inflate(R.menu.chapter_recent, popup.getMenu());
-
-        // Hide download and show delete if the chapter is downloaded and
-        if (mangaChapter.chapter.isDownloaded()) {
-            Menu menu = popup.getMenu();
-            menu.findItem(R.id.action_download).setVisible(false);
-            menu.findItem(R.id.action_delete).setVisible(true);
-        }
-
-        // Hide mark as unread when the chapter is unread
-        if (!mangaChapter.chapter.read /*&& mangaChapter.chapter.last_page_read == 0*/) {
-            popup.getMenu().findItem(R.id.action_mark_as_unread).setVisible(false);
-        }
-
-        // Hide mark as read when the chapter is read
-        if (mangaChapter.chapter.read) {
-            popup.getMenu().findItem(R.id.action_mark_as_read).setVisible(false);
-        }
-
-        // Set a listener so we are notified if a menu item is clicked
-        popup.setOnMenuItemClickListener(menuItem -> {
-            Observable<Chapter> chapterObservable = Observable.just(mangaChapter.chapter);
-
-            switch (menuItem.getItemId()) {
-                case R.id.action_download:
-                    return adapter.getFragment().onDownload(chapterObservable, mangaChapter.manga);
-                case R.id.action_delete:
-                    return adapter.getFragment().onDelete(chapterObservable, mangaChapter.manga);
-                case R.id.action_mark_as_read:
-                    return adapter.getFragment().onMarkAsRead(chapterObservable);
-                case R.id.action_mark_as_unread:
-                    return adapter.getFragment().onMarkAsUnread(chapterObservable);
-            }
-            return false;
-        });
-
-        // Finally show the PopupMenu
-        popup.show();
-    }
-
-
-}

+ 140 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersHolder.kt

@@ -0,0 +1,140 @@
+package eu.kanade.tachiyomi.ui.recent
+
+import android.support.v4.content.ContextCompat
+import android.view.View
+import android.widget.PopupMenu
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.database.models.Chapter
+import eu.kanade.tachiyomi.data.database.models.MangaChapter
+import eu.kanade.tachiyomi.data.download.model.Download
+import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder
+import kotlinx.android.synthetic.main.item_recent_chapter.view.*
+import rx.Observable
+
+/**
+ * Holder that contains chapter item
+ * Uses R.layout.item_recent_chapter.
+ * UI related actions should be called from here.
+ *
+ * @param view the inflated view for this holder.
+ * @param adapter the adapter handling this holder.
+ * @param listener a listener to react to single tap and long tap events.
+ * @constructor creates a new library holder.
+ */
+class RecentChaptersHolder(view: View, private val adapter: RecentChaptersAdapter, listener: FlexibleViewHolder.OnListItemClickListener) : FlexibleViewHolder(view, adapter, listener) {
+    /**
+     * Color of read chapter
+     */
+    private val readColor: Int
+
+    /**
+     * Color of unread chapter
+     */
+    private val unreadColor: Int
+
+    /**
+     * Object containing chapter information
+     */
+    private var mangaChapter: MangaChapter? = null
+
+    init {
+        // Set colors.
+        readColor = ContextCompat.getColor(view.context, R.color.hint_text)
+        unreadColor = ContextCompat.getColor(view.context, R.color.primary_text)
+
+        //Set OnClickListener for download menu
+        itemView.chapterMenu.setOnClickListener { v -> v.post({ showPopupMenu(v) }) }
+    }
+
+    /**
+     * Set values of view
+     *
+     * @param item item containing chapter information
+     */
+    fun onSetValues(item: MangaChapter) {
+        this.mangaChapter = item
+
+        // Set chapter title
+        itemView.chapter_title.text = item.chapter.name
+
+        // Set manga title
+        itemView.manga_title.text = item.manga.title
+
+        // Check if chapter is read and set correct color
+        if (item.chapter.read) {
+            itemView.chapter_title.setTextColor(readColor)
+            itemView.manga_title.setTextColor(readColor)
+        } else {
+            itemView.chapter_title.setTextColor(unreadColor)
+            itemView.manga_title.setTextColor(unreadColor)
+        }
+
+        // Set chapter status
+        onStatusChange(item.chapter.status)
+    }
+
+    /**
+     * Updates chapter status in view.
+
+     * @param status download status
+     */
+    fun onStatusChange(status: Int) {
+        when (status) {
+            Download.QUEUE -> itemView.download_text.setText(R.string.chapter_queued)
+            Download.DOWNLOADING -> itemView.download_text.setText(R.string.chapter_downloading)
+            Download.DOWNLOADED -> itemView.download_text.setText(R.string.chapter_downloaded)
+            Download.ERROR -> itemView.download_text.setText(R.string.chapter_error)
+            else -> itemView.download_text.text = ""
+        }
+    }
+
+    /**
+     * Show pop up menu
+     * @param view view containing popup menu.
+     */
+    private fun showPopupMenu(view: View) {
+        // Create a PopupMenu, giving it the clicked view for an anchor
+        val popup = PopupMenu(adapter.fragment.activity, view)
+
+        // Inflate our menu resource into the PopupMenu's Menu
+        popup.menuInflater.inflate(R.menu.chapter_recent, popup.menu)
+
+        mangaChapter?.let {
+
+            // Hide download and show delete if the chapter is downloaded and
+            if (it.chapter.isDownloaded) {
+                val menu = popup.menu
+                menu.findItem(R.id.action_download).isVisible = false
+                menu.findItem(R.id.action_delete).isVisible = true
+            }
+
+            // Hide mark as unread when the chapter is unread
+            if (!it.chapter.read /*&& mangaChapter.chapter.last_page_read == 0*/) {
+                popup.menu.findItem(R.id.action_mark_as_unread).isVisible = false
+            }
+
+            // Hide mark as read when the chapter is read
+            if (it.chapter.read) {
+                popup.menu.findItem(R.id.action_mark_as_read).isVisible = false
+            }
+
+
+            // Set a listener so we are notified if a menu item is clicked
+            popup.setOnMenuItemClickListener { menuItem ->
+                val chapterObservable = Observable.just<Chapter>(it.chapter)
+
+                when (menuItem.itemId) {
+                    R.id.action_download -> adapter.fragment.onDownload(chapterObservable, it.manga)
+                    R.id.action_delete -> adapter.fragment.onDelete(chapterObservable, it.manga)
+                    R.id.action_mark_as_read -> adapter.fragment.onMarkAsRead(chapterObservable);
+                    R.id.action_mark_as_unread -> adapter.fragment.onMarkAsUnread(chapterObservable);
+                }
+                false
+            }
+
+        }
+
+        // Finally show the PopupMenu
+        popup.show()
+    }
+}

+ 0 - 309
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersPresenter.java

@@ -1,309 +0,0 @@
-package eu.kanade.tachiyomi.ui.recent;
-
-import android.os.Bundle;
-
-import org.greenrobot.eventbus.EventBus;
-
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collection;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-
-import javax.inject.Inject;
-
-import eu.kanade.tachiyomi.data.database.DatabaseHelper;
-import eu.kanade.tachiyomi.data.database.models.Chapter;
-import eu.kanade.tachiyomi.data.database.models.Manga;
-import eu.kanade.tachiyomi.data.database.models.MangaChapter;
-import eu.kanade.tachiyomi.data.download.DownloadManager;
-import eu.kanade.tachiyomi.data.download.model.Download;
-import eu.kanade.tachiyomi.data.source.SourceManager;
-import eu.kanade.tachiyomi.data.source.base.Source;
-import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
-import eu.kanade.tachiyomi.event.ReaderEvent;
-import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
-import rx.Observable;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
-import timber.log.Timber;
-
-/**
- * Presenter of RecentChaptersFragment.
- * Contains information and data for fragment.
- * Observable updates should be called from here.
- */
-public class RecentChaptersPresenter extends BasePresenter<RecentChaptersFragment> {
-
-    /**
-     * The id of the restartable.
-     */
-    private static final int GET_RECENT_CHAPTERS = 1;
-
-    /**
-     * The id of the restartable.
-     */
-    private static final int CHAPTER_STATUS_CHANGES = 2;
-
-    /**
-     * Used to connect to database
-     */
-    @Inject DatabaseHelper db;
-
-    /**
-     * Used to get information from download manager
-     */
-    @Inject DownloadManager downloadManager;
-
-    /**
-     * Used to get source from source id
-     */
-    @Inject SourceManager sourceManager;
-
-    /**
-     * List containing chapter and manga information
-     */
-    private List<MangaChapter> mangaChapters;
-
-    @Override
-    protected void onCreate(Bundle savedState) {
-        super.onCreate(savedState);
-
-        // Used to get recent chapters
-        restartableLatestCache(GET_RECENT_CHAPTERS,
-                this::getRecentChaptersObservable,
-                (recentChaptersFragment, chapters) -> {
-                    // Update adapter to show recent manga's
-                    recentChaptersFragment.onNextMangaChapters(chapters);
-                    // Update download status
-                    updateChapterStatus(convertToMangaChaptersList(chapters));
-                });
-
-        // Used to update download status
-        startableLatestCache(CHAPTER_STATUS_CHANGES,
-                this::getChapterStatusObs,
-                RecentChaptersFragment::onChapterStatusChange,
-                (view, error) -> Timber.e(error.getCause(), error.getMessage()));
-
-        if (savedState == null) {
-            // Start fetching recent chapters
-            start(GET_RECENT_CHAPTERS);
-        }
-    }
-
-    /**
-     * Returns a list only containing MangaChapter objects.
-     *
-     * @param input the list that will be converted.
-     * @return list containing MangaChapters objects.
-     */
-    private List<MangaChapter> convertToMangaChaptersList(List<Object> input) {
-        // Create temp list
-        List<MangaChapter> tempMangaChapterList = new ArrayList<>();
-
-        // Only add MangaChapter objects
-        //noinspection Convert2streamapi
-        for (Object object : input) {
-            if (object instanceof MangaChapter) {
-                tempMangaChapterList.add((MangaChapter) object);
-            }
-        }
-
-        // Return temp list
-        return tempMangaChapterList;
-    }
-
-    /**
-     * Update status of chapters
-     *
-     * @param mangaChapters list containing recent chapters
-     */
-    private void updateChapterStatus(List<MangaChapter> mangaChapters) {
-        // Set global list of chapters.
-        this.mangaChapters = mangaChapters;
-
-        // Update status.
-        //noinspection Convert2streamapi
-        for (MangaChapter mangaChapter : mangaChapters)
-            setChapterStatus(mangaChapter);
-
-        // Start onChapterStatusChange restartable.
-        start(CHAPTER_STATUS_CHANGES);
-    }
-
-    /**
-     * Returns observable containing chapter status.
-     *
-     * @return download object containing download progress.
-     */
-    private Observable<Download> getChapterStatusObs() {
-        return downloadManager.getQueue().getStatusObservable()
-                .observeOn(AndroidSchedulers.mainThread())
-                .filter(download -> chapterIdEquals(download.chapter.id))
-                .doOnNext(this::updateChapterStatus);
-    }
-
-    /**
-     * Function to check if chapter is in recent list
-     * @param chaptersId id of chapter
-     * @return exist in recent list
-     */
-    private boolean chapterIdEquals(Long chaptersId) {
-        for (MangaChapter mangaChapter : mangaChapters) {
-            if (chaptersId.equals(mangaChapter.chapter.id)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Update status of chapters.
-     *
-     * @param download download object containing progress.
-     */
-    private void updateChapterStatus(Download download) {
-        // Loop through list
-        for (MangaChapter item : mangaChapters) {
-            if (download.chapter.id.equals(item.chapter.id)) {
-                item.chapter.status = download.getStatus();
-                    break;
-            }
-        }
-    }
-
-    /**
-     * Get observable containing recent chapters and date
-     * @return observable containing recent chapters and date
-     */
-    private Observable<List<Object>> getRecentChaptersObservable() {
-        // Set date for recent chapters
-        Calendar cal = Calendar.getInstance();
-        cal.setTime(new Date());
-        cal.add(Calendar.MONTH, -1);
-
-        // Get recent chapters from database.
-        return db.getRecentChapters(cal.getTime()).asRxObservable()
-                // Group chapters by the date they were fetched on a ordered map.
-                .flatMap(recents -> Observable.from(recents)
-                        .toMultimap(
-                                recent -> getMapKey(recent.chapter.date_fetch),
-                                recent -> recent,
-                                () -> new TreeMap<>((d1, d2) -> d2.compareTo(d1))))
-                // Add every day and all its chapters to a single list.
-                .map(recents -> {
-                    List<Object> items = new ArrayList<>();
-                    for (Map.Entry<Date, Collection<MangaChapter>> recent : recents.entrySet()) {
-                        items.add(recent.getKey());
-                        items.addAll(recent.getValue());
-                    }
-                    return items;
-                })
-                .observeOn(AndroidSchedulers.mainThread());
-    }
-
-    /**
-     * Set the chapter status
-     * @param mangaChapter MangaChapter which status gets updated
-     */
-    private void setChapterStatus(MangaChapter mangaChapter) {
-        // Check if chapter in queue
-        for (Download download : downloadManager.getQueue()) {
-            if (mangaChapter.chapter.id.equals(download.chapter.id)) {
-                mangaChapter.chapter.status = download.getStatus();
-                return;
-            }
-        }
-
-        // Get source of chapter
-        Source source = sourceManager.get(mangaChapter.manga.source);
-
-        // Check if chapter is downloaded
-        if (downloadManager.isChapterDownloaded(source, mangaChapter.manga, mangaChapter.chapter)) {
-            mangaChapter.chapter.status = Download.DOWNLOADED;
-        } else {
-            mangaChapter.chapter.status = Download.NOT_DOWNLOADED;
-        }
-    }
-
-    /**
-     * Get date as time key
-     * @param date desired date
-     * @return date as time key
-     */
-    private Date getMapKey(long date) {
-        Calendar cal = Calendar.getInstance();
-        cal.setTime(new Date(date));
-        cal.set(Calendar.HOUR_OF_DAY, 0);
-        cal.set(Calendar.MINUTE, 0);
-        cal.set(Calendar.SECOND, 0);
-        cal.set(Calendar.MILLISECOND, 0);
-        return cal.getTime();
-    }
-
-    /**
-     * Open chapter in reader
-     * @param item chapter that is opened
-     */
-    public void onOpenChapter(MangaChapter item) {
-        Source source = sourceManager.get(item.manga.source);
-        EventBus.getDefault().postSticky(new ReaderEvent(source, item.manga, item.chapter));
-    }
-
-    /**
-     * Download selected chapter
-     * @param selectedChapter chapter that is selected
-     * @param manga manga that belongs to chapter
-     */
-    public void downloadChapter(Observable<Chapter> selectedChapter, Manga manga) {
-        add(selectedChapter
-                .toList()
-                .subscribe(chapters -> {
-                    EventBus.getDefault().postSticky(new DownloadChaptersEvent(manga, chapters));
-                }));
-    }
-
-    /**
-     * Delete selected chapter
-     * @param chapter chapter that is selected
-     * @param manga manga that belongs to chapter
-     */
-    public void deleteChapter(Chapter chapter, Manga manga) {
-        Source source = sourceManager.get(manga.source);
-        downloadManager.deleteChapter(source, manga, chapter);
-    }
-
-    /**
-     * Delete selected chapter observable
-     * @param selectedChapters chapter that are selected
-     */
-    public void deleteChapters(Observable<Chapter> selectedChapters) {
-        add(selectedChapters
-                .subscribe(chapter -> {
-                    downloadManager.getQueue().remove(chapter);
-                }, error -> {
-                    Timber.e(error.getMessage());
-                }));
-    }
-
-    /**
-     * Mark selected chapter as read
-     * @param selectedChapters chapter that is selected
-     * @param read read status
-     */
-    public void markChaptersRead(Observable<Chapter> selectedChapters, boolean read) {
-        add(selectedChapters
-                .subscribeOn(Schedulers.io())
-                .map(chapter -> {
-                    chapter.read = read;
-                    if (!read) chapter.last_page_read = 0;
-                    return chapter;
-                })
-                .toList()
-                .flatMap(chapters -> db.insertChapters(chapters).asRxObservable())
-                .observeOn(AndroidSchedulers.mainThread())
-                .subscribe());
-    }
-}

+ 310 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/RecentChaptersPresenter.kt

@@ -0,0 +1,310 @@
+package eu.kanade.tachiyomi.ui.recent
+
+import android.os.Bundle
+import eu.kanade.tachiyomi.data.database.DatabaseHelper
+import eu.kanade.tachiyomi.data.database.models.Chapter
+import eu.kanade.tachiyomi.data.database.models.Manga
+import eu.kanade.tachiyomi.data.database.models.MangaChapter
+import eu.kanade.tachiyomi.data.download.DownloadManager
+import eu.kanade.tachiyomi.data.download.model.Download
+import eu.kanade.tachiyomi.data.source.SourceManager
+import eu.kanade.tachiyomi.event.DownloadChaptersEvent
+import eu.kanade.tachiyomi.event.ReaderEvent
+import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
+import org.greenrobot.eventbus.EventBus
+import rx.Observable
+import rx.android.schedulers.AndroidSchedulers
+import rx.schedulers.Schedulers
+import timber.log.Timber
+import java.util.*
+import javax.inject.Inject
+
+class RecentChaptersPresenter : BasePresenter<RecentChaptersFragment>() {
+    /**
+     * Used to connect to database
+     */
+    @Inject lateinit var db: DatabaseHelper
+
+    /**
+     * Used to get information from download manager
+     */
+    @Inject lateinit var downloadManager: DownloadManager
+
+    /**
+     * Used to get source from source id
+     */
+    @Inject lateinit var sourceManager: SourceManager
+
+    /**
+     * List containing chapter and manga information
+     */
+    private var mangaChapters: List<MangaChapter>? = null
+
+    /**
+     * The id of the restartable.
+     */
+    val GET_RECENT_CHAPTERS = 1
+
+    /**
+     * The id of the restartable.
+     */
+    val CHAPTER_STATUS_CHANGES = 2
+
+    override fun onCreate(savedState: Bundle?) {
+        super.onCreate(savedState)
+
+        // Used to get recent chapters
+        restartableLatestCache(GET_RECENT_CHAPTERS,
+                { getRecentChaptersObservable() },
+                { recentChaptersFragment, chapters ->
+                    // Update adapter to show recent manga's
+                    recentChaptersFragment.onNextMangaChapters(chapters)
+                    // Update download status
+                    updateChapterStatus(convertToMangaChaptersList(chapters))
+                }
+        )
+
+        // Used to update download status
+        startableLatestCache(CHAPTER_STATUS_CHANGES,
+                { getChapterStatusObs() },
+                { recentChaptersFragment, download ->
+                    // Set chapter status
+                    recentChaptersFragment.onChapterStatusChange(download)
+                },
+                { view, error -> Timber.e(error.cause, error.message) }
+        )
+
+
+        if (savedState == null) {
+            // Start fetching recent chapters
+            start(GET_RECENT_CHAPTERS)
+        }
+    }
+
+
+    /**
+     * Returns observable containing chapter status.
+
+     * @return download object containing download progress.
+     */
+    private fun getChapterStatusObs(): Observable<Download> {
+        return downloadManager.queue.statusObservable
+                .observeOn(AndroidSchedulers.mainThread())
+                .filter { download: Download ->
+                    if (chapterIdEquals(download.chapter.id))
+                        true
+                    else
+                        false
+                }
+                .doOnNext { download1: Download -> updateChapterStatus(download1) }
+
+    }
+
+    /**
+     * Function to check if chapter is in recent list
+     * @param chaptersId id of chapter
+     * *
+     * @return exist in recent list
+     */
+    private fun chapterIdEquals(chaptersId: Long): Boolean {
+        mangaChapters!!.forEach { mangaChapter ->
+            if (chaptersId == mangaChapter.chapter.id) {
+                return true
+            }
+        }
+        return false
+    }
+
+    /**
+     * Returns a list only containing MangaChapter objects.
+
+     * @param input the list that will be converted.
+     * *
+     * @return list containing MangaChapters objects.
+     */
+    private fun convertToMangaChaptersList(input: List<Any>): List<MangaChapter> {
+        // Create temp list
+        val tempMangaChapterList = ArrayList<MangaChapter>()
+
+        // Only add MangaChapter objects
+        //noinspection Convert2streamapi
+        input.forEach { `object` ->
+            if (`object` is MangaChapter) {
+                tempMangaChapterList.add(`object`)
+            }
+        }
+
+        // Return temp list
+        return tempMangaChapterList
+    }
+
+    /**
+     * Update status of chapters.
+
+     * @param download download object containing progress.
+     */
+    private fun updateChapterStatus(download: Download) {
+        // Loop through list
+        mangaChapters?.let {
+            for (item in it) {
+                if (download.chapter.id == item.chapter.id) {
+                    // Update status.
+                    item.chapter.status = download.status
+                    break
+                }
+            }
+        }
+    }
+
+    /**
+     * Update status of chapters
+
+     * @param mangaChapters list containing recent chapters
+     */
+    private fun updateChapterStatus(mangaChapters: List<MangaChapter>) {
+        // Set global list of chapters.
+        this.mangaChapters = mangaChapters
+
+        // Update status.
+        //noinspection Convert2streamapi
+        for (mangaChapter in mangaChapters)
+            setChapterStatus(mangaChapter)
+
+        // Start onChapterStatusChange restartable.
+        start(CHAPTER_STATUS_CHANGES)
+    }
+
+    /**
+     * Set the chapter status
+     * @param mangaChapter MangaChapter which status gets updated
+     */
+    private fun setChapterStatus(mangaChapter: MangaChapter) {
+        // Check if chapter in queue
+        for (download in downloadManager.queue) {
+            if (mangaChapter.chapter.id == download.chapter.id) {
+                mangaChapter.chapter.status = download.status
+                return
+            }
+        }
+
+        // Get source of chapter
+        val source = sourceManager.get(mangaChapter.manga.source)
+
+        // Check if chapter is downloaded
+        if (downloadManager.isChapterDownloaded(source, mangaChapter.manga, mangaChapter.chapter)) {
+            mangaChapter.chapter.status = Download.DOWNLOADED
+        } else {
+            mangaChapter.chapter.status = Download.NOT_DOWNLOADED
+        }
+    }
+
+    /**
+     * Get observable containing recent chapters and date
+     * @return observable containing recent chapters and date
+     */
+    fun getRecentChaptersObservable(): Observable<ArrayList<Any>>? {
+        // Set date for recent chapters
+        val cal = Calendar.getInstance()
+        cal.time = Date()
+        cal.add(Calendar.MONTH, -1)
+
+        return db.getRecentChapters(cal.time).asRxObservable()
+                // Group chapters by the date they were fetched on a ordered map.
+                .flatMap { recentItems ->
+                    Observable.from(recentItems)
+                            .toMultimap {
+                                recent ->
+                                (getMapKey(recent.chapter.date_fetch))
+                            }
+                }
+                // Add every day and all its chapters to a single list.
+                .map { recentItems ->
+                    val items = ArrayList<Any>()
+                    recentItems.entries.forEach { recent ->
+                        items.add(recent.key)
+                        items.addAll(recent.value)
+                    }
+                    items
+                }
+                .observeOn(AndroidSchedulers.mainThread())
+    }
+
+    /**
+     * Get date as time key
+     * @param date desired date
+     * *
+     * @return date as time key
+     */
+    private fun getMapKey(date: Long): Date {
+        val cal = Calendar.getInstance()
+        cal.time = Date(date)
+        cal[Calendar.HOUR_OF_DAY] = 0
+        cal[Calendar.MINUTE] = 0
+        cal[Calendar.SECOND] = 0
+        cal[Calendar.MILLISECOND] = 0
+        return cal.time
+    }
+
+    /**
+     * Open chapter in reader
+     * @param item chapter that is opened
+     */
+    fun onOpenChapter(item: MangaChapter) {
+        val source = sourceManager.get(item.manga.source)
+        EventBus.getDefault().postSticky(ReaderEvent(source, item.manga, item.chapter))
+    }
+
+    /**
+     * Download selected chapter
+     * @param selectedChapter chapter that is selected
+     * *
+     * @param manga manga that belongs to chapter
+     */
+    fun downloadChapter(selectedChapter: Observable<Chapter>, manga: Manga) {
+        add(selectedChapter.toList().subscribe { chapters -> EventBus.getDefault().postSticky(DownloadChaptersEvent(manga, chapters)) })
+    }
+
+    /**
+     * Delete selected chapter
+     * @param chapter chapter that is selected
+     * *
+     * @param manga manga that belongs to chapter
+     */
+    fun deleteChapter(chapter: Chapter, manga: Manga) {
+        val source = sourceManager.get(manga.source)
+        downloadManager.deleteChapter(source, manga, chapter)
+    }
+
+    /**
+     * Delete selected chapter observable
+     * @param selectedChapters chapter that are selected
+     */
+    fun deleteChapters(selectedChapters: Observable<Chapter>) {
+        add(selectedChapters
+                .subscribe(
+                        { chapter -> downloadManager.queue.remove(chapter) })
+                { error -> Timber.e(error.message) })
+    }
+
+    /**
+     * Mark selected chapter as read
+     * @param selectedChapters chapter that is selected
+     * *
+     * @param read read status
+     */
+    fun markChaptersRead(selectedChapters: Observable<Chapter>, read: Boolean) {
+        add(selectedChapters
+                .subscribeOn(Schedulers.io())
+                .map { chapter ->
+                    chapter.read = read
+                    if (!read) {
+                        chapter.last_page_read = 0
+                    }
+                    chapter
+                }
+                .toList()
+                .flatMap { chapters -> db.insertChapters(chapters).asRxObservable() }
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe())
+    }
+}

+ 26 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/recent/SectionViewHolder.kt

@@ -0,0 +1,26 @@
+package eu.kanade.tachiyomi.ui.recent
+
+import android.support.v7.widget.RecyclerView
+import android.text.format.DateUtils
+import android.view.View
+import kotlinx.android.synthetic.main.item_recent_chapter_section.view.*
+import java.util.*
+
+class SectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
+
+    /**
+     * Current date
+     */
+    private val now = Date().time
+
+    /**
+     * Set value of section header
+     *
+     * @param date of section header
+     */
+    fun onSetValues(date: Date) {
+        val s = DateUtils.getRelativeTimeSpanString(
+                date.time, now, DateUtils.DAY_IN_MILLIS)
+        itemView.section_text.text = s
+    }
+}

+ 1 - 1
app/src/main/res/layout/fragment_recent_chapters.xml

@@ -6,7 +6,7 @@
                 android:orientation="vertical">
 
     <android.support.v7.widget.RecyclerView
-        android:id="@+id/chapter_list"
+        android:id="@+id/recycler"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:descendantFocusability="blocksDescendants"

+ 4 - 1
app/src/main/res/layout/item_recent_chapter.xml

@@ -77,9 +77,12 @@
         android:gravity="center|end"
         android:paddingBottom="18dp"
         android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
-        android:paddingRight="?android:attr/listPreferredItemPaddingRight">
+        android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+        android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+        android:paddingLeft="?android:attr/listPreferredItemPaddingLeft">
 
         <ImageView
+            android:id="@+id/chapterMenu"
             android:layout_width="24dp"
             android:layout_height="24dp"
             android:layout_alignParentEnd="false"