Compare commits

...

31 Commits

Author SHA1 Message Date
0a31c223e3 Don't lint release builds 2016-01-25 15:15:47 +01:00
0f42956f3f Update readme 2016-01-25 14:01:37 +01:00
ac2485d4a7 Release 0.1.2 2016-01-25 13:56:58 +01:00
7993ec5074 Make toolbar always visible 2016-01-25 13:54:23 +01:00
4521174138 Fix layout overlapping 2016-01-25 13:43:21 +01:00
27b95e9d73 Minor changes 2016-01-25 13:19:03 +01:00
a54425f47d Merge pull request #69 from icewind1991/info-show-source
Show manga source in info panel
2016-01-25 13:01:44 +01:00
4918e67fda Show manga source in info panel 2016-01-25 12:49:56 +01:00
b174adbab0 Use a gradient at the bottom of the cover. Remove external repositories from gradle 2016-01-24 23:41:21 +01:00
59cc87c583 Fix #58 and #59 2016-01-24 13:57:20 +01:00
0e87dc995a Add backpressure buffer for downloads 2016-01-24 13:23:29 +01:00
fad7b75b96 Place reload button above the image 2016-01-24 12:52:41 +01:00
c99c90fc4c Merge pull request #57 from icewind1991/chapter-list-ellipsize
elipsize chapter list in the middle
2016-01-24 12:42:49 +01:00
594219848d Fix number of simultaneous downloads ignored (again) 2016-01-24 12:37:41 +01:00
fa301bfbd2 elipsize chapter list in the middle 2016-01-24 12:15:43 +01:00
50306f6ea3 Merge pull request #53 from icewind1991/sort-order
save per-manga sort order
2016-01-24 00:10:02 +01:00
9b90ad0a3b save per-manga sort order 2016-01-24 00:01:24 +01:00
5c854984e4 Fix #52 2016-01-23 21:58:36 +01:00
6c844cfd9c Merge pull request #51 from icewind1991/last-page
Load the last page when switching to the previous chapter (Fix #48)
2016-01-23 19:13:21 +01:00
9e666dcdb3 Load the last page when switching to the previous chapter 2016-01-23 17:10:56 +01:00
e81f98a975 Fix an UI refresh issue 2016-01-23 14:17:01 +01:00
11dc0d7e9e Change filename for downloaded chapters, using the last path from the url is not reliable. This will break compatibility with previously downloaded chapters, they have to be deleted and downloaded again.
Disable download progress in the chapters view, it will avoid some crashes.
2016-01-23 13:58:53 +01:00
07ed2e2ebb Hold the same manga instance (allowing to refresh manga state from the catalogue). Other minor changes. 2016-01-22 20:22:16 +01:00
e1aa460106 Allow to display manga from catalogue as a simple list (#35) 2016-01-22 17:37:23 +01:00
75a77566cf Trying switches instead of checkboxes 2016-01-21 16:55:18 +01:00
dd0a2d842a Improve recent chapters layout 2016-01-21 16:38:25 +01:00
fa71e906c9 Change recent chapters query, now it shows last month updates. Download manager now uses a thread pool. 2016-01-21 02:26:34 +01:00
e6a17e25a9 Tint navigation bar on Lollipop and higher 2016-01-20 22:06:22 +01:00
d88513de56 Reenable recent updates tab 2016-01-20 19:43:44 +01:00
ad97d03f1d Change toolbar color (Fix #43). Allow to also remove from library (Fix #44). Rewrite RxPager. 2016-01-20 19:21:17 +01:00
7fc23d526b Update readme 2016-01-20 14:46:05 +01:00
72 changed files with 745 additions and 318 deletions

View File

@ -14,7 +14,7 @@ Current features:
## Download
[![stable release](https://img.shields.io/badge/release-v0.1.0-blue.svg)](https://github.com/inorichi/tachiyomi/releases/download/v0.1.0/tachiyomi-v0.1.0.apk)
[![stable release](https://img.shields.io/badge/release-v0.1.2-blue.svg)](https://github.com/inorichi/tachiyomi/releases/download/v0.1.2/tachiyomi-v0.1.2.apk)
[![latest debug build](https://img.shields.io/badge/debug-latest%20build-blue.svg)](http://tachiyomi.kanade.eu/latest/app-debug.apk)
## License

View File

@ -39,8 +39,8 @@ android {
minSdkVersion 16
targetSdkVersion 23
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
versionCode 2
versionName "0.1.1"
versionCode 3
versionName "0.1.2"
buildConfigField "String", "COMMIT_COUNT", "\"${getCommitCount()}\""
buildConfigField "String", "COMMIT_SHA", "\"${getGitSha()}\""
@ -73,6 +73,7 @@ android {
lintOptions {
abortOnError false
checkReleaseBuilds false
}
}
@ -93,6 +94,7 @@ dependencies {
compile "com.android.support:design:$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:percent:$SUPPORT_LIBRARY_VERSION"
compile 'com.squareup.okhttp:okhttp-urlconnection:2.7.2'
compile 'com.squareup.okhttp:okhttp:2.7.2'
compile 'com.squareup.okio:okio:1.6.0'
@ -114,9 +116,9 @@ dependencies {
compile "frankiesardo:icepick:$ICEPICK_VERSION"
provided "frankiesardo:icepick-processor:$ICEPICK_VERSION"
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.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
compile 'com.github.amulyakhare:TextDrawable:558677e'
compile 'com.github.pwittchen:reactivenetwork:0.1.5'
compile "com.google.dagger:dagger:$DAGGER_VERSION"

View File

@ -162,11 +162,11 @@ public class DatabaseHelper {
.prepare();
}
public PreparedGetListOfObjects<MangaChapter> getRecentChapters() {
public PreparedGetListOfObjects<MangaChapter> getRecentChapters(Date date) {
return db.get()
.listOfObjects(MangaChapter.class)
.withQuery(RawQuery.builder()
.query(MangaChapterGetResolver.RECENT_CHAPTERS_QUERY)
.query(MangaChapterGetResolver.getRecentChaptersQuery(date))
.observesTables(ChapterTable.TABLE)
.build())
.withGetResolver(MangaChapterGetResolver.INSTANCE)

View File

@ -68,6 +68,10 @@ public class Manga implements Serializable {
public static final int COMPLETED = 2;
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 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
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -5,6 +5,8 @@ import android.support.annotation.NonNull;
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.ChapterStorIOSQLiteGetResolver;
import eu.kanade.tachiyomi.data.database.models.Manga;
@ -24,10 +26,12 @@ public class MangaChapterGetResolver extends DefaultGetResolver<MangaChapter> {
MangaTable.COLUMN_ID,
ChapterTable.COLUMN_MANGA_ID);
public static final String RECENT_CHAPTERS_QUERY = String.format(
QUERY + " WHERE %1$s = 1 ORDER BY %2$s DESC LIMIT 100",
MangaTable.COLUMN_FAVORITE,
ChapterTable.COLUMN_DATE_UPLOAD);
public static String getRecentChaptersQuery(Date date) {
return QUERY + String.format(" WHERE %1$s = 1 AND %2$s > %3$d ORDER BY %2$s DESC",
MangaTable.COLUMN_FAVORITE,
ChapterTable.COLUMN_DATE_UPLOAD,
date.getTime());
}
@NonNull
private final MangaStorIOSQLiteGetResolver mangaGetResolver;
@ -45,6 +49,7 @@ public class MangaChapterGetResolver extends DefaultGetResolver<MangaChapter> {
public MangaChapter mapFromCursor(@NonNull Cursor cursor) {
final Manga manga = mangaGetResolver.mapFromCursor(cursor);
final Chapter chapter = chapterGetResolver.mapFromCursor(cursor);
manga.id = chapter.manga_id;
return new MangaChapter(manga, chapter);
}

View File

@ -26,6 +26,8 @@ import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.data.source.model.Page;
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
import eu.kanade.tachiyomi.util.DiskUtils;
import eu.kanade.tachiyomi.util.DynamicConcurrentMergeOperator;
import eu.kanade.tachiyomi.util.UrlUtil;
import rx.Observable;
import rx.Subscription;
import rx.android.schedulers.AndroidSchedulers;
@ -45,6 +47,9 @@ public class DownloadManager {
private BehaviorSubject<Boolean> runningSubject;
private Subscription downloadsSubscription;
private BehaviorSubject<Integer> threadsSubject;
private Subscription threadsSubscription;
private DownloadQueue queue;
private volatile boolean isRunning;
@ -60,15 +65,19 @@ public class DownloadManager {
downloadsQueueSubject = PublishSubject.create();
runningSubject = BehaviorSubject.create();
threadsSubject = BehaviorSubject.create();
}
private void initializeSubscriptions() {
if (downloadsSubscription != null && !downloadsSubscription.isUnsubscribed())
downloadsSubscription.unsubscribe();
threadsSubscription = preferences.downloadThreads().asObservable()
.subscribe(threadsSubject::onNext);
downloadsSubscription = downloadsQueueSubject
.concatMap(downloads -> Observable.from(downloads)
.flatMap(this::downloadChapter, preferences.downloadThreads()))
.flatMap(Observable::from)
.lift(new DynamicConcurrentMergeOperator<>(this::downloadChapter, threadsSubject))
.onBackpressureBuffer()
.observeOn(AndroidSchedulers.mainThread())
.map(download -> areAllDownloadsFinished())
@ -94,6 +103,11 @@ public class DownloadManager {
downloadsSubscription.unsubscribe();
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
@ -181,8 +195,7 @@ public class DownloadManager {
// Or if the page list already exists, start from the file
Observable.just(download.pages);
return pageListObservable
.subscribeOn(Schedulers.io())
return Observable.defer(() -> pageListObservable
.doOnNext(pages -> {
download.downloadedImages = 0;
download.setStatus(Download.DOWNLOADING);
@ -199,7 +212,8 @@ public class DownloadManager {
.onErrorResumeNext(error -> {
download.setStatus(Download.ERROR);
return Observable.just(download);
});
}))
.subscribeOn(Schedulers.io());
}
// 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
private String getImageFilename(Page page) {
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.-]", "_");
}

View File

@ -116,6 +116,10 @@ public class PreferencesHelper {
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) {
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();
}
public int downloadThreads() {
return prefs.getInt(getKey(R.string.pref_download_slots_key), 1);
public Preference<Integer> downloadThreads() {
return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1);
}
public boolean downloadOnlyOverWifi() {

View 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;
}
}

View File

@ -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.
* @return True if the restartable is subscribed, false otherwise.
* @param restartableId id of the restartable.
* @return true if the subscription is null or unsubscribed, false otherwise.
*/
public boolean isSubscribed(int restartableId) {
Subscription s = restartableSubscriptions.get(restartableId);
return s != null && !s.isUnsubscribed();
public boolean isUnsubscribed(int restartableId) {
Subscription subscription = restartableSubscriptions.get(restartableId);
return subscription == null || subscription.isUnsubscribed();
}
/**

View File

@ -31,6 +31,10 @@ public class CatalogueAdapter extends FlexibleAdapter<CatalogueHolder, Manga> {
notifyDataSetChanged();
}
public List<Manga> getItems() {
return mItems;
}
@Override
public long getItemId(int position) {
return mItems.get(position).id;
@ -44,8 +48,13 @@ public class CatalogueAdapter extends FlexibleAdapter<CatalogueHolder, Manga> {
@Override
public CatalogueHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = fragment.getActivity().getLayoutInflater();
View v = inflater.inflate(R.layout.item_catalogue, parent, false);
return new CatalogueHolder(v, this, fragment);
if (parent.getId() == R.id.catalogue_grid) {
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

View File

@ -4,7 +4,10 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
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.Toolbar;
import android.text.TextUtils;
@ -14,9 +17,12 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.AdapterView;
import android.widget.ProgressBar;
import android.widget.Spinner;
import android.widget.ViewSwitcher;
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.ui.base.adapter.FlexibleViewHolder;
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.manga.MangaActivity;
import eu.kanade.tachiyomi.util.ToastUtil;
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 nucleus.factory.RequiresPresenter;
import rx.Subscription;
@ -45,14 +53,17 @@ import rx.subjects.PublishSubject;
public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
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_grid) ProgressBar progressGrid;
private Toolbar toolbar;
private Spinner spinner;
private CatalogueAdapter adapter;
private EndlessRecyclerScrollListener scrollListener;
private EndlessGridScrollListener gridScrollListener;
private EndlessListScrollListener listScrollListener;
@State String query = "";
@State int selectedIndex = -1;
@ -61,6 +72,9 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
private PublishSubject<String> queryDebouncerSubject;
private Subscription queryDebouncerSubscription;
private MenuItem displayMode;
private MenuItem searchItem;
public static CatalogueFragment newInstance() {
return new CatalogueFragment();
}
@ -77,13 +91,32 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
View view = inflater.inflate(R.layout.fragment_catalogue, container, false);
ButterKnife.bind(this, view);
// Initialize adapter and scroll listener
GridLayoutManager layoutManager = (GridLayoutManager) recycler.getLayoutManager();
// Initialize adapter, scroll listener and recycler views
adapter = new CatalogueAdapter(this);
scrollListener = new EndlessRecyclerScrollListener(layoutManager, this::requestNextPage);
recycler.setHasFixedSize(true);
recycler.setAdapter(adapter);
recycler.addOnScrollListener(scrollListener);
GridLayoutManager glm = (GridLayoutManager) catalogueGrid.getLayoutManager();
gridScrollListener = new EndlessGridScrollListener(glm, this::requestNextPage);
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
Context themedContext = getBaseActivity().getSupportActionBar() != null ?
@ -109,6 +142,8 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
} else {
selectedIndex = position;
showProgressBar();
glm.scrollToPositionWithOffset(0, 0);
llm.scrollToPositionWithOffset(0, 0);
getPresenter().startRequesting(source);
}
}
@ -130,7 +165,7 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
inflater.inflate(R.menu.catalogue_list, 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();
if (!TextUtils.isEmpty(query)) {
@ -151,6 +186,22 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
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
@ -167,6 +218,9 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
@Override
public void onDestroyView() {
if (searchItem != null && searchItem.isActionViewExpanded()) {
searchItem.collapseActionView();
}
toolbar.removeView(spinner);
super.onDestroyView();
}
@ -193,11 +247,13 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
private void restartRequest(String newQuery) {
// If text didn't change, do nothing
if (query.equals(newQuery)) return;
if (query.equals(newQuery) || getPresenter().getSource() == null)
return;
query = newQuery;
showProgressBar();
recycler.getLayoutManager().scrollToPosition(0);
catalogueGrid.getLayoutManager().scrollToPosition(0);
catalogueList.getLayoutManager().scrollToPosition(0);
getPresenter().restartRequest(query);
}
@ -211,9 +267,10 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
public void onAddPage(int page, List<Manga> mangas) {
hideProgressBar();
if (page == 1) {
if (page == 0) {
adapter.clear();
scrollListener.resetScroll();
gridScrollListener.resetScroll();
listScrollListener.resetScroll();
}
adapter.addItems(mangas);
}
@ -223,15 +280,28 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
}
public void updateImage(Manga manga) {
CatalogueHolder holder = getHolder(manga);
CatalogueGridHolder holder = getHolder(manga);
if (holder != null) {
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
private CatalogueHolder getHolder(Manga manga) {
return (CatalogueHolder) recycler.findViewHolderForItemId(manga.id);
private CatalogueGridHolder getHolder(Manga manga) {
return (CatalogueGridHolder) catalogueGrid.findViewHolderForItemId(manga.id);
}
private void showProgressBar() {
@ -261,12 +331,15 @@ public class CatalogueFragment extends BaseRxFragment<CataloguePresenter>
public void onListItemLongClick(int 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())
.items(getString(R.string.add_to_library))
.items(getString(textRes))
.itemsCallback((dialog, itemView, which, text) -> {
switch (which) {
case 0:
getPresenter().addMangaToLibrary(selectedManga);
getPresenter().changeMangaFavorite(selectedManga);
adapter.notifyItemChanged(position);
break;
}
})

View File

@ -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);
}
}
}

View File

@ -1,38 +1,15 @@
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;
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
public class CatalogueHolder extends FlexibleViewHolder {
@Bind(R.id.title) TextView title;
@Bind(R.id.thumbnail) ImageView thumbnail;
@Bind(R.id.favorite_sticker) ImageView favoriteSticker;
public abstract class CatalogueHolder extends FlexibleViewHolder {
public CatalogueHolder(View view, CatalogueAdapter adapter, OnListItemClickListener listener) {
super(view, adapter, listener);
ButterKnife.bind(this, view);
}
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);
}
}
}
abstract void onSetValues(Manga manga, CataloguePresenter presenter);
}

View File

@ -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);
}
}

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.ui.catalogue;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Pair;
import com.pushtorefresh.storio.sqlite.operations.put.PutResult;
@ -38,14 +37,16 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
private String query;
private int currentPage;
private RxPager pager;
private RxPager<Manga> pager;
private MangasPage lastMangasPage;
private PublishSubject<List<Manga>> mangaDetailSubject;
private boolean isListMode;
private static final int GET_MANGA_LIST = 1;
private static final int GET_MANGA_DETAIL = 2;
private static final int GET_MANGA_PAGE = 3;
@Override
protected void onCreate(Bundle savedState) {
@ -57,31 +58,46 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
mangaDetailSubject = PublishSubject.create();
pager = new RxPager<>();
restartableReplay(GET_MANGA_LIST,
() -> pager.pages().concatMap(page -> getMangasPageObservable(page + 1)),
(view, pair) -> view.onAddPage(pair.first, pair.second),
(view, error) -> {
view.onAddPageError();
Timber.e(error.getMessage());
});
pager::results,
(view, pair) -> view.onAddPage(pair.first, pair.second));
restartableFirst(GET_MANGA_PAGE,
() -> pager.request(page -> getMangasPageObservable(page + 1)),
(view, next) -> {},
(view, error) -> view.onAddPageError());
restartableLatestCache(GET_MANGA_DETAIL,
() -> mangaDetailSubject
.observeOn(Schedulers.io())
.flatMap(Observable::from)
.filter(manga -> !manga.initialized)
.window(3)
.concatMap(pack -> pack.concatMap(this::getMangaDetails))
.concatMap(this::getMangaDetails)
.onBackpressureBuffer()
.observeOn(AndroidSchedulers.mainThread()),
CatalogueFragment::updateImage,
(view, error) -> Timber.e(error.getMessage()));
add(prefs.catalogueAsList().asObservable()
.subscribe(this::setDisplayMode));
}
private void onProcessRestart() {
source = sourceManager.get(sourceId);
stop(GET_MANGA_LIST);
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) {
@ -92,20 +108,23 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
public void restartRequest(String query) {
this.query = query;
stop(GET_MANGA_LIST);
currentPage = 1;
pager = new RxPager();
stop(GET_MANGA_PAGE);
lastMangasPage = null;
start(GET_MANGA_DETAIL);
if (!isListMode) {
start(GET_MANGA_DETAIL);
}
start(GET_MANGA_LIST);
start(GET_MANGA_PAGE);
}
public void requestNext() {
if (hasNextPage())
pager.requestNext(++currentPage);
if (hasNextPage()) {
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);
if (page != 1) {
nextMangasPage.url = lastMangasPage.nextPageUrl;
@ -120,11 +139,7 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
.flatMap(mangasPage -> Observable.from(mangasPage.mangas))
.map(this::networkToLocalManga)
.toList()
.map(mangas -> Pair.create(page, mangas))
.doOnNext(pair -> {
if (mangaDetailSubject != null)
mangaDetailSubject.onNext(pair.second);
})
.doOnNext(this::initializeMangas)
.observeOn(AndroidSchedulers.mainThread());
}
@ -138,9 +153,12 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
return localManga;
}
public void initializeMangas(List<Manga> mangas) {
mangaDetailSubject.onNext(mangas);
}
private Observable<Manga> getMangaDetails(final Manga manga) {
return source.pullMangaFromNetwork(manga.url)
.subscribeOn(Schedulers.io())
.flatMap(networkManga -> {
manga.copyFrom(networkManga);
db.insertManga(manga).executeAsBlocking();
@ -170,8 +188,17 @@ public class CataloguePresenter extends BasePresenter<CatalogueFragment> {
return sourceManager.getSources();
}
public void addMangaToLibrary(Manga manga) {
manga.favorite = true;
public void changeMangaFavorite(Manga manga) {
manga.favorite = !manga.favorite;
db.insertManga(manga).executeAsBlocking();
}
public boolean isListMode() {
return isListMode;
}
public void swapDisplayMode() {
prefs.catalogueAsList().set(!isListMode);
}
}

View File

@ -90,6 +90,7 @@ public class DownloadPresenter extends BasePresenter<DownloadFragment> {
.flatMap(tick -> Observable.from(download.pages)
.map(Page::getProgress)
.reduce((x, y) -> x + y))
.onBackpressureLatest()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(progress -> {
if (download.totalProgress != progress) {

View File

@ -52,7 +52,7 @@ public class LibraryCategoryAdapter extends FlexibleAdapter<LibraryHolder, Manga
@Override
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);
}
@ -67,7 +67,7 @@ public class LibraryCategoryAdapter extends FlexibleAdapter<LibraryHolder, Manga
}
public int getCoverHeight() {
return fragment.recycler.getItemWidth() / 9 * 12;
return fragment.recycler.getItemWidth() / 3 * 4;
}
@Override

View File

@ -1,6 +1,7 @@
package eu.kanade.tachiyomi.ui.library;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
@ -17,6 +18,7 @@ import static android.widget.RelativeLayout.LayoutParams;
public class LibraryHolder extends FlexibleViewHolder {
@Bind(R.id.image_container) FrameLayout container;
@Bind(R.id.thumbnail) ImageView thumbnail;
@Bind(R.id.title) TextView title;
@Bind(R.id.unreadText) TextView unreadText;
@ -24,7 +26,7 @@ public class LibraryHolder extends FlexibleViewHolder {
public LibraryHolder(View view, LibraryCategoryAdapter adapter, OnListItemClickListener listener) {
super(view, adapter, listener);
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) {

View File

@ -58,7 +58,7 @@ public class LibraryPresenter extends BasePresenter<LibraryFragment> {
@Override
protected void onTakeView(LibraryFragment libraryFragment) {
super.onTakeView(libraryFragment);
if (!isSubscribed(GET_LIBRARY)) {
if (isUnsubscribed(GET_LIBRARY)) {
start(GET_LIBRARY);
}
}

View File

@ -72,9 +72,9 @@ public class MainActivity extends BaseActivity {
new PrimaryDrawerItem()
.withName(R.string.label_library)
.withIdentifier(R.id.nav_drawer_library),
// new PrimaryDrawerItem()
// .withName(R.string.label_recent_updates)
// .withIdentifier(R.id.nav_drawer_recent_updates),
new PrimaryDrawerItem()
.withName(R.string.label_recent_updates)
.withIdentifier(R.id.nav_drawer_recent_updates),
new PrimaryDrawerItem()
.withName(R.string.label_catalogues)
.withIdentifier(R.id.nav_drawer_catalogues),

View File

@ -14,6 +14,7 @@ import javax.inject.Inject;
import butterknife.Bind;
import butterknife.ButterKnife;
import de.greenrobot.event.EventBus;
import eu.kanade.tachiyomi.App;
import eu.kanade.tachiyomi.R;
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.tabs) TabLayout tabs;
@Bind(R.id.view_pager) ViewPager view_pager;
@Bind(R.id.view_pager) ViewPager viewPager;
@Inject PreferencesHelper preferences;
@Inject MangaSyncManager mangaSyncManager;
private MangaDetailAdapter adapter;
private long manga_id;
private boolean is_online;
private boolean isOnline;
public final static String MANGA_ID = "manga_id";
public final static String MANGA_ONLINE = "manga_online";
public static Intent newIntent(Context context, Manga manga) {
Intent intent = new Intent(context, MangaActivity.class);
intent.putExtra(MANGA_ID, manga.id);
if (manga != null) {
EventBus.getDefault().postSticky(manga);
}
return intent;
}
@ -59,23 +60,19 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
Intent intent = getIntent();
manga_id = intent.getLongExtra(MANGA_ID, -1);
is_online = intent.getBooleanExtra(MANGA_ONLINE, false);
isOnline = intent.getBooleanExtra(MANGA_ONLINE, false);
setupViewPager();
if (savedState == null)
getPresenter().queryManga(manga_id);
}
private void setupViewPager() {
adapter = new MangaDetailAdapter(getSupportFragmentManager(), this);
view_pager.setAdapter(adapter);
tabs.setupWithViewPager(view_pager);
viewPager.setAdapter(adapter);
tabs.setupWithViewPager(viewPager);
if (!is_online)
view_pager.setCurrentItem(MangaDetailAdapter.CHAPTERS_FRAGMENT);
if (!isOnline)
viewPager.setCurrentItem(MangaDetailAdapter.CHAPTERS_FRAGMENT);
}
public void setManga(Manga manga) {
@ -83,7 +80,7 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
}
public boolean isCatalogueManga() {
return is_online;
return isOnline;
}
class MangaDetailAdapter extends FragmentPagerAdapter {
@ -104,7 +101,7 @@ public class MangaActivity extends BaseRxActivity<MangaPresenter> {
};
pageCount = 2;
if (!is_online && mangaSyncManager.getMyAnimeList().isLogged())
if (!isOnline && mangaSyncManager.getMyAnimeList().isLogged())
pageCount++;
}

View File

@ -7,44 +7,48 @@ import javax.inject.Inject;
import de.greenrobot.event.EventBus;
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
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.util.EventBusHook;
import icepick.State;
import rx.Observable;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class MangaPresenter extends BasePresenter<MangaActivity> {
@Inject DatabaseHelper db;
@State long mangaId;
@State Manga manga;
private static final int DB_MANGA = 1;
private static final int GET_MANGA = 1;
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
restartableLatestCache(DB_MANGA, this::getDbMangaObservable, MangaActivity::setManga);
restartableLatestCache(GET_MANGA, this::getMangaObservable, MangaActivity::setManga);
if (savedState == null)
registerForStickyEvents();
}
@Override
protected void onDestroy() {
super.onDestroy();
// Avoid new instances receiving wrong manga
EventBus.getDefault().removeStickyEvent(Manga.class);
EventBus.getDefault().removeStickyEvent(MangaEvent.class);
}
private Observable<Manga> getDbMangaObservable() {
return db.getManga(mangaId).asRxObservable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(manga -> EventBus.getDefault().postSticky(manga));
private Observable<Manga> getMangaObservable() {
return Observable.just(manga)
.doOnNext(manga -> EventBus.getDefault().postSticky(new MangaEvent(manga)));
}
public void queryManga(long mangaId) {
this.mangaId = mangaId;
start(DB_MANGA);
@EventBusHook
public void onEventMainThread(Manga manga) {
EventBus.getDefault().removeStickyEvent(manga);
unregisterForEvents();
this.manga = manga;
start(GET_MANGA);
}
}

View File

@ -24,6 +24,7 @@ import butterknife.Bind;
import butterknife.ButterKnife;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.Chapter;
import eu.kanade.tachiyomi.data.database.models.Manga;
import eu.kanade.tachiyomi.data.download.DownloadService;
import eu.kanade.tachiyomi.data.download.model.Download;
import eu.kanade.tachiyomi.ui.base.adapter.FlexibleViewHolder;
@ -71,26 +72,14 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
// Init RecyclerView and adapter
linearLayout = new LinearLayoutManager(getActivity());
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);
adapter = new ChaptersAdapter(this);
recyclerView.setAdapter(adapter);
// Set initial values
setReadFilter();
setDownloadedFilter();
setSortIcon();
// Init listeners
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 -> {
Chapter chapter = getPresenter().getNextUnreadChapter();
if (chapter != null) {
@ -103,16 +92,26 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
return view;
}
@Override
public void onResume() {
super.onResume();
observeChapterDownloadProgress();
}
public void onNextManga(Manga manga) {
// Remove listeners before setting the values
readCb.setOnCheckedChangeListener(null);
downloadedCb.setOnCheckedChangeListener(null);
sortBtn.setOnClickListener(null);
@Override
public void onPause() {
unsubscribeChapterDownloadProgress();
super.onPause();
// Set initial values
setReadFilter();
setDownloadedFilter();
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) {
@ -175,10 +174,10 @@ public class ChaptersFragment extends BaseRxFragment<ChaptersPresenter> implemen
holder.onProgressChange(getContext(), download.downloadedImages, download.pages.size());
}
public void onChapterStatusChange(Chapter chapter) {
ChaptersHolder holder = getHolder(chapter);
public void onChapterStatusChange(Download download) {
ChaptersHolder holder = getHolder(download.chapter);
if (holder != null)
holder.onStatusChange(chapter.status);
holder.onStatusChange(download.getStatus());
}
@Nullable

View File

@ -31,23 +31,24 @@ public class ChaptersHolder extends FlexibleViewHolder {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
private final int readColor;
private final int unreadColor;
public ChaptersHolder(View view, ChaptersAdapter adapter, OnListItemClickListener listener) {
super(view, adapter, listener);
this.adapter = adapter;
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)));
}
public void onSetValues(Context context, Chapter chapter) {
this.item = chapter;
title.setText(chapter.name);
if (chapter.read) {
title.setTextColor(ContextCompat.getColor(context, R.color.hint_text));
} else {
title.setTextColor(ContextCompat.getColor(context, R.color.primary_text));
}
title.setTextColor(chapter.read ? readColor : unreadColor);
if (!chapter.read && chapter.last_page_read > 0) {
pages.setText(context.getString(R.string.chapter_progress, chapter.last_page_read + 1));

View File

@ -18,6 +18,7 @@ import eu.kanade.tachiyomi.data.source.SourceManager;
import eu.kanade.tachiyomi.data.source.base.Source;
import eu.kanade.tachiyomi.event.ChapterCountEvent;
import eu.kanade.tachiyomi.event.DownloadChaptersEvent;
import eu.kanade.tachiyomi.event.MangaEvent;
import eu.kanade.tachiyomi.event.ReaderEvent;
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
import eu.kanade.tachiyomi.util.EventBusHook;
@ -38,16 +39,16 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
private Manga manga;
private Source source;
private List<Chapter> chapters;
private boolean sortOrderAToZ = true;
private boolean onlyUnread = true;
private boolean onlyDownloaded;
@State boolean hasRequested;
private PublishSubject<List<Chapter>> chaptersSubject;
private static final int DB_CHAPTERS = 1;
private static final int FETCH_CHAPTERS = 2;
private static final int CHAPTER_STATUS_CHANGES = 3;
private static final int GET_MANGA = 1;
private static final int DB_CHAPTERS = 2;
private static final int FETCH_CHAPTERS = 3;
private static final int CHAPTER_STATUS_CHANGES = 4;
@Override
protected void onCreate(Bundle savedState) {
@ -59,6 +60,10 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
chaptersSubject = PublishSubject.create();
restartableLatestCache(GET_MANGA,
() -> Observable.just(manga),
ChaptersFragment::onNextManga);
restartableLatestCache(DB_CHAPTERS,
this::getDbChaptersObs,
ChaptersFragment::onNextChapters);
@ -70,13 +75,14 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
restartableLatestCache(CHAPTER_STATUS_CHANGES,
this::getChapterStatusObs,
(view, download) -> view.onChapterStatusChange(download.chapter),
(view, download) -> view.onChapterStatusChange(download),
(view, error) -> Timber.e(error.getCause(), error.getMessage()));
registerForStickyEvents();
}
private void onProcessRestart() {
stop(GET_MANGA);
stop(DB_CHAPTERS);
stop(FETCH_CHAPTERS);
stop(CHAPTER_STATUS_CHANGES);
@ -90,10 +96,11 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
}
@EventBusHook
public void onEventMainThread(Manga manga) {
this.manga = manga;
public void onEventMainThread(MangaEvent event) {
this.manga = event.manga;
start(GET_MANGA);
if (!isSubscribed(DB_CHAPTERS)) {
if (isUnsubscribed(DB_CHAPTERS)) {
source = sourceManager.get(manga.source);
start(DB_CHAPTERS);
@ -141,7 +148,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
if (onlyDownloaded) {
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(chapter.chapter_number, chapter2.chapter_number));
}
@ -241,8 +248,8 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
}
public void revertSortOrder() {
//TODO manga.chapter_order
sortOrderAToZ = !sortOrderAToZ;
manga.setChapterOrder(getSortOrder() ? Manga.SORT_ZA : Manga.SORT_AZ);
db.insertManga(manga).executeAsBlocking();
refreshChapters();
}
@ -258,7 +265,7 @@ public class ChaptersPresenter extends BasePresenter<ChaptersFragment> {
}
public boolean getSortOrder() {
return sortOrderAToZ;
return manga.sortChaptersAZ();
}
public boolean getReadFilter() {

View File

@ -16,6 +16,7 @@ import butterknife.ButterKnife;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.cache.CoverCache;
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 nucleus.factory.RequiresPresenter;
@ -29,6 +30,7 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
@Bind(R.id.manga_chapters) TextView chapterCount;
@Bind(R.id.manga_genres) TextView genres;
@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_cover) ImageView cover;
@ -60,18 +62,22 @@ public class MangaInfoFragment extends BaseRxFragment<MangaInfoPresenter> {
return view;
}
public void onNextManga(Manga manga) {
public void onNextManga(Manga manga, Source source) {
if (manga.initialized) {
setMangaInfo(manga);
setMangaInfo(manga, source);
} else {
// Initialize manga
fetchMangaFromSource();
}
}
private void setMangaInfo(Manga manga) {
private void setMangaInfo(Manga manga, Source mangaSource) {
artist.setText(manga.artist);
author.setText(manga.author);
if (mangaSource != null) {
source.setText(mangaSource.getName());
}
genres.setText(manga.genre);
status.setText(manga.getStatus(getActivity()));
description.setText(manga.description);

View File

@ -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.base.Source;
import eu.kanade.tachiyomi.event.ChapterCountEvent;
import eu.kanade.tachiyomi.event.MangaEvent;
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter;
import eu.kanade.tachiyomi.util.EventBusHook;
import rx.Observable;
@ -26,8 +27,6 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
protected Source source;
private int count = -1;
private boolean isFetching;
private static final int GET_MANGA = 1;
private static final int GET_CHAPTER_COUNT = 2;
private static final int FETCH_MANGA_INFO = 3;
@ -42,7 +41,7 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
restartableLatestCache(GET_MANGA,
() -> Observable.just(manga),
MangaInfoFragment::onNextManga);
(view, manga) -> view.onNextManga(manga, source));
restartableLatestCache(GET_CHAPTER_COUNT,
() -> Observable.just(count),
@ -69,10 +68,10 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
}
@EventBusHook
public void onEventMainThread(Manga manga) {
this.manga = manga;
public void onEventMainThread(MangaEvent event) {
this.manga = event.manga;
source = sourceManager.get(manga.source);
start(GET_MANGA);
refreshManga();
}
@EventBusHook
@ -84,8 +83,7 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
}
public void fetchMangaFromSource() {
if (!isFetching) {
isFetching = true;
if (isUnsubscribed(FETCH_MANGA_INFO)) {
start(FETCH_MANGA_INFO);
}
}
@ -97,15 +95,16 @@ public class MangaInfoPresenter extends BasePresenter<MangaInfoFragment> {
db.insertManga(manga).executeAsBlocking();
return Observable.just(manga);
})
.finallyDo(() -> isFetching = false)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(manga -> refreshManga());
}
public void toggleFavorite() {
manga.favorite = !manga.favorite;
onMangaFavoriteChange(manga.favorite);
db.insertManga(manga).executeAsBlocking();
refreshManga();
}
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);
}
}

View File

@ -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.mangasync.MangaSyncManager;
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.util.EventBusHook;
import eu.kanade.tachiyomi.util.ToastUtil;
@ -102,8 +103,8 @@ public class MyAnimeListPresenter extends BasePresenter<MyAnimeListFragment> {
}
@EventBusHook
public void onEventMainThread(Manga manga) {
this.manga = manga;
public void onEventMainThread(MangaEvent event) {
this.manga = event.manga;
start(GET_MANGA_SYNC);
}

View File

@ -164,6 +164,10 @@ public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
}
public void onChapterReady(List<Page> pages, Manga manga, Chapter chapter, int currentPage) {
if (currentPage == -1) {
currentPage = pages.size() - 1;
}
if (viewer == null) {
viewer = createViewer(manga);
getSupportFragmentManager().beginTransaction().replace(R.id.reader, viewer).commit();

View File

@ -215,8 +215,12 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
.doOnNext(mangaSync -> this.mangaSyncList = mangaSync);
}
// Loads the given 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
stopPreloadingNextChapter();
@ -227,7 +231,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
if (!chapter.read && chapter.last_page_read != 0)
currentPage = chapter.last_page_read;
else
currentPage = 0;
currentPage = requestedPage;
// Reset next and previous chapter. They have to be fetched again
nextChapter = null;
@ -312,7 +316,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
public boolean loadNextChapter() {
if (hasNextChapter()) {
onChapterLeft();
loadChapter(nextChapter);
loadChapter(nextChapter, 0);
return true;
}
return false;
@ -321,7 +325,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
public boolean loadPreviousChapter() {
if (hasPreviousChapter()) {
onChapterLeft();
loadChapter(previousChapter);
loadChapter(previousChapter, -1);
return true;
}
return false;
@ -342,7 +346,7 @@ public class ReaderPresenter extends BasePresenter<ReaderActivity> {
}
private void stopPreloadingNextChapter() {
if (isSubscribed(PRELOAD_NEXT_CHAPTER)) {
if (!isUnsubscribed(PRELOAD_NEXT_CHAPTER)) {
stop(PRELOAD_NEXT_CHAPTER);
if (nextChapterPageList != null)
source.savePageList(nextChapter.url, nextChapterPageList);

View File

@ -1,7 +1,7 @@
package eu.kanade.tachiyomi.ui.recent;
import android.support.v7.widget.RecyclerView;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -10,6 +10,8 @@ import android.widget.TextView;
import java.util.Date;
import java.util.List;
import butterknife.Bind;
import butterknife.ButterKnife;
import eu.davidea.flexibleadapter.FlexibleAdapter;
import eu.kanade.tachiyomi.R;
import eu.kanade.tachiyomi.data.database.models.MangaChapter;
@ -18,8 +20,8 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
private RecentChaptersFragment fragment;
private static final int CHAPTER = 0;
private static final int SECTION = 1;
private static final int VIEW_TYPE_CHAPTER = 0;
private static final int VIEW_TYPE_SECTION = 1;
public RecentChaptersAdapter(RecentChaptersFragment fragment) {
this.fragment = fragment;
@ -47,7 +49,7 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
@Override
public int getItemViewType(int position) {
return getItem(position) instanceof MangaChapter ? CHAPTER : SECTION;
return getItem(position) instanceof MangaChapter ? VIEW_TYPE_CHAPTER : VIEW_TYPE_SECTION;
}
@Override
@ -55,10 +57,10 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View v;
switch (viewType) {
case CHAPTER:
case VIEW_TYPE_CHAPTER:
v = inflater.inflate(R.layout.item_recent_chapter, parent, false);
return new RecentChaptersHolder(v, this, fragment);
case SECTION:
case VIEW_TYPE_SECTION:
v = inflater.inflate(R.layout.item_recent_chapter_section, parent, false);
return new SectionViewHolder(v);
}
@ -68,11 +70,11 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case CHAPTER:
case VIEW_TYPE_CHAPTER:
final MangaChapter chapter = (MangaChapter) getItem(position);
((RecentChaptersHolder) holder).onSetValues(chapter);
break;
case SECTION:
case VIEW_TYPE_SECTION:
final Date date = (Date) getItem(position);
((SectionViewHolder) holder).onSetValues(date);
break;
@ -86,18 +88,21 @@ public class RecentChaptersAdapter extends FlexibleAdapter<RecyclerView.ViewHold
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) {
super(view);
this.view = (TextView) view;
ButterKnife.bind(this, view);
}
public void onSetValues(Date date) {
String s = DateFormat.getDateFormat(view.getContext()).format(date);
view.setText(s);
CharSequence s = DateUtils.getRelativeTimeSpanString(
date.getTime(), now, DateUtils.DAY_IN_MILLIS);
section.setText(s);
}
}
}

View File

@ -42,7 +42,11 @@ public class RecentChaptersPresenter extends BasePresenter<RecentChaptersFragmen
}
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
.flatMap(recents -> Observable.from(recents)
.toMultimap(

View File

@ -1,32 +1,26 @@
package eu.kanade.tachiyomi.util;
import android.util.Pair;
import java.util.List;
import rx.Observable;
import rx.functions.Func1;
import rx.subjects.PublishSubject;
public class RxPager {
public class RxPager<T> {
private final int initialPageCount;
private final PublishSubject<Integer> requests = PublishSubject.create();
private final PublishSubject<List<T>> results = PublishSubject.create();
private int requestedCount;
public RxPager() {
this(1);
public Observable<Pair<Integer, List<T>>> results() {
requestedCount = 0;
return results.map(list -> Pair.create(requestedCount++, list));
}
public RxPager(int initialPageCount) {
this.initialPageCount = initialPageCount;
public Observable<List<T>> request(Func1<Integer, Observable<List<T>>> networkObservable) {
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);
}
}

View File

@ -5,6 +5,10 @@ import java.net.URISyntaxException;
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) {
try {
URI uri = new URI(s);
@ -18,4 +22,37 @@ public class UrlUtil {
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;
}
}

View File

@ -5,7 +5,7 @@ import android.support.v7.widget.RecyclerView;
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 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;
public EndlessRecyclerScrollListener(GridLayoutManager layoutManager, Action0 requestNext) {
public EndlessGridScrollListener(GridLayoutManager layoutManager, Action0 requestNext) {
this.layoutManager = layoutManager;
this.requestNext = requestNext;
}

View File

@ -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;
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 278 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

View 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>

View File

@ -23,6 +23,7 @@
android:layout_gravity="bottom|right"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_action_add_18dp"
app:backgroundTint="@color/colorPrimary"
app:layout_anchor="@id/categories_list"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="eu.kanade.tachiyomi.ui.base.fab.ScrollAwareFABBehavior"/>

View File

@ -11,17 +11,28 @@
android:id="@+id/progress"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|center_horizontal"
android:visibility="gone"/>
<eu.kanade.tachiyomi.widget.AutofitRecyclerView
android:id="@+id/recycler"
style="@style/AppTheme.GridView"
<ViewSwitcher
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:columnWidth="140dp"
tools:listitem="@layout/item_catalogue" />
android:id="@+id/switcher">
<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
android:id="@+id/progress_grid"

View File

@ -8,6 +8,6 @@
android:id="@+id/library_mangas"
style="@style/AppTheme.GridView"
android:columnWidth="140dp"
tools:listitem="@layout/item_catalogue" />
tools:listitem="@layout/item_catalogue_grid" />
</FrameLayout>

View File

@ -154,7 +154,31 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
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:focusable="false"
android:focusableInTouchMode="false"
@ -167,7 +191,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@id/manga_status_label"
android:layout_below="@id/manga_source_label"
android:focusable="false"
android:focusableInTouchMode="false"
android:text="@string/genres" />

View File

@ -12,13 +12,27 @@
android:layout_height="wrap_content"
android:background="@drawable/card_background">
<ImageView
android:id="@+id/thumbnail"
android:layout_width="match_parent"
<android.support.percent.PercentFrameLayout
android:layout_width="wrap_content"
android:layout_height="220dp"
android:background="@color/white"
tools:background="@color/md_red_100"
tools:src="@mipmap/ic_launcher"/>
android:id="@+id/image_container">
<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
android:id="@+id/unreadText"
@ -38,39 +52,32 @@
android:id="@+id/favorite_sticker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/thumbnail"
android:layout_alignRight="@+id/thumbnail"
android:layout_alignTop="@+id/thumbnail"
android:layout_alignEnd="@+id/image_container"
android:layout_alignRight="@+id/image_container"
android:layout_alignTop="@+id/image_container"
android:layout_marginRight="5dp"
android:layout_marginTop="5dp"
android:src="@drawable/ic_action_favorite_blue"
android:visibility="invisible"/>
<FrameLayout
<eu.kanade.tachiyomi.widget.PTSansTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/thumbnail"
android:background="@color/manga_cover_title_background">
<eu.kanade.tachiyomi.widget.PTSansTextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
app:typeface="ptsansNarrowBold"
android:lineSpacingExtra="-4dp"
android:ellipsize="end"
android:maxLines="2"
android:padding="8dp"
android:textColor="@color/white"
android:textSize="14sp"
android:shadowDx="0"
android:shadowDy="0"
android:shadowColor="@color/primary_text"
android:shadowRadius="4"
tools:text="Sample name"/>
</FrameLayout>
android:layout_gravity="center_vertical"
app:typeface="ptsansNarrowBold"
android:lineSpacingExtra="-4dp"
android:ellipsize="end"
android:maxLines="2"
android:padding="8dp"
android:textColor="@color/white"
android:textSize="14sp"
android:shadowDx="0"
android:shadowDy="0"
android:shadowColor="@color/primary_text"
android:shadowRadius="4"
android:layout_alignBottom="@+id/image_container"
tools:text="Sample name"/>
</RelativeLayout>

View 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>

View File

@ -66,7 +66,7 @@
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:layout_marginRight="30dp"
android:ellipsize="end"
android:ellipsize="middle"
android:gravity="center_vertical"
android:maxLines="1"
android:textSize="17sp"

View File

@ -30,6 +30,8 @@
</LinearLayout>
<include layout="@layout/chapter_image"/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -38,6 +40,4 @@
android:layout_gravity="center"
android:visibility="gone"/>
<include layout="@layout/chapter_image"/>
</FrameLayout>

View File

@ -1,14 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="48dp"
android:gravity="center_vertical"
android:paddingLeft="24dp"
android:singleLine="true"
android:textAllCaps="true"
android:textColor="@color/colorAccent"
android:background="@android:color/transparent"
android:textSize="16sp"
android:id="@+id/section_text"
android:textStyle="bold" />
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="32dp"
android:gravity="center_vertical"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:paddingLeft="?android:attr/listPreferredItemPaddingLeft"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:background="@color/colorPrimary">
<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>

View File

@ -8,7 +8,6 @@
android:theme="@style/AppTheme.Overlay.Dark"
android:background="@color/colorPrimary"
android:visibility="gone"
app:elevation="4dp"
app:tabGravity="center"
app:tabMode="scrollable"
app:tabMinWidth="75dp"

View File

@ -8,5 +8,4 @@
android:theme="@style/AppTheme.Overlay.Dark"
app:tabGravity="fill"
android:background="@color/colorPrimary"
app:elevation="4dp"
app:tabIndicatorColor="@color/white"/>

View File

@ -1,10 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/colorPrimary"
app:elevation="4dp"
android:theme="@style/AppTheme.ActionBar"
app:layout_scrollFlags="scroll|enterAlways|snap"/>
android:theme="@style/AppTheme.ActionBar"/>

View File

@ -1,11 +1,16 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" tools:context=".CatalogueListActivity">
<item
android:id="@+id/action_search"
android:title="@string/action_search"
android:icon="@drawable/ic_action_search"
android:orderInCategory="100"
app:showAsAction="collapseActionView|ifRoom"
app:actionViewClass="android.support.v7.widget.SearchView"/>
<item
android:id="@+id/action_display_mode"
android:title="@string/action_display_mode"
app:showAsAction="ifRoom"/>
</menu>

View File

@ -1,4 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<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>

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorAccent">@color/md_blue_A400</color>
<color name="colorPrimary">@color/md_blue_grey_500</color>
<color name="colorPrimaryDark">@color/md_blue_grey_700</color>
<color name="colorPrimary">#54759e</color>
<color name="colorPrimaryDark">#435e7e</color>
<color name="colorPrimarySuperDark">@color/md_blue_grey_900</color>
<color name="colorPrimaryLight">@color/md_blue_grey_100</color>

View File

@ -35,4 +35,6 @@
<string name="pref_version">pref_version</string>
<string name="pref_build_time">pref_build_time</string>
<string name="pref_display_catalogue_as_list">pref_display_catalogue_as_list</string>
</resources>

View File

@ -38,6 +38,7 @@
<string name="action_previous_chapter">Previous chapter</string>
<string name="action_next_chapter">Next chapter</string>
<string name="action_retry">Retry</string>
<string name="action_display_mode">Change display mode</string>
<!-- Buttons -->
@ -143,6 +144,7 @@
<string name="author">Author</string>
<string name="chapters">Chapters</string>
<string name="genres">Genres</string>
<string name="source">Source</string>
<string name="artist">Artist</string>
<string name="description">Description</string>
<string name="status">Status</string>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<CheckBoxPreference
<SwitchPreference
android:key="acra.enable"
android:title="@string/pref_enable_acra"
android:summary="@string/pref_acra_summary"

View File

@ -5,7 +5,7 @@
android:title="@string/pref_download_directory"
android:key="@string/pref_download_directory_key"/>
<CheckBoxPreference
<SwitchPreference
android:title="@string/pref_download_only_over_wifi"
android:key="@string/pref_download_only_over_wifi_key"
android:defaultValue="true"/>

View File

@ -14,17 +14,17 @@
android:defaultValue="0"
android:summary="%s"/>
<CheckBoxPreference
<SwitchPreference
android:key="@string/pref_update_only_non_completed_key"
android:title="@string/pref_update_only_non_completed"
android:defaultValue="false"/>
<CheckBoxPreference
<SwitchPreference
android:key="@string/pref_auto_update_manga_sync_key"
android:title="@string/pref_auto_update_manga_sync"
android:defaultValue="true"/>
<CheckBoxPreference
<SwitchPreference
android:key="@string/pref_ask_update_manga_sync_key"
android:title="@string/pref_ask_update_manga_sync"
android:defaultValue="false"

View File

@ -1,26 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<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:defaultValue="true" />
<CheckBoxPreference android:title="@string/pref_lock_orientation"
<SwitchPreference android:title="@string/pref_lock_orientation"
android:key="@string/pref_lock_orientation_key"
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: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: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
android:title="@string/pref_viewer_type"
android:key="@string/pref_default_viewer_key"

View File

@ -20,9 +20,6 @@ allprojects {
repositories {
jcenter()
maven { url "https://clojars.org/repo/" }
maven { url "http://dl.bintray.com/davideas/maven" }
maven { url "https://jitpack.io" }
maven { url 'http://dl.bintray.com/amulyakhare/maven' }
maven { url 'https://github.com/suckgamony/RapidDecoder/raw/master/repository' }
}
}

View File

@ -6,9 +6,9 @@ version = '3.4.1'
dependencies {
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:support-annotations:23.1.1"
compile 'rapid.decoder:library:0.3.0'
compile 'rapid.decoder:jpeg-decoder:0.3.0'
compile 'rapid.decoder:png-decoder:0.3.0'
compile 'com.github.suckgamony.RapidDecoder:library:7cdfca4'
compile 'com.github.suckgamony.RapidDecoder:jpeg-decoder:7cdfca4'
compile 'com.github.suckgamony.RapidDecoder:png-decoder:7cdfca4'
}
android {