Browse Source

Rewrite PreferencesHelper. Allow to customize navigation with volume keys and tapping. Closes #251 and closes #129.

len 9 years ago
parent
commit
a7840bc247
21 changed files with 256 additions and 197 deletions
  1. 2 1
      app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt
  2. 2 2
      app/src/main/java/eu/kanade/tachiyomi/data/mangasync/base/MangaSyncService.kt
  3. 2 2
      app/src/main/java/eu/kanade/tachiyomi/data/mangasync/services/MyAnimeList.kt
  4. 87 0
      app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt
  5. 48 123
      app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt
  6. 2 2
      app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.java
  7. 1 1
      app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateDownloader.java
  8. 1 1
      app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt
  9. 26 10
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
  10. 6 0
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/base/BaseReader.kt
  11. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt
  12. 2 2
      app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonReader.kt
  13. 16 12
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadsFragment.kt
  14. 1 5
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNestedFragment.kt
  15. 2 3
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt
  16. 2 3
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSyncFragment.kt
  17. 2 2
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt
  18. 2 2
      app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt
  19. 2 0
      app/src/main/res/values/keys.xml
  20. 3 0
      app/src/main/res/values/strings.xml
  21. 45 24
      app/src/main/res/xml/pref_reader.xml

+ 2 - 1
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt

@@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.download.model.Download
 import eu.kanade.tachiyomi.data.download.model.DownloadQueue
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
+import eu.kanade.tachiyomi.data.preference.getOrDefault
 import eu.kanade.tachiyomi.data.source.SourceManager
 import eu.kanade.tachiyomi.data.source.base.Source
 import eu.kanade.tachiyomi.data.source.model.Page
@@ -365,7 +366,7 @@ class DownloadManager(private val context: Context, private val sourceManager: S
                 File.separator +
                 manga.title.replace("[^\\sa-zA-Z0-9.-]".toRegex(), "_")
 
-        return File(preferences.downloadsDirectory, mangaRelativePath)
+        return File(preferences.downloadsDirectory().getOrDefault(), mangaRelativePath)
     }
 
     // Get the absolute path to the chapter directory

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/mangasync/base/MangaSyncService.kt

