Browse Source

Reader view in Kotlin. Upgrade gradle wrapper. Remove ButterKnife from the project

len 9 năm trước cách đây
mục cha
commit
4ccce424de

+ 0 - 1
app/build.gradle

@@ -136,7 +136,6 @@ dependencies {
     compile "com.pushtorefresh.storio:sqlite-annotations:$STORIO_VERSION"
     compile 'info.android15.nucleus:nucleus:2.0.5'
     compile 'com.github.bumptech.glide:glide:3.7.0'
-    compile 'com.jakewharton:butterknife:7.0.1'
     compile 'com.jakewharton.timber:timber:4.1.1'
     compile 'ch.acra:acra:4.8.3'
     compile "frankiesardo:icepick:$ICEPICK_VERSION"

+ 2 - 1
app/src/main/AndroidManifest.xml

@@ -31,7 +31,8 @@
         </activity>
         <activity
             android:name=".ui.reader.ReaderActivity"
-            android:parentActivityName=".ui.manga.MangaActivity">
+            android:parentActivityName=".ui.manga.MangaActivity"
+            android:theme="@style/Theme.Reader">
             <meta-data
                 android:name="android.support.PARENT_ACTIVITY"
                 android:value=".ui.manga.MangaActivity" />

+ 11 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/base/listener/SimpleAnimationListener.kt

@@ -0,0 +1,11 @@
+package eu.kanade.tachiyomi.ui.base.listener
+
+import android.view.animation.Animation
+
+open class SimpleAnimationListener : Animation.AnimationListener {
+    override fun onAnimationRepeat(animation: Animation) {}
+
+    override fun onAnimationEnd(animation: Animation) {}
+
+    override fun onAnimationStart(animation: Animation) {}
+}

+ 11 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/base/listener/SimpleSeekBarListener.kt

@@ -0,0 +1,11 @@
+package eu.kanade.tachiyomi.ui.base.listener
+
+import android.widget.SeekBar
+
+open class SimpleSeekBarListener : SeekBar.OnSeekBarChangeListener {
+    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {}
+
+    override fun onStartTrackingTouch(seekBar: SeekBar) {}
+
+    override fun onStopTrackingTouch(seekBar: SeekBar) {}
+}

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/widget/SimpleTextWatcher.kt → app/src/main/java/eu/kanade/tachiyomi/ui/base/listener/SimpleTextWatcher.kt

@@ -1,4 +1,4 @@
-package eu.kanade.tachiyomi.widget
+package eu.kanade.tachiyomi.ui.base.listener
 
 import android.text.Editable
 import android.text.TextWatcher

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/manga/myanimelist/MyAnimeListDialogFragment.kt

@@ -7,7 +7,7 @@ import android.view.View
 import com.afollestad.materialdialogs.MaterialDialog
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.models.MangaSync
-import eu.kanade.tachiyomi.widget.SimpleTextWatcher
+import eu.kanade.tachiyomi.ui.base.listener.SimpleTextWatcher
 import kotlinx.android.synthetic.main.dialog_myanimelist_search.view.*
 import rx.Subscription
 import rx.android.schedulers.AndroidSchedulers

+ 0 - 423
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.java

@@ -1,423 +0,0 @@
-package eu.kanade.tachiyomi.ui.reader;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.res.Configuration;
-import android.graphics.Color;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.Toolbar;
-import android.view.KeyEvent;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.view.View;
-import android.view.WindowManager;
-import android.widget.TextView;
-
-import com.afollestad.materialdialogs.MaterialDialog;
-
-import java.util.List;
-
-import butterknife.Bind;
-import butterknife.ButterKnife;
-import eu.kanade.tachiyomi.R;
-import eu.kanade.tachiyomi.data.database.models.Chapter;
-import eu.kanade.tachiyomi.data.database.models.Manga;
-import eu.kanade.tachiyomi.data.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;
-
-    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) {
-        setTheme(R.style.Theme_Reader);
-        super.onCreate(savedState);
-        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.getActivePage());
-        super.onPause();
-    }
-
-    @Override
-    protected void onDestroy() {
-        subscriptions.unsubscribe();
-        readerMenu.destroy();
-        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.getActivePage());
-        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();
-        }
-    }
-
-    @Override
-    public void onWindowFocusChanged(boolean hasFocus) {
-        super.onWindowFocusChanged(hasFocus);
-        if (hasFocus) {
-            setSystemUiVisibility();
-        }
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        int action = event.getAction();
-        int keyCode = event.getKeyCode();
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_VOLUME_DOWN:
-                if (action == KeyEvent.ACTION_UP && viewer != null)
-                    viewer.moveToNext();
-                return true;
-            case KeyEvent.KEYCODE_VOLUME_UP:
-                if (action == KeyEvent.ACTION_UP && viewer != null)
-                    viewer.moveToPrevious();
-                return true;
-            default:
-                return super.dispatchKeyEvent(event);
-        }
-    }
-
-    public void onChapterError() {
-        finish();
-        ToastUtil.showShort(this, R.string.page_list_error);
-    }
-
-    public void onChapterAppendError() {
-        // Ignore
-    }
-
-    public void onChapterReady(Manga manga, Chapter chapter, Page currentPage) {
-        List<Page> pages = chapter.getPages();
-        if (currentPage == null) {
-            currentPage = pages.get(pages.size() - 1);
-        }
-
-        if (viewer == null) {
-            viewer = getOrCreateViewer(manga);
-        }
-        viewer.onPageListReady(chapter, currentPage);
-        readerMenu.setActiveManga(manga);
-        readerMenu.setActiveChapter(chapter, currentPage.getPageNumber());
-    }
-
-    public void onEnterChapter(Chapter chapter, int currentPage) {
-        if (currentPage == -1) {
-            currentPage = chapter.getPages().size() - 1;
-        }
-        getPresenter().setActiveChapter(chapter);
-        readerMenu.setActiveChapter(chapter, currentPage);
-    }
-
-    public void onAppendChapter(Chapter chapter) {
-        viewer.onPageListAppendReady(chapter);
-    }
-
-    public void onAdjacentChapters(Chapter previous, Chapter next) {
-        readerMenu.onAdjacentChapters(previous, next);
-    }
-
-    private BaseReader getOrCreateViewer(Manga manga) {
-        int mangaViewer = manga.viewer == 0 ? getPreferences().getDefaultViewer() : manga.viewer;
-
-        FragmentManager fm = getSupportFragmentManager();
-
-        // Try to reuse the viewer using its tag
-        BaseReader fragment = (BaseReader) fm.findFragmentByTag(manga.viewer + "");
-        if (fragment == null) {
-            // Create a new viewer
-            switch (mangaViewer) {
-                case LEFT_TO_RIGHT: default:
-                    fragment = new LeftToRightReader();
-                    break;
-                case RIGHT_TO_LEFT:
-                    fragment = new RightToLeftReader();
-                    break;
-                case VERTICAL:
-                    fragment = new VerticalReader();
-                    break;
-                case WEBTOON:
-                    fragment = new WebtoonReader();
-                    break;
-            }
-
-            fm.beginTransaction().replace(R.id.reader, fragment, manga.viewer + "").commit();
-        }
-        return fragment;
-    }
-
-    public void onPageChanged(int currentPageIndex, int totalPages) {
-        String page = (currentPageIndex + 1) + "/" + totalPages;
-        pageNumber.setText(page);
-        readerMenu.onPageChanged(currentPageIndex);
-    }
-
-    public void gotoPageInCurrentChapter(int pageIndex) {
-        Page requestedPage = viewer.getActivePage().getChapter().getPages().get(pageIndex);
-        viewer.setActivePage(requestedPage);
-    }
-
-    public void onCenterSingleTap() {
-        readerMenu.toggle();
-    }
-
-    public void requestNextChapter() {
-        getPresenter().setCurrentPage(viewer.getActivePage());
-        if (!getPresenter().loadNextChapter()) {
-            ToastUtil.showShort(this, R.string.no_next_chapter);
-        }
-
-    }
-
-    public void requestPreviousChapter() {
-        getPresenter().setCurrentPage(viewer.getActivePage());
-        if (!getPresenter().loadPreviousChapter()) {
-            ToastUtil.showShort(this, R.string.no_previous_chapter);
-        }
-    }
-
-    private void initializeSettings() {
-        PreferencesHelper preferences = getPreferences();
-
-        subscriptions.add(preferences.showPageNumber()
-                .asObservable()
-                .subscribe(this::setPageNumberVisibility));
-
-        subscriptions.add(preferences.rotation()
-                .asObservable()
-                .subscribe(this::setRotation));
-
-        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 setRotation(int rotation) {
-        switch (rotation) {
-            // Rotation free
-            case 1:
-                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-                break;
-            // Lock in current rotation
-            case 2:
-                int currentOrientation = getResources().getConfiguration().orientation;
-                setRotation(currentOrientation == Configuration.ORIENTATION_PORTRAIT ? 3 : 4);
-                break;
-            // Lock in portrait
-            case 3:
-                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
-                break;
-            // Lock in landscape
-            case 4:
-                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
-                break;
-        }
-    }
-
-    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 = getPreferences().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_LAYOUT_STABLE
-                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
-                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
-                    | 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.textColorPrimaryDark));
-            pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundDark));
-        } else {
-            rootView.setBackgroundColor(Color.WHITE);
-            pageNumber.setTextColor(ContextCompat.getColor(this, R.color.textColorPrimaryLight));
-            pageNumber.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundLight));
-        }
-    }
-
-    public int getReaderTheme() {
-        return readerTheme;
-    }
-
-    public PreferencesHelper getPreferences() {
-        return getPresenter().prefs;
-    }
-
-    public BaseReader getViewer() {
-        return viewer;
-    }
-
-    public int getMaxBitmapSize() {
-        return maxBitmapSize;
-    }
-
-}

