Compare commits
31 Commits
Author | SHA1 | Date | |
---|---|---|---|
0a31c223e3 | |||
0f42956f3f | |||
ac2485d4a7 | |||
7993ec5074 | |||
4521174138 | |||
27b95e9d73 | |||
a54425f47d | |||
4918e67fda | |||
b174adbab0 | |||
59cc87c583 | |||
0e87dc995a | |||
fad7b75b96 | |||
c99c90fc4c | |||
594219848d | |||
fa301bfbd2 | |||
50306f6ea3 | |||
9b90ad0a3b | |||
5c854984e4 | |||
6c844cfd9c | |||
9e666dcdb3 | |||
e81f98a975 | |||
11dc0d7e9e | |||
07ed2e2ebb | |||
e1aa460106 | |||
75a77566cf | |||
dd0a2d842a | |||
fa71e906c9 | |||
e6a17e25a9 | |||
d88513de56 | |||
ad97d03f1d | |||
7fc23d526b |
@ -14,7 +14,7 @@ Current features:
|
|||||||
|
|
||||||
## Download
|
## Download
|
||||||
|
|
||||||
[](https://github.com/inorichi/tachiyomi/releases/download/v0.1.0/tachiyomi-v0.1.0.apk)
|
[](https://github.com/inorichi/tachiyomi/releases/download/v0.1.2/tachiyomi-v0.1.2.apk)
|
||||||
[](http://tachiyomi.kanade.eu/latest/app-debug.apk)
|
[](http://tachiyomi.kanade.eu/latest/app-debug.apk)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
@ -39,8 +39,8 @@ android {
|
|||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 23
|
targetSdkVersion 23
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
versionCode 2
|
versionCode 3
|
||||||
versionName "0.1.1"
|
versionName "0.1.2"
|
||||||
|
|
||||||
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
|
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
|
||||||
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
|
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
|
||||||
@ -73,6 +73,7 @@ android {
|
|||||||
|
|
||||||
lintOptions {
|
lintOptions {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
|
checkReleaseBuilds false
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -93,6 +94,7 @@ dependencies {
|
|||||||
compile "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
|
compile "com.android.support:design:$SUPPORT_LIBRARY_VERSION"
|
||||||
compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION"
|
compile "com.android.support:recyclerview-v7:$SUPPORT_LIBRARY_VERSION"
|
||||||
compile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
|
compile "com.android.support:support-annotations:$SUPPORT_LIBRARY_VERSION"
|
||||||
|
compile "com.android.support:percent:$SUPPORT_LIBRARY_VERSION"
|
||||||
compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.2'
|
compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.2'
|
||||||
compile 'com.squareup.okhttp:okhttp:2.7.2'
|
compile 'com.squareup.okhttp:okhttp:2.7.2'
|
||||||
compile 'com.squareup.okio:okio:1.6.0'
|
compile 'com.squareup.okio:okio:1.6.0'
|
||||||
@ -114,9 +116,9 @@ dependencies {
|
|||||||
compile "frankiesardo:icepick:$ICEPICK_VERSION"
|
compile "frankiesardo:icepick:$ICEPICK_VERSION"
|
||||||
provided "frankiesardo:icepick-processor:$ICEPICK_VERSION"
|
provided "frankiesardo:icepick-processor:$ICEPICK_VERSION"
|
||||||
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
compile 'com.github.dmytrodanylyk.android-process-button:library:1.0.4'
|
||||||
compile 'eu.davidea:flexible-adapter:4.2.0@aar'
|
compile 'eu.davidea:flexible-adapter:4.2.0'
|
||||||
compile 'com.nononsenseapps:filepicker:2.5.1'
|
compile 'com.nononsenseapps:filepicker:2.5.1'
|
||||||
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
compile 'com.github.amulyakhare:TextDrawable:558677e'
|
||||||
compile 'com.github.pwittchen:reactivenetwork:0.1.5'
|
compile 'com.github.pwittchen:reactivenetwork:0.1.5'
|
||||||
|
|
||||||
compile "com.google.dagger:dagger:$DAGGER_VERSION"
|
compile "com.google.dagger:dagger:$DAGGER_VERSION"
|
||||||
|
@ -162,11 +162,11 @@ public class DatabaseHelper {
|
|||||||
.prepare();
|
.prepare();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PreparedGetListOfObjects<MangaChapter> getRecentChapters() {
|
public PreparedGetListOfObjects<MangaChapter> getRecentChapters(Date date) {
|
||||||
return db.get()
|
return db.get()
|
||||||
.listOfObjects(MangaChapter.class)
|
.listOfObjects(MangaChapter.class)
|
||||||
.withQuery(RawQuery.builder()
|
.withQuery(RawQuery.builder()
|
||||||
.query(MangaChapterGetResolver.RECENT_CHAPTERS_QUERY)
|
.query(MangaChapterGetResolver.getRecentChaptersQuery(date))
|
||||||
.observesTables(ChapterTable.TABLE)
|
.observesTables(ChapterTable.TABLE)
|
||||||
.build())
|
.build())
|
||||||
.withGetResolver(MangaChapterGetResolver.INSTANCE)
|
.withGetResolver(MangaChapterGetResolver.INSTANCE)
|
||||||
|
@ -68,6 +68,10 @@ public class Manga implements Serializable {
|
|||||||
public static final int COMPLETED = 2;
|
public static final int COMPLETED = 2;
|
||||||
public static final int LICENSED = 3;
|
public static final int LICENSED = 3;
|
||||||
|
|
||||||
|
public static final int SORT_AZ = 0;
|
||||||
|
public static final int SORT_ZA = 1;
|
||||||
|
public static final int SORT_MASK = 1;
|
||||||
|
|
||||||
public Manga() {}
|
public Manga() {}
|
||||||
|
|
||||||
public static Manga create(String pathUrl) {
|
public static Manga create(String pathUrl) {
|
||||||
@ -120,6 +124,18 @@ public class Manga implements Serializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setFlags(int flag, int mask) {
|
||||||
|
chapter_flags = (chapter_flags & ~mask) | (flag & mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean sortChaptersAZ () {
|
||||||
|
return (this.chapter_flags & SORT_MASK) == SORT_AZ;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setChapterOrder(int order) {
|
||||||
|
setFlags(order, SORT_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -5,6 +5,8 @@ import android.support.annotation.NonNull;
|
|||||||
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver;
|
import com.pushtorefresh.storio.sqlite.operations.get.DefaultGetResolver;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
||||||
import eu.kanade.tachiyomi.data.database.models.ChapterStorIOSQLiteGetResolver;
|
import eu.kanade.tachiyomi.data.database.models.ChapterStorIOSQLiteGetResolver;
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
@ -24,10 +26,12 @@ public class MangaChapterGetResolver extends DefaultGetResolver<MangaChapter> {
|
|||||||
MangaTable.COLUMN_ID,
|
MangaTable.COLUMN_ID,
|
||||||
ChapterTable.COLUMN_MANGA_ID);
|
ChapterTable.COLUMN_MANGA_ID);
|
||||||
|
|
||||||
public static final String RECENT_CHAPTERS_QUERY = String.format(
|
public static String getRecentChaptersQuery(Date date) {
|
||||||
QUERY + " WHERE %1$s = 1 ORDER BY %2$s DESC LIMIT 100",
|
return QUERY + String.format(" WHERE %1$s = 1 AND %2$s > %3$d ORDER BY %2$s DESC",
|
||||||
MangaTable.COLUMN_FAVORITE,
|
MangaTable.COLUMN_FAVORITE,
|
||||||
ChapterTable.COLUMN_DATE_UPLOAD);
|
ChapterTable.COLUMN_DATE_UPLOAD,
|
||||||
|
date.getTime());
|
||||||
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final MangaStorIOSQLiteGetResolver mangaGetResolver;
|
private final MangaStorIOSQLiteGetResolver mangaGetResolver;
|
||||||
@ -45,6 +49,7 @@ public class MangaChapterGetResolver extends DefaultGetResolver<MangaChapter> {
|
|||||||
public MangaChapter mapFromCursor(@NonNull Cursor cursor) {
|
public MangaChapter mapFromCursor(@NonNull Cursor cursor) {
|
||||||
final Manga manga = mangaGetResolver.mapFromCursor(cursor);
|
final Manga manga = mangaGetResolver.mapFromCursor(cursor);
|
||||||
final Chapter chapter = chapterGetResolver.mapFromCursor(cursor);
|
final Chapter chapter = chapterGetResolver.mapFromCursor(cursor);
|
||||||
|
manga.id = chapter.manga_id;
|
||||||
|
|
||||||
return new MangaChapter(manga, chapter);
|
return new MangaChapter(manga, chapter);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,8 @@ import eu.kanade.tachiyomi.data.source.base.Source;
|
|||||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||||
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
|
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
|
||||||
import eu.kanade.tachiyomi.util.DiskUtils;
|
import eu.kanade.tachiyomi.util.DiskUtils;
|
||||||
|
import eu.kanade.tachiyomi.util.DynamicConcurrentMergeOperator;
|
||||||
|
import eu.kanade.tachiyomi.util.UrlUtil;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
import rx.Subscription;
|
import rx.Subscription;
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
import rx.android.schedulers.AndroidSchedulers;
|
||||||
@ -45,6 +47,9 @@ public class DownloadManager {
|
|||||||
private BehaviorSubject<Boolean> runningSubject;
|
private BehaviorSubject<Boolean> runningSubject;
|
||||||
private Subscription downloadsSubscription;
|
private Subscription downloadsSubscription;
|
||||||
|
|
||||||
|
private BehaviorSubject<Integer> threadsSubject;
|
||||||
|
private Subscription threadsSubscription;
|
||||||
|
|
||||||
private DownloadQueue queue;
|
private DownloadQueue queue;
|
||||||
private volatile boolean isRunning;
|
private volatile boolean isRunning;
|
||||||
|
|
||||||
@ -60,15 +65,19 @@ public class DownloadManager {
|
|||||||
|
|
||||||
downloadsQueueSubject = PublishSubject.create();
|
downloadsQueueSubject = PublishSubject.create();
|
||||||
runningSubject = BehaviorSubject.create();
|
runningSubject = BehaviorSubject.create();
|
||||||
|
threadsSubject = BehaviorSubject.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeSubscriptions() {
|
private void initializeSubscriptions() {
|
||||||
if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed())
|
if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed())
|
||||||
downloadsSubscription.unsubscribe();
|
downloadsSubscription.unsubscribe();
|
||||||
|
|
||||||
|
threadsSubscription = preferences.downloadThreads().asObservable()
|
||||||
|
.subscribe(threadsSubject::onNext);
|
||||||
|
|
||||||
downloadsSubscription = downloadsQueueSubject
|
downloadsSubscription = downloadsQueueSubject
|
||||||
.concatMap(downloads -> Observable.from(downloads)
|
.flatMap(Observable::from)
|
||||||
.flatMap(this::downloadChapter, preferences.downloadThreads()))
|
.lift(new DynamicConcurrentMergeOperator<>(this::downloadChapter, threadsSubject))
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.map(download -> areAllDownloadsFinished())
|
.map(download -> areAllDownloadsFinished())
|
||||||
@ -94,6 +103,11 @@ public class DownloadManager {
|
|||||||
downloadsSubscription.unsubscribe();
|
downloadsSubscription.unsubscribe();
|
||||||
downloadsSubscription = null;
|
downloadsSubscription = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (threadsSubscription != null && !threadsSubscription.isUnsubscribed()) {
|
||||||
|
threadsSubscription.unsubscribe();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a download object for every chapter in the event and add them to the downloads queue
|
// Create a download object for every chapter in the event and add them to the downloads queue
|
||||||
@ -181,8 +195,7 @@ public class DownloadManager {
|
|||||||
// Or if the page list already exists, start from the file
|
// Or if the page list already exists, start from the file
|
||||||
Observable.just(download.pages);
|
Observable.just(download.pages);
|
||||||
|
|
||||||
return pageListObservable
|
return Observable.defer(() -> pageListObservable
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.doOnNext(pages -> {
|
.doOnNext(pages -> {
|
||||||
download.downloadedImages = 0;
|
download.downloadedImages = 0;
|
||||||
download.setStatus(Download.DOWNLOADING);
|
download.setStatus(Download.DOWNLOADING);
|
||||||
@ -199,7 +212,8 @@ public class DownloadManager {
|
|||||||
.onErrorResumeNext(error -> {
|
.onErrorResumeNext(error -> {
|
||||||
download.setStatus(Download.ERROR);
|
download.setStatus(Download.ERROR);
|
||||||
return Observable.just(download);
|
return Observable.just(download);
|
||||||
});
|
}))
|
||||||
|
.subscribeOn(Schedulers.io());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the image from the filesystem if it exists or download from network
|
// Get the image from the filesystem if it exists or download from network
|
||||||
@ -271,6 +285,15 @@ public class DownloadManager {
|
|||||||
// Get the filename for an image given the page
|
// Get the filename for an image given the page
|
||||||
private String getImageFilename(Page page) {
|
private String getImageFilename(Page page) {
|
||||||
String url = page.getImageUrl();
|
String url = page.getImageUrl();
|
||||||
|
int number = page.getPageNumber() + 1;
|
||||||
|
// Try to preserve file extension
|
||||||
|
if (UrlUtil.isJpg(url)) {
|
||||||
|
return number + ".jpg";
|
||||||
|
} else if (UrlUtil.isPng(url)) {
|
||||||
|
return number + ".png";
|
||||||
|
} else if (UrlUtil.isGif(url)) {
|
||||||
|
return number + ".gif";
|
||||||
|
}
|
||||||
return Uri.parse(url).getLastPathSegment().replaceAll("[^\\sa-zA-Z0-9.-]", "_");
|
return Uri.parse(url).getLastPathSegment().replaceAll("[^\\sa-zA-Z0-9.-]", "_");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,6 +116,10 @@ public class PreferencesHelper {
|
|||||||
return rxPrefs.getInteger(getKey(R.string.pref_reader_theme_key), 0);
|
return rxPrefs.getInteger(getKey(R.string.pref_reader_theme_key), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Preference<Boolean> catalogueAsList() {
|
||||||
|
return rxPrefs.getBoolean(getKey(R.string.pref_display_catalogue_as_list), false);
|
||||||
|
}
|
||||||
|
|
||||||
public String getSourceUsername(Source source) {
|
public String getSourceUsername(Source source) {
|
||||||
return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.getId(), "");
|
return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.getId(), "");
|
||||||
}
|
}
|
||||||
@ -155,8 +159,8 @@ public class PreferencesHelper {
|
|||||||
prefs.edit().putString(getKey(R.string.pref_download_directory_key), path).apply();
|
prefs.edit().putString(getKey(R.string.pref_download_directory_key), path).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int downloadThreads() {
|
public Preference<Integer> downloadThreads() {
|
||||||
return prefs.getInt(getKey(R.string.pref_download_slots_key), 1);
|
return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean downloadOnlyOverWifi() {
|
public boolean downloadOnlyOverWifi() {
|
||||||
|
12
app/src/main/java/eu/kanade/tachiyomi/event/MangaEvent.java
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
package eu.kanade.tachiyomi.event;
|
||||||
|
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
|
|
||||||
|
public class MangaEvent {
|
||||||
|
|
||||||
|
public final Manga manga;
|
||||||
|
|
||||||
|
public MangaEvent(Manga manga) {
|
||||||
|
this.manga = manga;
|
||||||
|
}
|
||||||
|
}
|
@ -107,14 +107,14 @@ public class RxPresenter<View> extends Presenter<View> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a restartable is subscribed.
|
* Checks if a restartable is unsubscribed.
|
||||||
*
|
*
|
||||||
* @param restartableId id of a restartable.
|
* @param restartableId id of the restartable.
|
||||||
* @return True if the restartable is subscribed, false otherwise.
|
* @return true if the subscription is null or unsubscribed, false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean isSubscribed(int restartableId) {
|
public boolean isUnsubscribed(int restartableId) {
|
||||||
Subscription s = restartableSubscriptions.get(restartableId);
|
Subscription subscription = restartableSubscriptions.get(restartableId);
|
||||||
return s != null && !s.isUnsubscribed();
|
return subscription == null || subscription.isUnsubscribed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -31,6 +31,10 @@ public class CatalogueAdapter extends FlexibleAdapter<CatalogueHolder, Manga> {
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Manga> getItems() {
|
||||||
|
return mItems;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getItemId(int position) {
|
public long getItemId(int position) {
|
||||||
return mItems.get(position).id;
|
return mItems.get(position).id;
|
||||||
@ -44,8 +48,13 @@ public class CatalogueAdapter extends FlexibleAdapter<CatalogueHolder, Manga> {
|
|||||||
@Override
|
@Override
|
||||||
public CatalogueHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public CatalogueHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
LayoutInflater inflater = fragment.getActivity().getLayoutInflater();
|
LayoutInflater inflater = fragment.getActivity().getLayoutInflater();
|
||||||
View v = inflater.inflate(R.layout.item_catalogue, parent, false);
|
if (parent.getId() == R.id.catalogue_grid) {
|
||||||
return new CatalogueHolder(v, this, fragment);
|
View v = inflater.inflate(R.layout.item_catalogue_grid, parent, false);
|
||||||
|
return new CatalogueGridHolder(v, this, fragment);
|
||||||
|
} else {
|
||||||
|
View v = inflater.inflate(R.layout.item_catalogue_list, parent, false);
|
||||||
|
return new CatalogueListHolder(v, this, fragment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -4,7 +4,10 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
import android.support.v7.widget.GridLayoutManager;
|
import android.support.v7.widget.GridLayoutManager;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
@ -14,9 +17,12 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.Animation;
|
||||||
|
import android.view.animation.AnimationUtils;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.Spinner;
|
import android.widget.Spinner;
|
||||||
|
import android.widget.ViewSwitcher;
|
||||||
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
|
|
||||||
@ -30,11 +36,13 @@ import eu.kanade.tachiyomi.data.database.models.Manga;
|
|||||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||||
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.main.MainActivity;
|
import eu.kanade.tachiyomi.ui.main.MainActivity;
|
||||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity;
|
import eu.kanade.tachiyomi.ui.manga.MangaActivity;
|
||||||
import eu.kanade.tachiyomi.util.ToastUtil;
|
import eu.kanade.tachiyomi.util.ToastUtil;
|
||||||
import eu.kanade.tachiyomi.widget.AutofitRecyclerView;
|
import eu.kanade.tachiyomi.widget.AutofitRecyclerView;
|
||||||
import eu.kanade.tachiyomi.widget.EndlessRecyclerScrollListener;
|
import eu.kanade.tachiyomi.widget.EndlessGridScrollListener;
|
||||||
|
import eu.kanade.tachiyomi.widget.EndlessListScrollListener;
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import nucleus.factory.RequiresPresenter;
|
import nucleus.factory.RequiresPresenter;
|
||||||
import rx.Subscription;
|
import rx.Subscription;
|
||||||
@ -45,14 +53,17 @@ import rx.subjects.PublishSubject;
|
|||||||
public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
||||||
implements FlexibleViewHolder.OnListItemClickListener {
|
implements FlexibleViewHolder.OnListItemClickListener {
|
||||||
|
|
||||||
@Bind(R.id.recycler) AutofitRecyclerView recycler;
|
@Bind(R.id.switcher) ViewSwitcher switcher;
|
||||||
|
@Bind(R.id.catalogue_grid) AutofitRecyclerView catalogueGrid;
|
||||||
|
@Bind(R.id.catalogue_list) RecyclerView catalogueList;
|
||||||
@Bind(R.id.progress) ProgressBar progress;
|
@Bind(R.id.progress) ProgressBar progress;
|
||||||
@Bind(R.id.progress_grid) ProgressBar progressGrid;
|
@Bind(R.id.progress_grid) ProgressBar progressGrid;
|
||||||
|
|
||||||
private Toolbar toolbar;
|
private Toolbar toolbar;
|
||||||
private Spinner spinner;
|
private Spinner spinner;
|
||||||
private CatalogueAdapter adapter;
|
private CatalogueAdapter adapter;
|
||||||
private EndlessRecyclerScrollListener scrollListener;
|
private EndlessGridScrollListener gridScrollListener;
|
||||||
|
private EndlessListScrollListener listScrollListener;
|
||||||
|
|
||||||
@State String query = "";
|
@State String query = "";
|
||||||
@State int selectedIndex = -1;
|
@State int selectedIndex = -1;
|
||||||
@ -61,6 +72,9 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
private PublishSubject<String> queryDebouncerSubject;
|
private PublishSubject<String> queryDebouncerSubject;
|
||||||
private Subscription queryDebouncerSubscription;
|
private Subscription queryDebouncerSubscription;
|
||||||
|
|
||||||
|
private MenuItem displayMode;
|
||||||
|
private MenuItem searchItem;
|
||||||
|
|
||||||
public static CatalogueFragment newInstance() {
|
public static CatalogueFragment newInstance() {
|
||||||
return new CatalogueFragment();
|
return new CatalogueFragment();
|
||||||
}
|
}
|
||||||
@ -77,13 +91,32 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
View view = inflater.inflate(R.layout.fragment_catalogue, container, false);
|
View view = inflater.inflate(R.layout.fragment_catalogue, container, false);
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
// Initialize adapter and scroll listener
|
// Initialize adapter, scroll listener and recycler views
|
||||||
GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
|
|
||||||
adapter = new CatalogueAdapter(this);
|
adapter = new CatalogueAdapter(this);
|
||||||
scrollListener = new EndlessRecyclerScrollListener(layoutManager, this::requestNextPage);
|
|
||||||
recycler.setHasFixedSize(true);
|
GridLayoutManager glm = (GridLayoutManager) catalogueGrid.getLayoutManager();
|
||||||
recycler.setAdapter(adapter);
|
gridScrollListener = new EndlessGridScrollListener(glm, this::requestNextPage);
|
||||||
recycler.addOnScrollListener(scrollListener);
|
catalogueGrid.setHasFixedSize(true);
|
||||||
|
catalogueGrid.setAdapter(adapter);
|
||||||
|
catalogueGrid.addOnScrollListener(gridScrollListener);
|
||||||
|
|
||||||
|
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
|
||||||
|
listScrollListener = new EndlessListScrollListener(llm, this::requestNextPage);
|
||||||
|
catalogueList.setHasFixedSize(true);
|
||||||
|
catalogueList.setAdapter(adapter);
|
||||||
|
catalogueList.setLayoutManager(llm);
|
||||||
|
catalogueList.addOnScrollListener(listScrollListener);
|
||||||
|
catalogueList.addItemDecoration(new DividerItemDecoration(
|
||||||
|
ContextCompat.getDrawable(getContext(), R.drawable.line_divider)));
|
||||||
|
|
||||||
|
if (getPresenter().isListMode()) {
|
||||||
|
switcher.showNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
Animation inAnim = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_in);
|
||||||
|
Animation outAnim = AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out);
|
||||||
|
switcher.setInAnimation(inAnim);
|
||||||
|
switcher.setOutAnimation(outAnim);
|
||||||
|
|
||||||
// Create toolbar spinner
|
// Create toolbar spinner
|
||||||
Context themedContext = getBaseActivity().getSupportActionBar() != null ?
|
Context themedContext = getBaseActivity().getSupportActionBar() != null ?
|
||||||
@ -109,6 +142,8 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
} else {
|
} else {
|
||||||
selectedIndex = position;
|
selectedIndex = position;
|
||||||
showProgressBar();
|
showProgressBar();
|
||||||
|
glm.scrollToPositionWithOffset(0, 0);
|
||||||
|
llm.scrollToPositionWithOffset(0, 0);
|
||||||
getPresenter().startRequesting(source);
|
getPresenter().startRequesting(source);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +165,7 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
inflater.inflate(R.menu.catalogue_list, menu);
|
inflater.inflate(R.menu.catalogue_list, menu);
|
||||||
|
|
||||||
// Initialize search menu
|
// Initialize search menu
|
||||||
MenuItem searchItem = menu.findItem(R.id.action_search);
|
searchItem = menu.findItem(R.id.action_search);
|
||||||
final SearchView searchView = (SearchView) searchItem.getActionView();
|
final SearchView searchView = (SearchView) searchItem.getActionView();
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(query)) {
|
if (!TextUtils.isEmpty(query)) {
|
||||||
@ -151,6 +186,22 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show next display mode
|
||||||
|
displayMode = menu.findItem(R.id.action_display_mode);
|
||||||
|
int icon = getPresenter().isListMode() ?
|
||||||
|
R.drawable.ic_view_module_white_24dp : R.drawable.ic_view_list_white_24dp;
|
||||||
|
displayMode.setIcon(icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_display_mode:
|
||||||
|
swapDisplayMode();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -167,6 +218,9 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
|
if (searchItem != null && searchItem.isActionViewExpanded()) {
|
||||||
|
searchItem.collapseActionView();
|
||||||
|
}
|
||||||
toolbar.removeView(spinner);
|
toolbar.removeView(spinner);
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
@ -193,11 +247,13 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
|
|
||||||
private void restartRequest(String newQuery) {
|
private void restartRequest(String newQuery) {
|
||||||
// If text didn't change, do nothing
|
// If text didn't change, do nothing
|
||||||
if (query.equals(newQuery)) return;
|
if (query.equals(newQuery) || getPresenter().getSource() == null)
|
||||||
|
return;
|
||||||
|
|
||||||
query = newQuery;
|
query = newQuery;
|
||||||
showProgressBar();
|
showProgressBar();
|
||||||
recycler.getLayoutManager().scrollToPosition(0);
|
catalogueGrid.getLayoutManager().scrollToPosition(0);
|
||||||
|
catalogueList.getLayoutManager().scrollToPosition(0);
|
||||||
|
|
||||||
getPresenter().restartRequest(query);
|
getPresenter().restartRequest(query);
|
||||||
}
|
}
|
||||||
@ -211,9 +267,10 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
|
|
||||||
public void onAddPage(int page, List<Manga> mangas) {
|
public void onAddPage(int page, List<Manga> mangas) {
|
||||||
hideProgressBar();
|
hideProgressBar();
|
||||||
if (page == 1) {
|
if (page == 0) {
|
||||||
adapter.clear();
|
adapter.clear();
|
||||||
scrollListener.resetScroll();
|
gridScrollListener.resetScroll();
|
||||||
|
listScrollListener.resetScroll();
|
||||||
}
|
}
|
||||||
adapter.addItems(mangas);
|
adapter.addItems(mangas);
|
||||||
}
|
}
|
||||||
@ -223,15 +280,28 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateImage(Manga manga) {
|
public void updateImage(Manga manga) {
|
||||||
CatalogueHolder holder = getHolder(manga);
|
CatalogueGridHolder holder = getHolder(manga);
|
||||||
if (holder != null) {
|
if (holder != null) {
|
||||||
holder.setImage(manga, getPresenter());
|
holder.setImage(manga, getPresenter());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void swapDisplayMode() {
|
||||||
|
getPresenter().swapDisplayMode();
|
||||||
|
boolean isListMode = getPresenter().isListMode();
|
||||||
|
int icon = isListMode ?
|
||||||
|
R.drawable.ic_view_module_white_24dp : R.drawable.ic_view_list_white_24dp;
|
||||||
|
displayMode.setIcon(icon);
|
||||||
|
switcher.showNext();
|
||||||
|
if (!isListMode) {
|
||||||
|
// Initialize mangas if going to grid view
|
||||||
|
getPresenter().initializeMangas(adapter.getItems());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private CatalogueHolder getHolder(Manga manga) {
|
private CatalogueGridHolder getHolder(Manga manga) {
|
||||||
return (CatalogueHolder) recycler.findViewHolderForItemId(manga.id);
|
return (CatalogueGridHolder) catalogueGrid.findViewHolderForItemId(manga.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showProgressBar() {
|
private void showProgressBar() {
|
||||||
@ -261,12 +331,15 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
|
|||||||
public void onListItemLongClick(int position) {
|
public void onListItemLongClick(int position) {
|
||||||
final Manga selectedManga = adapter.getItem(position);
|
final Manga selectedManga = adapter.getItem(position);
|
||||||
|
|
||||||
|
int textRes = selectedManga.favorite ? R.string.remove_from_library : R.string.add_to_library;
|
||||||
|
|
||||||
new MaterialDialog.Builder(getActivity())
|
new MaterialDialog.Builder(getActivity())
|
||||||
.items(getString(R.string.add_to_library))
|
.items(getString(textRes))
|
||||||
.itemsCallback((dialog, itemView, which, text) -> {
|
.itemsCallback((dialog, itemView, which, text) -> {
|
||||||
switch (which) {
|
switch (which) {
|
||||||
case 0:
|
case 0:
|
||||||
getPresenter().addMangaToLibrary(selectedManga);
|
getPresenter().changeMangaFavorite(selectedManga);
|
||||||
|
adapter.notifyItemChanged(position);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.catalogue;
|
||||||
|
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import eu.kanade.tachiyomi.R;
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
|
|
||||||
|
public class CatalogueGridHolder extends CatalogueHolder {
|
||||||
|
|
||||||
|
@Bind(R.id.title) TextView title;
|
||||||
|
@Bind(R.id.thumbnail) ImageView thumbnail;
|
||||||
|
@Bind(R.id.favorite_sticker) ImageView favoriteSticker;
|
||||||
|
|
||||||
|
public CatalogueGridHolder(View view, CatalogueAdapter adapter, OnListItemClickListener listener) {
|
||||||
|
super(view, adapter, listener);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetValues(Manga manga, CataloguePresenter presenter) {
|
||||||
|
title.setText(manga.title);
|
||||||
|
favoriteSticker.setVisibility(manga.favorite ? View.VISIBLE : View.GONE);
|
||||||
|
setImage(manga, presenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setImage(Manga manga, CataloguePresenter presenter) {
|
||||||
|
if (manga.thumbnail_url != null) {
|
||||||
|
presenter.coverCache.loadFromNetwork(thumbnail, manga.thumbnail_url,
|
||||||
|
presenter.getSource().getGlideHeaders());
|
||||||
|
} else {
|
||||||
|
thumbnail.setImageResource(android.R.color.transparent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,38 +1,15 @@
|
|||||||
package eu.kanade.tachiyomi.ui.catalogue;
|
package eu.kanade.tachiyomi.ui.catalogue;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import butterknife.Bind;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import eu.kanade.tachiyomi.R;
|
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
||||||
|
|
||||||
public class CatalogueHolder extends FlexibleViewHolder {
|
public abstract class CatalogueHolder extends FlexibleViewHolder {
|
||||||
|
|
||||||
@Bind(R.id.title) TextView title;
|
|
||||||
@Bind(R.id.thumbnail) ImageView thumbnail;
|
|
||||||
@Bind(R.id.favorite_sticker) ImageView favoriteSticker;
|
|
||||||
|
|
||||||
public CatalogueHolder(View view, CatalogueAdapter adapter, OnListItemClickListener listener) {
|
public CatalogueHolder(View view, CatalogueAdapter adapter, OnListItemClickListener listener) {
|
||||||
super(view, adapter, listener);
|
super(view, adapter, listener);
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSetValues(Manga manga, CataloguePresenter presenter) {
|
abstract void onSetValues(Manga manga, CataloguePresenter presenter);
|
||||||
title.setText(manga.title);
|
}
|
||||||
favoriteSticker.setVisibility(manga.favorite ? View.VISIBLE : View.GONE);
|
|
||||||
setImage(manga, presenter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setImage(Manga manga, CataloguePresenter presenter) {
|
|
||||||
if (manga.thumbnail_url != null) {
|
|
||||||
presenter.coverCache.loadFromNetwork(thumbnail, manga.thumbnail_url,
|
|
||||||
presenter.getSource().getGlideHeaders());
|
|
||||||
} else {
|
|
||||||
thumbnail.setImageResource(android.R.color.transparent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,32 @@
|
|||||||
|
package eu.kanade.tachiyomi.ui.catalogue;
|
||||||
|
|
||||||
|
import android.support.v4.content.ContextCompat;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
|
import eu.kanade.tachiyomi.R;
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
|
|
||||||
|
public class CatalogueListHolder extends CatalogueHolder {
|
||||||
|
|
||||||
|
@Bind(R.id.title) TextView title;
|
||||||
|
|
||||||
|
private final int favoriteColor;
|
||||||
|
private final int unfavoriteColor;
|
||||||
|
|
||||||
|
public CatalogueListHolder(View view, CatalogueAdapter adapter, OnListItemClickListener listener) {
|
||||||
|
super(view, adapter, listener);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
favoriteColor = ContextCompat.getColor(view.getContext(), R.color.hint_text);
|
||||||
|
unfavoriteColor = ContextCompat.getColor(view.getContext(), R.color.primary_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetValues(Manga manga, CataloguePresenter presenter) {
|
||||||
|
title.setText(manga.title);
|
||||||
|
title.setTextColor(manga.favorite ? favoriteColor : unfavoriteColor);
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.catalogue;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
|
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
|
||||||
|
|
||||||
@ -38,14 +37,16 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
|
|||||||
|
|
||||||
private String query;
|
private String query;
|
||||||
|
|
||||||
private int currentPage;
|
private RxPager<Manga> pager;
|
||||||
private RxPager pager;
|
|
||||||
private MangasPage lastMangasPage;
|
private MangasPage lastMangasPage;
|
||||||
|
|
||||||
private PublishSubject<List<Manga>> mangaDetailSubject;
|
private PublishSubject<List<Manga>> mangaDetailSubject;
|
||||||
|
|
||||||
|
private boolean isListMode;
|
||||||
|
|
||||||
private static final int GET_MANGA_LIST = 1;
|
private static final int GET_MANGA_LIST = 1;
|
||||||
private static final int GET_MANGA_DETAIL = 2;
|
private static final int GET_MANGA_DETAIL = 2;
|
||||||
|
private static final int GET_MANGA_PAGE = 3;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedState) {
|
protected void onCreate(Bundle savedState) {
|
||||||
@ -57,31 +58,46 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
|
|||||||
|
|
||||||
mangaDetailSubject = PublishSubject.create();
|
mangaDetailSubject = PublishSubject.create();
|
||||||
|
|
||||||
|
pager = new RxPager<>();
|
||||||
|
|
||||||
restartableReplay(GET_MANGA_LIST,
|
restartableReplay(GET_MANGA_LIST,
|
||||||
() -> pager.pages().concatMap(page -> getMangasPageObservable(page + 1)),
|
pager::results,
|
||||||
(view, pair) -> view.onAddPage(pair.first, pair.second),
|
(view, pair) -> view.onAddPage(pair.first, pair.second));
|
||||||
(view, error) -> {
|
|
||||||
view.onAddPageError();
|
restartableFirst(GET_MANGA_PAGE,
|
||||||
Timber.e(error.getMessage());
|
() -> pager.request(page -> getMangasPageObservable(page + 1)),
|
||||||
});
|
(view, next) -> {},
|
||||||
|
(view, error) -> view.onAddPageError());
|
||||||
|
|
||||||
restartableLatestCache(GET_MANGA_DETAIL,
|
restartableLatestCache(GET_MANGA_DETAIL,
|
||||||
() -> mangaDetailSubject
|
() -> mangaDetailSubject
|
||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.flatMap(Observable::from)
|
.flatMap(Observable::from)
|
||||||
.filter(manga -> !manga.initialized)
|
.filter(manga -> !manga.initialized)
|
||||||
.window(3)
|
.concatMap(this::getMangaDetails)
|
||||||
.concatMap(pack -> pack.concatMap(this::getMangaDetails))
|
|
||||||
.onBackpressureBuffer()
|
.onBackpressureBuffer()
|
||||||
.observeOn(AndroidSchedulers.mainThread()),
|
.observeOn(AndroidSchedulers.mainThread()),
|
||||||
CatalogueFragment::updateImage,
|
CatalogueFragment::updateImage,
|
||||||
(view, error) -> Timber.e(error.getMessage()));
|
(view, error) -> Timber.e(error.getMessage()));
|
||||||
|
|
||||||
|
add(prefs.catalogueAsList().asObservable()
|
||||||
|
.subscribe(this::setDisplayMode));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onProcessRestart() {
|
private void onProcessRestart() {
|
||||||
source = sourceManager.get(sourceId);
|
source = sourceManager.get(sourceId);
|
||||||
stop(GET_MANGA_LIST);
|
stop(GET_MANGA_LIST);
|
||||||
stop(GET_MANGA_DETAIL);
|
stop(GET_MANGA_DETAIL);
|
||||||
|
stop(GET_MANGA_PAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDisplayMode(boolean asList) {
|
||||||
|
this.isListMode = asList;
|
||||||
|
if (asList) {
|
||||||
|
stop(GET_MANGA_DETAIL);
|
||||||
|
} else {
|
||||||
|
start(GET_MANGA_DETAIL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void startRequesting(Source source) {
|
public void startRequesting(Source source) {
|
||||||
@ -92,20 +108,23 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
|
|||||||
|
|
||||||
public void restartRequest(String query) {
|
public void restartRequest(String query) {
|
||||||
this.query = query;
|
this.query = query;
|
||||||
stop(GET_MANGA_LIST);
|
stop(GET_MANGA_PAGE);
|
||||||
currentPage = 1;
|
lastMangasPage = null;
|
||||||
pager = new RxPager();
|
|
||||||
|
|
||||||
start(GET_MANGA_DETAIL);
|
if (!isListMode) {
|
||||||
|
start(GET_MANGA_DETAIL);
|
||||||
|
}
|
||||||
start(GET_MANGA_LIST);
|
start(GET_MANGA_LIST);
|
||||||
|
start(GET_MANGA_PAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestNext() {
|
public void requestNext() {
|
||||||
if (hasNextPage())
|
if (hasNextPage()) {
|
||||||
pager.requestNext(++currentPage);
|
start(GET_MANGA_PAGE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observable<Pair<Integer, List<Manga>>> getMangasPageObservable(int page) {
|
private Observable<List<Manga>> getMangasPageObservable(int page) {
|
||||||
MangasPage nextMangasPage = new MangasPage(page);
|
MangasPage nextMangasPage = new MangasPage(page);
|
||||||
if (page != 1) {
|
if (page != 1) {
|
||||||
nextMangasPage.url = lastMangasPage.nextPageUrl;
|
nextMangasPage.url = lastMangasPage.nextPageUrl;
|
||||||
@ -120,11 +139,7 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
|
|||||||
.flatMap(mangasPage -> Observable.from(mangasPage.mangas))
|
.flatMap(mangasPage -> Observable.from(mangasPage.mangas))
|
||||||
.map(this::networkToLocalManga)
|
.map(this::networkToLocalManga)
|
||||||
.toList()
|
.toList()
|
||||||
.map(mangas -> Pair.create(page, mangas))
|
.doOnNext(this::initializeMangas)
|
||||||
.doOnNext(pair -> {
|
|
||||||
if (mangaDetailSubject != null)
|
|
||||||
mangaDetailSubject.onNext(pair.second);
|
|
||||||
})
|
|
||||||
.observeOn(AndroidSchedulers.mainThread());
|
.observeOn(AndroidSchedulers.mainThread());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,9 +153,12 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
|
|||||||
return localManga;
|
return localManga;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void initializeMangas(List<Manga> mangas) {
|
||||||
|
mangaDetailSubject.onNext(mangas);
|
||||||
|
}
|
||||||
|
|
||||||
private Observable<Manga> getMangaDetails(final Manga manga) {
|
private Observable<Manga> getMangaDetails(final Manga manga) {
|
||||||
return source.pullMangaFromNetwork(manga.url)
|
return source.pullMangaFromNetwork(manga.url)
|
||||||
.subscribeOn(Schedulers.io())
|
|
||||||
.flatMap(networkManga -> {
|
.flatMap(networkManga -> {
|
||||||
manga.copyFrom(networkManga);
|
manga.copyFrom(networkManga);
|
||||||
db.insertManga(manga).executeAsBlocking();
|
db.insertManga(manga).executeAsBlocking();
|
||||||
@ -170,8 +188,17 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
|
|||||||
return sourceManager.getSources();
|
return sourceManager.getSources();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMangaToLibrary(Manga manga) {
|
public void changeMangaFavorite(Manga manga) {
|
||||||
manga.favorite = true;
|
manga.favorite = !manga.favorite;
|
||||||
db.insertManga(manga).executeAsBlocking();
|
db.insertManga(manga).executeAsBlocking();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isListMode() {
|
||||||
|
return isListMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapDisplayMode() {
|
||||||
|
prefs.catalogueAsList().set(!isListMode);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,7 @@ public class DownloadPresenter extends BasePresenter<DownloadFragment> {
|
|||||||
.flatMap(tick -> Observable.from(download.pages)
|
.flatMap(tick -> Observable.from(download.pages)
|
||||||
.map(Page::getProgress)
|
.map(Page::getProgress)
|
||||||
.reduce((x, y) -> x + y))
|
.reduce((x, y) -> x + y))
|
||||||
|
.onBackpressureLatest()
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.subscribe(progress -> {
|
.subscribe(progress -> {
|
||||||
if (download.totalProgress != progress) {
|
if (download.totalProgress != progress) {
|
||||||
|
@ -52,7 +52,7 @@ public class LibraryCategoryAdapter extends FlexibleAdapter<LibraryHolder, Manga
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LibraryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public LibraryHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View v = LayoutInflater.from(fragment.getActivity()).inflate(R.layout.item_catalogue, parent, false);
|
View v = LayoutInflater.from(fragment.getActivity()).inflate(R.layout.item_catalogue_grid, parent, false);
|
||||||
return new LibraryHolder(v, this, fragment);
|
return new LibraryHolder(v, this, fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ public class LibraryCategoryAdapter extends FlexibleAdapter<LibraryHolder, Manga
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getCoverHeight() {
|
public int getCoverHeight() {
|
||||||
return fragment.recycler.getItemWidth() / 9 * 12;
|
return fragment.recycler.getItemWidth() / 3 * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.ui.library;
|
package eu.kanade.tachiyomi.ui.library;
|
||||||
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@ -17,6 +18,7 @@ import static android.widget.RelativeLayout.LayoutParams;
|
|||||||
|
|
||||||
public class LibraryHolder extends FlexibleViewHolder {
|
public class LibraryHolder extends FlexibleViewHolder {
|
||||||
|
|
||||||
|
@Bind(R.id.image_container) FrameLayout container;
|
||||||
@Bind(R.id.thumbnail) ImageView thumbnail;
|
@Bind(R.id.thumbnail) ImageView thumbnail;
|
||||||
@Bind(R.id.title) TextView title;
|
@Bind(R.id.title) TextView title;
|
||||||
@Bind(R.id.unreadText) TextView unreadText;
|
@Bind(R.id.unreadText) TextView unreadText;
|
||||||
@ -24,7 +26,7 @@ public class LibraryHolder extends FlexibleViewHolder {
|
|||||||
public LibraryHolder(View view, LibraryCategoryAdapter adapter, OnListItemClickListener listener) {
|
public LibraryHolder(View view, LibraryCategoryAdapter adapter, OnListItemClickListener listener) {
|
||||||
super(view, adapter, listener);
|
super(view, adapter, listener);
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
thumbnail.setLayoutParams(new LayoutParams(MATCH_PARENT, adapter.getCoverHeight()));
|
container.setLayoutParams(new LayoutParams(MATCH_PARENT, adapter.getCoverHeight()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSetValues(Manga manga, LibraryPresenter presenter) {
|
public void onSetValues(Manga manga, LibraryPresenter presenter) {
|
||||||
|
@ -58,7 +58,7 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onTakeView(LibraryFragment libraryFragment) {
|
protected void onTakeView(LibraryFragment libraryFragment) {
|
||||||
super.onTakeView(libraryFragment);
|
super.onTakeView(libraryFragment);
|
||||||
if (!isSubscribed(GET_LIBRARY)) {
|
if (isUnsubscribed(GET_LIBRARY)) {
|
||||||
start(GET_LIBRARY);
|
start(GET_LIBRARY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,9 +72,9 @@ public class MainActivity extends BaseActivity {
|
|||||||
new PrimaryDrawerItem()
|
new PrimaryDrawerItem()
|
||||||
.withName(R.string.label_library)
|
.withName(R.string.label_library)
|
||||||
.withIdentifier(R.id.nav_drawer_library),
|
.withIdentifier(R.id.nav_drawer_library),
|
||||||
// new PrimaryDrawerItem()
|
new PrimaryDrawerItem()
|
||||||
// .withName(R.string.label_recent_updates)
|
.withName(R.string.label_recent_updates)
|
||||||
// .withIdentifier(R.id.nav_drawer_recent_updates),
|
.withIdentifier(R.id.nav_drawer_recent_updates),
|
||||||
new PrimaryDrawerItem()
|
new PrimaryDrawerItem()
|
||||||
.withName(R.string.label_catalogues)
|
.withName(R.string.label_catalogues)
|
||||||
.withIdentifier(R.id.nav_drawer_catalogues),
|
.withIdentifier(R.id.nav_drawer_catalogues),
|
||||||
|
@ -14,6 +14,7 @@ import javax.inject.Inject;
|
|||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import de.greenrobot.event.EventBus;
|
||||||
import eu.kanade.tachiyomi.App;
|
import eu.kanade.tachiyomi.App;
|
||||||
import eu.kanade.tachiyomi.R;
|
import eu.kanade.tachiyomi.R;
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
@ -30,21 +31,21 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
|
|||||||
|
|
||||||
@Bind(R.id.toolbar) Toolbar toolbar;
|
@Bind(R.id.toolbar) Toolbar toolbar;
|
||||||
@Bind(R.id.tabs) TabLayout tabs;
|
@Bind(R.id.tabs) TabLayout tabs;
|
||||||
@Bind(R.id.view_pager) ViewPager view_pager;
|
@Bind(R.id.view_pager) ViewPager viewPager;
|
||||||
|
|
||||||
@Inject PreferencesHelper preferences;
|
@Inject PreferencesHelper preferences;
|
||||||
@Inject MangaSyncManager mangaSyncManager;
|
@Inject MangaSyncManager mangaSyncManager;
|
||||||
|
|
||||||
private MangaDetailAdapter adapter;
|
private MangaDetailAdapter adapter;
|
||||||
private long manga_id;
|
private boolean isOnline;
|
||||||
private boolean is_online;
|
|
||||||
|
|
||||||
public final static String MANGA_ID = "manga_id";
|
|
||||||
public final static String MANGA_ONLINE = "manga_online";
|
public final static String MANGA_ONLINE = "manga_online";
|
||||||
|
|
||||||
public static Intent newIntent(Context context, Manga manga) {
|
public static Intent newIntent(Context context, Manga manga) {
|
||||||
Intent intent = new Intent(context, MangaActivity.class);
|
Intent intent = new Intent(context, MangaActivity.class);
|
||||||
intent.putExtra(MANGA_ID, manga.id);
|
if (manga != null) {
|
||||||
|
EventBus.getDefault().postSticky(manga);
|
||||||
|
}
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,23 +60,19 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
|
|||||||
|
|
||||||
Intent intent = getIntent();
|
Intent intent = getIntent();
|
||||||
|
|
||||||
manga_id = intent.getLongExtra(MANGA_ID, -1);
|
isOnline = intent.getBooleanExtra(MANGA_ONLINE, false);
|
||||||
is_online = intent.getBooleanExtra(MANGA_ONLINE, false);
|
|
||||||
|
|
||||||
setupViewPager();
|
setupViewPager();
|
||||||
|
|
||||||
if (savedState == null)
|
|
||||||
getPresenter().queryManga(manga_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupViewPager() {
|
private void setupViewPager() {
|
||||||
adapter = new MangaDetailAdapter(getSupportFragmentManager(), this);
|
adapter = new MangaDetailAdapter(getSupportFragmentManager(), this);
|
||||||
|
|
||||||
view_pager.setAdapter(adapter);
|
viewPager.setAdapter(adapter);
|
||||||
tabs.setupWithViewPager(view_pager);
|
tabs.setupWithViewPager(viewPager);
|
||||||
|
|
||||||
if (!is_online)
|
if (!isOnline)
|
||||||
view_pager.setCurrentItem(MangaDetailAdapter.CHAPTERS_FRAGMENT);
|
viewPager.setCurrentItem(MangaDetailAdapter.CHAPTERS_FRAGMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setManga(Manga manga) {
|
public void setManga(Manga manga) {
|
||||||
@ -83,7 +80,7 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isCatalogueManga() {
|
public boolean isCatalogueManga() {
|
||||||
return is_online;
|
return isOnline;
|
||||||
}
|
}
|
||||||
|
|
||||||
class MangaDetailAdapter extends FragmentPagerAdapter {
|
class MangaDetailAdapter extends FragmentPagerAdapter {
|
||||||
@ -104,7 +101,7 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pageCount = 2;
|
pageCount = 2;
|
||||||
if (!is_online && mangaSyncManager.getMyAnimeList().isLogged())
|
if (!isOnline && mangaSyncManager.getMyAnimeList().isLogged())
|
||||||
pageCount++;
|
pageCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,44 +7,48 @@ import javax.inject.Inject;
|
|||||||
import de.greenrobot.event.EventBus;
|
import de.greenrobot.event.EventBus;
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
|
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||||
|
import eu.kanade.tachiyomi.util.EventBusHook;
|
||||||
import icepick.State;
|
import icepick.State;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
import rx.android.schedulers.AndroidSchedulers;
|
|
||||||
import rx.schedulers.Schedulers;
|
|
||||||
|
|
||||||
public class MangaPresenter extends BasePresenter<MangaActivity> {
|
public class MangaPresenter extends BasePresenter<MangaActivity> {
|
||||||
|
|
||||||
@Inject DatabaseHelper db;
|
@Inject DatabaseHelper db;
|
||||||
|
|
||||||
@State long mangaId;
|
@State Manga manga;
|
||||||
|
|
||||||
private static final int DB_MANGA = 1;
|
private static final int GET_MANGA = 1;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedState) {
|
protected void onCreate(Bundle savedState) {
|
||||||
super.onCreate(savedState);
|
super.onCreate(savedState);
|
||||||
|
|
||||||
restartableLatestCache(DB_MANGA, this::getDbMangaObservable, MangaActivity::setManga);
|
restartableLatestCache(GET_MANGA, this::getMangaObservable, MangaActivity::setManga);
|
||||||
|
|
||||||
|
if (savedState == null)
|
||||||
|
registerForStickyEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
// Avoid new instances receiving wrong manga
|
// Avoid new instances receiving wrong manga
|
||||||
EventBus.getDefault().removeStickyEvent(Manga.class);
|
EventBus.getDefault().removeStickyEvent(MangaEvent.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Observable<Manga> getDbMangaObservable() {
|
private Observable<Manga> getMangaObservable() {
|
||||||
return db.getManga(mangaId).asRxObservable()
|
return Observable.just(manga)
|
||||||
.subscribeOn(Schedulers.io())
|
.doOnNext(manga -> EventBus.getDefault().postSticky(new MangaEvent(manga)));
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
|
||||||
.doOnNext(manga -> EventBus.getDefault().postSticky(manga));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void queryManga(long mangaId) {
|
@EventBusHook
|
||||||
this.mangaId = mangaId;
|
public void onEventMainThread(Manga manga) {
|
||||||
start(DB_MANGA);
|
EventBus.getDefault().removeStickyEvent(manga);
|
||||||
|
unregisterForEvents();
|
||||||
|
this.manga = manga;
|
||||||
|
start(GET_MANGA);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ 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.Chapter;
|
||||||
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadService;
|
import eu.kanade.tachiyomi.data.download.DownloadService;
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download;
|
import eu.kanade.tachiyomi.data.download.model.Download;
|
||||||
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
|
||||||
@ -71,26 +72,14 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
|
|||||||
// Init RecyclerView and adapter
|
// Init RecyclerView and adapter
|
||||||
linearLayout = new LinearLayoutManager(getActivity());
|
linearLayout = new LinearLayoutManager(getActivity());
|
||||||
recyclerView.setLayoutManager(linearLayout);
|
recyclerView.setLayoutManager(linearLayout);
|
||||||
recyclerView.addItemDecoration(new DividerItemDecoration(ContextCompat.getDrawable(getContext(), R.drawable.line_divider)));
|
recyclerView.addItemDecoration(new DividerItemDecoration(
|
||||||
|
ContextCompat.getDrawable(getContext(), R.drawable.line_divider)));
|
||||||
recyclerView.setHasFixedSize(true);
|
recyclerView.setHasFixedSize(true);
|
||||||
adapter = new ChaptersAdapter(this);
|
adapter = new ChaptersAdapter(this);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
// Set initial values
|
|
||||||
setReadFilter();
|
|
||||||
setDownloadedFilter();
|
|
||||||
setSortIcon();
|
|
||||||
|
|
||||||
// Init listeners
|
|
||||||
swipeRefresh.setOnRefreshListener(this::fetchChapters);
|
swipeRefresh.setOnRefreshListener(this::fetchChapters);
|
||||||
readCb.setOnCheckedChangeListener((arg, isChecked) ->
|
|
||||||
getPresenter().setReadFilter(isChecked));
|
|
||||||
downloadedCb.setOnCheckedChangeListener((v, isChecked) ->
|
|
||||||
getPresenter().setDownloadedFilter(isChecked));
|
|
||||||
sortBtn.setOnClickListener(v -> {
|
|
||||||
getPresenter().revertSortOrder();
|
|
||||||
setSortIcon();
|
|
||||||
});
|
|
||||||
nextUnreadBtn.setOnClickListener(v -> {
|
nextUnreadBtn.setOnClickListener(v -> {
|
||||||
Chapter chapter = getPresenter().getNextUnreadChapter();
|
Chapter chapter = getPresenter().getNextUnreadChapter();
|
||||||
if (chapter != null) {
|
if (chapter != null) {
|
||||||
@ -103,16 +92,26 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void onNextManga(Manga manga) {
|
||||||
public void onResume() {
|
// Remove listeners before setting the values
|
||||||
super.onResume();
|
readCb.setOnCheckedChangeListener(null);
|
||||||
observeChapterDownloadProgress();
|
downloadedCb.setOnCheckedChangeListener(null);
|
||||||
}
|
sortBtn.setOnClickListener(null);
|
||||||
|
|
||||||
@Override
|
// Set initial values
|
||||||
public void onPause() {
|
setReadFilter();
|
||||||
unsubscribeChapterDownloadProgress();
|
setDownloadedFilter();
|
||||||
super.onPause();
|
setSortIcon();
|
||||||
|
|
||||||
|
// Init listeners
|
||||||
|
readCb.setOnCheckedChangeListener((arg, isChecked) ->
|
||||||
|
getPresenter().setReadFilter(isChecked));
|
||||||
|
downloadedCb.setOnCheckedChangeListener((v, isChecked) ->
|
||||||
|
getPresenter().setDownloadedFilter(isChecked));
|
||||||
|
sortBtn.setOnClickListener(v -> {
|
||||||
|
getPresenter().revertSortOrder();
|
||||||
|
setSortIcon();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onNextChapters(List<Chapter> chapters) {
|
public void onNextChapters(List<Chapter> chapters) {
|
||||||
@ -175,10 +174,10 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
|
|||||||
holder.onProgressChange(getContext(), download.downloadedImages, download.pages.size());
|
holder.onProgressChange(getContext(), download.downloadedImages, download.pages.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onChapterStatusChange(Chapter chapter) {
|
public void onChapterStatusChange(Download download) {
|
||||||
ChaptersHolder holder = getHolder(chapter);
|
ChaptersHolder holder = getHolder(download.chapter);
|
||||||
if (holder != null)
|
if (holder != null)
|
||||||
holder.onStatusChange(chapter.status);
|
holder.onStatusChange(download.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -31,23 +31,24 @@ public class ChaptersHolder extends FlexibleViewHolder {
|
|||||||
|
|
||||||
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
|
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
|
||||||
|
|
||||||
|
private final int readColor;
|
||||||
|
private final int unreadColor;
|
||||||
|
|
||||||
public ChaptersHolder(View view, ChaptersAdapter adapter, OnListItemClickListener listener) {
|
public ChaptersHolder(View view, ChaptersAdapter adapter, OnListItemClickListener listener) {
|
||||||
super(view, adapter, listener);
|
super(view, adapter, listener);
|
||||||
this.adapter = adapter;
|
this.adapter = adapter;
|
||||||
ButterKnife.bind(this, view);
|
ButterKnife.bind(this, view);
|
||||||
|
|
||||||
|
readColor = ContextCompat.getColor(view.getContext(), R.color.hint_text);
|
||||||
|
unreadColor = ContextCompat.getColor(view.getContext(), R.color.primary_text);
|
||||||
|
|
||||||
chapterMenu.setOnClickListener(v -> v.post(() -> showPopupMenu(v)));
|
chapterMenu.setOnClickListener(v -> v.post(() -> showPopupMenu(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSetValues(Context context, Chapter chapter) {
|
public void onSetValues(Context context, Chapter chapter) {
|
||||||
this.item = chapter;
|
this.item = chapter;
|
||||||
title.setText(chapter.name);
|
title.setText(chapter.name);
|
||||||
|
title.setTextColor(chapter.read ? readColor : unreadColor);
|
||||||
if (chapter.read) {
|
|
||||||
title.setTextColor(ContextCompat.getColor(context, R.color.hint_text));
|
|
||||||
} else {
|
|
||||||
title.setTextColor(ContextCompat.getColor(context, R.color.primary_text));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chapter.read && chapter.last_page_read > 0) {
|
if (!chapter.read && chapter.last_page_read > 0) {
|
||||||
pages.setText(context.getString(R.string.chapter_progress, chapter.last_page_read + 1));
|
pages.setText(context.getString(R.string.chapter_progress, chapter.last_page_read + 1));
|
||||||
|
@ -18,6 +18,7 @@ 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.ChapterCountEvent;
|
import eu.kanade.tachiyomi.event.ChapterCountEvent;
|
||||||
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
|
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
|
||||||
|
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||||
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 eu.kanade.tachiyomi.util.EventBusHook;
|
import eu.kanade.tachiyomi.util.EventBusHook;
|
||||||
@ -38,16 +39,16 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
private Manga manga;
|
private Manga manga;
|
||||||
private Source source;
|
private Source source;
|
||||||
private List<Chapter> chapters;
|
private List<Chapter> chapters;
|
||||||
private boolean sortOrderAToZ = true;
|
|
||||||
private boolean onlyUnread = true;
|
private boolean onlyUnread = true;
|
||||||
private boolean onlyDownloaded;
|
private boolean onlyDownloaded;
|
||||||
@State boolean hasRequested;
|
@State boolean hasRequested;
|
||||||
|
|
||||||
private PublishSubject<List<Chapter>> chaptersSubject;
|
private PublishSubject<List<Chapter>> chaptersSubject;
|
||||||
|
|
||||||
private static final int DB_CHAPTERS = 1;
|
private static final int GET_MANGA = 1;
|
||||||
private static final int FETCH_CHAPTERS = 2;
|
private static final int DB_CHAPTERS = 2;
|
||||||
private static final int CHAPTER_STATUS_CHANGES = 3;
|
private static final int FETCH_CHAPTERS = 3;
|
||||||
|
private static final int CHAPTER_STATUS_CHANGES = 4;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedState) {
|
protected void onCreate(Bundle savedState) {
|
||||||
@ -59,6 +60,10 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
|
|
||||||
chaptersSubject = PublishSubject.create();
|
chaptersSubject = PublishSubject.create();
|
||||||
|
|
||||||
|
restartableLatestCache(GET_MANGA,
|
||||||
|
() -> Observable.just(manga),
|
||||||
|
ChaptersFragment::onNextManga);
|
||||||
|
|
||||||
restartableLatestCache(DB_CHAPTERS,
|
restartableLatestCache(DB_CHAPTERS,
|
||||||
this::getDbChaptersObs,
|
this::getDbChaptersObs,
|
||||||
ChaptersFragment::onNextChapters);
|
ChaptersFragment::onNextChapters);
|
||||||
@ -70,13 +75,14 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
|
|
||||||
restartableLatestCache(CHAPTER_STATUS_CHANGES,
|
restartableLatestCache(CHAPTER_STATUS_CHANGES,
|
||||||
this::getChapterStatusObs,
|
this::getChapterStatusObs,
|
||||||
(view, download) -> view.onChapterStatusChange(download.chapter),
|
(view, download) -> view.onChapterStatusChange(download),
|
||||||
(view, error) -> Timber.e(error.getCause(), error.getMessage()));
|
(view, error) -> Timber.e(error.getCause(), error.getMessage()));
|
||||||
|
|
||||||
registerForStickyEvents();
|
registerForStickyEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onProcessRestart() {
|
private void onProcessRestart() {
|
||||||
|
stop(GET_MANGA);
|
||||||
stop(DB_CHAPTERS);
|
stop(DB_CHAPTERS);
|
||||||
stop(FETCH_CHAPTERS);
|
stop(FETCH_CHAPTERS);
|
||||||
stop(CHAPTER_STATUS_CHANGES);
|
stop(CHAPTER_STATUS_CHANGES);
|
||||||
@ -90,10 +96,11 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventBusHook
|
@EventBusHook
|
||||||
public void onEventMainThread(Manga manga) {
|
public void onEventMainThread(MangaEvent event) {
|
||||||
this.manga = manga;
|
this.manga = event.manga;
|
||||||
|
start(GET_MANGA);
|
||||||
|
|
||||||
if (!isSubscribed(DB_CHAPTERS)) {
|
if (isUnsubscribed(DB_CHAPTERS)) {
|
||||||
source = sourceManager.get(manga.source);
|
source = sourceManager.get(manga.source);
|
||||||
start(DB_CHAPTERS);
|
start(DB_CHAPTERS);
|
||||||
|
|
||||||
@ -141,7 +148,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
if (onlyDownloaded) {
|
if (onlyDownloaded) {
|
||||||
observable = observable.filter(chapter -> chapter.status == Download.DOWNLOADED);
|
observable = observable.filter(chapter -> chapter.status == Download.DOWNLOADED);
|
||||||
}
|
}
|
||||||
return observable.toSortedList((chapter, chapter2) -> sortOrderAToZ ?
|
return observable.toSortedList((chapter, chapter2) -> getSortOrder() ?
|
||||||
Float.compare(chapter2.chapter_number, chapter.chapter_number) :
|
Float.compare(chapter2.chapter_number, chapter.chapter_number) :
|
||||||
Float.compare(chapter.chapter_number, chapter2.chapter_number));
|
Float.compare(chapter.chapter_number, chapter2.chapter_number));
|
||||||
}
|
}
|
||||||
@ -241,8 +248,8 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void revertSortOrder() {
|
public void revertSortOrder() {
|
||||||
//TODO manga.chapter_order
|
manga.setChapterOrder(getSortOrder() ? Manga.SORT_ZA : Manga.SORT_AZ);
|
||||||
sortOrderAToZ = !sortOrderAToZ;
|
db.insertManga(manga).executeAsBlocking();
|
||||||
refreshChapters();
|
refreshChapters();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +265,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean getSortOrder() {
|
public boolean getSortOrder() {
|
||||||
return sortOrderAToZ;
|
return manga.sortChaptersAZ();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getReadFilter() {
|
public boolean getReadFilter() {
|
||||||
|
@ -16,6 +16,7 @@ import butterknife.ButterKnife;
|
|||||||
import eu.kanade.tachiyomi.R;
|
import eu.kanade.tachiyomi.R;
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache;
|
import eu.kanade.tachiyomi.data.cache.CoverCache;
|
||||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||||
|
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment;
|
||||||
import nucleus.factory.RequiresPresenter;
|
import nucleus.factory.RequiresPresenter;
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
|
|||||||
@Bind(R.id.manga_chapters) TextView chapterCount;
|
@Bind(R.id.manga_chapters) TextView chapterCount;
|
||||||
@Bind(R.id.manga_genres) TextView genres;
|
@Bind(R.id.manga_genres) TextView genres;
|
||||||
@Bind(R.id.manga_status) TextView status;
|
@Bind(R.id.manga_status) TextView status;
|
||||||
|
@Bind(R.id.manga_source) TextView source;
|
||||||
@Bind(R.id.manga_summary) TextView description;
|
@Bind(R.id.manga_summary) TextView description;
|
||||||
@Bind(R.id.manga_cover) ImageView cover;
|
@Bind(R.id.manga_cover) ImageView cover;
|
||||||
|
|
||||||
@ -60,18 +62,22 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
|
|||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onNextManga(Manga manga) {
|
public void onNextManga(Manga manga, Source source) {
|
||||||
if (manga.initialized) {
|
if (manga.initialized) {
|
||||||
setMangaInfo(manga);
|
setMangaInfo(manga, source);
|
||||||
} else {
|
} else {
|
||||||
// Initialize manga
|
// Initialize manga
|
||||||
fetchMangaFromSource();
|
fetchMangaFromSource();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMangaInfo(Manga manga) {
|
private void setMangaInfo(Manga manga, Source mangaSource) {
|
||||||
artist.setText(manga.artist);
|
artist.setText(manga.artist);
|
||||||
author.setText(manga.author);
|
author.setText(manga.author);
|
||||||
|
|
||||||
|
if (mangaSource != null) {
|
||||||
|
source.setText(mangaSource.getName());
|
||||||
|
}
|
||||||
genres.setText(manga.genre);
|
genres.setText(manga.genre);
|
||||||
status.setText(manga.getStatus(getActivity()));
|
status.setText(manga.getStatus(getActivity()));
|
||||||
description.setText(manga.description);
|
description.setText(manga.description);
|
||||||
|
@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga;
|
|||||||
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.ChapterCountEvent;
|
import eu.kanade.tachiyomi.event.ChapterCountEvent;
|
||||||
|
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||||
import eu.kanade.tachiyomi.util.EventBusHook;
|
import eu.kanade.tachiyomi.util.EventBusHook;
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
@ -26,8 +27,6 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
|||||||
protected Source source;
|
protected Source source;
|
||||||
private int count = -1;
|
private int count = -1;
|
||||||
|
|
||||||
private boolean isFetching;
|
|
||||||
|
|
||||||
private static final int GET_MANGA = 1;
|
private static final int GET_MANGA = 1;
|
||||||
private static final int GET_CHAPTER_COUNT = 2;
|
private static final int GET_CHAPTER_COUNT = 2;
|
||||||
private static final int FETCH_MANGA_INFO = 3;
|
private static final int FETCH_MANGA_INFO = 3;
|
||||||
@ -42,7 +41,7 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
|||||||
|
|
||||||
restartableLatestCache(GET_MANGA,
|
restartableLatestCache(GET_MANGA,
|
||||||
() -> Observable.just(manga),
|
() -> Observable.just(manga),
|
||||||
MangaInfoFragment::onNextManga);
|
(view, manga) -> view.onNextManga(manga, source));
|
||||||
|
|
||||||
restartableLatestCache(GET_CHAPTER_COUNT,
|
restartableLatestCache(GET_CHAPTER_COUNT,
|
||||||
() -> Observable.just(count),
|
() -> Observable.just(count),
|
||||||
@ -69,10 +68,10 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventBusHook
|
@EventBusHook
|
||||||
public void onEventMainThread(Manga manga) {
|
public void onEventMainThread(MangaEvent event) {
|
||||||
this.manga = manga;
|
this.manga = event.manga;
|
||||||
source = sourceManager.get(manga.source);
|
source = sourceManager.get(manga.source);
|
||||||
start(GET_MANGA);
|
refreshManga();
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventBusHook
|
@EventBusHook
|
||||||
@ -84,8 +83,7 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void fetchMangaFromSource() {
|
public void fetchMangaFromSource() {
|
||||||
if (!isFetching) {
|
if (isUnsubscribed(FETCH_MANGA_INFO)) {
|
||||||
isFetching = true;
|
|
||||||
start(FETCH_MANGA_INFO);
|
start(FETCH_MANGA_INFO);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,15 +95,16 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
|||||||
db.insertManga(manga).executeAsBlocking();
|
db.insertManga(manga).executeAsBlocking();
|
||||||
return Observable.just(manga);
|
return Observable.just(manga);
|
||||||
})
|
})
|
||||||
.finallyDo(() -> isFetching = false)
|
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(AndroidSchedulers.mainThread());
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.doOnNext(manga -> refreshManga());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleFavorite() {
|
public void toggleFavorite() {
|
||||||
manga.favorite = !manga.favorite;
|
manga.favorite = !manga.favorite;
|
||||||
onMangaFavoriteChange(manga.favorite);
|
onMangaFavoriteChange(manga.favorite);
|
||||||
db.insertManga(manga).executeAsBlocking();
|
db.insertManga(manga).executeAsBlocking();
|
||||||
|
refreshManga();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onMangaFavoriteChange(boolean isFavorite) {
|
private void onMangaFavoriteChange(boolean isFavorite) {
|
||||||
@ -116,4 +115,9 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used to refresh the view
|
||||||
|
private void refreshManga() {
|
||||||
|
start(GET_MANGA);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga;
|
|||||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
||||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager;
|
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager;
|
||||||
import eu.kanade.tachiyomi.data.mangasync.services.MyAnimeList;
|
import eu.kanade.tachiyomi.data.mangasync.services.MyAnimeList;
|
||||||
|
import eu.kanade.tachiyomi.event.MangaEvent;
|
||||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
|
||||||
import eu.kanade.tachiyomi.util.EventBusHook;
|
import eu.kanade.tachiyomi.util.EventBusHook;
|
||||||
import eu.kanade.tachiyomi.util.ToastUtil;
|
import eu.kanade.tachiyomi.util.ToastUtil;
|
||||||
@ -102,8 +103,8 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@EventBusHook
|
@EventBusHook
|
||||||
public void onEventMainThread(Manga manga) {
|
public void onEventMainThread(MangaEvent event) {
|
||||||
this.manga = manga;
|
this.manga = event.manga;
|
||||||
start(GET_MANGA_SYNC);
|
start(GET_MANGA_SYNC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,6 +164,10 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onChapterReady(List<Page> pages, Manga manga, Chapter chapter, int currentPage) {
|
public void onChapterReady(List<Page> pages, Manga manga, Chapter chapter, int currentPage) {
|
||||||
|
if (currentPage == -1) {
|
||||||
|
currentPage = pages.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (viewer == null) {
|
if (viewer == null) {
|
||||||
viewer = createViewer(manga);
|
viewer = createViewer(manga);
|
||||||
getSupportFragmentManager().beginTransaction().replace(R.id.reader, viewer).commit();
|
getSupportFragmentManager().beginTransaction().replace(R.id.reader, viewer).commit();
|
||||||
|
@ -215,8 +215,12 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|||||||
.doOnNext(mangaSync -> this.mangaSyncList = mangaSync);
|
.doOnNext(mangaSync -> this.mangaSyncList = mangaSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads the given chapter
|
|
||||||
private void loadChapter(Chapter chapter) {
|
private void loadChapter(Chapter chapter) {
|
||||||
|
loadChapter(chapter, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads the given chapter
|
||||||
|
private void loadChapter(Chapter chapter, int requestedPage) {
|
||||||
// Before loading the chapter, stop preloading (if it's working) and save current progress
|
// Before loading the chapter, stop preloading (if it's working) and save current progress
|
||||||
stopPreloadingNextChapter();
|
stopPreloadingNextChapter();
|
||||||
|
|
||||||
@ -227,7 +231,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|||||||
if (!chapter.read && chapter.last_page_read != 0)
|
if (!chapter.read && chapter.last_page_read != 0)
|
||||||
currentPage = chapter.last_page_read;
|
currentPage = chapter.last_page_read;
|
||||||
else
|
else
|
||||||
currentPage = 0;
|
currentPage = requestedPage;
|
||||||
|
|
||||||
// Reset next and previous chapter. They have to be fetched again
|
// Reset next and previous chapter. They have to be fetched again
|
||||||
nextChapter = null;
|
nextChapter = null;
|
||||||
@ -312,7 +316,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|||||||
public boolean loadNextChapter() {
|
public boolean loadNextChapter() {
|
||||||
if (hasNextChapter()) {
|
if (hasNextChapter()) {
|
||||||
onChapterLeft();
|
onChapterLeft();
|
||||||
loadChapter(nextChapter);
|
loadChapter(nextChapter, 0);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -321,7 +325,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|||||||
public boolean loadPreviousChapter() {
|
public boolean loadPreviousChapter() {
|
||||||
if (hasPreviousChapter()) {
|
if (hasPreviousChapter()) {
|
||||||
onChapterLeft();
|
onChapterLeft();
|
||||||
loadChapter(previousChapter);
|
loadChapter(previousChapter, -1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -342,7 +346,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void stopPreloadingNextChapter() {
|
private void stopPreloadingNextChapter() {
|
||||||
if (isSubscribed(PRELOAD_NEXT_CHAPTER)) {
|
if (!isUnsubscribed(PRELOAD_NEXT_CHAPTER)) {
|
||||||
stop(PRELOAD_NEXT_CHAPTER);
|
stop(PRELOAD_NEXT_CHAPTER);
|
||||||
if (nextChapterPageList != null)
|
if (nextChapterPageList != null)
|
||||||
source.savePageList(nextChapter.url, nextChapterPageList);
|
source.savePageList(nextChapter.url, nextChapterPageList);
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package eu.kanade.tachiyomi.ui.recent;
|
package eu.kanade.tachiyomi.ui.recent;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.text.format.DateFormat;
|
import android.text.format.DateUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -10,6 +10,8 @@ import android.widget.TextView;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
|
import butterknife.ButterKnife;
|
||||||
import eu.davidea.flexibleadapter.FlexibleAdapter;
|
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;
|
||||||
@ -18,8 +20,8 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
|
|||||||
|
|
||||||
private RecentChaptersFragment fragment;
|
private RecentChaptersFragment fragment;
|
||||||
|
|
||||||
private static final int CHAPTER = 0;
|
private static final int VIEW_TYPE_CHAPTER = 0;
|
||||||
private static final int SECTION = 1;
|
private static final int VIEW_TYPE_SECTION = 1;
|
||||||
|
|
||||||
public RecentChaptersAdapter(RecentChaptersFragment fragment) {
|
public RecentChaptersAdapter(RecentChaptersFragment fragment) {
|
||||||
this.fragment = fragment;
|
this.fragment = fragment;
|
||||||
@ -47,7 +49,7 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemViewType(int position) {
|
public int getItemViewType(int position) {
|
||||||
return getItem(position) instanceof MangaChapter ? CHAPTER : SECTION;
|
return getItem(position) instanceof MangaChapter ? VIEW_TYPE_CHAPTER : VIEW_TYPE_SECTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,10 +57,10 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
|
|||||||
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
|
||||||
View v;
|
View v;
|
||||||
switch (viewType) {
|
switch (viewType) {
|
||||||
case 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);
|
||||||
return new RecentChaptersHolder(v, this, fragment);
|
return new RecentChaptersHolder(v, this, fragment);
|
||||||
case SECTION:
|
case VIEW_TYPE_SECTION:
|
||||||
v = inflater.inflate(R.layout.item_recent_chapter_section, parent, false);
|
v = inflater.inflate(R.layout.item_recent_chapter_section, parent, false);
|
||||||
return new SectionViewHolder(v);
|
return new SectionViewHolder(v);
|
||||||
}
|
}
|
||||||
@ -68,11 +70,11 @@ 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) {
|
||||||
switch (holder.getItemViewType()) {
|
switch (holder.getItemViewType()) {
|
||||||
case CHAPTER:
|
case VIEW_TYPE_CHAPTER:
|
||||||
final MangaChapter chapter = (MangaChapter) getItem(position);
|
final MangaChapter chapter = (MangaChapter) getItem(position);
|
||||||
((RecentChaptersHolder) holder).onSetValues(chapter);
|
((RecentChaptersHolder) holder).onSetValues(chapter);
|
||||||
break;
|
break;
|
||||||
case SECTION:
|
case VIEW_TYPE_SECTION:
|
||||||
final Date date = (Date) getItem(position);
|
final Date date = (Date) getItem(position);
|
||||||
((SectionViewHolder) holder).onSetValues(date);
|
((SectionViewHolder) holder).onSetValues(date);
|
||||||
break;
|
break;
|
||||||
@ -86,18 +88,21 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
|
|||||||
return fragment;
|
return fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SectionViewHolder extends RecyclerView.ViewHolder {
|
public static class SectionViewHolder extends RecyclerView.ViewHolder {
|
||||||
|
|
||||||
private TextView view;
|
@Bind(R.id.section_text) TextView section;
|
||||||
|
|
||||||
|
private final long now = new Date().getTime();
|
||||||
|
|
||||||
public SectionViewHolder(View view) {
|
public SectionViewHolder(View view) {
|
||||||
super(view);
|
super(view);
|
||||||
this.view = (TextView) view;
|
ButterKnife.bind(this, view);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onSetValues(Date date) {
|
public void onSetValues(Date date) {
|
||||||
String s = DateFormat.getDateFormat(view.getContext()).format(date);
|
CharSequence s = DateUtils.getRelativeTimeSpanString(
|
||||||
view.setText(s);
|
date.getTime(), now, DateUtils.DAY_IN_MILLIS);
|
||||||
|
section.setText(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,11 @@ public class RecentChaptersPresenter extends BasePresenter<RecentChaptersFragmen
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Observable<List<Object>> getRecentChaptersObservable() {
|
private Observable<List<Object>> getRecentChaptersObservable() {
|
||||||
return db.getRecentChapters().asRxObservable()
|
Calendar cal = Calendar.getInstance();
|
||||||
|
cal.setTime(new Date());
|
||||||
|
cal.add(Calendar.MONTH, -1);
|
||||||
|
|
||||||
|
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(
|
||||||
|
@ -1,32 +1,26 @@
|
|||||||
package eu.kanade.tachiyomi.util;
|
package eu.kanade.tachiyomi.util;
|
||||||
|
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import rx.Observable;
|
import rx.Observable;
|
||||||
|
import rx.functions.Func1;
|
||||||
import rx.subjects.PublishSubject;
|
import rx.subjects.PublishSubject;
|
||||||
|
|
||||||
public class RxPager {
|
public class RxPager<T> {
|
||||||
|
|
||||||
private final int initialPageCount;
|
private final PublishSubject<List<T>> results = PublishSubject.create();
|
||||||
private final PublishSubject<Integer> requests = PublishSubject.create();
|
|
||||||
private int requestedCount;
|
private int requestedCount;
|
||||||
|
|
||||||
public RxPager() {
|
public Observable<Pair<Integer, List<T>>> results() {
|
||||||
this(1);
|
requestedCount = 0;
|
||||||
|
return results.map(list -> Pair.create(requestedCount++, list));
|
||||||
}
|
}
|
||||||
|
|
||||||
public RxPager(int initialPageCount) {
|
public Observable<List<T>> request(Func1<Integer, Observable<List<T>>> networkObservable) {
|
||||||
this.initialPageCount = initialPageCount;
|
return networkObservable.call(requestedCount).doOnNext(results::onNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestNext(int page) {
|
}
|
||||||
requests.onNext(page);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Observable<Integer> pages() {
|
|
||||||
return requests
|
|
||||||
.concatMap(targetPage -> targetPage <= requestedCount ?
|
|
||||||
Observable.<Integer>empty() :
|
|
||||||
Observable.range(requestedCount, targetPage - requestedCount))
|
|
||||||
.startWith(Observable.range(0, initialPageCount))
|
|
||||||
.doOnNext(it -> requestedCount = it + 1);
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,6 +5,10 @@ import java.net.URISyntaxException;
|
|||||||
|
|
||||||
public class UrlUtil {
|
public class UrlUtil {
|
||||||
|
|
||||||
|
private static final String JPG = ".jpg";
|
||||||
|
private static final String PNG = ".png";
|
||||||
|
private static final String GIF = ".gif";
|
||||||
|
|
||||||
public static String getPath(String s) {
|
public static String getPath(String s) {
|
||||||
try {
|
try {
|
||||||
URI uri = new URI(s);
|
URI uri = new URI(s);
|
||||||
@ -18,4 +22,37 @@ public class UrlUtil {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isJpg(String url) {
|
||||||
|
return containsIgnoreCase(url, JPG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isPng(String url) {
|
||||||
|
return containsIgnoreCase(url, PNG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isGif(String url) {
|
||||||
|
return containsIgnoreCase(url, GIF);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean containsIgnoreCase(String src, String what) {
|
||||||
|
final int length = what.length();
|
||||||
|
if (length == 0)
|
||||||
|
return true; // Empty string is contained
|
||||||
|
|
||||||
|
final char firstLo = Character.toLowerCase(what.charAt(0));
|
||||||
|
final char firstUp = Character.toUpperCase(what.charAt(0));
|
||||||
|
|
||||||
|
for (int i = src.length() - length; i >= 0; i--) {
|
||||||
|
// Quick check before calling the more expensive regionMatches() method:
|
||||||
|
final char ch = src.charAt(i);
|
||||||
|
if (ch != firstLo && ch != firstUp)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (src.regionMatches(true, i, what, 0, length))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import android.support.v7.widget.RecyclerView;
|
|||||||
|
|
||||||
import rx.functions.Action0;
|
import rx.functions.Action0;
|
||||||
|
|
||||||
public class EndlessRecyclerScrollListener extends RecyclerView.OnScrollListener {
|
public class EndlessGridScrollListener extends RecyclerView.OnScrollListener {
|
||||||
|
|
||||||
private int previousTotal = 0; // The total number of items in the dataset after the last load
|
private int previousTotal = 0; // The total number of items in the dataset after the last load
|
||||||
private boolean loading = true; // True if we are still waiting for the last set of data to load.
|
private boolean loading = true; // True if we are still waiting for the last set of data to load.
|
||||||
@ -16,7 +16,7 @@ public class EndlessRecyclerScrollListener extends RecyclerView.OnScrollListener
|
|||||||
|
|
||||||
private Action0 requestNext;
|
private Action0 requestNext;
|
||||||
|
|
||||||
public EndlessRecyclerScrollListener(GridLayoutManager layoutManager, Action0 requestNext) {
|
public EndlessGridScrollListener(GridLayoutManager layoutManager, Action0 requestNext) {
|
||||||
this.layoutManager = layoutManager;
|
this.layoutManager = layoutManager;
|
||||||
this.requestNext = requestNext;
|
this.requestNext = requestNext;
|
||||||
}
|
}
|
@ -0,0 +1,49 @@
|
|||||||
|
package eu.kanade.tachiyomi.widget;
|
||||||
|
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
|
||||||
|
import rx.functions.Action0;
|
||||||
|
|
||||||
|
public class EndlessListScrollListener extends RecyclerView.OnScrollListener {
|
||||||
|
|
||||||
|
private int previousTotal = 0; // The total number of items in the dataset after the last load
|
||||||
|
private boolean loading = true; // True if we are still waiting for the last set of data to load.
|
||||||
|
private int visibleThreshold = 5; // The minimum amount of items to have below your current scroll position before loading more.
|
||||||
|
int firstVisibleItem, visibleItemCount, totalItemCount;
|
||||||
|
|
||||||
|
private LinearLayoutManager layoutManager;
|
||||||
|
|
||||||
|
private Action0 requestNext;
|
||||||
|
|
||||||
|
public EndlessListScrollListener(LinearLayoutManager layoutManager, Action0 requestNext) {
|
||||||
|
this.layoutManager = layoutManager;
|
||||||
|
this.requestNext = requestNext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetScroll() {
|
||||||
|
previousTotal = 0;
|
||||||
|
loading = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy);
|
||||||
|
|
||||||
|
visibleItemCount = recyclerView.getChildCount();
|
||||||
|
totalItemCount = layoutManager.getItemCount();
|
||||||
|
firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
|
||||||
|
|
||||||
|
if (loading && (totalItemCount > previousTotal)) {
|
||||||
|
loading = false;
|
||||||
|
previousTotal = totalItemCount;
|
||||||
|
}
|
||||||
|
if (!loading && (totalItemCount - visibleItemCount)
|
||||||
|
<= (firstVisibleItem + visibleThreshold)) {
|
||||||
|
// End has been reached
|
||||||
|
requestNext.call();
|
||||||
|
loading = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
BIN
app/src/main/res/drawable-hdpi/ic_view_list_white_24dp.png
Normal file
After Width: | Height: | Size: 125 B |
BIN
app/src/main/res/drawable-hdpi/ic_view_module_white_24dp.png
Normal file
After Width: | Height: | Size: 115 B |
BIN
app/src/main/res/drawable-ldpi/ic_view_list_white_24dp.png
Normal file
After Width: | Height: | Size: 187 B |
BIN
app/src/main/res/drawable-ldpi/ic_view_module_white_24dp.png
Normal file
After Width: | Height: | Size: 278 B |
BIN
app/src/main/res/drawable-mdpi/ic_view_list_white_24dp.png
Normal file
After Width: | Height: | Size: 89 B |
BIN
app/src/main/res/drawable-mdpi/ic_view_module_white_24dp.png
Normal file
After Width: | Height: | Size: 87 B |
BIN
app/src/main/res/drawable-xhdpi/ic_view_list_white_24dp.png
Normal file
After Width: | Height: | Size: 117 B |
BIN
app/src/main/res/drawable-xhdpi/ic_view_module_white_24dp.png
Normal file
After Width: | Height: | Size: 115 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_view_list_white_24dp.png
Normal file
After Width: | Height: | Size: 144 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_view_module_white_24dp.png
Normal file
After Width: | Height: | Size: 139 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_view_list_white_24dp.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_view_module_white_24dp.png
Normal file
After Width: | Height: | Size: 171 B |
12
app/src/main/res/drawable/gradient_shape.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle" >
|
||||||
|
|
||||||
|
<gradient
|
||||||
|
android:angle="90"
|
||||||
|
android:startColor="#aa000000"
|
||||||
|
android:centerColor="#00000000"
|
||||||
|
android:endColor="#00ffffff"/>
|
||||||
|
|
||||||
|
<corners android:radius="0dp" />
|
||||||
|
</shape>
|
@ -23,6 +23,7 @@
|
|||||||
android:layout_gravity="bottom|right"
|
android:layout_gravity="bottom|right"
|
||||||
android:layout_margin="@dimen/fab_margin"
|
android:layout_margin="@dimen/fab_margin"
|
||||||
android:src="@drawable/ic_action_add_18dp"
|
android:src="@drawable/ic_action_add_18dp"
|
||||||
|
app:backgroundTint="@color/colorPrimary"
|
||||||
app:layout_anchor="@id/categories_list"
|
app:layout_anchor="@id/categories_list"
|
||||||
app:layout_anchorGravity="bottom|right|end"
|
app:layout_anchorGravity="bottom|right|end"
|
||||||
app:layout_behavior="eu.kanade.tachiyomi.ui.base.fab.ScrollAwareFABBehavior"/>
|
app:layout_behavior="eu.kanade.tachiyomi.ui.base.fab.ScrollAwareFABBehavior"/>
|
||||||
|
@ -11,17 +11,28 @@
|
|||||||
android:id="@+id/progress"
|
android:id="@+id/progress"
|
||||||
style="?android:attr/progressBarStyleLarge"
|
style="?android:attr/progressBarStyleLarge"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="fill_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center_vertical|center_horizontal"
|
android:layout_gravity="center_vertical|center_horizontal"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
<ViewSwitcher
|
||||||
android:id="@+id/recycler"
|
android:layout_width="match_parent"
|
||||||
style="@style/AppTheme.GridView"
|
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:columnWidth="140dp"
|
android:id="@+id/switcher">
|
||||||
tools:listitem="@layout/item_catalogue" />
|
|
||||||
|
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
|
||||||
|
android:id="@+id/catalogue_grid"
|
||||||
|
style="@style/AppTheme.GridView"
|
||||||
|
android:columnWidth="140dp"
|
||||||
|
tools:listitem="@layout/item_catalogue_grid" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.RecyclerView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:id="@+id/catalogue_list"/>
|
||||||
|
|
||||||
|
</ViewSwitcher>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress_grid"
|
android:id="@+id/progress_grid"
|
||||||
|
@ -8,6 +8,6 @@
|
|||||||
android:id="@+id/library_mangas"
|
android:id="@+id/library_mangas"
|
||||||
style="@style/AppTheme.GridView"
|
style="@style/AppTheme.GridView"
|
||||||
android:columnWidth="140dp"
|
android:columnWidth="140dp"
|
||||||
tools:listitem="@layout/item_catalogue" />
|
tools:listitem="@layout/item_catalogue_grid" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
@ -154,7 +154,31 @@
|
|||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBaseline="@id/manga_status_label"
|
android:layout_alignBaseline="@id/manga_status_label"
|
||||||
android:layout_toRightOf="@id/manga_chapters_label"
|
android:layout_toRightOf="@id/manga_status_label"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:singleLine="true" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_source_label"
|
||||||
|
style="@style/manga_detail_label"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentLeft="true"
|
||||||
|
android:layout_below="@id/manga_status_label"
|
||||||
|
android:focusable="false"
|
||||||
|
android:focusableInTouchMode="false"
|
||||||
|
android:text="@string/source" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/manga_source"
|
||||||
|
style="@style/manga_detail_text"
|
||||||
|
android:layout_width="fill_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignBaseline="@id/manga_source_label"
|
||||||
|
android:layout_toRightOf="@id/manga_source_label"
|
||||||
android:ellipsize="end"
|
android:ellipsize="end"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
@ -167,7 +191,7 @@
|
|||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_below="@id/manga_status_label"
|
android:layout_below="@id/manga_source_label"
|
||||||
android:focusable="false"
|
android:focusable="false"
|
||||||
android:focusableInTouchMode="false"
|
android:focusableInTouchMode="false"
|
||||||
android:text="@string/genres" />
|
android:text="@string/genres" />
|
||||||
|
@ -12,13 +12,27 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@drawable/card_background">
|
android:background="@drawable/card_background">
|
||||||
|
|
||||||
<ImageView
|
<android.support.percent.PercentFrameLayout
|
||||||
android:id="@+id/thumbnail"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="220dp"
|
android:layout_height="220dp"
|
||||||
android:background="@color/white"
|
android:id="@+id/image_container">
|
||||||
tools:background="@color/md_red_100"
|
|
||||||
tools:src="@mipmap/ic_launcher"/>
|
<ImageView
|
||||||
|
android:id="@+id/thumbnail"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/white"
|
||||||
|
tools:background="@color/md_red_100"
|
||||||
|
tools:src="@mipmap/ic_launcher"/>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_heightPercent="50%"
|
||||||
|
android:layout_gravity="bottom"
|
||||||
|
android:background="@drawable/gradient_shape"/>
|
||||||
|
|
||||||
|
</android.support.percent.PercentFrameLayout>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/unreadText"
|
android:id="@+id/unreadText"
|
||||||
@ -38,39 +52,32 @@
|
|||||||
android:id="@+id/favorite_sticker"
|
android:id="@+id/favorite_sticker"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignEnd="@+id/thumbnail"
|
android:layout_alignEnd="@+id/image_container"
|
||||||
android:layout_alignRight="@+id/thumbnail"
|
android:layout_alignRight="@+id/image_container"
|
||||||
android:layout_alignTop="@+id/thumbnail"
|
android:layout_alignTop="@+id/image_container"
|
||||||
android:layout_marginRight="5dp"
|
android:layout_marginRight="5dp"
|
||||||
android:layout_marginTop="5dp"
|
android:layout_marginTop="5dp"
|
||||||
android:src="@drawable/ic_action_favorite_blue"
|
android:src="@drawable/ic_action_favorite_blue"
|
||||||
android:visibility="invisible"/>
|
android:visibility="invisible"/>
|
||||||
|
|
||||||
<FrameLayout
|
<eu.kanade.tachiyomi.widget.PTSansTextView
|
||||||
|
android:id="@+id/title"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignBottom="@+id/thumbnail"
|
android:layout_gravity="center_vertical"
|
||||||
android:background="@color/manga_cover_title_background">
|
app:typeface="ptsansNarrowBold"
|
||||||
|
android:lineSpacingExtra="-4dp"
|
||||||
<eu.kanade.tachiyomi.widget.PTSansTextView
|
android:ellipsize="end"
|
||||||
android:id="@+id/title"
|
android:maxLines="2"
|
||||||
android:layout_width="match_parent"
|
android:padding="8dp"
|
||||||
android:layout_height="wrap_content"
|
android:textColor="@color/white"
|
||||||
android:layout_gravity="center_vertical"
|
android:textSize="14sp"
|
||||||
app:typeface="ptsansNarrowBold"
|
android:shadowDx="0"
|
||||||
android:lineSpacingExtra="-4dp"
|
android:shadowDy="0"
|
||||||
android:ellipsize="end"
|
android:shadowColor="@color/primary_text"
|
||||||
android:maxLines="2"
|
android:shadowRadius="4"
|
||||||
android:padding="8dp"
|
android:layout_alignBottom="@+id/image_container"
|
||||||
android:textColor="@color/white"
|
tools:text="Sample name"/>
|
||||||
android:textSize="14sp"
|
|
||||||
android:shadowDx="0"
|
|
||||||
android:shadowDy="0"
|
|
||||||
android:shadowColor="@color/primary_text"
|
|
||||||
android:shadowRadius="4"
|
|
||||||
tools:text="Sample name"/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
16
app/src/main/res/layout/item_catalogue_list.xml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<FrameLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?android:listPreferredItemHeightSmall"
|
||||||
|
android:background="@drawable/selector_chapter_light">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:paddingLeft="?android:listPreferredItemPaddingLeft"
|
||||||
|
android:paddingRight="?android:listPreferredItemPaddingLeft"
|
||||||
|
android:id="@+id/title"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
@ -66,7 +66,7 @@
|
|||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_alignWithParentIfMissing="true"
|
android:layout_alignWithParentIfMissing="true"
|
||||||
android:layout_marginRight="30dp"
|
android:layout_marginRight="30dp"
|
||||||
android:ellipsize="end"
|
android:ellipsize="middle"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textSize="17sp"
|
android:textSize="17sp"
|
||||||
|
@ -30,6 +30,8 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
<include layout="@layout/chapter_image"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -38,6 +40,4 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:visibility="gone"/>
|
android:visibility="gone"/>
|
||||||
|
|
||||||
<include layout="@layout/chapter_image"/>
|
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
@ -1,14 +1,26 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
|
||||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_height="48dp"
|
android:layout_width="match_parent"
|
||||||
android:gravity="center_vertical"
|
android:layout_height="32dp"
|
||||||
android:paddingLeft="24dp"
|
android:gravity="center_vertical"
|
||||||
android:singleLine="true"
|
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
|
||||||
android:textAllCaps="true"
|
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
|
||||||
android:textColor="@color/colorAccent"
|
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
|
||||||
android:background="@android:color/transparent"
|
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
|
||||||
android:textSize="16sp"
|
android:background="@color/colorPrimary">
|
||||||
android:id="@+id/section_text"
|
|
||||||
android:textStyle="bold" />
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
android:id="@+id/section_text"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
android:theme="@style/AppTheme.Overlay.Dark"
|
android:theme="@style/AppTheme.Overlay.Dark"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
app:elevation="4dp"
|
|
||||||
app:tabGravity="center"
|
app:tabGravity="center"
|
||||||
app:tabMode="scrollable"
|
app:tabMode="scrollable"
|
||||||
app:tabMinWidth="75dp"
|
app:tabMinWidth="75dp"
|
||||||
|
@ -8,5 +8,4 @@
|
|||||||
android:theme="@style/AppTheme.Overlay.Dark"
|
android:theme="@style/AppTheme.Overlay.Dark"
|
||||||
app:tabGravity="fill"
|
app:tabGravity="fill"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
app:elevation="4dp"
|
|
||||||
app:tabIndicatorColor="@color/white"/>
|
app:tabIndicatorColor="@color/white"/>
|
@ -1,10 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
android:background="@color/colorPrimary"
|
android:background="@color/colorPrimary"
|
||||||
app:elevation="4dp"
|
android:theme="@style/AppTheme.ActionBar"/>
|
||||||
android:theme="@style/AppTheme.ActionBar"
|
|
||||||
app:layout_scrollFlags="scroll|enterAlways|snap"/>
|
|
@ -1,11 +1,16 @@
|
|||||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools" tools:context=".CatalogueListActivity">
|
xmlns:tools="http://schemas.android.com/tools" tools:context=".CatalogueListActivity">
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_search"
|
android:id="@+id/action_search"
|
||||||
android:title="@string/action_search"
|
android:title="@string/action_search"
|
||||||
android:icon="@drawable/ic_action_search"
|
android:icon="@drawable/ic_action_search"
|
||||||
android:orderInCategory="100"
|
|
||||||
app:showAsAction="collapseActionView|ifRoom"
|
app:showAsAction="collapseActionView|ifRoom"
|
||||||
app:actionViewClass="android.support.v7.widget.SearchView"/>
|
app:actionViewClass="android.support.v7.widget.SearchView"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_display_mode"
|
||||||
|
android:title="@string/action_display_mode"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
@ -1,4 +1,14 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
|
||||||
|
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
<item name="alertDialogTheme">@style/AlertDialogStyle</item>
|
||||||
|
<item name="android:textColor">@color/primary_text</item>
|
||||||
|
<item name="windowActionModeOverlay">true</item>
|
||||||
|
<item name="android:navigationBarColor">@color/colorPrimaryDark</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="colorAccent">@color/md_blue_A400</color>
|
<color name="colorAccent">@color/md_blue_A400</color>
|
||||||
<color name="colorPrimary">@color/md_blue_grey_500</color>
|
<color name="colorPrimary">#54759e</color>
|
||||||
<color name="colorPrimaryDark">@color/md_blue_grey_700</color>
|
<color name="colorPrimaryDark">#435e7e</color>
|
||||||
<color name="colorPrimarySuperDark">@color/md_blue_grey_900</color>
|
<color name="colorPrimarySuperDark">@color/md_blue_grey_900</color>
|
||||||
<color name="colorPrimaryLight">@color/md_blue_grey_100</color>
|
<color name="colorPrimaryLight">@color/md_blue_grey_100</color>
|
||||||
|
|
||||||
|
@ -35,4 +35,6 @@
|
|||||||
|
|
||||||
<string name="pref_version">pref_version</string>
|
<string name="pref_version">pref_version</string>
|
||||||
<string name="pref_build_time">pref_build_time</string>
|
<string name="pref_build_time">pref_build_time</string>
|
||||||
|
|
||||||
|
<string name="pref_display_catalogue_as_list">pref_display_catalogue_as_list</string>
|
||||||
</resources>
|
</resources>
|
@ -38,6 +38,7 @@
|
|||||||
<string name="action_previous_chapter">Previous chapter</string>
|
<string name="action_previous_chapter">Previous chapter</string>
|
||||||
<string name="action_next_chapter">Next chapter</string>
|
<string name="action_next_chapter">Next chapter</string>
|
||||||
<string name="action_retry">Retry</string>
|
<string name="action_retry">Retry</string>
|
||||||
|
<string name="action_display_mode">Change display mode</string>
|
||||||
|
|
||||||
|
|
||||||
<!-- Buttons -->
|
<!-- Buttons -->
|
||||||
@ -143,6 +144,7 @@
|
|||||||
<string name="author">Author</string>
|
<string name="author">Author</string>
|
||||||
<string name="chapters">Chapters</string>
|
<string name="chapters">Chapters</string>
|
||||||
<string name="genres">Genres</string>
|
<string name="genres">Genres</string>
|
||||||
|
<string name="source">Source</string>
|
||||||
<string name="artist">Artist</string>
|
<string name="artist">Artist</string>
|
||||||
<string name="description">Description</string>
|
<string name="description">Description</string>
|
||||||
<string name="status">Status</string>
|
<string name="status">Status</string>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<CheckBoxPreference
|
<SwitchPreference
|
||||||
android:key="acra.enable"
|
android:key="acra.enable"
|
||||||
android:title="@string/pref_enable_acra"
|
android:title="@string/pref_enable_acra"
|
||||||
android:summary="@string/pref_acra_summary"
|
android:summary="@string/pref_acra_summary"
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
android:title="@string/pref_download_directory"
|
android:title="@string/pref_download_directory"
|
||||||
android:key="@string/pref_download_directory_key"/>
|
android:key="@string/pref_download_directory_key"/>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<SwitchPreference
|
||||||
android:title="@string/pref_download_only_over_wifi"
|
android:title="@string/pref_download_only_over_wifi"
|
||||||
android:key="@string/pref_download_only_over_wifi_key"
|
android:key="@string/pref_download_only_over_wifi_key"
|
||||||
android:defaultValue="true"/>
|
android:defaultValue="true"/>
|
||||||
|
@ -14,17 +14,17 @@
|
|||||||
android:defaultValue="0"
|
android:defaultValue="0"
|
||||||
android:summary="%s"/>
|
android:summary="%s"/>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<SwitchPreference
|
||||||
android:key="@string/pref_update_only_non_completed_key"
|
android:key="@string/pref_update_only_non_completed_key"
|
||||||
android:title="@string/pref_update_only_non_completed"
|
android:title="@string/pref_update_only_non_completed"
|
||||||
android:defaultValue="false"/>
|
android:defaultValue="false"/>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<SwitchPreference
|
||||||
android:key="@string/pref_auto_update_manga_sync_key"
|
android:key="@string/pref_auto_update_manga_sync_key"
|
||||||
android:title="@string/pref_auto_update_manga_sync"
|
android:title="@string/pref_auto_update_manga_sync"
|
||||||
android:defaultValue="true"/>
|
android:defaultValue="true"/>
|
||||||
|
|
||||||
<CheckBoxPreference
|
<SwitchPreference
|
||||||
android:key="@string/pref_ask_update_manga_sync_key"
|
android:key="@string/pref_ask_update_manga_sync_key"
|
||||||
android:title="@string/pref_ask_update_manga_sync"
|
android:title="@string/pref_ask_update_manga_sync"
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
|
@ -1,26 +1,22 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<CheckBoxPreference android:title="@string/pref_hide_status_bar"
|
<SwitchPreference android:title="@string/pref_hide_status_bar"
|
||||||
android:key="@string/pref_hide_status_bar_key"
|
android:key="@string/pref_hide_status_bar_key"
|
||||||
android:defaultValue="true" />
|
android:defaultValue="true" />
|
||||||
|
|
||||||
<CheckBoxPreference android:title="@string/pref_lock_orientation"
|
<SwitchPreference android:title="@string/pref_lock_orientation"
|
||||||
android:key="@string/pref_lock_orientation_key"
|
android:key="@string/pref_lock_orientation_key"
|
||||||
android:defaultValue="true" />
|
android:defaultValue="true" />
|
||||||
|
|
||||||
<CheckBoxPreference android:title="@string/pref_enable_transitions"
|
<SwitchPreference android:title="@string/pref_enable_transitions"
|
||||||
android:key="@string/pref_enable_transitions_key"
|
android:key="@string/pref_enable_transitions_key"
|
||||||
android:defaultValue="true" />
|
android:defaultValue="true" />
|
||||||
|
|
||||||
<CheckBoxPreference android:title="@string/pref_show_page_number"
|
<SwitchPreference android:title="@string/pref_show_page_number"
|
||||||
android:key="@string/pref_show_page_number_key"
|
android:key="@string/pref_show_page_number_key"
|
||||||
android:defaultValue="true" />
|
android:defaultValue="true" />
|
||||||
|
|
||||||
<CheckBoxPreference android:title="@string/pref_custom_brightness"
|
|
||||||
android:key="@string/pref_custom_brightness_key"
|
|
||||||
android:defaultValue="false" />
|
|
||||||
|
|
||||||
<eu.kanade.tachiyomi.widget.preference.IntListPreference
|
<eu.kanade.tachiyomi.widget.preference.IntListPreference
|
||||||
android:title="@string/pref_viewer_type"
|
android:title="@string/pref_viewer_type"
|
||||||
android:key="@string/pref_default_viewer_key"
|
android:key="@string/pref_default_viewer_key"
|
||||||
|
@ -20,9 +20,6 @@ allprojects {
|
|||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
maven { url "https://clojars.org/repo/" }
|
maven { url "https://clojars.org/repo/" }
|
||||||
maven { url "http://dl.bintray.com/davideas/maven" }
|
|
||||||
maven { url "https://jitpack.io" }
|
maven { url "https://jitpack.io" }
|
||||||
maven { url 'http://dl.bintray.com/amulyakhare/maven' }
|
|
||||||
maven { url 'https://github.com/suckgamony/RapidDecoder/raw/master/repository' }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,9 @@ version = '3.4.1'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
compile "com.android.support:support-annotations:23.1.1"
|
compile "com.android.support:support-annotations:23.1.1"
|
||||||
compile 'rapid.decoder:library:0.3.0'
|
compile 'com.github.suckgamony.RapidDecoder:library:7cdfca4'
|
||||||
compile 'rapid.decoder:jpeg-decoder:0.3.0'
|
compile 'com.github.suckgamony.RapidDecoder:jpeg-decoder:7cdfca4'
|
||||||
compile 'rapid.decoder:png-decoder:0.3.0'
|
compile 'com.github.suckgamony.RapidDecoder:png-decoder:7cdfca4'
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|