@@ -24,8 +24,8 @@ abstract class MangaSyncService(private val context: Context, val id: Int) {
     abstract fun login(username: String, password: String): Observable<Boolean>
 
     open val isLogged: Boolean
-        get() = !preferences.getMangaSyncUsername(this).isEmpty() &&
-                !preferences.getMangaSyncPassword(this).isEmpty()
+        get() = !preferences.mangaSyncUsername(this).isEmpty() &&
+                !preferences.mangaSyncPassword(this).isEmpty()
 
     abstract fun update(manga: MangaSync): Observable<Response>
 

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/mangasync/services/MyAnimeList.kt

@@ -46,8 +46,8 @@ class MyAnimeList(private val context: Context, id: Int) : MangaSyncService(cont
     }
 
     init {
-        val username = preferences.getMangaSyncUsername(this)
-        val password = preferences.getMangaSyncPassword(this)
+        val username = preferences.mangaSyncUsername(this)
+        val password = preferences.mangaSyncPassword(this)
 
         if (!username.isEmpty() && !password.isEmpty()) {
             createHeaders(username, password)

+ 87 - 0
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt

@@ -0,0 +1,87 @@
+package eu.kanade.tachiyomi.data.preference
+
+import android.content.Context
+import eu.kanade.tachiyomi.R
+
+/**
+ * This class stores the keys for the preferences in the application. Most of them are defined
+ * in the file "keys.xml". By using this class we can define preferences in one place and get them
+ * referenced here.
+ */
+class PreferenceKeys(context: Context) {
+
+    val rotation = context.getString(R.string.pref_rotation_type_key)
+
+    val enableTransitions = context.getString(R.string.pref_enable_transitions_key)
+
+    val showPageNumber = context.getString(R.string.pref_show_page_number_key)
+
+    val hideStatusBar = context.getString(R.string.pref_hide_status_bar_key)
+
+    val keepScreenOn = context.getString(R.string.pref_keep_screen_on_key)
+
+    val customBrightness = context.getString(R.string.pref_custom_brightness_key)
+
+    val customBrightnessValue = context.getString(R.string.pref_custom_brightness_value_key)
+
+    val defaultViewer = context.getString(R.string.pref_default_viewer_key)
+
+    val imageScaleType = context.getString(R.string.pref_image_scale_type_key)
+
+    val imageDecoder = context.getString(R.string.pref_image_decoder_key)
+
+    val zoomStart = context.getString(R.string.pref_zoom_start_key)
+
+    val readerTheme = context.getString(R.string.pref_reader_theme_key)
+
+    val readWithTapping = context.getString(R.string.pref_read_with_tapping_key)
+
+    val readWithVolumeKeys = context.getString(R.string.pref_read_with_volume_keys_key)
+
+    val portraitColumns = context.getString(R.string.pref_library_columns_portrait_key)
+
+    val landscapeColumns = context.getString(R.string.pref_library_columns_landscape_key)
+
+    val updateOnlyNonCompleted = context.getString(R.string.pref_update_only_non_completed_key)
+
+    val autoUpdateMangaSync = context.getString(R.string.pref_auto_update_manga_sync_key)
+
+    val askUpdateMangaSync = context.getString(R.string.pref_ask_update_manga_sync_key)
+
+    val lastUsedCatalogueSource = context.getString(R.string.pref_last_catalogue_source_key)
+
+    val seamlessMode = context.getString(R.string.pref_seamless_mode_key)
+
+    val catalogueAsList = context.getString(R.string.pref_display_catalogue_as_list)
+
+    val enabledLanguages = context.getString(R.string.pref_source_languages)
+
+    val downloadsDirectory = context.getString(R.string.pref_download_directory_key)
+
+    val downloadThreads = context.getString(R.string.pref_download_slots_key)
+
+    val downloadOnlyOverWifi = context.getString(R.string.pref_download_only_over_wifi_key)
+
+    val removeAfterRead = context.getString(R.string.pref_remove_after_read_key)
+
+    val removeAfterReadPrevious = context.getString(R.string.pref_remove_after_read_previous_key)
+
+    val removeAfterMarkedAsRead = context.getString(R.string.pref_remove_after_marked_as_read_key)
+
+    val updateOnlyWhenCharging = context.getString(R.string.pref_update_only_when_charging_key)
+
+    val libraryUpdateInterval = context.getString(R.string.pref_library_update_interval_key)
+
+    val filterDownloaded = context.getString(R.string.pref_filter_downloaded_key)
+
+    val filterUnread = context.getString(R.string.pref_filter_unread_key)
+
+    fun sourceUsername(sourceId: Int) = "pref_source_username_$sourceId}"
+
+    fun sourcePassword(sourceId: Int) = "pref_source_password_$sourceId"
+
+    fun syncUsername(syncId: Int) = "pref_mangasync_username_$syncId"
+
+    fun syncPassword(syncId: Int) = "pref_mangasync_password_$syncId"
+
+}

+ 48 - 123
app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

@@ -15,31 +15,25 @@ fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
 
 class PreferencesHelper(private val context: Context) {
 
+    val keys = PreferenceKeys(context)
+
     private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
     private val rxPrefs = RxSharedPreferences.create(prefs)
 
-    private val defaultDownloadsDir: File
+    private val defaultDownloadsDir = File(Environment.getExternalStorageDirectory().absolutePath +
+            File.separator + context.getString(R.string.app_name), "downloads")
 
     init {
-        defaultDownloadsDir = File(Environment.getExternalStorageDirectory().absolutePath +
-                File.separator + context.getString(R.string.app_name), "downloads")
-
         // Don't display downloaded chapters in gallery apps creating a ".nomedia" file
         try {
-            File(downloadsDirectory, ".nomedia").createNewFile()
+            File(downloadsDirectory().getOrDefault(), ".nomedia").createNewFile()
         } catch (e: IOException) {
             /* Ignore */
         }
-
     }
 
     companion object {
 
-        const val SOURCE_ACCOUNT_USERNAME = "pref_source_username_"
-        const val SOURCE_ACCOUNT_PASSWORD = "pref_source_password_"
-        const val MANGASYNC_ACCOUNT_USERNAME = "pref_mangasync_username_"
-        const val MANGASYNC_ACCOUNT_PASSWORD = "pref_mangasync_password_"
-
         fun getLibraryUpdateInterval(context: Context): Int {
             return PreferenceManager.getDefaultSharedPreferences(context).getInt(
                     context.getString(R.string.pref_library_update_interval_key), 0)
@@ -52,165 +46,96 @@ class PreferencesHelper(private val context: Context) {
         }
     }
 
-    private fun getKey(keyResource: Int): String {
-        return context.getString(keyResource)
-    }
-
     fun clear() {
         prefs.edit().clear().apply()
     }
 
-    fun rotation(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_rotation_type_key), 1)
-    }
+    fun rotation() = rxPrefs.getInteger(keys.rotation, 1)
 
-    fun enableTransitions(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_enable_transitions_key), true)
-    }
+    fun enableTransitions() = rxPrefs.getBoolean(keys.enableTransitions, true)
 
-    fun showPageNumber(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_show_page_number_key), true)
-    }
+    fun showPageNumber() = rxPrefs.getBoolean(keys.showPageNumber, true)
 