+ 536 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt

@@ -0,0 +1,536 @@
+package eu.kanade.tachiyomi.ui.reader
+
+import android.app.Dialog
+import android.content.Context
+import android.content.Intent
+import android.content.pm.ActivityInfo
+import android.content.res.Configuration
+import android.graphics.Color
+import android.os.Build
+import android.os.Bundle
+import android.support.v4.content.ContextCompat
+import android.view.*
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.view.animation.Animation
+import android.view.animation.AnimationUtils
+import android.widget.SeekBar
+import com.afollestad.materialdialogs.MaterialDialog
+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.preference.getOrDefault
+import eu.kanade.tachiyomi.data.source.model.Page
+import eu.kanade.tachiyomi.ui.base.activity.BaseRxActivity
+import eu.kanade.tachiyomi.ui.base.listener.SimpleAnimationListener
+import eu.kanade.tachiyomi.ui.base.listener.SimpleSeekBarListener
+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.toast
+import kotlinx.android.synthetic.main.activity_reader.*
+import kotlinx.android.synthetic.main.reader_menu.*
+import nucleus.factory.RequiresPresenter
+import rx.Subscription
+import rx.subscriptions.CompositeSubscription
+import java.text.DecimalFormat
+
+@RequiresPresenter(ReaderPresenter::class)
+class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
+
+    companion object {
+        @Suppress("unused")
+        const val LEFT_TO_RIGHT = 1
+        const val RIGHT_TO_LEFT = 2
+        const val VERTICAL = 3
+        const val WEBTOON = 4
+
+        const val BLACK_THEME = 1
+
+        const val MENU_VISIBLE = "menu_visible"
+
+        fun newIntent(context: Context): Intent {
+            return Intent(context, ReaderActivity::class.java)
+        }
+    }
+
+    private var viewer: BaseReader? = null
+
+    private var uiFlags: Int = 0
+
+    lateinit var subscriptions: CompositeSubscription
+        private set
+
+    private var customBrightnessSubscription: Subscription? = null
+
+    var readerTheme: Int = 0
+        private set
+
+    var maxBitmapSize: Int = 0
+        private set
+
+    private val decimalFormat = DecimalFormat("#.###")
+
+    private var popupMenu: ReaderPopupMenu? = null
+
+    private var nextChapterBtn: MenuItem? = null
+
+    private var prevChapterBtn: MenuItem? = null
+
+    val preferences: PreferencesHelper
+        get() = presenter.prefs
+
+    public override fun onCreate(savedState: Bundle?) {
+        super.onCreate(savedState)
+        setContentView(R.layout.activity_reader)
+
+        setupToolbar(toolbar)
+        subscriptions = CompositeSubscription()
+
+        initializeMenu()
+        initializeSettings()
+
+        if (savedState != null) {
+            setMenuVisibility(savedState.getBoolean(MENU_VISIBLE), animate = false)
+        }
+
+        maxBitmapSize = GLUtil.getMaxTextureSize()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        setSystemUiVisibility()
+    }
+
+    override fun onPause() {
+        viewer?.let {
+            presenter.currentPage = it.getActivePage()
+        }
+        super.onPause()
+    }
+
+    override fun onDestroy() {
+        subscriptions.unsubscribe()
+        popupMenu?.dismiss()
+        viewer = null
+        super.onDestroy()
+    }
+
+    override fun onCreateOptionsMenu(menu: Menu): Boolean {
+        menuInflater.inflate(R.menu.reader, menu)
+        nextChapterBtn = menu.findItem(R.id.action_next_chapter)
+        prevChapterBtn = menu.findItem(R.id.action_previous_chapter)
+        setAdjacentChaptersVisibility()
+        return true
+    }
+
+    override fun onOptionsItemSelected(item: MenuItem): Boolean {
+        when (item.itemId) {
+            R.id.action_previous_chapter -> requestPreviousChapter()
+            R.id.action_next_chapter -> requestNextChapter()
+            else -> return super.onOptionsItemSelected(item)
+        }
+        return true
+    }
+
+    override fun onSaveInstanceState(outState: Bundle) {
+        outState.putBoolean(MENU_VISIBLE, reader_menu.visibility == View.VISIBLE)
+        super.onSaveInstanceState(outState)
+    }
+
+    override fun onBackPressed() {
+        presenter.onChapterLeft()
+
+        val chapterToUpdate = presenter.mangaSyncChapterToUpdate
+
+        if (chapterToUpdate > 0) {
+            if (presenter.prefs.askUpdateMangaSync()) {
+                MaterialDialog.Builder(this)
+                        .content(getString(R.string.confirm_update_manga_sync, chapterToUpdate))
+                        .positiveText(android.R.string.yes)
+                        .negativeText(android.R.string.no)
+                        .onPositive { dialog, which -> presenter.updateMangaSyncLastChapterRead() }
+                        .onAny { dialog1, which1 -> finish() }
+                        .show()
+            } else {
+                presenter.updateMangaSyncLastChapterRead()
+                finish()
+            }
+        } else {
+            super.onBackPressed()
+        }
+    }
+
+    override fun onWindowFocusChanged(hasFocus: Boolean) {
+        super.onWindowFocusChanged(hasFocus)
+        if (hasFocus) {
+            setSystemUiVisibility()
+        }
+    }
+
+    override fun dispatchKeyEvent(event: KeyEvent): Boolean {
+        val action = event.action
+        val keyCode = event.keyCode
+        when (keyCode) {
+            KeyEvent.KEYCODE_VOLUME_DOWN -> {
+                if (action == KeyEvent.ACTION_UP)
+                    viewer?.moveToNext()
+                return true
+            }
+            KeyEvent.KEYCODE_VOLUME_UP -> {
+                if (action == KeyEvent.ACTION_UP)
+                    viewer?.moveToPrevious()
+                return true
+            }
+            else -> return super.dispatchKeyEvent(event)
+        }
+    }
+
+    fun onChapterError() {
+        finish()
+        toast(R.string.page_list_error)
+    }
+
+    fun onChapterAppendError() {
+        // Ignore
+    }
+
+    fun onChapterReady(manga: Manga, chapter: Chapter, currentPage: Page?) {
+        val activePage = currentPage ?: chapter.pages.last()
+
+        if (viewer == null) {
+            viewer = getOrCreateViewer(manga)
+        }
+        viewer?.onPageListReady(chapter, activePage)
+
+        if (viewer is RightToLeftReader && page_seekbar.rotation != 180f) {
+            // Invert the seekbar for the right to left reader
+            page_seekbar.rotation = 180f
+        }
+        setToolbarTitle(manga.title)
+        setActiveChapter(chapter, activePage.pageNumber)
+    }
+
+    fun onEnterChapter(chapter: Chapter, currentPage: Int) {
+        val activePage = if (currentPage == -1) chapter.pages.lastIndex else currentPage
+        presenter.setActiveChapter(chapter)
+        setActiveChapter(chapter, activePage)
+    }
+
+    fun setActiveChapter(chapter: Chapter, currentPage: Int) {
+        val numPages = chapter.pages.size
+        if (page_seekbar.rotation != 180f) {
+            right_page_text.text = "$numPages"
+            left_page_text.text = "${currentPage + 1}"
+        } else {
+            left_page_text.text = "$numPages"
+            right_page_text.text = "${currentPage + 1}"
+        }
+        page_seekbar.max = numPages - 1
+        page_seekbar.progress = currentPage
+
+        setToolbarSubtitle(if (chapter.chapter_number != -1f)
+            getString(R.string.chapter_subtitle, decimalFormat.format(chapter.chapter_number.toDouble()))
+        else
+            chapter.name)
+    }
+
+    fun onAppendChapter(chapter: Chapter) {
+        viewer?.onPageListAppendReady(chapter)
+    }
+
+    @Suppress("UNUSED_PARAMETER")
+    fun onAdjacentChapters(previous: Chapter, next: Chapter) {
+        setAdjacentChaptersVisibility()
+    }
+
+    private fun setAdjacentChaptersVisibility() {
+        prevChapterBtn?.isVisible = presenter.hasPreviousChapter()
+        nextChapterBtn?.isVisible = presenter.hasNextChapter()
+    }
+
+    private fun getOrCreateViewer(manga: Manga): BaseReader {
+        val mangaViewer = if (manga.viewer == 0) preferences.defaultViewer else manga.viewer
+
+        // Try to reuse the viewer using its tag
+        var fragment: BaseReader? = supportFragmentManager.findFragmentByTag(manga.viewer.toString()) as? BaseReader
+        if (fragment == null) {
+            // Create a new viewer
+            when (mangaViewer) {
+                RIGHT_TO_LEFT -> fragment = RightToLeftReader()
+                VERTICAL -> fragment = VerticalReader()
+                WEBTOON -> fragment = WebtoonReader()
+                else -> fragment = LeftToRightReader()
+            }
+
+            supportFragmentManager.beginTransaction().replace(R.id.reader, fragment, manga.viewer.toString()).commit()
+        }
+        return fragment
+    }
+
+    fun onPageChanged(currentPageIndex: Int, totalPages: Int) {
+        val page = currentPageIndex + 1
+        page_number.text = "$page/$totalPages"
+        if (page_seekbar.rotation != 180f) {
+            left_page_text.text = "$page"
+        } else {
+            right_page_text.text = "$page"
+        }
+        page_seekbar.progress = currentPageIndex
+    }
+
+    fun gotoPageInCurrentChapter(pageIndex: Int) {
+        viewer?.let {
+            val requestedPage = it.getActivePage().chapter.pages[pageIndex]
+            it.setActivePage(requestedPage)
+        }
+    }
+
+    fun onCenterSingleTap() {
+        setMenuVisibility(reader_menu.visibility == View.GONE)
+    }
+
+    fun requestNextChapter() {
+        if (!presenter.loadNextChapter()) {
+            toast(R.string.no_next_chapter)
+        }
+    }
+
+    fun requestPreviousChapter() {
+        if (!presenter.loadPreviousChapter()) {
+            toast(R.string.no_previous_chapter)
+        }
+    }
+
+    private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
+        if (visible) {
+            reader_menu.visibility = View.VISIBLE
+
+            if (animate) {
+                val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_top)
+                toolbar.startAnimation(toolbarAnimation)
+
+                val bottomMenuAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_bottom)
+                reader_menu_bottom.startAnimation(bottomMenuAnimation)
+            }
+        } else {
+            val toolbarAnimation = AnimationUtils.loadAnimation(this, R.anim.exit_to_top)
+            toolbarAnimation.setAnimationListener(object : SimpleAnimationListener() {
+                override fun onAnimationEnd(animation: Animation) {
+                    reader_menu.visibility = View.GONE
+                }
+            })
+            toolbar.startAnimation(toolbarAnimation)
+
+            val bottomMenuAnimation = AnimationUtils.loadAnimation(this, R.anim.exit_to_bottom)
+            reader_menu_bottom.startAnimation(bottomMenuAnimation)
+
+            popupMenu?.dismiss()
+        }
+    }
+
+    private fun initializeMenu() {
+        // Intercept all events in this layout
+        reader_menu_bottom.setOnTouchListener { v, event -> true }
+
+        page_seekbar.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
+            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+                if (fromUser) {
+                    gotoPageInCurrentChapter(progress)
+                }
+            }
+        })
+
+        lock_orientation.setOnClickListener { v ->
+            showImmersiveDialog(MaterialDialog.Builder(this)
+                    .title(R.string.pref_rotation_type)
+                    .items(R.array.rotation_type)
+                    .itemsCallbackSingleChoice(preferences.rotation().getOrDefault() - 1,
+                            { d, itemView, which, text ->
+                                preferences.rotation().set(which + 1)
+                                true
+                            })
+                    .build())
+        }
+
+        reader_zoom_selector.setOnClickListener { v ->
+            showImmersiveDialog(MaterialDialog.Builder(this)
+                    .title(R.string.pref_zoom_start)
+                    .items(R.array.zoom_start)
+                    .itemsCallbackSingleChoice(preferences.zoomStart().getOrDefault() - 1,
+                            { d, itemView, which, text ->
+                                preferences.zoomStart().set(which + 1)
+                                true
+                            })
+                    .build())
+        }
+
+        reader_scale_type_selector.setOnClickListener { v ->
+            showImmersiveDialog(MaterialDialog.Builder(this)
+                    .title(R.string.pref_image_scale_type)
+                    .items(R.array.image_scale_type)
+                    .itemsCallbackSingleChoice(preferences.imageScaleType().getOrDefault() - 1,
+                            { d, itemView, which, text ->
+                                preferences.imageScaleType().set(which + 1)
+                                true
+                            })
+                    .build())
+        }
+
+        reader_selector.setOnClickListener { v ->
+            showImmersiveDialog(MaterialDialog.Builder(this)
+                    .title(R.string.pref_viewer_type)
+                    .items(R.array.viewers_selector)
+                    .itemsCallbackSingleChoice(presenter.manga.viewer,
+                            { d, itemView, which, text ->
+                                presenter.updateMangaViewer(which)
+                                recreate()
+                                true
+                            })
+                    .build())
+        }
+
+        val popupView = layoutInflater.inflate(R.layout.reader_popup, null)
+        popupMenu = ReaderPopupMenu(this, popupView)
+
+        reader_extra_settings.setOnClickListener {
+            popupMenu?.let {
+                if (!it.isShowing)
+                    it.showAtLocation(reader_extra_settings,
+                            Gravity.BOTTOM or Gravity.RIGHT, 0, reader_menu_bottom.height)
+                else
+                    it.dismiss()
+            }
+
+        }
+
+    }
+
+    private fun initializeSettings() {
+        subscriptions.add(preferences.showPageNumber().asObservable()
+                .subscribe { setPageNumberVisibility(it) })
+
+        subscriptions.add(preferences.rotation().asObservable()
+                .subscribe {
+                    setRotation(it)
+
+                    val isPortrait = resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
+
+                    val resourceId = if (it == 1)
+                        R.drawable.ic_screen_rotation_white_24dp
+                    else if (isPortrait)
+                        R.drawable.ic_screen_lock_portrait_white_24dp
+                    else
+                        R.drawable.ic_screen_lock_landscape_white_24dp
+
+                    lock_orientation.setImageResource(resourceId)
+                })
+
+        subscriptions.add(preferences.hideStatusBar().asObservable()
+                .subscribe { setStatusBarVisibility(it) })
+
+        subscriptions.add(preferences.keepScreenOn().asObservable()
+                .subscribe { setKeepScreenOn(it) })
+
+        subscriptions.add(preferences.customBrightness().asObservable()
+                .subscribe { setCustomBrightness(it) })
+
+        subscriptions.add(preferences.readerTheme().asObservable()
+                .distinctUntilChanged()
+                .subscribe { applyTheme(it) })
+    }
+
+    private fun setRotation(rotation: Int) {
+        when (rotation) {
+            // Rotation free
+            1 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
+            // Lock in current rotation
+            2 -> {
+                val currentOrientation = resources.configuration.orientation
+                setRotation(if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) 3 else 4)
+            }
+            // Lock in portrait
+            3 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT
+            // Lock in landscape
+            4 -> requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
+        }
+    }
+
+    private fun setPageNumberVisibility(visible: Boolean) {
+        page_number.visibility = if (visible) View.VISIBLE else View.INVISIBLE
+    }
+
+    private fun setKeepScreenOn(enabled: Boolean) {
+        if (enabled) {
+            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+        } else {
+            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+        }
+    }
+
+    private fun setCustomBrightness(enabled: Boolean) {
+        if (enabled) {
+            customBrightnessSubscription = preferences.customBrightnessValue().asObservable()
+                    .subscribe { setCustomBrightnessValue(it) }
+
+            subscriptions.add(customBrightnessSubscription)
+        } else {
+            if (customBrightnessSubscription != null)
+                subscriptions.remove(customBrightnessSubscription)
+            setCustomBrightnessValue(-1f)
+        }
+    }
+
+    private fun setCustomBrightnessValue(value: Float) {
+        window.attributes = window.attributes.apply { screenBrightness = value }
+    }
+
+    private fun setStatusBarVisibility(hidden: Boolean) {
+        createUiHideFlags(hidden)
+        setSystemUiVisibility()
+    }
+
+    private fun createUiHideFlags(statusBarHidden: Boolean) {
+        uiFlags = 0
+        uiFlags = uiFlags or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+        if (statusBarHidden) {
+            uiFlags = uiFlags or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or
+                    View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or
+                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or
+                    View.SYSTEM_UI_FLAG_FULLSCREEN
+        }
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+            uiFlags = uiFlags or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
+        }
+    }
+
+    fun setSystemUiVisibility() {
+        window.decorView.systemUiVisibility = uiFlags
+    }
+
+    private fun applyTheme(theme: Int) {
+        readerTheme = theme
+        val rootView = window.decorView.rootView
+        if (theme == BLACK_THEME) {
+            rootView.setBackgroundColor(Color.BLACK)
+            page_number.setTextColor(ContextCompat.getColor(this, R.color.textColorPrimaryDark))
+            page_number.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundDark))
+        } else {
+            rootView.setBackgroundColor(Color.WHITE)
+            page_number.setTextColor(ContextCompat.getColor(this, R.color.textColorPrimaryLight))
+            page_number.setBackgroundColor(ContextCompat.getColor(this, R.color.backgroundLight))
+        }
+    }
+
+    private fun showImmersiveDialog(dialog: Dialog) {
+        // Hack to not leave immersive mode
+        dialog.window.setFlags(FLAG_NOT_FOCUSABLE, FLAG_NOT_FOCUSABLE)
+        dialog.show()
+        dialog.window.decorView.systemUiVisibility = window.decorView.systemUiVisibility
+        dialog.window.clearFlags(FLAG_NOT_FOCUSABLE)
+    }
+
+}

