mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 06:17:57 +01:00 
			
		
		
		
	Rewrote Recent to Kotlin
This commit is contained in:
		| @@ -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) | ||||
|   | ||||
| @@ -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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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. | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -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; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -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 | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -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(); | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
| @@ -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() | ||||
|     } | ||||
| } | ||||
| @@ -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()); | ||||
|     } | ||||
| } | ||||
| @@ -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()) | ||||
|     } | ||||
| } | ||||
| @@ -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 | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user