-    fun hideStatusBar(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_hide_status_bar_key), true)
-    }
+    fun hideStatusBar() = rxPrefs.getBoolean(keys.hideStatusBar, true)
 
-    fun keepScreenOn(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_keep_screen_on_key), true)
-    }
+    fun keepScreenOn() = rxPrefs.getBoolean(keys.keepScreenOn, true)
 
-    fun customBrightness(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_custom_brightness_key), false)
-    }
+    fun customBrightness() = rxPrefs.getBoolean(keys.customBrightness, false)
 
-    fun customBrightnessValue(): Preference<Float> {
-        return rxPrefs.getFloat(getKey(R.string.pref_custom_brightness_value_key), 0f)
-    }
+    fun customBrightnessValue() = rxPrefs.getFloat(keys.customBrightnessValue, 0f)
 
-    val defaultViewer: Int
-        get() = prefs.getInt(getKey(R.string.pref_default_viewer_key), 1)
+    fun defaultViewer() = prefs.getInt(keys.defaultViewer, 1)
 
-    fun imageScaleType(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_image_scale_type_key), 1)
-    }
+    fun imageScaleType() = rxPrefs.getInteger(keys.imageScaleType, 1)
 
-    fun imageDecoder(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_image_decoder_key), 0)
-    }
+    fun imageDecoder() = rxPrefs.getInteger(keys.imageDecoder, 0)
 
-    fun zoomStart(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_zoom_start_key), 1)
-    }
+    fun zoomStart() = rxPrefs.getInteger(keys.zoomStart, 1)
 
-    fun readerTheme(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_reader_theme_key), 0)
-    }
+    fun readerTheme() = rxPrefs.getInteger(keys.readerTheme, 0)
 
-    fun portraitColumns(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_library_columns_portrait_key), 0)
-    }
+    fun readWithTapping() = rxPrefs.getBoolean(keys.readWithTapping, true)
 
-    fun landscapeColumns(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_library_columns_landscape_key), 0)
-    }
+    fun readWithVolumeKeys() = rxPrefs.getBoolean(keys.readWithVolumeKeys, false)
 
-    fun updateOnlyNonCompleted(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_update_only_non_completed_key), false)
-    }
+    fun portraitColumns() = rxPrefs.getInteger(keys.portraitColumns, 0)
 
-    fun autoUpdateMangaSync(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_auto_update_manga_sync_key), true)
-    }
+    fun landscapeColumns() = rxPrefs.getInteger(keys.landscapeColumns, 0)
 
-    fun askUpdateMangaSync(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_ask_update_manga_sync_key), false)
-    }
+    fun updateOnlyNonCompleted() = prefs.getBoolean(keys.updateOnlyNonCompleted, false)
 
-    fun lastUsedCatalogueSource(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_last_catalogue_source_key), -1)
-    }
+    fun autoUpdateMangaSync() = prefs.getBoolean(keys.autoUpdateMangaSync, true)
 
-    fun seamlessMode(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_seamless_mode_key), true)
-    }
+    fun askUpdateMangaSync() = prefs.getBoolean(keys.askUpdateMangaSync, false)
 
-    fun catalogueAsList(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_display_catalogue_as_list), false)
-    }
+    fun lastUsedCatalogueSource() = rxPrefs.getInteger(keys.lastUsedCatalogueSource, -1)
 
-    fun enabledLanguages(): Preference<MutableSet<String>> {
-        return rxPrefs.getStringSet(getKey(R.string.pref_source_languages), setOf("EN"))
-    }
+    fun seamlessMode() = prefs.getBoolean(keys.seamlessMode, true)
 
-    fun getSourceUsername(source: Source): String {
-        return prefs.getString(SOURCE_ACCOUNT_USERNAME + source.id, "")
-    }
+    fun catalogueAsList() = rxPrefs.getBoolean(keys.catalogueAsList, false)
 