+ 0 - 421
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderMenu.java

@@ -1,421 +0,0 @@
-package eu.kanade.tachiyomi.ui.reader;
-
-import android.app.Dialog;
-import android.content.res.Configuration;
-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.ViewGroup;
-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 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_zoom_selector) ImageButton zoomSelector;
-    @Bind(R.id.reader_scale_type_selector) ImageButton scaleTypeSelector;
-    @Bind(R.id.reader_selector) ImageButton readerSelector;
-    @Bind(R.id.reader_extra_settings) ImageButton extraSettings;
-
-    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 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;
-
-        initializeMenu();
-    }
-
-    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();
-
-        showing = false;
-    }
-
-    public void destroy() {
-        if (settingsPopup != null) {
-            settingsPopup.dismiss();
-        }
-    }
-
-    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 setActiveManga(Manga manga) {
-        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;
-        }
-        activity.setToolbarTitle(manga.title);
-    }
-
-    public void setActiveChapter(Chapter chapter, int currentPageIndex) {
-        // Set initial values
-        int numPages = chapter.getPages().size();
-        totalPages.setText("" + numPages);
-        currentPage.setText("" + (currentPageIndex + 1));
-        seekBar.setMax(numPages - 1);
-        seekBar.setProgress(currentPageIndex);
-
-        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);
-    }
-
-    @SuppressWarnings("ConstantConditions")
-    private void initializeMenu() {
-        // Orientation selector
-        add(preferences.rotation().asObservable()
-                .subscribe(value -> {
-                    boolean isPortrait = activity.getResources().getConfiguration()
-                            .orientation == Configuration.ORIENTATION_PORTRAIT;
-                    int resourceId = value == 1 ? R.drawable.ic_screen_rotation_white_24dp : isPortrait ?
-                            R.drawable.ic_screen_lock_portrait_white_24dp :
-                            R.drawable.ic_screen_lock_landscape_white_24dp;
-
-                    lockOrientation.setImageResource(resourceId);
-                }));
-
-        lockOrientation.setOnClickListener(v -> {
-            showImmersiveDialog(new MaterialDialog.Builder(activity)
-                    .title(R.string.pref_rotation_type)
-                    .items(R.array.rotation_type)
-                    .itemsCallbackSingleChoice(preferences.rotation().get() - 1,
-                            (d, itemView, which, text) -> {
-                                preferences.rotation().set(which + 1);
-                                return true;
-                            })
-                    .build());
-        });
-
-        // Zoom selector
-        zoomSelector.setOnClickListener(v -> {
-            showImmersiveDialog(new MaterialDialog.Builder(activity)
-                    .title(R.string.pref_zoom_start)
-                    .items(R.array.zoom_start)
-                    .itemsCallbackSingleChoice(preferences.zoomStart().get() - 1,
-                            (d, itemView, which, text) -> {
-                                preferences.zoomStart().set(which + 1);
-                                return true;
-                            })
-                    .build());
-        });
-
-        // Scale type selector
-        scaleTypeSelector.setOnClickListener(v -> {
-            showImmersiveDialog(new MaterialDialog.Builder(activity)
-                    .title(R.string.pref_image_scale_type)
-                    .items(R.array.image_scale_type)
-                    .itemsCallbackSingleChoice(preferences.imageScaleType().get() - 1,
-                            (d, itemView, which, text) -> {
-                                preferences.imageScaleType().set(which + 1);
-                                return true;
-                            })
-                    .build());
-        });
-
-        // Reader selector
-        readerSelector.setOnClickListener(v -> {
-            final Manga manga = activity.getPresenter().getManga();
-            showImmersiveDialog(new MaterialDialog.Builder(activity)
-                    .title(R.string.pref_viewer_type)
-                    .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();
-        });
-
-    }
-
-    private void showImmersiveDialog(Dialog dialog) {
-        // Hack to not leave immersive mode
-        dialog.getWindow().setFlags(LayoutParams.FLAG_NOT_FOCUSABLE, LayoutParams.FLAG_NOT_FOCUSABLE);
-        dialog.show();
-        dialog.getWindow().getDecorView().setSystemUiVisibility(
-                activity.getWindow().getDecorView().getSystemUiVisibility());
-        dialog.getWindow().clearFlags(LayoutParams.FLAG_NOT_FOCUSABLE);
-    }
-
-    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_container) ViewGroup imageDecoderContainer;
-        @Bind(R.id.image_decoder) TextView imageDecoder;
-        @Bind(R.id.image_decoder_initial) TextView imageDecoderInitial;
-        @Bind(R.id.custom_brightness) CheckBox customBrightness;
-        @Bind(R.id.brightness_seekbar) SeekBar brightnessSeekbar;
-
-        public SettingsPopupWindow(View view) {
-            super(view, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
-            setAnimationStyle(R.style.reader_settings_popup_animation);
-            ButterKnife.bind(this, view);
-            initializePopupMenu();
-        }
-
-        @SuppressWarnings("ConstantConditions")
-        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));
-
-            imageDecoderContainer.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());
-            });
-
-            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());
-        }
-
-        private void setDecoderInitial(int decoder) {
-            String initial;
-            switch (decoder) {
-                case 0:
-                    initial = "R";
-                    break;
-                case 1:
-                    initial = "S";
-                    break;
-                default:
-                    initial = "";
-                    break;
-            }
-            imageDecoderInitial.setText(initial);
-        }
-
-    }
-
-    class PageSeekBarChangeListener implements SeekBar.OnSeekBarChangeListener {
-
-        @Override
-        public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
-            if (fromUser) {
-                activity.gotoPageInCurrentChapter(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) {
-
-        }
-    }
-
-}

