Merge pull request #160 from NoodleMage/issue_118

Implements Issue #118 download from recent tab
This commit is contained in:
inorichi 2016-02-19 20:36:31 +01:00
commit 8581e4667a
6 changed files with 617 additions and 10 deletions

View File

@ -16,13 +16,33 @@ import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.tachiyomi.R; import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.MangaChapter; 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> { public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHolder, Object> {
private RecentChaptersFragment fragment; /**
* Fragment of RecentChaptersFragment
*/
private final RecentChaptersFragment fragment;
/**
* The id of the view type
*/
private static final int VIEW_TYPE_CHAPTER = 0; private static final int VIEW_TYPE_CHAPTER = 0;
/**
* The id of the view type
*/
private static final int VIEW_TYPE_SECTION = 1; private static final int VIEW_TYPE_SECTION = 1;
/**
* Constructor
*
* @param fragment fragment
*/
public RecentChaptersAdapter(RecentChaptersFragment fragment) { public RecentChaptersAdapter(RecentChaptersFragment fragment) {
this.fragment = fragment; this.fragment = fragment;
setHasStableIds(true); setHasStableIds(true);
@ -37,6 +57,11 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
return item.hashCode(); return item.hashCode();
} }
/**
* Update items
*
* @param items items
*/
public void setItems(List<Object> items) { public void setItems(List<Object> items) {
mItems = items; mItems = items;
notifyDataSetChanged(); notifyDataSetChanged();
@ -56,6 +81,8 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext()); LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v; View v;
// Check which view type and set correct values.
switch (viewType) { switch (viewType) {
case VIEW_TYPE_CHAPTER: case VIEW_TYPE_CHAPTER:
v = inflater.inflate(R.layout.item_recent_chapter, parent, false); v = inflater.inflate(R.layout.item_recent_chapter, parent, false);
@ -69,6 +96,7 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
@Override @Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
// Check which view type and set correct values.
switch (holder.getItemViewType()) { switch (holder.getItemViewType()) {
case VIEW_TYPE_CHAPTER: case VIEW_TYPE_CHAPTER:
final MangaChapter chapter = (MangaChapter) getItem(position); final MangaChapter chapter = (MangaChapter) getItem(position);
@ -84,6 +112,10 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
holder.itemView.setActivated(isSelected(position)); holder.itemView.setActivated(isSelected(position));
} }
/**
* Returns fragment
* @return RecentChaptersFragment
*/
public RecentChaptersFragment getFragment() { public RecentChaptersFragment getFragment() {
return fragment; return fragment;
} }

View File

@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.recent;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
@ -9,18 +10,32 @@ import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import com.afollestad.materialdialogs.MaterialDialog;
import java.util.List; import java.util.List;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.kanade.tachiyomi.R; 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.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.adapter.FlexibleViewHolder;
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment; import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
import eu.kanade.tachiyomi.ui.decoration.DividerItemDecoration; import eu.kanade.tachiyomi.ui.decoration.DividerItemDecoration;
import eu.kanade.tachiyomi.ui.reader.ReaderActivity; import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
import nucleus.factory.RequiresPresenter; 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) @RequiresPresenter(RecentChaptersPresenter.class)
public class RecentChaptersFragment extends BaseRxFragment<RecentChaptersPresenter> implements FlexibleViewHolder.OnListItemClickListener { public class RecentChaptersFragment extends BaseRxFragment<RecentChaptersPresenter> implements FlexibleViewHolder.OnListItemClickListener {
@ -50,14 +65,21 @@ public class RecentChaptersFragment extends BaseRxFragment<RecentChaptersPresent
return view; return view;
} }
/**
* Populate adapter with chapters
*
* @param chapters list of chapters
*/
public void onNextMangaChapters(List<Object> chapters) { public void onNextMangaChapters(List<Object> chapters) {
adapter.setItems(chapters); adapter.setItems(chapters);
} }
@Override @Override
public boolean onListItemClick(int position) { public boolean onListItemClick(int position) {
// Get item from position
Object item = adapter.getItem(position); Object item = adapter.getItem(position);
if (item instanceof MangaChapter) { if (item instanceof MangaChapter) {
// Open chapter in reader
openChapter((MangaChapter) item); openChapter((MangaChapter) item);
} }
return false; return false;
@ -65,11 +87,114 @@ public class RecentChaptersFragment extends BaseRxFragment<RecentChaptersPresent
@Override @Override
public void onListItemLongClick(int position) { public void onListItemLongClick(int position) {
// Empty function
} }
protected void openChapter(MangaChapter chapter) { /**
* Open chapter in reader
*
* @param chapter selected chapter
*/
private void openChapter(MangaChapter chapter) {
getPresenter().onOpenChapter(chapter); getPresenter().onOpenChapter(chapter);
Intent intent = ReaderActivity.newIntent(getActivity()); Intent intent = ReaderActivity.newIntent(getActivity());
startActivity(intent); 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;
}
} }

View File

@ -1,35 +1,102 @@
package eu.kanade.tachiyomi.ui.recent; package eu.kanade.tachiyomi.ui.recent;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
import android.view.Menu;
import android.view.View; import android.view.View;
import android.widget.PopupMenu;
import android.widget.RelativeLayout;
import android.widget.TextView; import android.widget.TextView;
import butterknife.Bind; import butterknife.Bind;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import eu.kanade.tachiyomi.R; 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.database.models.MangaChapter;
import eu.kanade.tachiyomi.data.download.model.Download;
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder; 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 { public class RecentChaptersHolder extends FlexibleViewHolder {
/**
* Adapter for recent chapters
*/
private final RecentChaptersAdapter adapter;
/**
* TextView containing chapter title
*/
@Bind(R.id.chapter_title) TextView chapterTitle; @Bind(R.id.chapter_title) TextView chapterTitle;
/**
* TextView containing manga name
*/
@Bind(R.id.manga_title) TextView mangaTitle; @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; private final int readColor;
/**
* Color of unread chapter
*/
private final int unreadColor; 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) { public RecentChaptersHolder(View view, RecentChaptersAdapter adapter, OnListItemClickListener onListItemClickListener) {
super(view, adapter, onListItemClickListener); super(view, adapter, onListItemClickListener);
this.adapter = adapter;
ButterKnife.bind(this, view); ButterKnife.bind(this, view);
// Set colors.
readColor = ContextCompat.getColor(view.getContext(), R.color.hint_text); readColor = ContextCompat.getColor(view.getContext(), R.color.hint_text);
unreadColor = ContextCompat.getColor(view.getContext(), R.color.primary_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) { public void onSetValues(MangaChapter item) {
this.mangaChapter = item;
// Set chapter title
chapterTitle.setText(item.chapter.name); chapterTitle.setText(item.chapter.name);
// Set manga title
mangaTitle.setText(item.manga.title); mangaTitle.setText(item.manga.title);
// Check if chapter is read and set correct color
if (item.chapter.read) { if (item.chapter.read) {
chapterTitle.setTextColor(readColor); chapterTitle.setTextColor(readColor);
mangaTitle.setTextColor(readColor); mangaTitle.setTextColor(readColor);
@ -37,6 +104,84 @@ public class RecentChaptersHolder extends FlexibleViewHolder {
chapterTitle.setTextColor(unreadColor); chapterTitle.setTextColor(unreadColor);
mangaTitle.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();
}
} }

View File

@ -15,46 +15,184 @@ import java.util.TreeMap;
import javax.inject.Inject; import javax.inject.Inject;
import eu.kanade.tachiyomi.data.database.DatabaseHelper; 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.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.SourceManager;
import eu.kanade.tachiyomi.data.source.base.Source; import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
import eu.kanade.tachiyomi.event.ReaderEvent; import eu.kanade.tachiyomi.event.ReaderEvent;
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter; import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
import rx.Observable; import rx.Observable;
import rx.android.schedulers.AndroidSchedulers; 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> { 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; @Inject DatabaseHelper db;
/**
* Used to get information from download manager
*/
@Inject DownloadManager downloadManager;
/**
* Used to get source from source id
*/
@Inject SourceManager sourceManager; @Inject SourceManager sourceManager;
private static final int GET_RECENT_CHAPTERS = 1; /**
* List containing chapter and manga information
*/
private List<MangaChapter> mangaChapters;
@Override @Override
protected void onCreate(Bundle savedState) { protected void onCreate(Bundle savedState) {
super.onCreate(savedState); super.onCreate(savedState);
// Used to get recent chapters
restartableLatestCache(GET_RECENT_CHAPTERS, restartableLatestCache(GET_RECENT_CHAPTERS,
this::getRecentChaptersObservable, this::getRecentChaptersObservable,
RecentChaptersFragment::onNextMangaChapters); (recentChaptersFragment, chapters) -> {
// Update adapter to show recent manga's
recentChaptersFragment.onNextMangaChapters(chapters);
// Update download status
updateChapterStatus(convertToMangaChaptersList(chapters));
});
if (savedState == null) // 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); 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() { private Observable<List<Object>> getRecentChaptersObservable() {
// Set date for recent chapters
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(new Date()); cal.setTime(new Date());
cal.add(Calendar.MONTH, -1); cal.add(Calendar.MONTH, -1);
// Get recent chapters from database.
return db.getRecentChapters(cal.getTime()).asRxObservable() return db.getRecentChapters(cal.getTime()).asRxObservable()
// group chapters by the date they were fetched on a ordered map // Group chapters by the date they were fetched on a ordered map.
.flatMap(recents -> Observable.from(recents) .flatMap(recents -> Observable.from(recents)
.toMultimap( .toMultimap(
recent -> getMapKey(recent.chapter.date_fetch), recent -> getMapKey(recent.chapter.date_fetch),
recent -> recent, recent -> recent,
() -> new TreeMap<>((d1, d2) -> d2.compareTo(d1)))) () -> new TreeMap<>((d1, d2) -> d2.compareTo(d1))))
// add every day and all its chapters to a single list // Add every day and all its chapters to a single list.
.map(recents -> { .map(recents -> {
List<Object> items = new ArrayList<>(); List<Object> items = new ArrayList<>();
for (Map.Entry<Date, Collection<MangaChapter>> recent : recents.entrySet()) { for (Map.Entry<Date, Collection<MangaChapter>> recent : recents.entrySet()) {
@ -66,6 +204,35 @@ public class RecentChaptersPresenter extends BasePresenter<RecentChaptersFragmen
.observeOn(AndroidSchedulers.mainThread()); .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) { private Date getMapKey(long date) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTime(new Date(date)); cal.setTime(new Date(date));
@ -76,8 +243,67 @@ public class RecentChaptersPresenter extends BasePresenter<RecentChaptersFragmen
return cal.getTime(); return cal.getTime();
} }
/**
* Open chapter in reader
* @param item chapter that is opened
*/
public void onOpenChapter(MangaChapter item) { public void onOpenChapter(MangaChapter item) {
Source source = sourceManager.get(item.manga.source); Source source = sourceManager.get(item.manga.source);
EventBus.getDefault().postSticky(new ReaderEvent(source, item.manga, item.chapter)); 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());
}
} }

View File

@ -5,6 +5,7 @@
android:layout_height="?android:attr/listPreferredItemHeight" android:layout_height="?android:attr/listPreferredItemHeight"
android:background="@drawable/selector_chapter_light"> android:background="@drawable/selector_chapter_light">
<RelativeLayout <RelativeLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -13,13 +14,34 @@
android:paddingRight="?android:attr/listPreferredItemPaddingRight" android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"> android:paddingStart="?android:attr/listPreferredItemPaddingStart">
<RelativeLayout
android:id="@+id/relativeLayout"
android:layout_width="fill_parent"
android:layout_height="18dp"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true">
<TextView
android:id="@+id/download_text"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:textAllCaps="true"
android:textColor="@color/accent_text"
android:textSize="12sp"/>
</RelativeLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="30dp"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" android:layout_alignParentStart="true"
android:layout_centerVertical="true" android:layout_centerVertical="true"
android:layout_marginRight="30dp"
android:layout_marginEnd="30dp"
android:orientation="vertical"> android:orientation="vertical">
<TextView <TextView
@ -27,8 +49,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
android:singleLine="true" android:singleLine="true"
android:textAppearance="@style/TextAppearance.AppCompat.Medium"
tools:text="My manga"/> tools:text="My manga"/>
<TextView <TextView
@ -36,12 +58,37 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:ellipsize="end" android:ellipsize="end"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:maxLines="1" android:maxLines="1"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
tools:text="Title"/> tools:text="Title"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
<RelativeLayout
android:id="@+id/chapter_menu"
android:layout_width="50dp"
android:layout_height="fill_parent"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:gravity="center|end"
android:paddingBottom="18dp"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingRight="?android:attr/listPreferredItemPaddingRight">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentEnd="false"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="?android:selectableItemBackground"
android:src="@drawable/ic_more_horiz_black_24dp"
/>
</RelativeLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_download"
android:title="@string/action_download"
android:icon="@drawable/ic_file_download"
android:visible="true"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_delete"
android:title="@string/action_delete"
android:icon="@drawable/ic_action_delete"
android:visible="false"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_mark_as_read"
android:title="@string/action_mark_as_read"
android:icon="@drawable/ic_action_done_all"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_mark_as_unread"
android:title="@string/action_mark_as_unread"
android:icon="@drawable/ic_action_undone_all"
app:showAsAction="ifRoom"/>
</menu>