-    fun getSourcePassword(source: Source): String {
-        return prefs.getString(SOURCE_ACCOUNT_PASSWORD + source.id, "")
-    }
+    fun enabledLanguages() = rxPrefs.getStringSet(keys.enabledLanguages, setOf("EN"))
+
+    fun sourceUsername(source: Source) = prefs.getString(keys.sourceUsername(source.id), "")
+
+    fun sourcePassword(source: Source) = prefs.getString(keys.sourcePassword(source.id), "")
 
     fun setSourceCredentials(source: Source, username: String, password: String) {
         prefs.edit()
-                .putString(SOURCE_ACCOUNT_USERNAME + source.id, username)
-                .putString(SOURCE_ACCOUNT_PASSWORD + source.id, password)
+                .putString(keys.sourceUsername(source.id), username)
+                .putString(keys.sourcePassword(source.id), password)
                 .apply()
     }
 
-    fun getMangaSyncUsername(sync: MangaSyncService): String {
-        return prefs.getString(MANGASYNC_ACCOUNT_USERNAME + sync.id, "")
-    }
+    fun mangaSyncUsername(sync: MangaSyncService) = prefs.getString(keys.syncUsername(sync.id), "")
 
-    fun getMangaSyncPassword(sync: MangaSyncService): String {
-        return prefs.getString(MANGASYNC_ACCOUNT_PASSWORD + sync.id, "")
-    }
+    fun mangaSyncPassword(sync: MangaSyncService) = prefs.getString(keys.syncPassword(sync.id), "")
 
     fun setMangaSyncCredentials(sync: MangaSyncService, username: String, password: String) {
         prefs.edit()
-                .putString(MANGASYNC_ACCOUNT_USERNAME + sync.id, username)
-                .putString(MANGASYNC_ACCOUNT_PASSWORD + sync.id, password)
+                .putString(keys.syncUsername(sync.id), username)
+                .putString(keys.syncPassword(sync.id), password)
                 .apply()
     }
 
-    var downloadsDirectory: String
-        get() = prefs.getString(getKey(R.string.pref_download_directory_key), defaultDownloadsDir.absolutePath)
-        set(path) = prefs.edit().putString(getKey(R.string.pref_download_directory_key), path).apply()
+    fun downloadsDirectory() = rxPrefs.getString(keys.downloadsDirectory, defaultDownloadsDir.absolutePath)
 
-    fun downloadThreads(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_download_slots_key), 1)
-    }
+    fun downloadThreads() = rxPrefs.getInteger(keys.downloadThreads, 1)
 
-    fun downloadOnlyOverWifi(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_download_only_over_wifi_key), true)
-    }
+    fun downloadOnlyOverWifi() = prefs.getBoolean(keys.downloadOnlyOverWifi, true)
 
-    fun removeAfterRead(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_remove_after_read_key), false)
-    }
+    fun removeAfterRead() = prefs.getBoolean(keys.removeAfterRead, false)
 
-    fun removeAfterReadPrevious(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_remove_after_read_previous_key), false)
-    }
+    fun removeAfterReadPrevious() = prefs.getBoolean(keys.removeAfterReadPrevious, false)
 
-    fun removeAfterMarkedAsRead(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_remove_after_marked_as_read_key), false)
-    }
+    fun removeAfterMarkedAsRead() = prefs.getBoolean(keys.removeAfterMarkedAsRead, false)
 
-    fun updateOnlyWhenCharging(): Boolean {
-        return prefs.getBoolean(getKey(R.string.pref_update_only_when_charging_key), false)
-    }
+    fun updateOnlyWhenCharging() = prefs.getBoolean(keys.updateOnlyWhenCharging, false)
 
-    fun libraryUpdateInterval(): Preference<Int> {
-        return rxPrefs.getInteger(getKey(R.string.pref_library_update_interval_key), 0)
-    }
+    fun libraryUpdateInterval() = rxPrefs.getInteger(keys.libraryUpdateInterval, 0)
 
-    fun filterDownloaded(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_filter_downloaded_key), false)
-    }
+    fun filterDownloaded() = rxPrefs.getBoolean(keys.filterDownloaded, false)
 