+ 108 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPopupMenu.kt

@@ -0,0 +1,108 @@
+package eu.kanade.tachiyomi.ui.reader
+
+import android.app.Dialog
+import android.view.View
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+import android.widget.PopupWindow
+import android.widget.SeekBar
+import com.afollestad.materialdialogs.MaterialDialog
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.preference.PreferencesHelper
+import eu.kanade.tachiyomi.data.preference.getOrDefault
+import eu.kanade.tachiyomi.ui.base.listener.SimpleSeekBarListener
+import kotlinx.android.synthetic.main.reader_popup.view.*
+import java.lang.ref.WeakReference
+
+class ReaderPopupMenu(activity: ReaderActivity, val view: View) : PopupWindow(view, WRAP_CONTENT, WRAP_CONTENT) {
+
+    val activity = WeakReference(activity)
+
+    init {
+        animationStyle = R.style.reader_settings_popup_animation
+        setValues(activity.preferences)
+    }
+
+    private fun setValues(preferences: PreferencesHelper) = with(view) {
+        enable_transitions.isChecked = preferences.enableTransitions().getOrDefault()
+        show_page_number.isChecked = preferences.showPageNumber().getOrDefault()
+        hide_status_bar.isChecked = preferences.hideStatusBar().getOrDefault()
+        keep_screen_on.isChecked = preferences.keepScreenOn().getOrDefault()
+        reader_theme.isChecked = preferences.readerTheme().getOrDefault() == 1
+
+        setDecoderInitial(preferences.imageDecoder().getOrDefault())
+
+        // Add a listener to change the corresponding setting
+        enable_transitions.setOnCheckedChangeListener { v, isChecked ->
+            preferences.enableTransitions().set(isChecked)
+        }
+
+        show_page_number.setOnCheckedChangeListener { v, isChecked ->
+            preferences.showPageNumber().set(isChecked)
+        }
+
+        hide_status_bar.setOnCheckedChangeListener { v, isChecked ->
+            preferences.hideStatusBar().set(isChecked)
+        }
+
+        keep_screen_on.setOnCheckedChangeListener { v, isChecked ->
+            preferences.keepScreenOn().set(isChecked)
+        }
+
+        reader_theme.setOnCheckedChangeListener { v, isChecked ->
+            preferences.readerTheme().set(if (isChecked) 1 else 0)
+        }
+
+        image_decoder_container.setOnClickListener { v ->
+            showImmersiveDialog(MaterialDialog.Builder(view.context)
+                    .title(R.string.pref_image_decoder)
+                    .items(R.array.image_decoders)
+                    .itemsCallbackSingleChoice(preferences.imageDecoder().getOrDefault(), { dialog, itemView, which, text ->
+                        preferences.imageDecoder().set(which)
+                        setDecoderInitial(which)
+                        true
+                    })
+                    .build())
+        }
+
+        activity.get().subscriptions.add(preferences.customBrightness().asObservable()
+                .subscribe { isEnabled ->
+                    custom_brightness.isChecked = isEnabled
+                    brightness_seekbar.isEnabled = isEnabled
+                })
+
+        custom_brightness.setOnCheckedChangeListener { v, isChecked ->
+            preferences.customBrightness().set(isChecked)
+        }
+
+        brightness_seekbar.max = 100
+        brightness_seekbar.progress = Math.round(
+                preferences.customBrightnessValue().getOrDefault() * brightness_seekbar.max)
+        brightness_seekbar.setOnSeekBarChangeListener(object : SimpleSeekBarListener() {
+            override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
+                if (fromUser) {
+                    preferences.customBrightnessValue().set(progress.toFloat() / seekBar.max)
+                }
+            }
+        })
+    }
+
+    private fun setDecoderInitial(decoder: Int) {
+        val initial: String
+        when (decoder) {
+            0 -> initial = "R"
+            1 -> initial = "S"
+            else -> initial = ""
+        }
+        view.image_decoder_initial.text = initial
+    }
+
+    private fun showImmersiveDialog(dialog: Dialog) {
+        // Hack to not leave immersive mode
+        dialog.window.setFlags(FLAG_NOT_FOCUSABLE, FLAG_NOT_FOCUSABLE)
+        dialog.show()
+        dialog.window.decorView.systemUiVisibility = activity.get().window.decorView.systemUiVisibility
+        dialog.window.clearFlags(FLAG_NOT_FOCUSABLE)
+    }
+
+}

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt

