Rename project
This commit is contained in:
@@ -0,0 +1,358 @@
|
||||
package eu.kanade.tachiyomi.ui.reader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.App;
|
||||
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.preference.PreferencesHelper;
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.LeftToRightReader;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal.RightToLeftReader;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical.VerticalReader;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonReader;
|
||||
import eu.kanade.tachiyomi.util.GLUtil;
|
||||
import eu.kanade.tachiyomi.util.ToastUtil;
|
||||
import icepick.Icepick;
|
||||
import nucleus.factory.RequiresPresenter;
|
||||
import rx.Subscription;
|
||||
import rx.subscriptions.CompositeSubscription;
|
||||
|
||||
@RequiresPresenter(ReaderPresenter.class)
|
||||
public class ReaderActivity extends BaseRxActivity<ReaderPresenter> {
|
||||
|
||||
@Bind(R.id.page_number) TextView pageNumber;
|
||||
@Bind(R.id.toolbar) Toolbar toolbar;
|
||||
|
||||
@Inject PreferencesHelper preferences;
|
||||
|
||||
private BaseReader viewer;
|
||||
private ReaderMenu readerMenu;
|
||||
|
||||
private int uiFlags;
|
||||
private int readerTheme;
|
||||
protected CompositeSubscription subscriptions;
|
||||
private Subscription customBrightnessSubscription;
|
||||
|
||||
private int maxBitmapSize;
|
||||
|
||||
public static final int LEFT_TO_RIGHT = 1;
|
||||
public static final int RIGHT_TO_LEFT = 2;
|
||||
public static final int VERTICAL = 3;
|
||||
public static final int WEBTOON = 4;
|
||||
|
||||
public static final int BLACK_THEME = 1;
|
||||
|
||||
public static Intent newIntent(Context context) {
|
||||
return new Intent(context, ReaderActivity.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
App.get(this).getComponent().inject(this);
|
||||
setContentView(R.layout.activity_reader);
|
||||
ButterKnife.bind(this);
|
||||
|
||||
setupToolbar(toolbar);
|
||||
subscriptions = new CompositeSubscription();
|
||||
|
||||
readerMenu = new ReaderMenu(this);
|
||||
Icepick.restoreInstanceState(readerMenu, savedState);
|
||||
if (savedState != null && readerMenu.showing)
|
||||
readerMenu.show(false);
|
||||
|
||||
initializeSettings();
|
||||
|
||||
maxBitmapSize = GLUtil.getMaxTextureSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
setSystemUiVisibility();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
if (viewer != null)
|
||||
getPresenter().setCurrentPage(viewer.getCurrentPage());
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
subscriptions.unsubscribe();
|
||||
viewer = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
return readerMenu.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
return readerMenu.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
Icepick.saveInstanceState(readerMenu, outState);
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (viewer != null)
|
||||
getPresenter().setCurrentPage(viewer.getCurrentPage());
|
||||
getPresenter().onChapterLeft();
|
||||
|
||||
int chapterToUpdate = getPresenter().getMangaSyncChapterToUpdate();
|
||||
|
||||
if (chapterToUpdate > 0) {
|
||||
if (getPresenter().prefs.askUpdateMangaSync()) {
|
||||
new MaterialDialog.Builder(this)
|
||||
.content(getString(R.string.confirm_update_manga_sync, chapterToUpdate))
|
||||
.positiveText(R.string.button_yes)
|
||||
.negativeText(R.string.button_no)
|
||||
.onPositive((dialog, which) -> {
|
||||
getPresenter().updateMangaSyncLastChapterRead();
|
||||
})
|
||||
.onAny((dialog1, which1) -> {
|
||||
finish();
|
||||
})
|
||||
.show();
|
||||
} else {
|
||||
getPresenter().updateMangaSyncLastChapterRead();
|
||||
finish();
|
||||
}
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
public void onChapterError() {
|
||||
finish();
|
||||
ToastUtil.showShort(this, R.string.page_list_error);
|
||||
}
|
||||
|
||||
public void onChapterReady(List<Page> pages, Manga manga, Chapter chapter, int currentPage) {
|
||||
if (viewer == null) {
|
||||
viewer = createViewer(manga);
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.reader, viewer).commit();
|
||||
}
|
||||
viewer.onPageListReady(pages, currentPage);
|
||||
readerMenu.onChapterReady(pages.size(), manga, chapter, currentPage);
|
||||
}
|
||||
|
||||
public void onAdjacentChapters(Chapter previous, Chapter next) {
|
||||
readerMenu.onAdjacentChapters(previous, next);
|
||||
}
|
||||
|
||||
private BaseReader createViewer(Manga manga) {
|
||||
int mangaViewer = manga.viewer == 0 ? preferences.getDefaultViewer() : manga.viewer;
|
||||
|
||||
switch (mangaViewer) {
|
||||
case LEFT_TO_RIGHT: default:
|
||||
return new LeftToRightReader();
|
||||
case RIGHT_TO_LEFT:
|
||||
return new RightToLeftReader();
|
||||
case VERTICAL:
|
||||
return new VerticalReader();
|
||||
case WEBTOON:
|
||||
return new WebtoonReader();
|
||||
}
|
||||
}
|
||||
|
||||
public void onPageChanged(int currentPageIndex, int totalPages) {
|
||||
String page = (currentPageIndex + 1) + "/" + totalPages;
|
||||
pageNumber.setText(page);
|
||||
readerMenu.onPageChanged(currentPageIndex);
|
||||
}
|
||||
|
||||
public void setSelectedPage(int pageIndex) {
|
||||
viewer.setSelectedPage(pageIndex);
|
||||
}
|
||||
|
||||
public void onCenterSingleTap() {
|
||||
readerMenu.toggle();
|
||||
}
|
||||
|
||||
public void requestNextChapter() {
|
||||
getPresenter().setCurrentPage(viewer != null ? viewer.getCurrentPage() : 0);
|
||||
if (!getPresenter().loadNextChapter()) {
|
||||
ToastUtil.showShort(this, R.string.no_next_chapter);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void requestPreviousChapter() {
|
||||
getPresenter().setCurrentPage(viewer != null ? viewer.getCurrentPage() : 0);
|
||||
if (!getPresenter().loadPreviousChapter()) {
|
||||
ToastUtil.showShort(this, R.string.no_previous_chapter);
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeSettings() {
|
||||
subscriptions.add(preferences.showPageNumber()
|
||||
.asObservable()
|
||||
.subscribe(this::setPageNumberVisibility));
|
||||
|
||||
subscriptions.add(preferences.lockOrientation()
|
||||
.asObservable()
|
||||
.subscribe(this::setOrientation));
|
||||
|
||||
subscriptions.add(preferences.hideStatusBar()
|
||||
.asObservable()
|
||||
.subscribe(this::setStatusBarVisibility));
|
||||
|
||||
subscriptions.add(preferences.keepScreenOn()
|
||||
.asObservable()
|
||||
.subscribe(this::setKeepScreenOn));
|
||||
|
||||
subscriptions.add(preferences.customBrightness()
|
||||
.asObservable()
|
||||
.subscribe(this::setCustomBrightness));
|
||||
|
||||
subscriptions.add(preferences.readerTheme()
|
||||
.asObservable()
|
||||
.distinctUntilChanged()
|
||||
.subscribe(this::applyTheme));
|
||||
}
|
||||
|
||||
private void setOrientation(boolean locked) {
|
||||
if (locked) {
|
||||
int orientation;
|
||||
int rotation = ((WindowManager) getSystemService(
|
||||
Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
|
||||
switch (rotation) {
|
||||
case Surface.ROTATION_0:
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
|
||||
break;
|
||||
default:
|
||||
orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
|
||||
break;
|
||||
}
|
||||
setRequestedOrientation(orientation);
|
||||
} else {
|
||||
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
|
||||
}
|
||||
}
|
||||
|
||||
private void setPageNumberVisibility(boolean visible) {
|
||||
pageNumber.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
|
||||
}
|
||||
|
||||
private void setKeepScreenOn(boolean enabled) {
|
||||
if (enabled) {
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
} else {
|
||||
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCustomBrightness(boolean enabled) {
|
||||
if (enabled) {
|
||||
subscriptions.add(customBrightnessSubscription = preferences.customBrightnessValue()
|
||||
.asObservable()
|
||||
.subscribe(this::setCustomBrightnessValue));
|
||||
} else {
|
||||
if (customBrightnessSubscription != null)
|
||||
subscriptions.remove(customBrightnessSubscription);
|
||||
setCustomBrightnessValue(-1);
|
||||
}
|
||||
}
|
||||
|
||||
private void setCustomBrightnessValue(float value) {
|
||||
WindowManager.LayoutParams layout = getWindow().getAttributes();
|
||||
layout.screenBrightness = value;
|
||||
getWindow().setAttributes(layout);
|
||||
}
|
||||
|
||||
private void setStatusBarVisibility(boolean hidden) {
|
||||
createUiHideFlags(hidden);
|
||||
setSystemUiVisibility();
|
||||
}
|
||||
|
||||
private void createUiHideFlags(boolean statusBarHidden) {
|
||||
uiFlags = 0;
|
||||
uiFlags |= View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
if (statusBarHidden)
|
||||
uiFlags |= View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
|
||||
uiFlags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
|
||||
}
|
||||
|
||||
public void setSystemUiVisibility() {
|
||||
getWindow().getDecorView().setSystemUiVisibility(uiFlags);
|
||||
}
|
||||
|
||||
protected void setMangaDefaultViewer(int viewer) {
|
||||
getPresenter().updateMangaViewer(viewer);
|
||||
recreate();
|
||||
}
|
||||
|
||||
private void applyTheme(int theme) {
|
||||
readerTheme = theme;
|
||||
View rootView = getWindow().getDecorView().getRootView();
|
||||
if (theme == BLACK_THEME) {
|
||||
rootView.setBackgroundColor(Color.BLACK);
|
||||
pageNumber.setTextColor(ContextCompat.getColor(this, R.color.light_grey));
|
||||
pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.page_number_background_black));
|
||||
} else {
|
||||
rootView.setBackgroundColor(Color.WHITE);
|
||||
pageNumber.setTextColor(ContextCompat.getColor(this, R.color.primary_text));
|
||||
pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.page_number_background));
|
||||
}
|
||||
}
|
||||
|
||||
public int getReaderTheme() {
|
||||
return readerTheme;
|
||||
}
|
||||
|
||||
public PreferencesHelper getPreferences() {
|
||||
return preferences;
|
||||
}
|
||||
|
||||
public BaseReader getViewer() {
|
||||
return viewer;
|
||||
}
|
||||
|
||||
public int getMaxBitmapSize() {
|
||||
return maxBitmapSize;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package eu.kanade.tachiyomi.ui.reader;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Gravity;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManager.LayoutParams;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
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.preference.PreferencesHelper;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
|
||||
import icepick.State;
|
||||
import rx.Subscription;
|
||||
|
||||
public class ReaderMenu {
|
||||
|
||||
@Bind(R.id.reader_menu) RelativeLayout menu;
|
||||
@Bind(R.id.reader_menu_bottom) LinearLayout bottomMenu;
|
||||
@Bind(R.id.toolbar) Toolbar toolbar;
|
||||
@Bind(R.id.current_page) TextView currentPage;
|
||||
@Bind(R.id.page_seeker) SeekBar seekBar;
|
||||
@Bind(R.id.total_pages) TextView totalPages;
|
||||
@Bind(R.id.lock_orientation) ImageButton lockOrientation;
|
||||
@Bind(R.id.reader_selector) ImageButton readerSelector;
|
||||
@Bind(R.id.reader_extra_settings) ImageButton extraSettings;
|
||||
@Bind(R.id.reader_brightness) ImageButton brightnessSettings;
|
||||
|
||||
private MenuItem nextChapterBtn;
|
||||
private MenuItem prevChapterBtn;
|
||||
private Chapter prevChapter;
|
||||
private Chapter nextChapter;
|
||||
|
||||
private ReaderActivity activity;
|
||||
private PreferencesHelper preferences;
|
||||
|
||||
@State boolean showing;
|
||||
private PopupWindow settingsPopup;
|
||||
private PopupWindow brightnessPopup;
|
||||
private boolean inverted;
|
||||
|
||||
private DecimalFormat decimalFormat;
|
||||
|
||||
public ReaderMenu(ReaderActivity activity) {
|
||||
this.activity = activity;
|
||||
this.preferences = activity.getPreferences();
|
||||
ButterKnife.bind(this, activity);
|
||||
|
||||
// Intercept all image events in this layout
|
||||
bottomMenu.setOnTouchListener((v, event) -> true);
|
||||
|
||||
seekBar.setOnSeekBarChangeListener(new PageSeekBarChangeListener());
|
||||
decimalFormat = new DecimalFormat("#.##");
|
||||
inverted = false;
|
||||
|
||||
initializeOptions();
|
||||
}
|
||||
|
||||
public void add(Subscription subscription) {
|
||||
activity.subscriptions.add(subscription);
|
||||
}
|
||||
|
||||
public void toggle() {
|
||||
if (showing)
|
||||
hide();
|
||||
else
|
||||
show(true);
|
||||
}
|
||||
|
||||
public void show(boolean animate) {
|
||||
menu.setVisibility(View.VISIBLE);
|
||||
|
||||
if (animate) {
|
||||
Animation toolbarAnimation = AnimationUtils.loadAnimation(activity, R.anim.enter_from_top);
|
||||
toolbar.startAnimation(toolbarAnimation);
|
||||
|
||||
Animation bottomMenuAnimation = AnimationUtils.loadAnimation(activity, R.anim.enter_from_bottom);
|
||||
bottomMenu.startAnimation(bottomMenuAnimation);
|
||||
}
|
||||
|
||||
showing = true;
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
Animation toolbarAnimation = AnimationUtils.loadAnimation(activity, R.anim.exit_to_top);
|
||||
toolbarAnimation.setAnimationListener(new HideMenuAnimationListener());
|
||||
toolbar.startAnimation(toolbarAnimation);
|
||||
|
||||
Animation bottomMenuAnimation = AnimationUtils.loadAnimation(activity, R.anim.exit_to_bottom);
|
||||
bottomMenu.startAnimation(bottomMenuAnimation);
|
||||
|
||||
settingsPopup.dismiss();
|
||||
brightnessPopup.dismiss();
|
||||
|
||||
showing = false;
|
||||
}
|
||||
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
activity.getMenuInflater().inflate(R.menu.reader, menu);
|
||||
nextChapterBtn = menu.findItem(R.id.action_next_chapter);
|
||||
prevChapterBtn = menu.findItem(R.id.action_previous_chapter);
|
||||
setAdjacentChaptersVisibility();
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item == prevChapterBtn) {
|
||||
activity.requestPreviousChapter();
|
||||
} else if (item == nextChapterBtn) {
|
||||
activity.requestNextChapter();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void onChapterReady(int numPages, Manga manga, Chapter chapter, int currentPageIndex) {
|
||||
if (manga.viewer == ReaderActivity.RIGHT_TO_LEFT && !inverted) {
|
||||
// Invert the seekbar and textview fields for the right to left reader
|
||||
seekBar.setRotation(180);
|
||||
TextView aux = currentPage;
|
||||
currentPage = totalPages;
|
||||
totalPages = aux;
|
||||
// Don't invert again on chapter change
|
||||
inverted = true;
|
||||
}
|
||||
|
||||
// Set initial values
|
||||
totalPages.setText("" + numPages);
|
||||
currentPage.setText("" + (currentPageIndex + 1));
|
||||
seekBar.setProgress(currentPageIndex);
|
||||
seekBar.setMax(numPages - 1);
|
||||
|
||||
activity.setToolbarTitle(manga.title);
|
||||
activity.setToolbarSubtitle(chapter.chapter_number != -1 ?
|
||||
activity.getString(R.string.chapter_subtitle,
|
||||
decimalFormat.format(chapter.chapter_number)) :
|
||||
chapter.name);
|
||||
|
||||
}
|
||||
|
||||
public void onPageChanged(int pageIndex) {
|
||||
currentPage.setText("" + (pageIndex + 1));
|
||||
seekBar.setProgress(pageIndex);
|
||||
}
|
||||
|
||||
public void onAdjacentChapters(Chapter previous, Chapter next) {
|
||||
prevChapter = previous;
|
||||
nextChapter = next;
|
||||
setAdjacentChaptersVisibility();
|
||||
}
|
||||
|
||||
private void setAdjacentChaptersVisibility() {
|
||||
if (prevChapterBtn != null) prevChapterBtn.setVisible(prevChapter != null);
|
||||
if (nextChapterBtn != null) nextChapterBtn.setVisible(nextChapter != null);
|
||||
}
|
||||
|
||||
private void initializeOptions() {
|
||||
// Orientation changes
|
||||
add(preferences.lockOrientation().asObservable()
|
||||
.subscribe(locked -> {
|
||||
int resourceId = !locked ? R.drawable.ic_screen_rotation :
|
||||
activity.getResources().getConfiguration().orientation == 1 ?
|
||||
R.drawable.ic_screen_lock_portrait :
|
||||
R.drawable.ic_screen_lock_landscape;
|
||||
|
||||
lockOrientation.setImageResource(resourceId);
|
||||
}));
|
||||
|
||||
lockOrientation.setOnClickListener(v ->
|
||||
preferences.lockOrientation().set(!preferences.lockOrientation().get()));
|
||||
|
||||
// Reader selector
|
||||
readerSelector.setOnClickListener(v -> {
|
||||
final Manga manga = activity.getPresenter().getManga();
|
||||
showImmersiveDialog(new MaterialDialog.Builder(activity)
|
||||
.items(R.array.viewers_selector)
|
||||
.itemsCallbackSingleChoice(manga.viewer,
|
||||
(d, itemView, which, text) -> {
|
||||
activity.setMangaDefaultViewer(which);
|
||||
return true;
|
||||
})
|
||||
.build());
|
||||
});
|
||||
|
||||
// Extra settings menu
|
||||
final View popupView = activity.getLayoutInflater().inflate(R.layout.reader_popup, null);
|
||||
settingsPopup = new SettingsPopupWindow(popupView);
|
||||
|
||||
extraSettings.setOnClickListener(v -> {
|
||||
if (!settingsPopup.isShowing())
|
||||
settingsPopup.showAtLocation(extraSettings,
|
||||
Gravity.BOTTOM | Gravity.RIGHT, 0, bottomMenu.getHeight());
|
||||
else
|
||||
settingsPopup.dismiss();
|
||||
});
|
||||
|
||||
// Brightness popup
|
||||
final View brightnessView = activity.getLayoutInflater().inflate(R.layout.reader_brightness, null);
|
||||
brightnessPopup = new BrightnessPopupWindow(brightnessView);
|
||||
|
||||
brightnessSettings.setOnClickListener(v -> {
|
||||
if (!brightnessPopup.isShowing())
|
||||
brightnessPopup.showAtLocation(brightnessSettings,
|
||||
Gravity.BOTTOM | Gravity.LEFT, 0, bottomMenu.getHeight());
|
||||
else
|
||||
brightnessPopup.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
private void showImmersiveDialog(Dialog dialog) {
|
||||
// Hack to not leave immersive mode
|
||||
dialog.getWindow().setFlags(LayoutParams.FLAG_NOT_FOCUSABLE,
|
||||
LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
dialog.getWindow().getDecorView().setSystemUiVisibility(
|
||||
activity.getWindow().getDecorView().getSystemUiVisibility());
|
||||
dialog.show();
|
||||
dialog.getWindow().clearFlags(LayoutParams.FLAG_NOT_FOCUSABLE);
|
||||
WindowManager wm = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE);
|
||||
wm.updateViewLayout(activity.getWindow().getDecorView(), activity.getWindow().getAttributes());
|
||||
}
|
||||
|
||||
class SettingsPopupWindow extends PopupWindow {
|
||||
|
||||
@Bind(R.id.enable_transitions) CheckBox enableTransitions;
|
||||
@Bind(R.id.show_page_number) CheckBox showPageNumber;
|
||||
@Bind(R.id.hide_status_bar) CheckBox hideStatusBar;
|
||||
@Bind(R.id.keep_screen_on) CheckBox keepScreenOn;
|
||||
@Bind(R.id.reader_theme) CheckBox readerTheme;
|
||||
@Bind(R.id.image_decoder) TextView imageDecoder;
|
||||
@Bind(R.id.image_decoder_initial) TextView imageDecoderInitial;
|
||||
|
||||
public SettingsPopupWindow(View view) {
|
||||
super(view, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
setAnimationStyle(R.style.reader_settings_popup_animation);
|
||||
ButterKnife.bind(this, view);
|
||||
initializePopupMenu();
|
||||
}
|
||||
|
||||
private void initializePopupMenu() {
|
||||
// Load values from preferences
|
||||
enableTransitions.setChecked(preferences.enableTransitions().get());
|
||||
showPageNumber.setChecked(preferences.showPageNumber().get());
|
||||
hideStatusBar.setChecked(preferences.hideStatusBar().get());
|
||||
keepScreenOn.setChecked(preferences.keepScreenOn().get());
|
||||
readerTheme.setChecked(preferences.readerTheme().get() == 1);
|
||||
setDecoderInitial(preferences.imageDecoder().get());
|
||||
|
||||
// Add a listener to change the corresponding setting
|
||||
enableTransitions.setOnCheckedChangeListener((view, isChecked) ->
|
||||
preferences.enableTransitions().set(isChecked));
|
||||
|
||||
showPageNumber.setOnCheckedChangeListener((view, isChecked) ->
|
||||
preferences.showPageNumber().set(isChecked));
|
||||
|
||||
hideStatusBar.setOnCheckedChangeListener((view, isChecked) ->
|
||||
preferences.hideStatusBar().set(isChecked));
|
||||
|
||||
keepScreenOn.setOnCheckedChangeListener((view, isChecked) ->
|
||||
preferences.keepScreenOn().set(isChecked));
|
||||
|
||||
readerTheme.setOnCheckedChangeListener((view, isChecked) ->
|
||||
preferences.readerTheme().set(isChecked ? 1 : 0));
|
||||
|
||||
imageDecoder.setOnClickListener(v -> {
|
||||
showImmersiveDialog(new MaterialDialog.Builder(activity)
|
||||
.title(R.string.pref_image_decoder)
|
||||
.items(R.array.image_decoders)
|
||||
.itemsCallbackSingleChoice(preferences.imageDecoder().get(),
|
||||
(dialog, itemView, which, text) -> {
|
||||
preferences.imageDecoder().set(which);
|
||||
setDecoderInitial(which);
|
||||
return true;
|
||||
})
|
||||
.build());
|
||||
});
|
||||
}
|
||||
|
||||
private void setDecoderInitial(int decoder) {
|
||||
String initial;
|
||||
switch (decoder) {
|
||||
case BaseReader.SKIA_DECODER:
|
||||
initial = "S";
|
||||
break;
|
||||
case BaseReader.RAPID_DECODER:
|
||||
initial = "R";
|
||||
break;
|
||||
default:
|
||||
initial = "";
|
||||
break;
|
||||
}
|
||||
imageDecoderInitial.setText(initial);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class BrightnessPopupWindow extends PopupWindow {
|
||||
|
||||
@Bind(R.id.custom_brightness) CheckBox customBrightness;
|
||||
@Bind(R.id.brightness_seekbar) SeekBar brightnessSeekbar;
|
||||
|
||||
public BrightnessPopupWindow(View view) {
|
||||
super(view, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
|
||||
setAnimationStyle(R.style.reader_brightness_popup_animation);
|
||||
ButterKnife.bind(this, view);
|
||||
initializePopupMenu();
|
||||
}
|
||||
|
||||
private void initializePopupMenu() {
|
||||
add(preferences.customBrightness()
|
||||
.asObservable()
|
||||
.subscribe(isEnabled -> {
|
||||
customBrightness.setChecked(isEnabled);
|
||||
brightnessSeekbar.setEnabled(isEnabled);
|
||||
}));
|
||||
|
||||
customBrightness.setOnCheckedChangeListener((view, isChecked) ->
|
||||
preferences.customBrightness().set(isChecked));
|
||||
|
||||
brightnessSeekbar.setMax(100);
|
||||
brightnessSeekbar.setProgress(Math.round(
|
||||
preferences.customBrightnessValue().get() * brightnessSeekbar.getMax()));
|
||||
brightnessSeekbar.setOnSeekBarChangeListener(new BrightnessSeekBarChangeListener());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PageSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
activity.setSelectedPage(progress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {}
|
||||
}
|
||||
|
||||
class BrightnessSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
if (fromUser) {
|
||||
preferences.customBrightnessValue().set((float) progress / seekBar.getMax());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class HideMenuAnimationListener implements Animation.AnimationListener {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animation animation) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animation animation) {
|
||||
menu.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animation animation) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,361 @@
|
||||
package eu.kanade.tachiyomi.ui.reader;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper;
|
||||
import eu.kanade.tachiyomi.data.database.models.Chapter;
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga;
|
||||
import eu.kanade.tachiyomi.data.database.models.MangaSync;
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager;
|
||||
import eu.kanade.tachiyomi.data.mangasync.MangaSyncManager;
|
||||
import eu.kanade.tachiyomi.data.mangasync.base.MangaSyncService;
|
||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper;
|
||||
import eu.kanade.tachiyomi.data.source.SourceManager;
|
||||
import eu.kanade.tachiyomi.data.source.base.Source;
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
import eu.kanade.tachiyomi.data.sync.UpdateMangaSyncService;
|
||||
import eu.kanade.tachiyomi.event.ReaderEvent;
|
||||
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;
|
||||
import rx.subjects.PublishSubject;
|
||||
import timber.log.Timber;
|
||||
|
||||
public class ReaderPresenter extends BasePresenter<ReaderActivity> {
|
||||
|
||||
@Inject PreferencesHelper prefs;
|
||||
@Inject DatabaseHelper db;
|
||||
@Inject DownloadManager downloadManager;
|
||||
@Inject MangaSyncManager syncManager;
|
||||
@Inject SourceManager sourceManager;
|
||||
|
||||
@State Manga manga;
|
||||
@State Chapter chapter;
|
||||
@State int sourceId;
|
||||
@State boolean isDownloaded;
|
||||
@State int currentPage;
|
||||
private Source source;
|
||||
private Chapter nextChapter;
|
||||
private Chapter previousChapter;
|
||||
private List<Page> pageList;
|
||||
private List<Page> nextChapterPageList;
|
||||
private List<MangaSync> mangaSyncList;
|
||||
|
||||
private PublishSubject<Page> retryPageSubject;
|
||||
|
||||
private static final int GET_PAGE_LIST = 1;
|
||||
private static final int GET_PAGE_IMAGES = 2;
|
||||
private static final int GET_ADJACENT_CHAPTERS = 3;
|
||||
private static final int RETRY_IMAGES = 4;
|
||||
private static final int PRELOAD_NEXT_CHAPTER = 5;
|
||||
private static final int GET_MANGA_SYNC = 6;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedState) {
|
||||
super.onCreate(savedState);
|
||||
|
||||
if (savedState != null) {
|
||||
onProcessRestart();
|
||||
}
|
||||
|
||||
retryPageSubject = PublishSubject.create();
|
||||
|
||||
restartableLatestCache(PRELOAD_NEXT_CHAPTER,
|
||||
this::getPreloadNextChapterObservable,
|
||||
(view, pages) -> {},
|
||||
(view, error) -> Timber.e("An error occurred while preloading a chapter"));
|
||||
|
||||
restartableLatestCache(GET_PAGE_IMAGES,
|
||||
this::getPageImagesObservable,
|
||||
(view, page) -> {},
|
||||
(view, error) -> Timber.e("An error occurred while downloading an image"));
|
||||
|
||||
restartableLatestCache(GET_ADJACENT_CHAPTERS,
|
||||
this::getAdjacentChaptersObservable,
|
||||
(view, pair) -> view.onAdjacentChapters(pair.first, pair.second),
|
||||
(view, error) -> Timber.e("An error occurred while getting adjacent chapters"));
|
||||
|
||||
restartableLatestCache(RETRY_IMAGES,
|
||||
this::getRetryPageObservable,
|
||||
(view, page) -> {},
|
||||
(view, error) -> Timber.e("An error occurred while downloading an image"));
|
||||
|
||||
restartableLatestCache(GET_PAGE_LIST,
|
||||
() -> getPageListObservable()
|
||||
.doOnNext(pages -> pageList = pages)
|
||||
.doOnCompleted(() -> {
|
||||
start(GET_ADJACENT_CHAPTERS);
|
||||
start(GET_PAGE_IMAGES);
|
||||
start(RETRY_IMAGES);
|
||||
}),
|
||||
(view, pages) -> view.onChapterReady(pages, manga, chapter, currentPage),
|
||||
(view, error) -> view.onChapterError());
|
||||
|
||||
restartableFirst(GET_MANGA_SYNC, this::getMangaSyncObservable,
|
||||
(view, mangaSync) -> {},
|
||||
(view, error) -> {});
|
||||
|
||||
registerForStickyEvents();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
unregisterForEvents();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSave(@NonNull Bundle state) {
|
||||
onChapterLeft();
|
||||
super.onSave(state);
|
||||
}
|
||||
|
||||
private void onProcessRestart() {
|
||||
source = sourceManager.get(sourceId);
|
||||
|
||||
// These are started by GET_PAGE_LIST, so we don't let them restart itselves
|
||||
stop(GET_PAGE_IMAGES);
|
||||
stop(GET_ADJACENT_CHAPTERS);
|
||||
stop(RETRY_IMAGES);
|
||||
stop(PRELOAD_NEXT_CHAPTER);
|
||||
}
|
||||
|
||||
@EventBusHook
|
||||
public void onEventMainThread(ReaderEvent event) {
|
||||
EventBus.getDefault().removeStickyEvent(event);
|
||||
manga = event.getManga();
|
||||
source = event.getSource();
|
||||
sourceId = source.getId();
|
||||
loadChapter(event.getChapter());
|
||||
if (prefs.autoUpdateMangaSync()) {
|
||||
start(GET_MANGA_SYNC);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the page list of a chapter
|
||||
private Observable<List<Page>> getPageListObservable() {
|
||||
return isDownloaded ?
|
||||
// Fetch the page list from disk
|
||||
Observable.just(downloadManager.getSavedPageList(source, manga, chapter)) :
|
||||
// Fetch the page list from cache or fallback to network
|
||||
source.getCachedPageListOrPullFromNetwork(chapter.url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
// Get the chapter images from network or disk
|
||||
private Observable<Page> getPageImagesObservable() {
|
||||
Observable<Page> pageObservable;
|
||||
|
||||
if (!isDownloaded) {
|
||||
pageObservable = source.getAllImageUrlsFromPageList(pageList)
|
||||
.flatMap(source::getCachedImage, 2);
|
||||
} else {
|
||||
File chapterDir = downloadManager.getAbsoluteChapterDirectory(source, manga, chapter);
|
||||
pageObservable = Observable.from(pageList)
|
||||
.flatMap(page -> downloadManager.getDownloadedImage(page, chapterDir));
|
||||
}
|
||||
return pageObservable.subscribeOn(Schedulers.io())
|
||||
.doOnCompleted(this::preloadNextChapter);
|
||||
}
|
||||
|
||||
private Observable<Pair<Chapter, Chapter>> getAdjacentChaptersObservable() {
|
||||
return Observable.zip(
|
||||
db.getPreviousChapter(chapter).createObservable().take(1),
|
||||
db.getNextChapter(chapter).createObservable().take(1),
|
||||
Pair::create)
|
||||
.doOnNext(pair -> {
|
||||
previousChapter = pair.first;
|
||||
nextChapter = pair.second;
|
||||
})
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
// Listen for retry page events
|
||||
private Observable<Page> getRetryPageObservable() {
|
||||
return retryPageSubject
|
||||
.observeOn(Schedulers.io())
|
||||
.flatMap(page -> page.getImageUrl() == null ?
|
||||
source.getImageUrlFromPage(page) :
|
||||
Observable.just(page))
|
||||
.flatMap(source::getCachedImage)
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
// Preload the first pages of the next chapter
|
||||
private Observable<Page> getPreloadNextChapterObservable() {
|
||||
return source.getCachedPageListOrPullFromNetwork(nextChapter.url)
|
||||
.flatMap(pages -> {
|
||||
nextChapterPageList = pages;
|
||||
// Preload at most 5 pages
|
||||
int pagesToPreload = Math.min(pages.size(), 5);
|
||||
return Observable.from(pages).take(pagesToPreload);
|
||||
})
|
||||
.concatMap(page -> page.getImageUrl() == null ?
|
||||
source.getImageUrlFromPage(page) :
|
||||
Observable.just(page))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnCompleted(this::stopPreloadingNextChapter);
|
||||
}
|
||||
|
||||
private Observable<List<MangaSync>> getMangaSyncObservable() {
|
||||
return db.getMangasSync(manga).createObservable()
|
||||
.doOnNext(mangaSync -> this.mangaSyncList = mangaSync);
|
||||
}
|
||||
|
||||
// Loads the given chapter
|
||||
private void loadChapter(Chapter chapter) {
|
||||
// Before loading the chapter, stop preloading (if it's working) and save current progress
|
||||
stopPreloadingNextChapter();
|
||||
|
||||
this.chapter = chapter;
|
||||
isDownloaded = isChapterDownloaded(chapter);
|
||||
|
||||
// If the chapter is partially read, set the starting page to the last the user read
|
||||
if (!chapter.read && chapter.last_page_read != 0)
|
||||
currentPage = chapter.last_page_read;
|
||||
else
|
||||
currentPage = 0;
|
||||
|
||||
// Reset next and previous chapter. They have to be fetched again
|
||||
nextChapter = null;
|
||||
previousChapter = null;
|
||||
nextChapterPageList = null;
|
||||
|
||||
start(GET_PAGE_LIST);
|
||||
}
|
||||
|
||||
// Check whether the given chapter is downloaded
|
||||
public boolean isChapterDownloaded(Chapter chapter) {
|
||||
return downloadManager.isChapterDownloaded(source, manga, chapter);
|
||||
}
|
||||
|
||||
public void retryPage(Page page) {
|
||||
page.setStatus(Page.QUEUE);
|
||||
retryPageSubject.onNext(page);
|
||||
}
|
||||
|
||||
// Called before loading another chapter or leaving the reader. It allows to do operations
|
||||
// over the chapter read like saving progress
|
||||
public void onChapterLeft() {
|
||||
if (pageList == null)
|
||||
return;
|
||||
|
||||
// Cache current page list progress for online chapters to allow a faster reopen
|
||||
if (!isDownloaded)
|
||||
source.savePageList(chapter.url, pageList);
|
||||
|
||||
// Save current progress of the chapter. Mark as read if the chapter is finished
|
||||
chapter.last_page_read = currentPage;
|
||||
if (isChapterFinished()) {
|
||||
chapter.read = true;
|
||||
}
|
||||
db.insertChapter(chapter).createObservable().subscribe();
|
||||
}
|
||||
|
||||
// Check whether the chapter has been read
|
||||
private boolean isChapterFinished() {
|
||||
return !chapter.read && currentPage == pageList.size() - 1;
|
||||
}
|
||||
|
||||
public int getMangaSyncChapterToUpdate() {
|
||||
if (pageList == null || mangaSyncList == null || mangaSyncList.isEmpty())
|
||||
return 0;
|
||||
|
||||
int lastChapterReadLocal = 0;
|
||||
// If the current chapter has been read, we check with this one
|
||||
if (chapter.read)
|
||||
lastChapterReadLocal = (int) Math.floor(chapter.chapter_number);
|
||||
// If not, we check if the previous chapter has been read
|
||||
else if (previousChapter != null && previousChapter.read)
|
||||
lastChapterReadLocal = (int) Math.floor(previousChapter.chapter_number);
|
||||
|
||||
// We know the chapter we have to check, but we don't know yet if an update is required.
|
||||
// This boolean is used to return 0 if no update is required
|
||||
boolean hasToUpdate = false;
|
||||
|
||||
for (MangaSync mangaSync : mangaSyncList) {
|
||||
if (lastChapterReadLocal > mangaSync.last_chapter_read) {
|
||||
mangaSync.last_chapter_read = lastChapterReadLocal;
|
||||
mangaSync.update = true;
|
||||
hasToUpdate = true;
|
||||
}
|
||||
}
|
||||
return hasToUpdate ? lastChapterReadLocal : 0;
|
||||
}
|
||||
|
||||
public void updateMangaSyncLastChapterRead() {
|
||||
for (MangaSync mangaSync : mangaSyncList) {
|
||||
MangaSyncService service = syncManager.getSyncService(mangaSync.sync_id);
|
||||
if (service.isLogged() && mangaSync.update) {
|
||||
UpdateMangaSyncService.start(getContext(), mangaSync);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setCurrentPage(int currentPage) {
|
||||
this.currentPage = currentPage;
|
||||
}
|
||||
|
||||
public boolean loadNextChapter() {
|
||||
if (hasNextChapter()) {
|
||||
onChapterLeft();
|
||||
loadChapter(nextChapter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean loadPreviousChapter() {
|
||||
if (hasPreviousChapter()) {
|
||||
onChapterLeft();
|
||||
loadChapter(previousChapter);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasNextChapter() {
|
||||
return nextChapter != null;
|
||||
}
|
||||
|
||||
public boolean hasPreviousChapter() {
|
||||
return previousChapter != null;
|
||||
}
|
||||
|
||||
private void preloadNextChapter() {
|
||||
if (hasNextChapter() && !isChapterDownloaded(nextChapter)) {
|
||||
start(PRELOAD_NEXT_CHAPTER);
|
||||
}
|
||||
}
|
||||
|
||||
private void stopPreloadingNextChapter() {
|
||||
if (isSubscribed(PRELOAD_NEXT_CHAPTER)) {
|
||||
stop(PRELOAD_NEXT_CHAPTER);
|
||||
if (nextChapterPageList != null)
|
||||
source.savePageList(nextChapter.url, nextChapterPageList);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMangaViewer(int viewer) {
|
||||
manga.viewer = viewer;
|
||||
db.insertManga(manga).executeAsBlocking();
|
||||
}
|
||||
|
||||
public Manga getManga() {
|
||||
return manga;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.base;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.davemorrissey.labs.subscaleview.decoder.ImageRegionDecoder;
|
||||
import com.davemorrissey.labs.subscaleview.decoder.RapidImageRegionDecoder;
|
||||
import com.davemorrissey.labs.subscaleview.decoder.SkiaImageRegionDecoder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment;
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
|
||||
|
||||
public abstract class BaseReader extends BaseFragment {
|
||||
|
||||
protected int currentPage;
|
||||
protected List<Page> pages;
|
||||
protected Class<? extends ImageRegionDecoder> regionDecoderClass;
|
||||
|
||||
public static final int RAPID_DECODER = 0;
|
||||
public static final int SKIA_DECODER = 1;
|
||||
|
||||
public void updatePageNumber() {
|
||||
getReaderActivity().onPageChanged(getCurrentPage(), getTotalPages());
|
||||
}
|
||||
|
||||
public int getCurrentPage() {
|
||||
return currentPage;
|
||||
}
|
||||
|
||||
public int getPageForPosition(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
public int getPositionForPage(int page) {
|
||||
return page;
|
||||
}
|
||||
|
||||
public void onPageChanged(int position) {
|
||||
currentPage = getPageForPosition(position);
|
||||
updatePageNumber();
|
||||
}
|
||||
|
||||
public int getTotalPages() {
|
||||
return pages == null ? 0 : pages.size();
|
||||
}
|
||||
|
||||
public abstract void setSelectedPage(int pageNumber);
|
||||
public abstract void onPageListReady(List<Page> pages, int currentPage);
|
||||
public abstract boolean onImageTouch(MotionEvent motionEvent);
|
||||
|
||||
public void setRegionDecoderClass(int value) {
|
||||
switch (value) {
|
||||
case RAPID_DECODER:
|
||||
default:
|
||||
regionDecoderClass = RapidImageRegionDecoder.class;
|
||||
break;
|
||||
case SKIA_DECODER:
|
||||
regionDecoderClass = SkiaImageRegionDecoder.class;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public Class<? extends ImageRegionDecoder> getRegionDecoderClass() {
|
||||
return regionDecoderClass;
|
||||
}
|
||||
|
||||
public ReaderActivity getReaderActivity() {
|
||||
return (ReaderActivity) getActivity();
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.base;
|
||||
|
||||
public interface OnChapterBoundariesOutListener {
|
||||
void onFirstPageOutEvent();
|
||||
void onLastPageOutEvent();
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.base;
|
||||
|
||||
public interface OnChapterSingleTapListener {
|
||||
void onCenterTap();
|
||||
void onLeftSideTap();
|
||||
void onRightSideTap();
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
|
||||
|
||||
import android.support.v4.view.PagerAdapter;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
|
||||
import rx.functions.Action1;
|
||||
|
||||
public interface Pager {
|
||||
|
||||
void setId(int id);
|
||||
void setLayoutParams(ViewGroup.LayoutParams layoutParams);
|
||||
|
||||
void setOffscreenPageLimit(int limit);
|
||||
|
||||
int getCurrentItem();
|
||||
void setCurrentItem(int item, boolean smoothScroll);
|
||||
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
|
||||
PagerAdapter getAdapter();
|
||||
void setAdapter(PagerAdapter adapter);
|
||||
|
||||
boolean onImageTouch(MotionEvent motionEvent);
|
||||
|
||||
void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener listener);
|
||||
void setOnChapterSingleTapListener(OnChapterSingleTapListener listener);
|
||||
|
||||
OnChapterBoundariesOutListener getChapterBoundariesListener();
|
||||
OnChapterSingleTapListener getChapterSingleTapListener();
|
||||
|
||||
void setOnPageChangeListener(Action1<Integer> onPageChanged);
|
||||
void clearOnPageChangeListeners();
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
|
||||
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
public class PagerGestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
|
||||
private Pager pager;
|
||||
|
||||
private static final float LEFT_REGION = 0.33f;
|
||||
private static final float RIGHT_REGION = 0.66f;
|
||||
|
||||
public PagerGestureListener(Pager pager) {
|
||||
this.pager = pager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
final int position = pager.getCurrentItem();
|
||||
final float positionX = e.getX();
|
||||
|
||||
if (positionX < pager.getWidth() * LEFT_REGION) {
|
||||
if (position != 0) {
|
||||
onLeftSideTap();
|
||||
} else {
|
||||
onFirstPageOut();
|
||||
}
|
||||
} else if (positionX > pager.getWidth() * RIGHT_REGION) {
|
||||
if (position != pager.getAdapter().getCount() - 1) {
|
||||
onRightSideTap();
|
||||
} else {
|
||||
onLastPageOut();
|
||||
}
|
||||
} else {
|
||||
onCenterTap();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void onLeftSideTap() {
|
||||
if (pager.getChapterSingleTapListener() != null) {
|
||||
pager.getChapterSingleTapListener().onLeftSideTap();
|
||||
}
|
||||
}
|
||||
|
||||
private void onRightSideTap() {
|
||||
if (pager.getChapterSingleTapListener() != null) {
|
||||
pager.getChapterSingleTapListener().onRightSideTap();
|
||||
}
|
||||
}
|
||||
|
||||
private void onCenterTap() {
|
||||
if (pager.getChapterSingleTapListener() != null) {
|
||||
pager.getChapterSingleTapListener().onCenterTap();
|
||||
}
|
||||
}
|
||||
|
||||
private void onFirstPageOut() {
|
||||
if (pager.getChapterBoundariesListener() != null) {
|
||||
pager.getChapterBoundariesListener().onFirstPageOutEvent();
|
||||
}
|
||||
}
|
||||
|
||||
private void onLastPageOut() {
|
||||
if (pager.getChapterBoundariesListener() != null) {
|
||||
pager.getChapterBoundariesListener().onLastPageOutEvent();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
|
||||
import rx.subscriptions.CompositeSubscription;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
public abstract class PagerReader extends BaseReader {
|
||||
|
||||
protected PagerReaderAdapter adapter;
|
||||
protected Pager pager;
|
||||
|
||||
protected boolean transitions;
|
||||
protected CompositeSubscription subscriptions;
|
||||
|
||||
protected void initializePager(Pager pager) {
|
||||
this.pager = pager;
|
||||
pager.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
pager.setOffscreenPageLimit(1);
|
||||
pager.setId(R.id.view_pager);
|
||||
pager.setOnChapterBoundariesOutListener(new OnChapterBoundariesOutListener() {
|
||||
@Override
|
||||
public void onFirstPageOutEvent() {
|
||||
onFirstPageOut();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLastPageOutEvent() {
|
||||
onLastPageOut();
|
||||
}
|
||||
});
|
||||
pager.setOnChapterSingleTapListener(new OnChapterSingleTapListener() {
|
||||
@Override
|
||||
public void onCenterTap() {
|
||||
getReaderActivity().onCenterSingleTap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLeftSideTap() {
|
||||
pager.setCurrentItem(pager.getCurrentItem() - 1, transitions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRightSideTap() {
|
||||
pager.setCurrentItem(pager.getCurrentItem() + 1, transitions);
|
||||
}
|
||||
});
|
||||
|
||||
adapter = new PagerReaderAdapter(getChildFragmentManager());
|
||||
pager.setAdapter(adapter);
|
||||
|
||||
subscriptions = new CompositeSubscription();
|
||||
subscriptions.add(getReaderActivity().getPreferences().imageDecoder()
|
||||
.asObservable()
|
||||
.doOnNext(this::setRegionDecoderClass)
|
||||
.skip(1)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(v -> adapter.notifyDataSetChanged()));
|
||||
|
||||
subscriptions.add(getReaderActivity().getPreferences().enableTransitions()
|
||||
.asObservable()
|
||||
.subscribe(value -> transitions = value));
|
||||
|
||||
setPages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
subscriptions.unsubscribe();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageListReady(List<Page> pages, int currentPage) {
|
||||
if (this.pages != pages) {
|
||||
this.pages = pages;
|
||||
this.currentPage = currentPage;
|
||||
if (isResumed()) {
|
||||
setPages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void setPages() {
|
||||
if (pages != null) {
|
||||
pager.clearOnPageChangeListeners();
|
||||
adapter.setPages(pages);
|
||||
setSelectedPage(currentPage);
|
||||
updatePageNumber();
|
||||
pager.setOnPageChangeListener(this::onPageChanged);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedPage(int pageNumber) {
|
||||
pager.setCurrentItem(getPositionForPage(pageNumber), false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onImageTouch(MotionEvent motionEvent) {
|
||||
return pager.onImageTouch(motionEvent);
|
||||
}
|
||||
|
||||
public abstract void onFirstPageOut();
|
||||
public abstract void onLastPageOut();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
|
||||
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.app.FragmentStatePagerAdapter;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
|
||||
public class PagerReaderAdapter extends FragmentStatePagerAdapter {
|
||||
|
||||
private List<Page> pages;
|
||||
|
||||
public PagerReaderAdapter(FragmentManager fragmentManager) {
|
||||
super(fragmentManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return pages == null ? 0 : pages.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fragment getItem(int position) {
|
||||
return PagerReaderFragment.newInstance(pages.get(position));
|
||||
}
|
||||
|
||||
public List<Page> getPages() {
|
||||
return pages;
|
||||
}
|
||||
|
||||
public void setPages(List<Page> pages) {
|
||||
this.pages = pages;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemPosition(Object object) {
|
||||
return POSITION_NONE;
|
||||
}
|
||||
|
||||
}
|
||||
+219
@@ -0,0 +1,219 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource;
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment;
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
|
||||
import rx.Observable;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.schedulers.Schedulers;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
public class PagerReaderFragment extends BaseFragment {
|
||||
|
||||
@Bind(R.id.page_image_view) SubsamplingScaleImageView imageView;
|
||||
@Bind(R.id.progress_container) LinearLayout progressContainer;
|
||||
@Bind(R.id.progress) ProgressBar progressBar;
|
||||
@Bind(R.id.progress_text) TextView progressText;
|
||||
@Bind(R.id.retry_button) Button retryButton;
|
||||
|
||||
private Page page;
|
||||
private Subscription progressSubscription;
|
||||
private Subscription statusSubscription;
|
||||
|
||||
public static PagerReaderFragment newInstance(Page page) {
|
||||
PagerReaderFragment fragment = new PagerReaderFragment();
|
||||
fragment.setPage(page);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.item_pager_reader, container, false);
|
||||
ButterKnife.bind(this, view);
|
||||
ReaderActivity activity = getReaderActivity();
|
||||
BaseReader parentFragment = (BaseReader) getParentFragment();
|
||||
|
||||
if (activity.getReaderTheme() == ReaderActivity.BLACK_THEME) {
|
||||
progressText.setTextColor(ContextCompat.getColor(getContext(), R.color.light_grey));
|
||||
}
|
||||
|
||||
imageView.setParallelLoadingEnabled(true);
|
||||
imageView.setMaxDimensions(activity.getMaxBitmapSize(), activity.getMaxBitmapSize());
|
||||
imageView.setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_FIXED);
|
||||
imageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
|
||||
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
|
||||
imageView.setRegionDecoderClass(parentFragment.getRegionDecoderClass());
|
||||
imageView.setOnTouchListener((v, motionEvent) -> parentFragment.onImageTouch(motionEvent));
|
||||
imageView.setOnImageEventListener(new SubsamplingScaleImageView.DefaultOnImageEventListener() {
|
||||
@Override
|
||||
public void onImageLoadError(Exception e) {
|
||||
showImageLoadError();
|
||||
}
|
||||
});
|
||||
|
||||
retryButton.setOnTouchListener((v, event) -> {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (page != null)
|
||||
activity.getPresenter().retryPage(page);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
observeStatus();
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
unsubscribeProgress();
|
||||
unsubscribeStatus();
|
||||
ButterKnife.unbind(this);
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
public void setPage(Page page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
private void showImage() {
|
||||
if (page == null || page.getImagePath() == null)
|
||||
return;
|
||||
|
||||
imageView.setImage(ImageSource.uri(page.getImagePath()));
|
||||
progressContainer.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showDownloading() {
|
||||
progressContainer.setVisibility(View.VISIBLE);
|
||||
progressText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void showLoading() {
|
||||
progressContainer.setVisibility(View.VISIBLE);
|
||||
progressText.setVisibility(View.VISIBLE);
|
||||
progressText.setText(R.string.downloading);
|
||||
}
|
||||
|
||||
private void showError() {
|
||||
progressContainer.setVisibility(View.GONE);
|
||||
retryButton.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void hideError() {
|
||||
retryButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showImageLoadError() {
|
||||
ViewGroup view = (ViewGroup) getView();
|
||||
if (view == null)
|
||||
return;
|
||||
|
||||
TextView errorText = new TextView(getContext());
|
||||
errorText.setGravity(Gravity.CENTER);
|
||||
errorText.setText(R.string.decode_image_error);
|
||||
errorText.setTextColor(getReaderActivity().getReaderTheme() == ReaderActivity.BLACK_THEME ?
|
||||
ContextCompat.getColor(getContext(), R.color.light_grey) :
|
||||
ContextCompat.getColor(getContext(), R.color.primary_text));
|
||||
|
||||
view.addView(errorText);
|
||||
}
|
||||
|
||||
private void processStatus(int status) {
|
||||
switch (status) {
|
||||
case Page.QUEUE:
|
||||
hideError();
|
||||
break;
|
||||
case Page.LOAD_PAGE:
|
||||
showLoading();
|
||||
break;
|
||||
case Page.DOWNLOAD_IMAGE:
|
||||
observeProgress();
|
||||
showDownloading();
|
||||
break;
|
||||
case Page.READY:
|
||||
showImage();
|
||||
unsubscribeProgress();
|
||||
unsubscribeStatus();
|
||||
break;
|
||||
case Page.ERROR:
|
||||
showError();
|
||||
unsubscribeProgress();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void observeStatus() {
|
||||
if (page == null || statusSubscription != null)
|
||||
return;
|
||||
|
||||
PublishSubject<Integer> statusSubject = PublishSubject.create();
|
||||
page.setStatusSubject(statusSubject);
|
||||
|
||||
statusSubscription = statusSubject
|
||||
.startWith(page.getStatus())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(this::processStatus);
|
||||
}
|
||||
|
||||
private void observeProgress() {
|
||||
if (progressSubscription != null)
|
||||
return;
|
||||
|
||||
final AtomicInteger currentValue = new AtomicInteger(-1);
|
||||
|
||||
progressSubscription = Observable.interval(75, TimeUnit.MILLISECONDS, Schedulers.newThread())
|
||||
.onBackpressureDrop()
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(tick -> {
|
||||
// Refresh UI only if progress change
|
||||
if (page.getProgress() != currentValue.get()) {
|
||||
currentValue.set(page.getProgress());
|
||||
progressText.setText(getString(R.string.download_progress, page.getProgress()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void unsubscribeStatus() {
|
||||
if (statusSubscription != null) {
|
||||
page.setStatusSubject(null);
|
||||
statusSubscription.unsubscribe();
|
||||
statusSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void unsubscribeProgress() {
|
||||
if (progressSubscription != null) {
|
||||
progressSubscription.unsubscribe();
|
||||
progressSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
private ReaderActivity getReaderActivity() {
|
||||
return (ReaderActivity) getActivity();
|
||||
}
|
||||
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerGestureListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.Pager;
|
||||
import rx.functions.Action1;
|
||||
|
||||
public class HorizontalPager extends ViewPager implements Pager {
|
||||
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
private OnChapterBoundariesOutListener onChapterBoundariesOutListener;
|
||||
private OnChapterSingleTapListener onChapterSingleTapListener;
|
||||
|
||||
private static final float SWIPE_TOLERANCE = 0.25f;
|
||||
private float startDragX;
|
||||
|
||||
public HorizontalPager(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public HorizontalPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
gestureDetector = new GestureDetector(context, new PagerGestureListener(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
try {
|
||||
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
|
||||
if (getCurrentItem() == 0 || getCurrentItem() == getAdapter().getCount() - 1) {
|
||||
startDragX = ev.getX();
|
||||
}
|
||||
}
|
||||
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
try {
|
||||
if (onChapterBoundariesOutListener != null) {
|
||||
if (getCurrentItem() == 0) {
|
||||
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
float displacement = ev.getX() - startDragX;
|
||||
|
||||
if (ev.getX() > startDragX && displacement > getWidth() * SWIPE_TOLERANCE) {
|
||||
onChapterBoundariesOutListener.onFirstPageOutEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
startDragX = 0;
|
||||
}
|
||||
} else if (getCurrentItem() == getAdapter().getCount() - 1) {
|
||||
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
float displacement = startDragX - ev.getX();
|
||||
|
||||
if (ev.getX() < startDragX && displacement > getWidth() * SWIPE_TOLERANCE) {
|
||||
onChapterBoundariesOutListener.onLastPageOutEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
startDragX = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(ev);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onImageTouch(MotionEvent event) {
|
||||
return gestureDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener listener) {
|
||||
onChapterBoundariesOutListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnChapterSingleTapListener(OnChapterSingleTapListener listener) {
|
||||
onChapterSingleTapListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnChapterBoundariesOutListener getChapterBoundariesListener() {
|
||||
return onChapterBoundariesOutListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnChapterSingleTapListener getChapterSingleTapListener() {
|
||||
return onChapterSingleTapListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnPageChangeListener(Action1<Integer> function) {
|
||||
addOnPageChangeListener(new SimpleOnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
function.call(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader;
|
||||
|
||||
public abstract class HorizontalReader extends PagerReader {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
|
||||
HorizontalPager pager = new HorizontalPager(getActivity());
|
||||
initializePager(pager);
|
||||
return pager;
|
||||
}
|
||||
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
|
||||
|
||||
public class LeftToRightReader extends HorizontalReader {
|
||||
|
||||
@Override
|
||||
public void onFirstPageOut() {
|
||||
getReaderActivity().requestPreviousChapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLastPageOut() {
|
||||
getReaderActivity().requestNextChapter();
|
||||
}
|
||||
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager.horizontal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
|
||||
public class RightToLeftReader extends HorizontalReader {
|
||||
|
||||
@Override
|
||||
public void onPageListReady(List<Page> pages, int currentPage) {
|
||||
ArrayList<Page> inversedPages = new ArrayList<>(pages);
|
||||
Collections.reverse(inversedPages);
|
||||
super.onPageListReady(inversedPages, currentPage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPageForPosition(int position) {
|
||||
return (getTotalPages() - 1) - position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPositionForPage(int page) {
|
||||
return (getTotalPages() - 1) - page;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPageOut() {
|
||||
getReaderActivity().requestNextChapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLastPageOut() {
|
||||
getReaderActivity().requestPreviousChapter();
|
||||
}
|
||||
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterBoundariesOutListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.OnChapterSingleTapListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerGestureListener;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.Pager;
|
||||
import rx.functions.Action1;
|
||||
|
||||
public class VerticalPager extends VerticalViewPagerImpl implements Pager {
|
||||
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
private OnChapterBoundariesOutListener onChapterBoundariesOutListener;
|
||||
private OnChapterSingleTapListener onChapterSingleTapListener;
|
||||
|
||||
private static final float SWIPE_TOLERANCE = 0.25f;
|
||||
private float startDragY;
|
||||
|
||||
public VerticalPager(Context context) {
|
||||
super(context);
|
||||
init(context);
|
||||
}
|
||||
|
||||
public VerticalPager(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context);
|
||||
}
|
||||
|
||||
private void init(Context context) {
|
||||
gestureDetector = new GestureDetector(context, new VerticalPagerGestureListener(this));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
try {
|
||||
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) {
|
||||
if (getCurrentItem() == 0 || getCurrentItem() == getAdapter().getCount() - 1) {
|
||||
startDragY = ev.getY();
|
||||
}
|
||||
}
|
||||
|
||||
return super.onInterceptTouchEvent(ev);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
try {
|
||||
if (onChapterBoundariesOutListener != null) {
|
||||
if (getCurrentItem() == 0) {
|
||||
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
float displacement = ev.getY() - startDragY;
|
||||
|
||||
if (ev.getY() > startDragY && displacement > getHeight() * SWIPE_TOLERANCE) {
|
||||
onChapterBoundariesOutListener.onFirstPageOutEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
startDragY = 0;
|
||||
}
|
||||
} else if (getCurrentItem() == getAdapter().getCount() - 1) {
|
||||
if ((ev.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_UP) {
|
||||
float displacement = startDragY - ev.getY();
|
||||
|
||||
if (ev.getY() < startDragY && displacement > getHeight() * SWIPE_TOLERANCE) {
|
||||
onChapterBoundariesOutListener.onLastPageOutEvent();
|
||||
return true;
|
||||
}
|
||||
|
||||
startDragY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return super.onTouchEvent(ev);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onImageTouch(MotionEvent event) {
|
||||
return gestureDetector.onTouchEvent(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnChapterBoundariesOutListener(OnChapterBoundariesOutListener listener) {
|
||||
onChapterBoundariesOutListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnChapterSingleTapListener(OnChapterSingleTapListener listener) {
|
||||
onChapterSingleTapListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnChapterBoundariesOutListener getChapterBoundariesListener() {
|
||||
return onChapterBoundariesOutListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnChapterSingleTapListener getChapterSingleTapListener() {
|
||||
return onChapterSingleTapListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnPageChangeListener(Action1<Integer> function) {
|
||||
addOnPageChangeListener(new SimpleOnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
function.call(position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static class VerticalPagerGestureListener extends PagerGestureListener {
|
||||
|
||||
public VerticalPagerGestureListener(Pager pager) {
|
||||
super(pager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
// Vertical view pager ignores scrolling events sometimes.
|
||||
// Returning true here fixes it, but we lose touch events on the image like
|
||||
// double tap to zoom
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.pager.vertical;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerReader;
|
||||
|
||||
public class VerticalReader extends PagerReader {
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
|
||||
VerticalPager pager = new VerticalPager(getActivity());
|
||||
initializePager(pager);
|
||||
return pager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFirstPageOut() {
|
||||
getReaderActivity().requestPreviousChapter();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLastPageOut() {
|
||||
getReaderActivity().requestNextChapter();
|
||||
}
|
||||
|
||||
}
|
||||
+2991
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,68 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
|
||||
public class WebtoonAdapter extends RecyclerView.Adapter<WebtoonHolder> {
|
||||
|
||||
private WebtoonReader fragment;
|
||||
private List<Page> pages;
|
||||
private View.OnTouchListener touchListener;
|
||||
|
||||
public WebtoonAdapter(WebtoonReader fragment) {
|
||||
this.fragment = fragment;
|
||||
pages = new ArrayList<>();
|
||||
touchListener = (v, event) -> fragment.onImageTouch(event);
|
||||
}
|
||||
|
||||
public Page getItem(int position) {
|
||||
return pages.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebtoonHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
LayoutInflater inflater = fragment.getActivity().getLayoutInflater();
|
||||
View v = inflater.inflate(R.layout.item_webtoon_reader, parent, false);
|
||||
return new WebtoonHolder(v, this, touchListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(WebtoonHolder holder, int position) {
|
||||
final Page page = getItem(position);
|
||||
holder.onSetValues(page);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return pages.size();
|
||||
}
|
||||
|
||||
public void setPages(List<Page> pages) {
|
||||
this.pages = pages;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
if (pages != null) {
|
||||
pages.clear();
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void retryPage(Page page) {
|
||||
fragment.getReaderActivity().getPresenter().retryPage(page);
|
||||
}
|
||||
|
||||
public WebtoonReader getReader() {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon;
|
||||
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
|
||||
import com.davemorrissey.labs.subscaleview.ImageSource;
|
||||
import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView;
|
||||
|
||||
import butterknife.Bind;
|
||||
import butterknife.ButterKnife;
|
||||
import eu.kanade.tachiyomi.R;
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
|
||||
public class WebtoonHolder extends RecyclerView.ViewHolder {
|
||||
|
||||
@Bind(R.id.page_image_view) SubsamplingScaleImageView imageView;
|
||||
@Bind(R.id.frame_container) ViewGroup container;
|
||||
@Bind(R.id.progress) ProgressBar progressBar;
|
||||
@Bind(R.id.retry_button) Button retryButton;
|
||||
|
||||
private Animation fadeInAnimation;
|
||||
private Page page;
|
||||
private WebtoonAdapter adapter;
|
||||
|
||||
public WebtoonHolder(View view, WebtoonAdapter adapter, View.OnTouchListener touchListener) {
|
||||
super(view);
|
||||
this.adapter = adapter;
|
||||
ButterKnife.bind(this, view);
|
||||
|
||||
fadeInAnimation = AnimationUtils.loadAnimation(view.getContext(), R.anim.fade_in);
|
||||
|
||||
imageView.setParallelLoadingEnabled(true);
|
||||
imageView.setDoubleTapZoomStyle(SubsamplingScaleImageView.ZOOM_FOCUS_FIXED);
|
||||
imageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE);
|
||||
imageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_INSIDE);
|
||||
imageView.setOnTouchListener(touchListener);
|
||||
imageView.setOnImageEventListener(new SubsamplingScaleImageView.DefaultOnImageEventListener() {
|
||||
@Override
|
||||
public void onImageLoaded() {
|
||||
imageView.startAnimation(fadeInAnimation);
|
||||
}
|
||||
});
|
||||
progressBar.setMinimumHeight(view.getResources().getDisplayMetrics().heightPixels);
|
||||
|
||||
container.setOnTouchListener(touchListener);
|
||||
retryButton.setOnTouchListener((v, event) -> {
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (page != null)
|
||||
adapter.retryPage(page);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
public void onSetValues(Page page) {
|
||||
this.page = page;
|
||||
switch (page.getStatus()) {
|
||||
case Page.QUEUE:
|
||||
onQueue();
|
||||
break;
|
||||
case Page.LOAD_PAGE:
|
||||
onLoading();
|
||||
break;
|
||||
case Page.DOWNLOAD_IMAGE:
|
||||
onLoading();
|
||||
break;
|
||||
case Page.READY:
|
||||
onReady();
|
||||
break;
|
||||
case Page.ERROR:
|
||||
onError();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onLoading() {
|
||||
setErrorButtonVisible(false);
|
||||
setImageVisible(false);
|
||||
setProgressVisible(true);
|
||||
}
|
||||
|
||||
private void onReady() {
|
||||
setErrorButtonVisible(false);
|
||||
setProgressVisible(false);
|
||||
setImageVisible(true);
|
||||
imageView.setRegionDecoderClass(adapter.getReader().getRegionDecoderClass());
|
||||
imageView.setImage(ImageSource.uri(page.getImagePath()));
|
||||
}
|
||||
|
||||
private void onError() {
|
||||
setImageVisible(false);
|
||||
setProgressVisible(false);
|
||||
setErrorButtonVisible(true);
|
||||
}
|
||||
|
||||
private void onQueue() {
|
||||
setImageVisible(false);
|
||||
setErrorButtonVisible(false);
|
||||
setProgressVisible(false);
|
||||
}
|
||||
|
||||
private void setProgressVisible(boolean visible) {
|
||||
progressBar.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void setImageVisible(boolean visible) {
|
||||
imageView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
private void setErrorButtonVisible(boolean visible) {
|
||||
retryButton.setVisibility(visible ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package eu.kanade.tachiyomi.ui.reader.viewer.webtoon;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.tachiyomi.data.source.model.Page;
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.base.BaseReader;
|
||||
import eu.kanade.tachiyomi.widget.PreCachingLayoutManager;
|
||||
import rx.Subscription;
|
||||
import rx.android.schedulers.AndroidSchedulers;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
import static android.view.GestureDetector.SimpleOnGestureListener;
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
|
||||
public class WebtoonReader extends BaseReader {
|
||||
|
||||
private WebtoonAdapter adapter;
|
||||
private RecyclerView recycler;
|
||||
private PreCachingLayoutManager layoutManager;
|
||||
private Subscription subscription;
|
||||
private Subscription decoderSubscription;
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
|
||||
adapter = new WebtoonAdapter(this);
|
||||
layoutManager = new PreCachingLayoutManager(getActivity());
|
||||
layoutManager.setExtraLayoutSpace(getResources().getDisplayMetrics().heightPixels);
|
||||
|
||||
recycler = new RecyclerView(getActivity());
|
||||
recycler.setLayoutParams(new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||
recycler.setLayoutManager(layoutManager);
|
||||
recycler.setItemAnimator(null);
|
||||
recycler.setAdapter(adapter);
|
||||
|
||||
decoderSubscription = getReaderActivity().getPreferences().imageDecoder()
|
||||
.asObservable()
|
||||
.doOnNext(this::setRegionDecoderClass)
|
||||
.skip(1)
|
||||
.distinctUntilChanged()
|
||||
.subscribe(v -> adapter.notifyDataSetChanged());
|
||||
|
||||
gestureDetector = new GestureDetector(getActivity(), new SimpleOnGestureListener() {
|
||||
@Override
|
||||
public boolean onSingleTapConfirmed(MotionEvent e) {
|
||||
getReaderActivity().onCenterSingleTap();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onDown(MotionEvent e) {
|
||||
// The only way I've found to allow panning. Double tap event (zoom) is lost
|
||||
// but panning should be the most used one
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
setPages();
|
||||
|
||||
return recycler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
decoderSubscription.unsubscribe();
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
unsubscribeStatus();
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
private void unsubscribeStatus() {
|
||||
if (subscription != null && !subscription.isUnsubscribed())
|
||||
subscription.unsubscribe();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedPage(int pageNumber) {
|
||||
recycler.scrollToPosition(getPositionForPage(pageNumber));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageListReady(List<Page> pages, int currentPage) {
|
||||
if (this.pages != pages) {
|
||||
this.pages = pages;
|
||||
if (isResumed()) {
|
||||
setPages();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setPages() {
|
||||
if (pages != null) {
|
||||
unsubscribeStatus();
|
||||
recycler.clearOnScrollListeners();
|
||||
adapter.clear();
|
||||
recycler.scrollTo(0, 0);
|
||||
adapter.setPages(pages);
|
||||
setScrollListener();
|
||||
observeStatus(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void setScrollListener() {
|
||||
recycler.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@Override
|
||||
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
|
||||
super.onScrolled(recyclerView, dx, dy);
|
||||
|
||||
currentPage = layoutManager.findLastVisibleItemPosition();
|
||||
updatePageNumber();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onImageTouch(MotionEvent motionEvent) {
|
||||
return gestureDetector.onTouchEvent(motionEvent);
|
||||
}
|
||||
|
||||
private void observeStatus(int position) {
|
||||
if (position == pages.size())
|
||||
return;
|
||||
|
||||
final Page page = pages.get(position);
|
||||
|
||||
PublishSubject<Integer> statusSubject = PublishSubject.create();
|
||||
page.setStatusSubject(statusSubject);
|
||||
|
||||
// Unsubscribe from the previous page
|
||||
unsubscribeStatus();
|
||||
|
||||
subscription = statusSubject
|
||||
.startWith(page.getStatus())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(status -> processStatus(position, status));
|
||||
}
|
||||
|
||||
private void processStatus(int position, int status) {
|
||||
adapter.notifyItemChanged(position);
|
||||
if (status == Page.READY) {
|
||||
observeStatus(position + 1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user