-    fun filterUnread(): Preference<Boolean> {
-        return rxPrefs.getBoolean(getKey(R.string.pref_filter_unread_key), false)
-    }
+    fun filterUnread() = rxPrefs.getBoolean(keys.filterUnread, false)
 
 }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/data/source/online/english/Batoto.java

@@ -372,8 +372,8 @@ public class Batoto extends LoginSource {
     @Override
     public Observable<List<Chapter>> pullChaptersFromNetwork(final String mangaUrl) {
         Observable<List<Chapter>> observable;
-        String username = getPrefs().getSourceUsername(this);
-        String password = getPrefs().getSourcePassword(this);
+        String username = getPrefs().sourceUsername(this);
+        String password = getPrefs().sourcePassword(this);
         if (username.isEmpty() && password.isEmpty()) {
             observable = Observable.error(new Exception("User not logged"));
         }

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/data/updater/UpdateDownloader.java

@@ -41,7 +41,7 @@ public class UpdateDownloader extends AsyncTask<String, Void, Void> {
         this.context = context;
 
         // Get cache directory from parameter.
-        cacheDir = new File(preferencesHelper.getDownloadsDirectory(), PARAMETER_CACHE_DIRECTORY);
+        cacheDir = new File(preferencesHelper.downloadsDirectory().get(), PARAMETER_CACHE_DIRECTORY);
 
         // Create cache directory.
         createCacheDir();

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt

@@ -288,7 +288,7 @@ class CataloguePresenter : BasePresenter<CatalogueFragment>() {
         if (!isLoginRequired || isLogged)
             return true
 
-        prefs.getSourceUsername(this) != "" && prefs.getSourcePassword(this) != ""
+        prefs.sourceUsername(this) != "" && prefs.sourcePassword(this) != ""
     }
 
     /**

+ 26 - 10
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt

@@ -81,6 +81,8 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
 
     private var prevChapterBtn: MenuItem? = null
 
+    private val volumeKeysEnabled by lazy { preferences.readWithVolumeKeys().getOrDefault() }
+
     val preferences: PreferencesHelper
         get() = presenter.prefs
 
@@ -174,18 +176,32 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
 
     override fun dispatchKeyEvent(event: KeyEvent): Boolean {
         if (!isFinishing) {
-            val action = event.action
-            val keyCode = event.keyCode
-            when (keyCode) {
-                KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT -> {
-                    if (action == KeyEvent.ACTION_UP)
+            when (event.keyCode) {
+                KeyEvent.KEYCODE_VOLUME_DOWN -> {
+                    if (volumeKeysEnabled) {
+                        if (event.action == KeyEvent.ACTION_UP) {
+                            viewer?.moveToNext()
+                        }
+                        return true
+                    }
+                }
+                KeyEvent.KEYCODE_VOLUME_UP -> {
+                    if (volumeKeysEnabled) {
+                        if (event.action == KeyEvent.ACTION_UP) {
+                            viewer?.moveToPrevious()
+                        }
+                        return true
+                    }
+                }
+                KeyEvent.KEYCODE_DPAD_RIGHT -> {
+                    if (event.action == KeyEvent.ACTION_UP) {
                         viewer?.moveToNext()
-                    return true
+                    }
                 }
-                KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_DPAD_LEFT -> {
-                    if (action == KeyEvent.ACTION_UP)
+                KeyEvent.KEYCODE_DPAD_LEFT -> {
+                    if (event.action == KeyEvent.ACTION_UP) {
                         viewer?.moveToPrevious()
-                    return true
+                    }
                 }
             }
         }
@@ -258,7 +274,7 @@ class ReaderActivity : BaseRxActivity<ReaderPresenter>() {
 
 
     private fun getOrCreateViewer(manga: Manga): BaseReader {
-        val mangaViewer = if (manga.viewer == 0) preferences.defaultViewer else manga.viewer
+        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

+ 6 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/base/BaseReader.kt

@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.ui.reader.viewer.base
 
 import com.davemorrissey.labs.subscaleview.decoder.*
 import eu.kanade.tachiyomi.data.database.models.Chapter
+import eu.kanade.tachiyomi.data.preference.getOrDefault
 import eu.kanade.tachiyomi.data.source.model.Page
 import eu.kanade.tachiyomi.ui.base.fragment.BaseFragment
 import eu.kanade.tachiyomi.ui.reader.ReaderActivity
@@ -54,6 +55,11 @@ abstract class BaseReader : BaseFragment() {
     lateinit var bitmapDecoderClass: Class<out ImageDecoder>
         private set
 
+    /**
+     * Whether tap navigation is enabled or not.
+     */
+    val tappingEnabled by lazy { readerActivity.preferences.readWithTapping().getOrDefault() }
+
     /**
      * Whether the reader has requested to append a chapter. Used with seamless mode to avoid
      * restarting requests when changing pages.

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/pager/PagerReader.kt

@@ -164,9 +164,9 @@ abstract class PagerReader : BaseReader() {
                 val positionX = e.x
 
                 if (positionX < pager.width * LEFT_REGION) {
-                    onLeftSideTap()
+                    if (tappingEnabled) onLeftSideTap()
                 } else if (positionX > pager.width * RIGHT_REGION) {
-                    onRightSideTap()
+                    if (tappingEnabled) onRightSideTap()
                 } else {
                     readerActivity.onCenterSingleTap()
                 }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/ui/reader/viewer/webtoon/WebtoonReader.kt

@@ -126,9 +126,9 @@ class WebtoonReader : BaseReader() {
                 val positionX = e.x
 
                 if (positionX < recycler.width * LEFT_REGION) {
-                    moveToPrevious()
+                    if (tappingEnabled) moveToPrevious()
                 } else if (positionX > recycler.width * RIGHT_REGION) {
-                    moveToNext()
+                    if (tappingEnabled) moveToNext()
                 } else {
                     readerActivity.onCenterSingleTap()
                 }

+ 16 - 12
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadsFragment.kt

@@ -14,12 +14,13 @@ import com.nononsenseapps.filepicker.FilePickerActivity
 import com.nononsenseapps.filepicker.FilePickerFragment
 import com.nononsenseapps.filepicker.LogicHandler
 import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.data.preference.getOrDefault
 import eu.kanade.tachiyomi.util.inflate
+import rx.Subscription
 import java.io.File
 
 class SettingsDownloadsFragment : SettingsNestedFragment() {
 
-    val downloadDirPref by lazy { findPreference(getString(R.string.pref_download_directory_key)) }
     companion object {
 
         val DOWNLOAD_DIR_CODE = 103
@@ -31,11 +32,16 @@ class SettingsDownloadsFragment : SettingsNestedFragment() {
         }
     }
 
+    val downloadDirPref by lazy { findPreference(getString(R.string.pref_download_directory_key)) }
+
+    var downloadDirSubscription: Subscription? = null
+
     override fun onViewCreated(view: View, savedState: Bundle?) {
         downloadDirPref.setOnPreferenceClickListener {
 
+            val currentDir = preferences.downloadsDirectory().getOrDefault()
             val externalDirs = getExternalFilesDirs()
-            val selectedIndex = externalDirs.indexOf(File(preferences.downloadsDirectory))
+            val selectedIndex = externalDirs.indexOf(File(currentDir))
 
             MaterialDialog.Builder(activity)
                     .items(externalDirs + getString(R.string.custom_dir))
@@ -46,13 +52,12 @@ class SettingsDownloadsFragment : SettingsNestedFragment() {
                             i.putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
                             i.putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
                             i.putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR)
-                            i.putExtra(FilePickerActivity.EXTRA_START_PATH, preferences.downloadsDirectory)
+                            i.putExtra(FilePickerActivity.EXTRA_START_PATH, currentDir)
 
                             startActivityForResult(i, DOWNLOAD_DIR_CODE)
                         } else {
                             // One of the predefined folders was selected
-                            preferences.downloadsDirectory = text.toString()
-                            updateDownloadsDir()
+                            preferences.downloadsDirectory().set(text.toString())
                         }
                         true
                     })
@@ -60,15 +65,14 @@ class SettingsDownloadsFragment : SettingsNestedFragment() {
 
             true
         }
-    }
 
-    override fun onResume() {
-        super.onResume()
-        updateDownloadsDir()
+        downloadDirSubscription = preferences.downloadsDirectory().asObservable()
+                .subscribe { downloadDirPref.summary = it }
     }
 
-    fun updateDownloadsDir() {
-        downloadDirPref.summary = preferences.downloadsDirectory
+    override fun onDestroyView() {
+        downloadDirSubscription?.unsubscribe()
+        super.onDestroyView()
     }
 
     fun getExternalFilesDirs(): List<File> {
@@ -81,7 +85,7 @@ class SettingsDownloadsFragment : SettingsNestedFragment() {
 
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         if (data != null && requestCode == DOWNLOAD_DIR_CODE && resultCode == Activity.RESULT_OK) {
-            preferences.downloadsDirectory = data.data.path
+            preferences.downloadsDirectory().set(data.data.path)
         }
     }
 

+ 1 - 5
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsNestedFragment.kt

@@ -44,9 +44,5 @@ open class SettingsNestedFragment : PreferenceFragment() {
         get() = settingsActivity.preferences
 
     val fragmentManagerCompat: FragmentManager
-        get() = if (Build.VERSION.SDK_INT >= 17) {
-            childFragmentManager
-        } else {
-            fragmentManager
-        }
+        get() = if (Build.VERSION.SDK_INT >= 17) childFragmentManager else fragmentManager
 }

+ 2 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt

@@ -6,7 +6,6 @@ import android.support.v14.preference.MultiSelectListPreference
 import android.support.v7.preference.Preference
 import android.support.v7.preference.PreferenceGroup
 import android.view.View
-import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.data.preference.getOrDefault
 import eu.kanade.tachiyomi.data.source.base.Source
 import eu.kanade.tachiyomi.data.source.getLanguages
@@ -65,7 +64,7 @@ class SettingsSourcesFragment : SettingsNestedFragment() {
 
     fun createSource(source: Source): Preference {
         return LoginPreference(preferenceManager.context).apply {
-            key = PreferencesHelper.SOURCE_ACCOUNT_USERNAME + source.id
+            key = preferences.keys.sourceUsername(source.id)
             title = source.visibleName
 
             setOnPreferenceClickListener {
@@ -80,7 +79,7 @@ class SettingsSourcesFragment : SettingsNestedFragment() {
 
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         if (requestCode == SOURCE_CHANGE_REQUEST) {
-            val pref = findPreference(PreferencesHelper.SOURCE_ACCOUNT_USERNAME + resultCode) as? LoginPreference
+            val pref = findPreference(preferences.keys.sourceUsername(resultCode)) as? LoginPreference
             pref?.notifyChanged()
         }
     }

+ 2 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSyncFragment.kt

@@ -4,7 +4,6 @@ import android.content.Intent
 import android.os.Bundle
 import android.support.v7.preference.PreferenceCategory
 import android.view.View
-import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.widget.preference.LoginPreference
 import eu.kanade.tachiyomi.widget.preference.MangaSyncLoginDialog
 
@@ -27,7 +26,7 @@ class SettingsSyncFragment : SettingsNestedFragment() {
 
         for (sync in settingsActivity.syncManager.services) {
             val pref = LoginPreference(themedContext).apply {
-                key = PreferencesHelper.MANGASYNC_ACCOUNT_USERNAME + sync.id
+                key = preferences.keys.syncUsername(sync.id)
                 title = sync.name
 
                 setOnPreferenceClickListener {
@@ -44,7 +43,7 @@ class SettingsSyncFragment : SettingsNestedFragment() {
 
     override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
         if (requestCode == SYNC_CHANGE_REQUEST) {
-            val pref = findPreference(PreferencesHelper.MANGASYNC_ACCOUNT_USERNAME + resultCode) as? LoginPreference
+            val pref = findPreference(preferences.keys.syncUsername(resultCode)) as? LoginPreference
             pref?.notifyChanged()
         }
     }

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/widget/preference/MangaSyncLoginDialog.kt

@@ -34,8 +34,8 @@ class MangaSyncLoginDialog : LoginDialogPreference() {
 
     override fun setCredentialsOnView(view: View) = with(view) {
         dialog_title.text = getString(R.string.login_title, sync.name)
-        username.setText(preferences.getMangaSyncUsername(sync))
-        password.setText(preferences.getMangaSyncPassword(sync))
+        username.setText(preferences.mangaSyncUsername(sync))
+        password.setText(preferences.mangaSyncPassword(sync))
     }
 
     override fun checkLogin() {

+ 2 - 2
app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt

@@ -34,8 +34,8 @@ class SourceLoginDialog : LoginDialogPreference() {
 
     override fun setCredentialsOnView(view: View) = with(view) {
         dialog_title.text = getString(R.string.login_title, source.visibleName)
-        username.setText(preferences.getSourceUsername(source))
-        password.setText(preferences.getSourcePassword(source))
+        username.setText(preferences.sourceUsername(source))
+        password.setText(preferences.sourcePassword(source))
     }
 
     override fun checkLogin() {

+ 2 - 0
app/src/main/res/values/keys.xml

@@ -31,6 +31,8 @@
     <string name="pref_reader_theme_key">pref_reader_theme_key</string>
     <string name="pref_image_decoder_key">pref_image_decoder_key</string>
     <string name="pref_seamless_mode_key">pref_seamless_mode_key</string>
+    <string name="pref_read_with_volume_keys_key">reader_volume_keys</string>
+    <string name="pref_read_with_tapping_key">reader_tap</string>
     <string name="pref_filter_downloaded_key">pref_filter_downloaded_key</string>
     <string name="pref_filter_unread_key">pref_filter_unread_key</string>
 

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -97,6 +97,9 @@
     <string name="pref_custom_brightness">Use custom brightness</string>
     <string name="pref_seamless_mode">Seamless chapter transitions</string>
     <string name="pref_keep_screen_on">Keep screen on</string>
+    <string name="pref_reader_navigation">Navigation</string>
+    <string name="pref_read_with_volume_keys">Volume keys</string>
+    <string name="pref_read_with_tapping">Tapping</string>
     <string name="pref_reader_theme">Background color</string>
     <string name="white_background">White</string>
     <string name="black_background">Black</string>

+ 45 - 24
app/src/main/res/xml/pref_reader.xml

@@ -2,30 +2,6 @@
 <android.support.v7.preference.PreferenceScreen
     xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <SwitchPreferenceCompat android:title="@string/pref_hide_status_bar"
-        android:key="@string/pref_hide_status_bar_key"
-        android:defaultValue="true" />
-
-    <SwitchPreferenceCompat android:title="@string/pref_enable_transitions"
-        android:key="@string/pref_enable_transitions_key"
-        android:defaultValue="true" />
-
-    <SwitchPreferenceCompat android:title="@string/pref_show_page_number"
-        android:key="@string/pref_show_page_number_key"
-        android:defaultValue="true" />
-
-    <SwitchPreferenceCompat android:title="@string/pref_custom_brightness"
-        android:key="@string/pref_custom_brightness_key"
-        android:defaultValue="false" />
-
-    <SwitchPreferenceCompat android:title="@string/pref_keep_screen_on"
-        android:key="@string/pref_keep_screen_on_key"
-        android:defaultValue="true" />
-
-    <SwitchPreferenceCompat android:title="@string/pref_seamless_mode"
-        android:key="@string/pref_seamless_mode_key"
-        android:defaultValue="true" />
-
     <eu.kanade.tachiyomi.widget.preference.IntListPreference
         android:title="@string/pref_viewer_type"
         android:key="@string/pref_default_viewer_key"
@@ -74,4 +50,49 @@
         android:defaultValue="0"
         android:summary="%s" />
 
+    <SwitchPreferenceCompat
+        android:title="@string/pref_hide_status_bar"
+        android:key="@string/pref_hide_status_bar_key"
+        android:defaultValue="true" />
+
+    <SwitchPreferenceCompat
+        android:title="@string/pref_enable_transitions"
+        android:key="@string/pref_enable_transitions_key"
+        android:defaultValue="true" />
+
+    <SwitchPreferenceCompat
+        android:title="@string/pref_show_page_number"
+        android:key="@string/pref_show_page_number_key"
+        android:defaultValue="true" />
+
+    <SwitchPreferenceCompat
+        android:title="@string/pref_custom_brightness"
+        android:key="@string/pref_custom_brightness_key"
+        android:defaultValue="false" />
+
+    <SwitchPreferenceCompat
+        android:title="@string/pref_keep_screen_on"
+        android:key="@string/pref_keep_screen_on_key"
+        android:defaultValue="true" />
+
+    <SwitchPreferenceCompat
+        android:title="@string/pref_seamless_mode"
+        android:key="@string/pref_seamless_mode_key"
+        android:defaultValue="true" />
+
+    <PreferenceCategory
+        android:title="@string/pref_reader_navigation">
+
+        <SwitchPreferenceCompat
+            android:title="@string/pref_read_with_tapping"
+            android:key="@string/pref_read_with_tapping_key"
+            android:defaultValue="true" />
+
+        <SwitchPreferenceCompat
+            android:title="@string/pref_read_with_volume_keys"
+            android:key="@string/pref_read_with_volume_keys_key"
+            android:defaultValue="false" />
+
+    </PreferenceCategory>
+
 </android.support.v7.preference.PreferenceScreen>