@@ -12,7 +12,7 @@ import com.dd.processbutton.iml.ActionProcessButton
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.ui.setting.SettingsActivity
-import eu.kanade.tachiyomi.widget.SimpleTextWatcher
+import eu.kanade.tachiyomi.ui.base.listener.SimpleTextWatcher
 import kotlinx.android.synthetic.main.pref_account_login.view.*
 import rx.Subscription
 

+ 3 - 3
app/src/main/res/layout/reader_menu.xml

@@ -29,7 +29,7 @@
             android:layout_height="?attr/actionBarSize">
 
             <TextView
-                android:id="@+id/current_page"
+                android:id="@+id/left_page_text"
                 android:layout_width="32dp"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
@@ -40,7 +40,7 @@
                 android:textSize="15sp"/>
 
             <SeekBar
-                android:id="@+id/page_seeker"
+                android:id="@+id/page_seekbar"
                 android:layout_width="0dp"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"
@@ -48,7 +48,7 @@
                 />
 
             <TextView
-                android:id="@+id/total_pages"
+                android:id="@+id/right_page_text"
                 android:layout_width="32dp"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_vertical"

+ 1 - 1
build.gradle

@@ -6,7 +6,7 @@ buildscript {
         jcenter()
     }
     dependencies {
-        classpath 'com.android.tools.build:gradle:2.1.0-alpha1'
+        classpath 'com.android.tools.build:gradle:2.1.0-alpha3'
         classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
         classpath 'me.tatarka:gradle-retrolambda:3.2.4'
         classpath 'com.github.ben-manes:gradle-versions-plugin:0.12.0'

+ 1 - 1
gradle/wrapper/gradle-wrapper.properties

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.12-all.zip