mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-11-03 23:58:55 +01:00 
			
		
		
		
	Preferences with conductor (#792)
* Settings with conductor WIP * Add downloads preference controller. Implement source/track login * Improve settings controllers * Backup settings controller * Delete preferences xml * Remove keys from xml * PreferenceKeys is now an object * Remove now unused dependency
This commit is contained in:
		@@ -101,6 +101,7 @@ android {
 | 
			
		||||
dependencies {
 | 
			
		||||
 | 
			
		||||
    compile "com.bluelinelabs:conductor:2.1.3"
 | 
			
		||||
    compile 'com.github.inorichi:conductor-support-preference:master-SNAPSHOT'
 | 
			
		||||
 | 
			
		||||
    final rxbindings_version = '1.0.1'
 | 
			
		||||
    compile "com.jakewharton.rxbinding:rxbinding-kotlin:$rxbindings_version"
 | 
			
		||||
@@ -204,7 +205,6 @@ dependencies {
 | 
			
		||||
    compile 'com.nononsenseapps:filepicker:2.5.2'
 | 
			
		||||
    compile 'com.github.amulyakhare:TextDrawable:558677e'
 | 
			
		||||
    compile 'com.afollestad.material-dialogs:core:0.9.4.2'
 | 
			
		||||
    compile 'net.xpece.android:support-preference:1.2.5'
 | 
			
		||||
    compile 'me.zhanghai.android.systemuihelper:library:1.0.0'
 | 
			
		||||
    compile 'de.hdodenhof:circleimageview:2.1.0'
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -35,10 +35,6 @@
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".ui.reader.ReaderActivity"
 | 
			
		||||
            android:theme="@style/Theme.Reader" />
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".ui.setting.SettingsActivity"
 | 
			
		||||
            android:label="@string/label_settings"
 | 
			
		||||
            android:parentActivityName=".ui.main.MainActivity" />
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".widget.CustomLayoutPickerActivity"
 | 
			
		||||
            android:label="@string/app_name"
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,23 @@
 | 
			
		||||
package eu.kanade.tachiyomi.data.backup
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
object BackupConst {
 | 
			
		||||
 | 
			
		||||
    const val INTENT_FILTER = "SettingsBackupFragment"
 | 
			
		||||
    const val ACTION_BACKUP_COMPLETED_DIALOG = "$ID.$INTENT_FILTER.ACTION_BACKUP_COMPLETED_DIALOG"
 | 
			
		||||
    const val ACTION_SET_PROGRESS_DIALOG = "$ID.$INTENT_FILTER.ACTION_SET_PROGRESS_DIALOG"
 | 
			
		||||
    const val ACTION_ERROR_BACKUP_DIALOG = "$ID.$INTENT_FILTER.ACTION_ERROR_BACKUP_DIALOG"
 | 
			
		||||
    const val ACTION_ERROR_RESTORE_DIALOG = "$ID.$INTENT_FILTER.ACTION_ERROR_RESTORE_DIALOG"
 | 
			
		||||
    const val ACTION_RESTORE_COMPLETED_DIALOG = "$ID.$INTENT_FILTER.ACTION_RESTORE_COMPLETED_DIALOG"
 | 
			
		||||
    const val ACTION = "$ID.$INTENT_FILTER.ACTION"
 | 
			
		||||
    const val EXTRA_PROGRESS = "$ID.$INTENT_FILTER.EXTRA_PROGRESS"
 | 
			
		||||
    const val EXTRA_AMOUNT = "$ID.$INTENT_FILTER.EXTRA_AMOUNT"
 | 
			
		||||
    const val EXTRA_ERRORS = "$ID.$INTENT_FILTER.EXTRA_ERRORS"
 | 
			
		||||
    const val EXTRA_CONTENT = "$ID.$INTENT_FILTER.EXTRA_CONTENT"
 | 
			
		||||
    const val EXTRA_ERROR_MESSAGE = "$ID.$INTENT_FILTER.EXTRA_ERROR_MESSAGE"
 | 
			
		||||
    const val EXTRA_URI = "$ID.$INTENT_FILTER.EXTRA_URI"
 | 
			
		||||
    const val EXTRA_TIME = "$ID.$INTENT_FILTER.EXTRA_TIME"
 | 
			
		||||
    const val EXTRA_ERROR_FILE_PATH = "$ID.$INTENT_FILTER.EXTRA_ERROR_FILE_PATH"
 | 
			
		||||
    const val EXTRA_ERROR_FILE = "$ID.$INTENT_FILTER.EXTRA_ERROR_FILE"
 | 
			
		||||
}
 | 
			
		||||
@@ -13,7 +13,6 @@ import eu.kanade.tachiyomi.data.backup.models.Backup.CATEGORIES
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.MANGAS
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup.VERSION
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.ui.setting.SettingsBackupFragment
 | 
			
		||||
import eu.kanade.tachiyomi.util.AndroidComponentUtil
 | 
			
		||||
import eu.kanade.tachiyomi.util.sendLocalBroadcast
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
@@ -28,8 +27,6 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
        // Name of class
 | 
			
		||||
        private const val NAME = "BackupCreateService"
 | 
			
		||||
 | 
			
		||||
        // Uri as string
 | 
			
		||||
        private const val EXTRA_URI = "$ID.$NAME.EXTRA_URI"
 | 
			
		||||
        // Backup called from job
 | 
			
		||||
        private const val EXTRA_IS_JOB = "$ID.$NAME.EXTRA_IS_JOB"
 | 
			
		||||
        // Options for backup
 | 
			
		||||
@@ -56,7 +53,7 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
         */
 | 
			
		||||
        fun makeBackup(context: Context, path: String, flags: Int, isJob: Boolean = false) {
 | 
			
		||||
            val intent = Intent(context, BackupCreateService::class.java).apply {
 | 
			
		||||
                putExtra(EXTRA_URI, path)
 | 
			
		||||
                putExtra(BackupConst.EXTRA_URI, path)
 | 
			
		||||
                putExtra(EXTRA_IS_JOB, isJob)
 | 
			
		||||
                putExtra(EXTRA_FLAGS, flags)
 | 
			
		||||
            }
 | 
			
		||||
@@ -74,7 +71,7 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
        if (intent == null) return
 | 
			
		||||
 | 
			
		||||
        // Get values
 | 
			
		||||
        val uri = intent.getStringExtra(EXTRA_URI)
 | 
			
		||||
        val uri = intent.getStringExtra(BackupConst.EXTRA_URI)
 | 
			
		||||
        val isJob = intent.getBooleanExtra(EXTRA_IS_JOB, false)
 | 
			
		||||
        val flags = intent.getIntExtra(EXTRA_FLAGS, 0)
 | 
			
		||||
        // Create backup
 | 
			
		||||
@@ -150,9 +147,9 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Show completed dialog
 | 
			
		||||
                val intent = Intent(SettingsBackupFragment.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(SettingsBackupFragment.ACTION, SettingsBackupFragment.ACTION_BACKUP_COMPLETED_DIALOG)
 | 
			
		||||
                    putExtra(SettingsBackupFragment.EXTRA_URI, file.uri.toString())
 | 
			
		||||
                val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(BackupConst.ACTION, BackupConst.ACTION_BACKUP_COMPLETED_DIALOG)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_URI, file.uri.toString())
 | 
			
		||||
                }
 | 
			
		||||
                sendLocalBroadcast(intent)
 | 
			
		||||
            }
 | 
			
		||||
@@ -160,9 +157,9 @@ class BackupCreateService : IntentService(NAME) {
 | 
			
		||||
            Timber.e(e)
 | 
			
		||||
            if (!isJob) {
 | 
			
		||||
                // Show error dialog
 | 
			
		||||
                val intent = Intent(SettingsBackupFragment.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(SettingsBackupFragment.ACTION, SettingsBackupFragment.ACTION_ERROR_BACKUP_DIALOG)
 | 
			
		||||
                    putExtra(SettingsBackupFragment.EXTRA_ERROR_MESSAGE, e.message)
 | 
			
		||||
                val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                    putExtra(BackupConst.ACTION, BackupConst.ACTION_ERROR_BACKUP_DIALOG)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_ERROR_MESSAGE, e.message)
 | 
			
		||||
                }
 | 
			
		||||
                sendLocalBroadcast(intent)
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ class BackupCreatorJob : Job() {
 | 
			
		||||
        val preferences = Injekt.get<PreferencesHelper>()
 | 
			
		||||
        val path = preferences.backupsDirectory().getOrDefault()
 | 
			
		||||
        val flags = BackupCreateService.BACKUP_ALL
 | 
			
		||||
        BackupCreateService.makeBackup(context,path,flags,true)
 | 
			
		||||
        BackupCreateService.makeBackup(context, path, flags, true)
 | 
			
		||||
        return Result.SUCCESS
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,6 @@ import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.util.syncChaptersWithSource
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class BackupManager(val context: Context, version: Int = CURRENT_VERSION) {
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,6 @@ import eu.kanade.tachiyomi.data.backup.models.DHistory
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.*
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import eu.kanade.tachiyomi.ui.setting.SettingsBackupFragment
 | 
			
		||||
import eu.kanade.tachiyomi.util.AndroidComponentUtil
 | 
			
		||||
import eu.kanade.tachiyomi.util.chop
 | 
			
		||||
import eu.kanade.tachiyomi.util.sendLocalBroadcast
 | 
			
		||||
@@ -36,7 +35,6 @@ import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import java.util.concurrent.ExecutorService
 | 
			
		||||
import java.util.concurrent.Executors
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Restores backup from json file
 | 
			
		||||
@@ -44,11 +42,6 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
 | 
			
		||||
class BackupRestoreService : Service() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        // Name of service
 | 
			
		||||
        private const val NAME = "BackupRestoreService"
 | 
			
		||||
 | 
			
		||||
        // Uri as string
 | 
			
		||||
        private const val EXTRA_URI = "$ID.$NAME.EXTRA_URI"
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Returns the status of the service.
 | 
			
		||||
@@ -69,7 +62,7 @@ class BackupRestoreService : Service() {
 | 
			
		||||
        fun start(context: Context, uri: Uri) {
 | 
			
		||||
            if (!isRunning(context)) {
 | 
			
		||||
                val intent = Intent(context, BackupRestoreService::class.java).apply {
 | 
			
		||||
                    putExtra(EXTRA_URI, uri)
 | 
			
		||||
                    putExtra(BackupConst.EXTRA_URI, uri)
 | 
			
		||||
                }
 | 
			
		||||
                context.startService(intent)
 | 
			
		||||
            }
 | 
			
		||||
@@ -164,7 +157,7 @@ class BackupRestoreService : Service() {
 | 
			
		||||
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
 | 
			
		||||
        if (intent == null) return Service.START_NOT_STICKY
 | 
			
		||||
 | 
			
		||||
        val uri = intent.getParcelableExtra<Uri>(EXTRA_URI)
 | 
			
		||||
        val uri = intent.getParcelableExtra<Uri>(BackupConst.EXTRA_URI)
 | 
			
		||||
 | 
			
		||||
        // Unsubscribe from any previous subscription if needed.
 | 
			
		||||
        subscription?.unsubscribe()
 | 
			
		||||
@@ -236,12 +229,12 @@ class BackupRestoreService : Service() {
 | 
			
		||||
                    val endTime = System.currentTimeMillis()
 | 
			
		||||
                    val time = endTime - startTime
 | 
			
		||||
                    val logFile = writeErrorLog()
 | 
			
		||||
                    val completeIntent = Intent(SettingsBackupFragment.INTENT_FILTER).apply {
 | 
			
		||||
                        putExtra(SettingsBackupFragment.EXTRA_TIME, time)
 | 
			
		||||
                        putExtra(SettingsBackupFragment.EXTRA_ERRORS, errors.size)
 | 
			
		||||
                        putExtra(SettingsBackupFragment.EXTRA_ERROR_FILE_PATH, logFile.parent)
 | 
			
		||||
                        putExtra(SettingsBackupFragment.EXTRA_ERROR_FILE, logFile.name)
 | 
			
		||||
                        putExtra(SettingsBackupFragment.ACTION, SettingsBackupFragment.ACTION_RESTORE_COMPLETED_DIALOG)
 | 
			
		||||
                    val completeIntent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                        putExtra(BackupConst.EXTRA_TIME, time)
 | 
			
		||||
                        putExtra(BackupConst.EXTRA_ERRORS, errors.size)
 | 
			
		||||
                        putExtra(BackupConst.EXTRA_ERROR_FILE_PATH, logFile.parent)
 | 
			
		||||
                        putExtra(BackupConst.EXTRA_ERROR_FILE, logFile.name)
 | 
			
		||||
                        putExtra(BackupConst.ACTION, BackupConst.ACTION_RESTORE_COMPLETED_DIALOG)
 | 
			
		||||
                    }
 | 
			
		||||
                    sendLocalBroadcast(completeIntent)
 | 
			
		||||
 | 
			
		||||
@@ -249,9 +242,9 @@ class BackupRestoreService : Service() {
 | 
			
		||||
                .doOnError { error ->
 | 
			
		||||
                    Timber.e(error)
 | 
			
		||||
                    writeErrorLog()
 | 
			
		||||
                    val errorIntent = Intent(SettingsBackupFragment.INTENT_FILTER).apply {
 | 
			
		||||
                        putExtra(SettingsBackupFragment.ACTION, SettingsBackupFragment.ACTION_ERROR_RESTORE_DIALOG)
 | 
			
		||||
                        putExtra(SettingsBackupFragment.EXTRA_ERROR_MESSAGE, error.message)
 | 
			
		||||
                    val errorIntent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
                        putExtra(BackupConst.ACTION, BackupConst.ACTION_ERROR_RESTORE_DIALOG)
 | 
			
		||||
                        putExtra(BackupConst.EXTRA_ERROR_MESSAGE, error.message)
 | 
			
		||||
                    }
 | 
			
		||||
                    sendLocalBroadcast(errorIntent)
 | 
			
		||||
                }
 | 
			
		||||
@@ -392,7 +385,7 @@ class BackupRestoreService : Service() {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Called to update dialog in [SettingsBackupFragment]
 | 
			
		||||
     * Called to update dialog in [BackupConst]
 | 
			
		||||
     *
 | 
			
		||||
     * @param progress restore progress
 | 
			
		||||
     * @param amount total restoreAmount of manga
 | 
			
		||||
@@ -400,12 +393,12 @@ class BackupRestoreService : Service() {
 | 
			
		||||
     */
 | 
			
		||||
    private fun showRestoreProgress(progress: Int, amount: Int, title: String, errors: Int,
 | 
			
		||||
                                    content: String = getString(R.string.dialog_restoring_backup, title.chop(15))) {
 | 
			
		||||
        val intent = Intent(SettingsBackupFragment.INTENT_FILTER).apply {
 | 
			
		||||
            putExtra(SettingsBackupFragment.EXTRA_PROGRESS, progress)
 | 
			
		||||
            putExtra(SettingsBackupFragment.EXTRA_AMOUNT, amount)
 | 
			
		||||
            putExtra(SettingsBackupFragment.EXTRA_CONTENT, content)
 | 
			
		||||
            putExtra(SettingsBackupFragment.EXTRA_ERRORS, errors)
 | 
			
		||||
            putExtra(SettingsBackupFragment.ACTION, SettingsBackupFragment.ACTION_SET_PROGRESS_DIALOG)
 | 
			
		||||
        val intent = Intent(BackupConst.INTENT_FILTER).apply {
 | 
			
		||||
            putExtra(BackupConst.EXTRA_PROGRESS, progress)
 | 
			
		||||
            putExtra(BackupConst.EXTRA_AMOUNT, amount)
 | 
			
		||||
            putExtra(BackupConst.EXTRA_CONTENT, content)
 | 
			
		||||
            putExtra(BackupConst.EXTRA_ERRORS, errors)
 | 
			
		||||
            putExtra(BackupConst.ACTION, BackupConst.ACTION_SET_PROGRESS_DIALOG)
 | 
			
		||||
        }
 | 
			
		||||
        sendLocalBroadcast(intent)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,120 +1,114 @@
 | 
			
		||||
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.
 | 
			
		||||
 */
 | 
			
		||||
@Suppress("HasPlatformType")
 | 
			
		||||
class PreferenceKeys(context: Context) {
 | 
			
		||||
 | 
			
		||||
    val theme = context.getString(R.string.pref_theme_key)
 | 
			
		||||
 | 
			
		||||
    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 fullscreen = context.getString(R.string.pref_fullscreen_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 colorFilter = context.getString(R.string.pref_color_filter_key)
 | 
			
		||||
 | 
			
		||||
    val colorFilterValue = context.getString(R.string.pref_color_filter_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 cropBorders = context.getString(R.string.pref_crop_borders_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 autoUpdateTrack = context.getString(R.string.pref_auto_update_manga_sync_key)
 | 
			
		||||
 | 
			
		||||
    val askUpdateTrack = context.getString(R.string.pref_ask_update_manga_sync_key)
 | 
			
		||||
 | 
			
		||||
    val lastUsedCatalogueSource = context.getString(R.string.pref_last_catalogue_source_key)
 | 
			
		||||
 | 
			
		||||
    val lastUsedCategory = context.getString(R.string.pref_last_used_category_key)
 | 
			
		||||
 | 
			
		||||
    val catalogueAsList = context.getString(R.string.pref_display_catalogue_as_list)
 | 
			
		||||
 | 
			
		||||
    val enabledLanguages = context.getString(R.string.pref_source_languages)
 | 
			
		||||
 | 
			
		||||
    val backupDirectory = context.getString(R.string.pref_backup_directory_key)
 | 
			
		||||
 | 
			
		||||
    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 numberOfBackups = context.getString(R.string.pref_backup_slots_key)
 | 
			
		||||
 | 
			
		||||
    val backupInterval = context.getString(R.string.pref_backup_interval_key)
 | 
			
		||||
 | 
			
		||||
    val removeAfterReadSlots = context.getString(R.string.pref_remove_after_read_slots_key)
 | 
			
		||||
 | 
			
		||||
    val removeAfterMarkedAsRead = context.getString(R.string.pref_remove_after_marked_as_read_key)
 | 
			
		||||
 | 
			
		||||
    val libraryUpdateInterval = context.getString(R.string.pref_library_update_interval_key)
 | 
			
		||||
 | 
			
		||||
    val libraryUpdateRestriction = context.getString(R.string.pref_library_update_restriction_key)
 | 
			
		||||
 | 
			
		||||
    val libraryUpdateCategories = context.getString(R.string.pref_library_update_categories_key)
 | 
			
		||||
 | 
			
		||||
    val filterDownloaded = context.getString(R.string.pref_filter_downloaded_key)
 | 
			
		||||
 | 
			
		||||
    val filterUnread = context.getString(R.string.pref_filter_unread_key)
 | 
			
		||||
 | 
			
		||||
    val librarySortingMode = context.getString(R.string.pref_library_sorting_mode_key)
 | 
			
		||||
 | 
			
		||||
    val automaticUpdates = context.getString(R.string.pref_enable_automatic_updates_key)
 | 
			
		||||
 | 
			
		||||
    val startScreen = context.getString(R.string.pref_start_screen_key)
 | 
			
		||||
 | 
			
		||||
    val downloadNew = context.getString(R.string.pref_download_new_key)
 | 
			
		||||
 | 
			
		||||
    val downloadNewCategories = context.getString(R.string.pref_download_new_categories_key)
 | 
			
		||||
 | 
			
		||||
    fun sourceUsername(sourceId: Long) = "pref_source_username_$sourceId"
 | 
			
		||||
 | 
			
		||||
    fun sourcePassword(sourceId: Long) = "pref_source_password_$sourceId"
 | 
			
		||||
 | 
			
		||||
    fun trackUsername(syncId: Int) = "pref_mangasync_username_$syncId"
 | 
			
		||||
 | 
			
		||||
    fun trackPassword(syncId: Int) = "pref_mangasync_password_$syncId"
 | 
			
		||||
 | 
			
		||||
    fun trackToken(syncId: Int) = "track_token_$syncId"
 | 
			
		||||
 | 
			
		||||
    val libraryAsList = context.getString(R.string.pref_display_library_as_list)
 | 
			
		||||
 | 
			
		||||
    val lang = context.getString(R.string.pref_language_key)
 | 
			
		||||
 | 
			
		||||
    val defaultCategory = context.getString(R.string.default_category_key)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
package eu.kanade.tachiyomi.data.preference
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class stores the keys for the preferences in the application.
 | 
			
		||||
 */
 | 
			
		||||
object PreferenceKeys {
 | 
			
		||||
 | 
			
		||||
    const val theme = "pref_theme_key"
 | 
			
		||||
 | 
			
		||||
    const val rotation = "pref_rotation_type_key"
 | 
			
		||||
 | 
			
		||||
    const val enableTransitions = "pref_enable_transitions_key"
 | 
			
		||||
 | 
			
		||||
    const val showPageNumber = "pref_show_page_number_key"
 | 
			
		||||
 | 
			
		||||
    const val fullscreen = "fullscreen"
 | 
			
		||||
 | 
			
		||||
    const val keepScreenOn = "pref_keep_screen_on_key"
 | 
			
		||||
 | 
			
		||||
    const val customBrightness = "pref_custom_brightness_key"
 | 
			
		||||
 | 
			
		||||
    const val customBrightnessValue = "custom_brightness_value"
 | 
			
		||||
 | 
			
		||||
    const val colorFilter = "pref_color_filter_key"
 | 
			
		||||
 | 
			
		||||
    const val colorFilterValue = "color_filter_value"
 | 
			
		||||
 | 
			
		||||
    const val defaultViewer = "pref_default_viewer_key"
 | 
			
		||||
 | 
			
		||||
    const val imageScaleType = "pref_image_scale_type_key"
 | 
			
		||||
 | 
			
		||||
    const val imageDecoder = "image_decoder"
 | 
			
		||||
 | 
			
		||||
    const val zoomStart = "pref_zoom_start_key"
 | 
			
		||||
 | 
			
		||||
    const val readerTheme = "pref_reader_theme_key"
 | 
			
		||||
 | 
			
		||||
    const val cropBorders = "crop_borders"
 | 
			
		||||
 | 
			
		||||
    const val readWithTapping = "reader_tap"
 | 
			
		||||
 | 
			
		||||
    const val readWithVolumeKeys = "reader_volume_keys"
 | 
			
		||||
 | 
			
		||||
    const val portraitColumns = "pref_library_columns_portrait_key"
 | 
			
		||||
 | 
			
		||||
    const val landscapeColumns = "pref_library_columns_landscape_key"
 | 
			
		||||
 | 
			
		||||
    const val updateOnlyNonCompleted = "pref_update_only_non_completed_key"
 | 
			
		||||
 | 
			
		||||
    const val autoUpdateTrack = "pref_auto_update_manga_sync_key"
 | 
			
		||||
 | 
			
		||||
    const val askUpdateTrack = "pref_ask_update_manga_sync_key"
 | 
			
		||||
 | 
			
		||||
    const val lastUsedCatalogueSource = "last_catalogue_source"
 | 
			
		||||
 | 
			
		||||
    const val lastUsedCategory = "last_used_category"
 | 
			
		||||
 | 
			
		||||
    const val catalogueAsList = "pref_display_catalogue_as_list"
 | 
			
		||||
 | 
			
		||||
    const val enabledLanguages = "source_languages"
 | 
			
		||||
 | 
			
		||||
    const val backupDirectory = "backup_directory"
 | 
			
		||||
 | 
			
		||||
    const val downloadsDirectory = "download_directory"
 | 
			
		||||
 | 
			
		||||
    const val downloadThreads = "pref_download_slots_key"
 | 
			
		||||
 | 
			
		||||
    const val downloadOnlyOverWifi = "pref_download_only_over_wifi_key"
 | 
			
		||||
 | 
			
		||||
    const val numberOfBackups = "backup_slots"
 | 
			
		||||
 | 
			
		||||
    const val backupInterval = "backup_interval"
 | 
			
		||||
 | 
			
		||||
    const val removeAfterReadSlots = "remove_after_read_slots"
 | 
			
		||||
 | 
			
		||||
    const val removeAfterMarkedAsRead = "pref_remove_after_marked_as_read_key"
 | 
			
		||||
 | 
			
		||||
    const val libraryUpdateInterval = "pref_library_update_interval_key"
 | 
			
		||||
 | 
			
		||||
    const val libraryUpdateRestriction = "library_update_restriction"
 | 
			
		||||
 | 
			
		||||
    const val libraryUpdateCategories = "library_update_categories"
 | 
			
		||||
 | 
			
		||||
    const val filterDownloaded = "pref_filter_downloaded_key"
 | 
			
		||||
 | 
			
		||||
    const val filterUnread = "pref_filter_unread_key"
 | 
			
		||||
 | 
			
		||||
    const val librarySortingMode = "library_sorting_mode"
 | 
			
		||||
 | 
			
		||||
    const val automaticUpdates = "automatic_updates"
 | 
			
		||||
 | 
			
		||||
    const val startScreen = "start_screen"
 | 
			
		||||
 | 
			
		||||
    const val downloadNew = "download_new"
 | 
			
		||||
 | 
			
		||||
    const val downloadNewCategories = "download_new_categories"
 | 
			
		||||
 | 
			
		||||
    const val libraryAsList = "pref_display_library_as_list"
 | 
			
		||||
 | 
			
		||||
    const val lang = "app_language"
 | 
			
		||||
 | 
			
		||||
    const val defaultCategory = "default_category"
 | 
			
		||||
 | 
			
		||||
    fun sourceUsername(sourceId: Long) = "pref_source_username_$sourceId"
 | 
			
		||||
 | 
			
		||||
    fun sourcePassword(sourceId: Long) = "pref_source_password_$sourceId"
 | 
			
		||||
 | 
			
		||||
    fun trackUsername(syncId: Int) = "pref_mangasync_username_$syncId"
 | 
			
		||||
 | 
			
		||||
    fun trackPassword(syncId: Int) = "pref_mangasync_password_$syncId"
 | 
			
		||||
 | 
			
		||||
    fun trackToken(syncId: Int) = "track_token_$syncId"
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,7 @@ import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackService
 | 
			
		||||
import eu.kanade.tachiyomi.source.Source
 | 
			
		||||
import java.io.File
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
fun <T> Preference<T>.getOrDefault(): T = get() ?: defaultValue()!!
 | 
			
		||||
 | 
			
		||||
@@ -17,8 +18,6 @@ fun Preference<Boolean>.invert(): Boolean = getOrDefault().let { set(!it); !it }
 | 
			
		||||
 | 
			
		||||
class PreferencesHelper(val context: Context) {
 | 
			
		||||
 | 
			
		||||
    val keys = PreferenceKeys(context)
 | 
			
		||||
 | 
			
		||||
    private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
 | 
			
		||||
    private val rxPrefs = RxSharedPreferences.create(prefs)
 | 
			
		||||
 | 
			
		||||
@@ -30,134 +29,134 @@ class PreferencesHelper(val context: Context) {
 | 
			
		||||
            File(Environment.getExternalStorageDirectory().absolutePath + File.separator +
 | 
			
		||||
                    context.getString(R.string.app_name), "backup"))
 | 
			
		||||
 | 
			
		||||
    fun startScreen() = prefs.getInt(keys.startScreen, 1)
 | 
			
		||||
    fun startScreen() = prefs.getInt(Keys.startScreen, 1)
 | 
			
		||||
 | 
			
		||||
    fun clear() = prefs.edit().clear().apply()
 | 
			
		||||
 | 
			
		||||
    fun theme() = prefs.getInt(keys.theme, 1)
 | 
			
		||||
    fun theme() = prefs.getInt(Keys.theme, 1)
 | 
			
		||||
 | 
			
		||||
    fun rotation() = rxPrefs.getInteger(keys.rotation, 1)
 | 
			
		||||
    fun rotation() = rxPrefs.getInteger(Keys.rotation, 1)
 | 
			
		||||
 | 
			
		||||
    fun pageTransitions() = rxPrefs.getBoolean(keys.enableTransitions, true)
 | 
			
		||||
    fun pageTransitions() = rxPrefs.getBoolean(Keys.enableTransitions, true)
 | 
			
		||||
 | 
			
		||||
    fun showPageNumber() = rxPrefs.getBoolean(keys.showPageNumber, true)
 | 
			
		||||
    fun showPageNumber() = rxPrefs.getBoolean(Keys.showPageNumber, true)
 | 
			
		||||
 | 
			
		||||
    fun fullscreen() = rxPrefs.getBoolean(keys.fullscreen, true)
 | 
			
		||||
    fun fullscreen() = rxPrefs.getBoolean(Keys.fullscreen, true)
 | 
			
		||||
 | 
			
		||||
    fun keepScreenOn() = rxPrefs.getBoolean(keys.keepScreenOn, true)
 | 
			
		||||
    fun keepScreenOn() = rxPrefs.getBoolean(Keys.keepScreenOn, true)
 | 
			
		||||
 | 
			
		||||
    fun customBrightness() = rxPrefs.getBoolean(keys.customBrightness, false)
 | 
			
		||||
    fun customBrightness() = rxPrefs.getBoolean(Keys.customBrightness, false)
 | 
			
		||||
 | 
			
		||||
    fun customBrightnessValue() = rxPrefs.getInteger(keys.customBrightnessValue, 0)
 | 
			
		||||
    fun customBrightnessValue() = rxPrefs.getInteger(Keys.customBrightnessValue, 0)
 | 
			
		||||
 | 
			
		||||
    fun colorFilter() = rxPrefs.getBoolean(keys.colorFilter, false)
 | 
			
		||||
    fun colorFilter() = rxPrefs.getBoolean(Keys.colorFilter, false)
 | 
			
		||||
 | 
			
		||||
    fun colorFilterValue() = rxPrefs.getInteger(keys.colorFilterValue, 0)
 | 
			
		||||
    fun colorFilterValue() = rxPrefs.getInteger(Keys.colorFilterValue, 0)
 | 
			
		||||
 | 
			
		||||
    fun defaultViewer() = prefs.getInt(keys.defaultViewer, 1)
 | 
			
		||||
    fun defaultViewer() = prefs.getInt(Keys.defaultViewer, 1)
 | 
			
		||||
 | 
			
		||||
    fun imageScaleType() = rxPrefs.getInteger(keys.imageScaleType, 1)
 | 
			
		||||
    fun imageScaleType() = rxPrefs.getInteger(Keys.imageScaleType, 1)
 | 
			
		||||
 | 
			
		||||
    fun imageDecoder() = rxPrefs.getInteger(keys.imageDecoder, 0)
 | 
			
		||||
    fun imageDecoder() = rxPrefs.getInteger(Keys.imageDecoder, 0)
 | 
			
		||||
 | 
			
		||||
    fun zoomStart() = rxPrefs.getInteger(keys.zoomStart, 1)
 | 
			
		||||
    fun zoomStart() = rxPrefs.getInteger(Keys.zoomStart, 1)
 | 
			
		||||
 | 
			
		||||
    fun readerTheme() = rxPrefs.getInteger(keys.readerTheme, 0)
 | 
			
		||||
    fun readerTheme() = rxPrefs.getInteger(Keys.readerTheme, 0)
 | 
			
		||||
 | 
			
		||||
    fun cropBorders() = rxPrefs.getBoolean(keys.cropBorders, false)
 | 
			
		||||
    fun cropBorders() = rxPrefs.getBoolean(Keys.cropBorders, false)
 | 
			
		||||
 | 
			
		||||
    fun readWithTapping() = rxPrefs.getBoolean(keys.readWithTapping, true)
 | 
			
		||||
    fun readWithTapping() = rxPrefs.getBoolean(Keys.readWithTapping, true)
 | 
			
		||||
 | 
			
		||||
    fun readWithVolumeKeys() = rxPrefs.getBoolean(keys.readWithVolumeKeys, false)
 | 
			
		||||
    fun readWithVolumeKeys() = rxPrefs.getBoolean(Keys.readWithVolumeKeys, false)
 | 
			
		||||
 | 
			
		||||
    fun portraitColumns() = rxPrefs.getInteger(keys.portraitColumns, 0)
 | 
			
		||||
    fun portraitColumns() = rxPrefs.getInteger(Keys.portraitColumns, 0)
 | 
			
		||||
 | 
			
		||||
    fun landscapeColumns() = rxPrefs.getInteger(keys.landscapeColumns, 0)
 | 
			
		||||
    fun landscapeColumns() = rxPrefs.getInteger(Keys.landscapeColumns, 0)
 | 
			
		||||
 | 
			
		||||
    fun updateOnlyNonCompleted() = prefs.getBoolean(keys.updateOnlyNonCompleted, false)
 | 
			
		||||
    fun updateOnlyNonCompleted() = prefs.getBoolean(Keys.updateOnlyNonCompleted, false)
 | 
			
		||||
 | 
			
		||||
    fun autoUpdateTrack() = prefs.getBoolean(keys.autoUpdateTrack, true)
 | 
			
		||||
    fun autoUpdateTrack() = prefs.getBoolean(Keys.autoUpdateTrack, true)
 | 
			
		||||
 | 
			
		||||
    fun askUpdateTrack() = prefs.getBoolean(keys.askUpdateTrack, false)
 | 
			
		||||
    fun askUpdateTrack() = prefs.getBoolean(Keys.askUpdateTrack, false)
 | 
			
		||||
 | 
			
		||||
    fun lastUsedCatalogueSource() = rxPrefs.getLong(keys.lastUsedCatalogueSource, -1)
 | 
			
		||||
    fun lastUsedCatalogueSource() = rxPrefs.getLong(Keys.lastUsedCatalogueSource, -1)
 | 
			
		||||
 | 
			
		||||
    fun lastUsedCategory() = rxPrefs.getInteger(keys.lastUsedCategory, 0)
 | 
			
		||||
    fun lastUsedCategory() = rxPrefs.getInteger(Keys.lastUsedCategory, 0)
 | 
			
		||||
 | 
			
		||||
    fun lastVersionCode() = rxPrefs.getInteger("last_version_code", 0)
 | 
			
		||||
 | 
			
		||||
    fun catalogueAsList() = rxPrefs.getBoolean(keys.catalogueAsList, false)
 | 
			
		||||
    fun catalogueAsList() = rxPrefs.getBoolean(Keys.catalogueAsList, false)
 | 
			
		||||
 | 
			
		||||
    fun enabledLanguages() = rxPrefs.getStringSet(keys.enabledLanguages, setOf("en"))
 | 
			
		||||
    fun enabledLanguages() = rxPrefs.getStringSet(Keys.enabledLanguages, setOf("en"))
 | 
			
		||||
 | 
			
		||||
    fun sourceUsername(source: Source) = prefs.getString(keys.sourceUsername(source.id), "")
 | 
			
		||||
    fun sourceUsername(source: Source) = prefs.getString(Keys.sourceUsername(source.id), "")
 | 
			
		||||
 | 
			
		||||
    fun sourcePassword(source: Source) = prefs.getString(keys.sourcePassword(source.id), "")
 | 
			
		||||
    fun sourcePassword(source: Source) = prefs.getString(Keys.sourcePassword(source.id), "")
 | 
			
		||||
 | 
			
		||||
    fun setSourceCredentials(source: Source, username: String, password: String) {
 | 
			
		||||
        prefs.edit()
 | 
			
		||||
                .putString(keys.sourceUsername(source.id), username)
 | 
			
		||||
                .putString(keys.sourcePassword(source.id), password)
 | 
			
		||||
                .putString(Keys.sourceUsername(source.id), username)
 | 
			
		||||
                .putString(Keys.sourcePassword(source.id), password)
 | 
			
		||||
                .apply()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun trackUsername(sync: TrackService) = prefs.getString(keys.trackUsername(sync.id), "")
 | 
			
		||||
    fun trackUsername(sync: TrackService) = prefs.getString(Keys.trackUsername(sync.id), "")
 | 
			
		||||
 | 
			
		||||
    fun trackPassword(sync: TrackService) = prefs.getString(keys.trackPassword(sync.id), "")
 | 
			
		||||
    fun trackPassword(sync: TrackService) = prefs.getString(Keys.trackPassword(sync.id), "")
 | 
			
		||||
 | 
			
		||||
    fun setTrackCredentials(sync: TrackService, username: String, password: String) {
 | 
			
		||||
        prefs.edit()
 | 
			
		||||
                .putString(keys.trackUsername(sync.id), username)
 | 
			
		||||
                .putString(keys.trackPassword(sync.id), password)
 | 
			
		||||
                .putString(Keys.trackUsername(sync.id), username)
 | 
			
		||||
                .putString(Keys.trackPassword(sync.id), password)
 | 
			
		||||
                .apply()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun trackToken(sync: TrackService) = rxPrefs.getString(keys.trackToken(sync.id), "")
 | 
			
		||||
    fun trackToken(sync: TrackService) = rxPrefs.getString(Keys.trackToken(sync.id), "")
 | 
			
		||||
 | 
			
		||||
    fun anilistScoreType() = rxPrefs.getInteger("anilist_score_type", 0)
 | 
			
		||||
 | 
			
		||||
    fun backupsDirectory() = rxPrefs.getString(keys.backupDirectory, defaultBackupDir.toString())
 | 
			
		||||
    fun backupsDirectory() = rxPrefs.getString(Keys.backupDirectory, defaultBackupDir.toString())
 | 
			
		||||
 | 
			
		||||
    fun downloadsDirectory() = rxPrefs.getString(keys.downloadsDirectory, defaultDownloadsDir.toString())
 | 
			
		||||
    fun downloadsDirectory() = rxPrefs.getString(Keys.downloadsDirectory, defaultDownloadsDir.toString())
 | 
			
		||||
 | 
			
		||||
    fun downloadThreads() = rxPrefs.getInteger(keys.downloadThreads, 1)
 | 
			
		||||
    fun downloadThreads() = rxPrefs.getInteger(Keys.downloadThreads, 1)
 | 
			
		||||
 | 
			
		||||
    fun downloadOnlyOverWifi() = prefs.getBoolean(keys.downloadOnlyOverWifi, true)
 | 
			
		||||
    fun downloadOnlyOverWifi() = prefs.getBoolean(Keys.downloadOnlyOverWifi, true)
 | 
			
		||||
 | 
			
		||||
    fun numberOfBackups() = rxPrefs.getInteger(keys.numberOfBackups, 1)
 | 
			
		||||
    fun numberOfBackups() = rxPrefs.getInteger(Keys.numberOfBackups, 1)
 | 
			
		||||
 | 
			
		||||
    fun backupInterval() = rxPrefs.getInteger(keys.backupInterval, 0)
 | 
			
		||||
    fun backupInterval() = rxPrefs.getInteger(Keys.backupInterval, 0)
 | 
			
		||||
 | 
			
		||||
    fun removeAfterReadSlots() = prefs.getInt(keys.removeAfterReadSlots, -1)
 | 
			
		||||
    fun removeAfterReadSlots() = prefs.getInt(Keys.removeAfterReadSlots, -1)
 | 
			
		||||
 | 
			
		||||
    fun removeAfterMarkedAsRead() = prefs.getBoolean(keys.removeAfterMarkedAsRead, false)
 | 
			
		||||
    fun removeAfterMarkedAsRead() = prefs.getBoolean(Keys.removeAfterMarkedAsRead, false)
 | 
			
		||||
 | 
			
		||||
    fun libraryUpdateInterval() = rxPrefs.getInteger(keys.libraryUpdateInterval, 0)
 | 
			
		||||
    fun libraryUpdateInterval() = rxPrefs.getInteger(Keys.libraryUpdateInterval, 0)
 | 
			
		||||
 | 
			
		||||
    fun libraryUpdateRestriction() = prefs.getStringSet(keys.libraryUpdateRestriction, emptySet())
 | 
			
		||||
    fun libraryUpdateRestriction() = prefs.getStringSet(Keys.libraryUpdateRestriction, emptySet())
 | 
			
		||||
 | 
			
		||||
    fun libraryUpdateCategories() = rxPrefs.getStringSet(keys.libraryUpdateCategories, emptySet())
 | 
			
		||||
    fun libraryUpdateCategories() = rxPrefs.getStringSet(Keys.libraryUpdateCategories, emptySet())
 | 
			
		||||
 | 
			
		||||
    fun libraryAsList() = rxPrefs.getBoolean(keys.libraryAsList, false)
 | 
			
		||||
    fun libraryAsList() = rxPrefs.getBoolean(Keys.libraryAsList, false)
 | 
			
		||||
 | 
			
		||||
    fun filterDownloaded() = rxPrefs.getBoolean(keys.filterDownloaded, false)
 | 
			
		||||
    fun filterDownloaded() = rxPrefs.getBoolean(Keys.filterDownloaded, false)
 | 
			
		||||
 | 
			
		||||
    fun filterUnread() = rxPrefs.getBoolean(keys.filterUnread, false)
 | 
			
		||||
    fun filterUnread() = rxPrefs.getBoolean(Keys.filterUnread, false)
 | 
			
		||||
 | 
			
		||||
    fun librarySortingMode() = rxPrefs.getInteger(keys.librarySortingMode, 0)
 | 
			
		||||
    fun librarySortingMode() = rxPrefs.getInteger(Keys.librarySortingMode, 0)
 | 
			
		||||
 | 
			
		||||
    fun librarySortingAscending() = rxPrefs.getBoolean("library_sorting_ascending", true)
 | 
			
		||||
 | 
			
		||||
    fun automaticUpdates() = prefs.getBoolean(keys.automaticUpdates, false)
 | 
			
		||||
    fun automaticUpdates() = prefs.getBoolean(Keys.automaticUpdates, false)
 | 
			
		||||
 | 
			
		||||
    fun hiddenCatalogues() = rxPrefs.getStringSet("hidden_catalogues", emptySet())
 | 
			
		||||
 | 
			
		||||
    fun downloadNew() = rxPrefs.getBoolean(keys.downloadNew, false)
 | 
			
		||||
    fun downloadNew() = rxPrefs.getBoolean(Keys.downloadNew, false)
 | 
			
		||||
 | 
			
		||||
    fun downloadNewCategories() = rxPrefs.getStringSet(keys.downloadNewCategories, emptySet())
 | 
			
		||||
    fun downloadNewCategories() = rxPrefs.getStringSet(Keys.downloadNewCategories, emptySet())
 | 
			
		||||
 | 
			
		||||
    fun lang() = prefs.getString(keys.lang, "")
 | 
			
		||||
    fun lang() = prefs.getString(Keys.lang, "")
 | 
			
		||||
 | 
			
		||||
    fun defaultCategory() = prefs.getInt(keys.defaultCategory, -1)
 | 
			
		||||
    fun defaultCategory() = prefs.getInt(Keys.defaultCategory, -1)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,7 +8,6 @@ import android.view.ViewGroup
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeHandler
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeType
 | 
			
		||||
import com.bluelinelabs.conductor.RestoreViewOnCreateController
 | 
			
		||||
import com.bluelinelabs.conductor.Router
 | 
			
		||||
 | 
			
		||||
abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateController(bundle) {
 | 
			
		||||
 | 
			
		||||
@@ -45,13 +44,4 @@ abstract class BaseController(bundle: Bundle? = null) : RestoreViewOnCreateContr
 | 
			
		||||
        (activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun Router.popControllerWithTag(tag: String): Boolean {
 | 
			
		||||
        val controller = getControllerWithTag(tag)
 | 
			
		||||
        if (controller != null) {
 | 
			
		||||
            popController(controller)
 | 
			
		||||
            return true
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,12 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.base.controller
 | 
			
		||||
 | 
			
		||||
import com.bluelinelabs.conductor.Router
 | 
			
		||||
 | 
			
		||||
fun Router.popControllerWithTag(tag: String): Boolean {
 | 
			
		||||
    val controller = getControllerWithTag(tag)
 | 
			
		||||
    if (controller != null) {
 | 
			
		||||
        popController(controller)
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
    return false
 | 
			
		||||
}
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.main
 | 
			
		||||
 | 
			
		||||
import android.animation.ObjectAnimator
 | 
			
		||||
import android.app.TaskStackBuilder
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
@@ -25,7 +24,7 @@ import eu.kanade.tachiyomi.ui.library.LibraryController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recent_updates.RecentChaptersController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.recently_read.RecentlyReadController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.setting.SettingsActivity
 | 
			
		||||
import eu.kanade.tachiyomi.ui.setting.SettingsMainController
 | 
			
		||||
import kotlinx.android.synthetic.main.activity_main.*
 | 
			
		||||
import kotlinx.android.synthetic.main.toolbar.*
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -85,10 +84,10 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
                    R.id.nav_drawer_downloads -> {
 | 
			
		||||
                        startActivity(Intent(this, DownloadActivity::class.java))
 | 
			
		||||
                    }
 | 
			
		||||
                    R.id.nav_drawer_settings -> {
 | 
			
		||||
                        val intent = Intent(this, SettingsActivity::class.java)
 | 
			
		||||
                        startActivityForResult(intent, REQUEST_OPEN_SETTINGS)
 | 
			
		||||
                    }
 | 
			
		||||
                    R.id.nav_drawer_settings ->
 | 
			
		||||
                        router.pushController(RouterTransaction.with(SettingsMainController())
 | 
			
		||||
                                .pushChangeHandler(FadeChangeHandler())
 | 
			
		||||
                                .popChangeHandler(FadeChangeHandler()))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            drawer.closeDrawer(GravityCompat.START)
 | 
			
		||||
@@ -216,26 +215,7 @@ class MainActivity : BaseActivity() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        if (requestCode == REQUEST_OPEN_SETTINGS && resultCode != 0) {
 | 
			
		||||
            if (resultCode and SettingsActivity.FLAG_DATABASE_CLEARED != 0) {
 | 
			
		||||
                // If database is cleared avoid undefined behavior by recreating the stack.
 | 
			
		||||
                TaskStackBuilder.create(this)
 | 
			
		||||
                        .addNextIntent(Intent(this, MainActivity::class.java))
 | 
			
		||||
                        .startActivities()
 | 
			
		||||
            } else if (resultCode and SettingsActivity.FLAG_THEME_CHANGED != 0) {
 | 
			
		||||
                // Delay activity recreation to avoid fragment leaks.
 | 
			
		||||
                nav_view.post { recreate() }
 | 
			
		||||
            } else if (resultCode and SettingsActivity.FLAG_LANG_CHANGED != 0) {
 | 
			
		||||
                nav_view.post { recreate() }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            super.onActivityResult(requestCode, resultCode, data)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        private const val REQUEST_OPEN_SETTINGS = 200
 | 
			
		||||
        // Shortcut actions
 | 
			
		||||
        private const val SHORTCUT_LIBRARY = "eu.kanade.tachiyomi.SHOW_LIBRARY"
 | 
			
		||||
        private const val SHORTCUT_RECENTLY_UPDATED = "eu.kanade.tachiyomi.SHOW_RECENTLY_UPDATED"
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ import eu.kanade.tachiyomi.data.database.models.Chapter
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.models.Manga
 | 
			
		||||
import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
 | 
			
		||||
import eu.kanade.tachiyomi.util.getCoordinates
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,7 @@ import eu.kanade.tachiyomi.data.download.model.Download
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.NoToolbarElevationController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag
 | 
			
		||||
import eu.kanade.tachiyomi.ui.manga.MangaController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
@@ -336,4 +337,4 @@ class RecentChaptersController : NucleusController<RecentChaptersPresenter>(),
 | 
			
		||||
        actionMode = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -8,6 +8,7 @@ import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
 | 
			
		||||
import android.widget.FrameLayout
 | 
			
		||||
import android.widget.ProgressBar
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackManager
 | 
			
		||||
import eu.kanade.tachiyomi.ui.main.MainActivity
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
@@ -41,7 +42,7 @@ class AnilistLoginActivity : AppCompatActivity() {
 | 
			
		||||
    private fun returnToSettings() {
 | 
			
		||||
        finish()
 | 
			
		||||
 | 
			
		||||
        val intent = Intent(this, SettingsActivity::class.java)
 | 
			
		||||
        val intent = Intent(this, MainActivity::class.java)
 | 
			
		||||
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
 | 
			
		||||
        startActivity(intent)
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,102 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.support.v4.graphics.drawable.DrawableCompat
 | 
			
		||||
import android.support.v7.preference.*
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
 | 
			
		||||
@DslMarker
 | 
			
		||||
@Target(AnnotationTarget.TYPE)
 | 
			
		||||
annotation class DSL
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceManager.newScreen(context: Context, block: (@DSL PreferenceScreen).() -> Unit): PreferenceScreen {
 | 
			
		||||
    return createPreferenceScreen(context).also { it.block() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.preference(block: (@DSL Preference).() -> Unit): Preference {
 | 
			
		||||
    return initThenAdd(Preference(context), block)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.switchPreference(block: (@DSL SwitchPreferenceCompat).() -> Unit): SwitchPreferenceCompat {
 | 
			
		||||
    return initThenAdd(SwitchPreferenceCompat(context), block)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.checkBoxPreference(block: (@DSL CheckBoxPreference).() -> Unit): CheckBoxPreference {
 | 
			
		||||
    return initThenAdd(CheckBoxPreference(context), block)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.editTextPreference(block: (@DSL EditTextPreference).() -> Unit): EditTextPreference {
 | 
			
		||||
    return initThenAdd(EditTextPreference(context), block).also(::initDialog)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.listPreference(block: (@DSL ListPreference).() -> Unit): ListPreference {
 | 
			
		||||
    return initThenAdd(ListPreference(context), block).also(::initDialog)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.intListPreference(block: (@DSL IntListPreference).() -> Unit): IntListPreference {
 | 
			
		||||
    return initThenAdd(IntListPreference(context), block).also(::initDialog)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceGroup.multiSelectListPreference(block: (@DSL MultiSelectListPreference).() -> Unit): MultiSelectListPreference {
 | 
			
		||||
    return initThenAdd(MultiSelectListPreference(context), block).also(::initDialog)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceScreen.preferenceCategory(block: (@DSL PreferenceCategory).() -> Unit): PreferenceCategory {
 | 
			
		||||
    return addThenInit(PreferenceCategory(context), block)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun PreferenceScreen.preferenceScreen(block: (@DSL PreferenceScreen).() -> Unit): PreferenceScreen {
 | 
			
		||||
    return addThenInit(preferenceManager.createPreferenceScreen(context), block)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fun initDialog(dialogPreference: DialogPreference) {
 | 
			
		||||
    with(dialogPreference) {
 | 
			
		||||
        if (dialogTitle == null) {
 | 
			
		||||
            dialogTitle = title
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <P : Preference> PreferenceGroup.initThenAdd(p: P, block: P.() -> Unit): P {
 | 
			
		||||
    return p.apply { block(); addPreference(this); }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <P : Preference> PreferenceGroup.addThenInit(p: P, block: P.() -> Unit): P {
 | 
			
		||||
    return p.apply { addPreference(this); block() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun Preference.onClick(crossinline block: () -> Unit) {
 | 
			
		||||
    setOnPreferenceClickListener { block(); true }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun Preference.onChange(crossinline block: (Any?) -> Boolean) {
 | 
			
		||||
    setOnPreferenceChangeListener { _, newValue -> block(newValue) }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var Preference.defaultValue: Any?
 | 
			
		||||
    get() = null // set only
 | 
			
		||||
    set(value) { setDefaultValue(value) }
 | 
			
		||||
 | 
			
		||||
var Preference.titleRes: Int
 | 
			
		||||
    get() = 0 // set only
 | 
			
		||||
    set(value) { setTitle(value) }
 | 
			
		||||
 | 
			
		||||
var Preference.iconRes: Int
 | 
			
		||||
    get() = 0 // set only
 | 
			
		||||
    set(value) { setIcon(value) }
 | 
			
		||||
 | 
			
		||||
var Preference.summaryRes: Int
 | 
			
		||||
    get() = 0 // set only
 | 
			
		||||
    set(value) { setSummary(value) }
 | 
			
		||||
 | 
			
		||||
var Preference.iconTint: Int
 | 
			
		||||
    get() = 0 // set only
 | 
			
		||||
    set(value) { DrawableCompat.setTint(icon, value) }
 | 
			
		||||
 | 
			
		||||
var ListPreference.entriesRes: Array<Int>
 | 
			
		||||
    get() = emptyArray() // set only
 | 
			
		||||
    set(value) { entries = value.map { context.getString(it) }.toTypedArray() }
 | 
			
		||||
 | 
			
		||||
var MultiSelectListPreference.entriesRes: Array<Int>
 | 
			
		||||
    get() = emptyArray() // set only
 | 
			
		||||
    set(value) { entries = value.map { context.getString(it) }.toTypedArray() }
 | 
			
		||||
@@ -0,0 +1,166 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateResult
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateDownloaderService
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.text.DateFormat
 | 
			
		||||
import java.text.ParseException
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
class SettingsAboutController : SettingsController() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks for new releases
 | 
			
		||||
     */
 | 
			
		||||
    private val updateChecker by lazy { GithubUpdateChecker() }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The subscribtion service of the obtained release object
 | 
			
		||||
     */
 | 
			
		||||
    private var releaseSubscription: Subscription? = null
 | 
			
		||||
 | 
			
		||||
    private val isUpdaterEnabled = !BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_about
 | 
			
		||||
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = "acra.enable"
 | 
			
		||||
            titleRes = R.string.pref_enable_acra
 | 
			
		||||
            summaryRes = R.string.pref_acra_summary
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.automaticUpdates
 | 
			
		||||
            titleRes = R.string.pref_enable_automatic_updates
 | 
			
		||||
            summaryRes = R.string.pref_enable_automatic_updates_summary
 | 
			
		||||
            defaultValue = false
 | 
			
		||||
 | 
			
		||||
            if (isUpdaterEnabled) {
 | 
			
		||||
                onChange { newValue ->
 | 
			
		||||
                    val checked = newValue as Boolean
 | 
			
		||||
                    if (checked) {
 | 
			
		||||
                        UpdateCheckerJob.setupTask()
 | 
			
		||||
                    } else {
 | 
			
		||||
                        UpdateCheckerJob.cancelTask()
 | 
			
		||||
                    }
 | 
			
		||||
                    true
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                isVisible = false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.version
 | 
			
		||||
            summary = if (BuildConfig.DEBUG)
 | 
			
		||||
                "r" + BuildConfig.COMMIT_COUNT
 | 
			
		||||
            else
 | 
			
		||||
                BuildConfig.VERSION_NAME
 | 
			
		||||
 | 
			
		||||
            if (isUpdaterEnabled) {
 | 
			
		||||
                onClick { checkVersion() }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.build_time
 | 
			
		||||
            summary = getFormattedBuildTime()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView(view: View) {
 | 
			
		||||
        super.onDestroyView(view)
 | 
			
		||||
        releaseSubscription?.unsubscribe()
 | 
			
		||||
        releaseSubscription = null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks version and shows a user prompt if an update is available.
 | 
			
		||||
     */
 | 
			
		||||
    private fun checkVersion() {
 | 
			
		||||
        if (activity == null) return
 | 
			
		||||
 | 
			
		||||
        activity?.toast(R.string.update_check_look_for_updates)
 | 
			
		||||
        releaseSubscription?.unsubscribe()
 | 
			
		||||
        releaseSubscription = updateChecker.checkForUpdate()
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe({ result ->
 | 
			
		||||
                    when (result) {
 | 
			
		||||
                        is GithubUpdateResult.NewUpdate -> {
 | 
			
		||||
                            val body = result.release.changeLog
 | 
			
		||||
                            val url = result.release.downloadLink
 | 
			
		||||
 | 
			
		||||
                            // Create confirmation window
 | 
			
		||||
                            NewUpdateDialogController(body, url).showDialog(router)
 | 
			
		||||
                        }
 | 
			
		||||
                        is GithubUpdateResult.NoNewUpdate -> {
 | 
			
		||||
                            activity?.toast(R.string.update_check_no_new_updates)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }, { error ->
 | 
			
		||||
                    Timber.e(error)
 | 
			
		||||
                })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class NewUpdateDialogController(bundle: Bundle? = null) : DialogController(bundle) {
 | 
			
		||||
 | 
			
		||||
        constructor(body: String, url: String) : this(Bundle().apply {
 | 
			
		||||
            putString(BODY_KEY, body)
 | 
			
		||||
            putString(URL_KEY, url)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.update_check_title)
 | 
			
		||||
                    .content(args.getString(BODY_KEY))
 | 
			
		||||
                    .positiveText(R.string.update_check_confirm)
 | 
			
		||||
                    .negativeText(R.string.update_check_ignore)
 | 
			
		||||
                    .onPositive { _, _ ->
 | 
			
		||||
                        val appContext = applicationContext
 | 
			
		||||
                        if (appContext != null) {
 | 
			
		||||
                            // Start download
 | 
			
		||||
                            val url = args.getString(URL_KEY)
 | 
			
		||||
                            UpdateDownloaderService.downloadUpdate(appContext, url)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private companion object {
 | 
			
		||||
            const val BODY_KEY = "NewUpdateDialogController.body"
 | 
			
		||||
            const val URL_KEY = "NewUpdateDialogController.key"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getFormattedBuildTime(): String {
 | 
			
		||||
        try {
 | 
			
		||||
            val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US)
 | 
			
		||||
            inputDf.timeZone = TimeZone.getTimeZone("UTC")
 | 
			
		||||
            val date = inputDf.parse(BuildConfig.BUILD_TIME)
 | 
			
		||||
 | 
			
		||||
            val outputDf = DateFormat.getDateTimeInstance(
 | 
			
		||||
                    DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
 | 
			
		||||
            outputDf.timeZone = TimeZone.getDefault()
 | 
			
		||||
 | 
			
		||||
            return outputDf.format(date)
 | 
			
		||||
        } catch (e: ParseException) {
 | 
			
		||||
            return BuildConfig.BUILD_TIME
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,139 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateChecker
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.GithubUpdateResult
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateCheckerJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.updater.UpdateDownloaderService
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import net.xpece.android.support.preference.SwitchPreference
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
import java.text.DateFormat
 | 
			
		||||
import java.text.ParseException
 | 
			
		||||
import java.text.SimpleDateFormat
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class SettingsAboutFragment : SettingsFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun newInstance(rootKey: String): SettingsAboutFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsAboutFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks for new releases
 | 
			
		||||
     */
 | 
			
		||||
    private val updateChecker by lazy { GithubUpdateChecker() }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The subscribtion service of the obtained release object
 | 
			
		||||
     */
 | 
			
		||||
    private var releaseSubscription: Subscription? = null
 | 
			
		||||
 | 
			
		||||
    val automaticUpdates: SwitchPreference by bindPref(R.string.pref_enable_automatic_updates_key)
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        val version = findPreference(getString(R.string.pref_version))
 | 
			
		||||
        val buildTime = findPreference(getString(R.string.pref_build_time))
 | 
			
		||||
 | 
			
		||||
        version.summary = if (BuildConfig.DEBUG)
 | 
			
		||||
            "r" + BuildConfig.COMMIT_COUNT
 | 
			
		||||
        else
 | 
			
		||||
            BuildConfig.VERSION_NAME
 | 
			
		||||
 | 
			
		||||
        if (!BuildConfig.DEBUG && BuildConfig.INCLUDE_UPDATER) {
 | 
			
		||||
            //Set onClickListener to check for new version
 | 
			
		||||
            version.setOnPreferenceClickListener {
 | 
			
		||||
                checkVersion()
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            automaticUpdates.setOnPreferenceChangeListener { preference, any ->
 | 
			
		||||
                val checked = any as Boolean
 | 
			
		||||
                if (checked) {
 | 
			
		||||
                    UpdateCheckerJob.setupTask()
 | 
			
		||||
                } else {
 | 
			
		||||
                    UpdateCheckerJob.cancelTask()
 | 
			
		||||
                }
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            automaticUpdates.isVisible = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        buildTime.summary = getFormattedBuildTime()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView() {
 | 
			
		||||
        releaseSubscription?.unsubscribe()
 | 
			
		||||
        super.onDestroyView()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getFormattedBuildTime(): String {
 | 
			
		||||
        try {
 | 
			
		||||
            val inputDf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'")
 | 
			
		||||
            inputDf.timeZone = TimeZone.getTimeZone("UTC")
 | 
			
		||||
            val date = inputDf.parse(BuildConfig.BUILD_TIME)
 | 
			
		||||
 | 
			
		||||
            val outputDf = DateFormat.getDateTimeInstance(
 | 
			
		||||
                    DateFormat.MEDIUM, DateFormat.SHORT, Locale.getDefault())
 | 
			
		||||
            outputDf.timeZone = TimeZone.getDefault()
 | 
			
		||||
 | 
			
		||||
            return outputDf.format(date)
 | 
			
		||||
        } catch (e: ParseException) {
 | 
			
		||||
            return BuildConfig.BUILD_TIME
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Checks version and shows a user prompt if an update is available.
 | 
			
		||||
     */
 | 
			
		||||
    private fun checkVersion() {
 | 
			
		||||
        releaseSubscription?.unsubscribe()
 | 
			
		||||
 | 
			
		||||
        context.toast(R.string.update_check_look_for_updates)
 | 
			
		||||
 | 
			
		||||
        releaseSubscription = updateChecker.checkForUpdate()
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe({ result ->
 | 
			
		||||
                    when (result) {
 | 
			
		||||
                        is GithubUpdateResult.NewUpdate -> {
 | 
			
		||||
                            val body = result.release.changeLog
 | 
			
		||||
                            val url = result.release.downloadLink
 | 
			
		||||
 | 
			
		||||
                            // Create confirmation window
 | 
			
		||||
                            MaterialDialog.Builder(context)
 | 
			
		||||
                                    .title(R.string.update_check_title)
 | 
			
		||||
                                    .content(body)
 | 
			
		||||
                                    .positiveText(getString(R.string.update_check_confirm))
 | 
			
		||||
                                    .negativeText(getString(R.string.update_check_ignore))
 | 
			
		||||
                                    .onPositive { dialog, which ->
 | 
			
		||||
                                        // Start download
 | 
			
		||||
                                        UpdateDownloaderService.downloadUpdate(context, url)
 | 
			
		||||
                                    }
 | 
			
		||||
                                    .show()
 | 
			
		||||
                        }
 | 
			
		||||
                        is GithubUpdateResult.NoNewUpdate -> {
 | 
			
		||||
                            context.toast(R.string.update_check_no_new_updates)
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }, { error ->
 | 
			
		||||
                    Timber.e(error)
 | 
			
		||||
                })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.PreferenceFragmentCompat
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import android.view.MenuItem
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
 | 
			
		||||
import kotlinx.android.synthetic.main.toolbar.*
 | 
			
		||||
import net.xpece.android.support.preference.PreferenceScreenNavigationStrategy
 | 
			
		||||
import net.xpece.android.support.preference.PreferenceScreenNavigationStrategy.ReplaceFragment
 | 
			
		||||
 | 
			
		||||
class SettingsActivity : BaseActivity(),
 | 
			
		||||
        PreferenceFragmentCompat.OnPreferenceStartScreenCallback,
 | 
			
		||||
        PreferenceScreenNavigationStrategy.ReplaceFragment.Callbacks {
 | 
			
		||||
 | 
			
		||||
    private lateinit var replaceFragmentStrategy: ReplaceFragment
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Flags to send to the parent activity for reacting to preference changes.
 | 
			
		||||
     */
 | 
			
		||||
    var parentFlags = 0
 | 
			
		||||
        set(value) {
 | 
			
		||||
            field = field or value
 | 
			
		||||
            setResult(field)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedState: Bundle?) {
 | 
			
		||||
        setAppTheme()
 | 
			
		||||
        super.onCreate(savedState)
 | 
			
		||||
        setTitle(R.string.label_settings)
 | 
			
		||||
        setContentView(R.layout.activity_preferences)
 | 
			
		||||
 | 
			
		||||
        replaceFragmentStrategy = ReplaceFragment(this,
 | 
			
		||||
                R.anim.abc_fade_in, R.anim.abc_fade_out,
 | 
			
		||||
                R.anim.abc_fade_in, R.anim.abc_fade_out)
 | 
			
		||||
 | 
			
		||||
        if (savedState == null) {
 | 
			
		||||
            supportFragmentManager.beginTransaction()
 | 
			
		||||
                .add(R.id.settings_content, SettingsFragment.newInstance(null), "Settings")
 | 
			
		||||
                .commit()
 | 
			
		||||
        } else {
 | 
			
		||||
            parentFlags = savedState.getInt(SettingsActivity::parentFlags.name)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setupToolbar(toolbar, backNavigation = false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onSaveInstanceState(outState: Bundle) {
 | 
			
		||||
        outState.putInt(SettingsActivity::parentFlags.name, parentFlags)
 | 
			
		||||
        super.onSaveInstanceState(outState)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
 | 
			
		||||
        when (item.itemId) {
 | 
			
		||||
            android.R.id.home -> onBackPressed()
 | 
			
		||||
            else -> return super.onOptionsItemSelected(item)
 | 
			
		||||
        }
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onBuildPreferenceFragment(key: String?): PreferenceFragmentCompat {
 | 
			
		||||
        return when (key) {
 | 
			
		||||
            "general_screen" -> SettingsGeneralFragment.newInstance(key)
 | 
			
		||||
            "downloads_screen" -> SettingsDownloadsFragment.newInstance(key)
 | 
			
		||||
            "sources_screen" -> SettingsSourcesFragment.newInstance(key)
 | 
			
		||||
            "tracking_screen" -> SettingsTrackingFragment.newInstance(key)
 | 
			
		||||
            "backup_screen" -> SettingsBackupFragment.newInstance(key)
 | 
			
		||||
            "advanced_screen" -> SettingsAdvancedFragment.newInstance(key)
 | 
			
		||||
            "about_screen" -> SettingsAboutFragment.newInstance(key)
 | 
			
		||||
            else -> SettingsFragment.newInstance(key)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPreferenceStartScreen(p0: PreferenceFragmentCompat, p1: PreferenceScreen): Boolean {
 | 
			
		||||
        replaceFragmentStrategy.onPreferenceStartScreen(supportFragmentManager, p0, p1)
 | 
			
		||||
        return true
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val FLAG_THEME_CHANGED = 0x1
 | 
			
		||||
        const val FLAG_DATABASE_CLEARED = 0x2
 | 
			
		||||
        const val FLAG_LANG_CHANGED = 0x4
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,159 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.bluelinelabs.conductor.RouterTransaction
 | 
			
		||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
 | 
			
		||||
import eu.kanade.tachiyomi.network.NetworkHelper
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.library.LibraryController
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class SettingsAdvancedController : SettingsController() {
 | 
			
		||||
 | 
			
		||||
    private val network: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val chapterCache: ChapterCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_advanced
 | 
			
		||||
 | 
			
		||||
        preference {
 | 
			
		||||
            key = CLEAR_CACHE_KEY
 | 
			
		||||
            titleRes = R.string.pref_clear_chapter_cache
 | 
			
		||||
            summary = context.getString(R.string.used_cache, chapterCache.readableSize)
 | 
			
		||||
 | 
			
		||||
            onClick { clearChapterCache() }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.pref_clear_cookies
 | 
			
		||||
 | 
			
		||||
            onClick {
 | 
			
		||||
                network.cookies.removeAll()
 | 
			
		||||
                activity?.toast(R.string.cookies_cleared)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.pref_clear_database
 | 
			
		||||
            summaryRes = R.string.pref_clear_database_summary
 | 
			
		||||
 | 
			
		||||
            onClick {
 | 
			
		||||
                val ctrl = ClearDatabaseDialogController()
 | 
			
		||||
                ctrl.targetController = this@SettingsAdvancedController
 | 
			
		||||
                ctrl.showDialog(router)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.pref_refresh_library_metadata
 | 
			
		||||
            summaryRes = R.string.pref_refresh_library_metadata_summary
 | 
			
		||||
 | 
			
		||||
            onClick { LibraryUpdateService.start(context, details = true) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun clearChapterCache() {
 | 
			
		||||
        if (activity == null) return
 | 
			
		||||
        val files = chapterCache.cacheDir.listFiles() ?: return
 | 
			
		||||
 | 
			
		||||
        var deletedFiles = 0
 | 
			
		||||
 | 
			
		||||
        val ctrl = DeletingFilesDialogController()
 | 
			
		||||
        ctrl.total = files.size
 | 
			
		||||
        ctrl.showDialog(router)
 | 
			
		||||
 | 
			
		||||
        Observable.defer { Observable.from(files) }
 | 
			
		||||
                .doOnNext { file ->
 | 
			
		||||
                    if (chapterCache.removeFileFromCache(file.name)) {
 | 
			
		||||
                        deletedFiles++
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe({
 | 
			
		||||
                    ctrl.setProgress(deletedFiles)
 | 
			
		||||
                }, {
 | 
			
		||||
                    activity?.toast(R.string.cache_delete_error)
 | 
			
		||||
                }, {
 | 
			
		||||
                    ctrl.finish()
 | 
			
		||||
                    activity?.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
 | 
			
		||||
                    findPreference(CLEAR_CACHE_KEY)?.summary =
 | 
			
		||||
                            resources?.getString(R.string.used_cache, chapterCache.readableSize)
 | 
			
		||||
                })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class DeletingFilesDialogController : DialogController() {
 | 
			
		||||
 | 
			
		||||
        var total = 0
 | 
			
		||||
 | 
			
		||||
        private var materialDialog: MaterialDialog? = null
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.deleting)
 | 
			
		||||
                    .progress(false, total, true)
 | 
			
		||||
                    .cancelable(false)
 | 
			
		||||
                    .build()
 | 
			
		||||
                    .also { materialDialog = it }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onDestroyView(view: View) {
 | 
			
		||||
            super.onDestroyView(view)
 | 
			
		||||
            materialDialog = null
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onRestoreInstanceState(savedInstanceState: Bundle) {
 | 
			
		||||
            super.onRestoreInstanceState(savedInstanceState)
 | 
			
		||||
            finish()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun setProgress(deletedFiles: Int) {
 | 
			
		||||
            materialDialog?.setProgress(deletedFiles)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun finish() {
 | 
			
		||||
            router.popController(this)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class ClearDatabaseDialogController : DialogController() {
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .content(R.string.clear_database_confirmation)
 | 
			
		||||
                    .positiveText(android.R.string.yes)
 | 
			
		||||
                    .negativeText(android.R.string.no)
 | 
			
		||||
                    .onPositive { _, _ ->
 | 
			
		||||
                        (targetController as? SettingsAdvancedController)?.clearDatabase()
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun clearDatabase() {
 | 
			
		||||
        // Avoid weird behavior by going back to the library.
 | 
			
		||||
        val newBackstack = listOf(RouterTransaction.with(LibraryController())) +
 | 
			
		||||
                router.backstack.drop(1)
 | 
			
		||||
 | 
			
		||||
        router.setBackstack(newBackstack, FadeChangeHandler())
 | 
			
		||||
 | 
			
		||||
        db.deleteMangasNotInLibrary().executeAsBlocking()
 | 
			
		||||
        db.deleteHistoryNoLastRead().executeAsBlocking()
 | 
			
		||||
        activity?.toast(R.string.clear_database_completed)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private companion object {
 | 
			
		||||
        const val CLEAR_CACHE_KEY = "pref_clear_cache_key"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,117 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.Preference
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
 | 
			
		||||
import eu.kanade.tachiyomi.network.NetworkHelper
 | 
			
		||||
import eu.kanade.tachiyomi.util.plusAssign
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.util.concurrent.atomic.AtomicInteger
 | 
			
		||||
 | 
			
		||||
class SettingsAdvancedFragment : SettingsFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun newInstance(rootKey: String): SettingsAdvancedFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsAdvancedFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val network: NetworkHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val chapterCache: ChapterCache by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val clearCache: Preference by bindPref(R.string.pref_clear_chapter_cache_key)
 | 
			
		||||
 | 
			
		||||
    private val clearDatabase: Preference by bindPref(R.string.pref_clear_database_key)
 | 
			
		||||
 | 
			
		||||
    private val clearCookies: Preference by bindPref(R.string.pref_clear_cookies_key)
 | 
			
		||||
 | 
			
		||||
    private val refreshMetadata: Preference by bindPref(R.string.pref_refresh_library_metadata_key)
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        clearCache.setOnPreferenceClickListener {
 | 
			
		||||
            clearChapterCache()
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
        clearCache.summary = getString(R.string.used_cache, chapterCache.readableSize)
 | 
			
		||||
 | 
			
		||||
        clearCookies.setOnPreferenceClickListener {
 | 
			
		||||
            network.cookies.removeAll()
 | 
			
		||||
            activity.toast(R.string.cookies_cleared)
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        clearDatabase.setOnPreferenceClickListener {
 | 
			
		||||
            clearDatabase()
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        refreshMetadata.setOnPreferenceClickListener {
 | 
			
		||||
            LibraryUpdateService.start(context, details = true)
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun clearChapterCache() {
 | 
			
		||||
        val deletedFiles = AtomicInteger()
 | 
			
		||||
 | 
			
		||||
        val files = chapterCache.cacheDir.listFiles() ?: return
 | 
			
		||||
 | 
			
		||||
        val dialog = MaterialDialog.Builder(activity)
 | 
			
		||||
                .title(R.string.deleting)
 | 
			
		||||
                .progress(false, files.size, true)
 | 
			
		||||
                .cancelable(false)
 | 
			
		||||
                .show()
 | 
			
		||||
 | 
			
		||||
        subscriptions += Observable.defer { Observable.from(files) }
 | 
			
		||||
                .concatMap { file ->
 | 
			
		||||
                    if (chapterCache.removeFileFromCache(file.name)) {
 | 
			
		||||
                        deletedFiles.incrementAndGet()
 | 
			
		||||
                    }
 | 
			
		||||
                    Observable.just(file)
 | 
			
		||||
                }
 | 
			
		||||
                .subscribeOn(Schedulers.io())
 | 
			
		||||
                .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                .subscribe({
 | 
			
		||||
                    dialog.incrementProgress(1)
 | 
			
		||||
                }, {
 | 
			
		||||
                    dialog.dismiss()
 | 
			
		||||
                    activity.toast(R.string.cache_delete_error)
 | 
			
		||||
                }, {
 | 
			
		||||
                    dialog.dismiss()
 | 
			
		||||
                    activity.toast(getString(R.string.cache_deleted, deletedFiles.get()))
 | 
			
		||||
                    clearCache.summary = getString(R.string.used_cache, chapterCache.readableSize)
 | 
			
		||||
                })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun clearDatabase() {
 | 
			
		||||
        MaterialDialog.Builder(activity)
 | 
			
		||||
                .content(R.string.clear_database_confirmation)
 | 
			
		||||
                .positiveText(android.R.string.yes)
 | 
			
		||||
                .negativeText(android.R.string.no)
 | 
			
		||||
                .onPositive { dialog, which ->
 | 
			
		||||
                    (activity as SettingsActivity).parentFlags = SettingsActivity.FLAG_DATABASE_CLEARED
 | 
			
		||||
                    db.deleteMangasNotInLibrary().executeAsBlocking()
 | 
			
		||||
                    db.deleteHistoryNoLastRead().executeAsBlocking()
 | 
			
		||||
                    activity.toast(R.string.clear_database_completed)
 | 
			
		||||
                }
 | 
			
		||||
                .show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,458 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.Manifest.permission.READ_EXTERNAL_STORAGE
 | 
			
		||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.content.BroadcastReceiver
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.IntentFilter
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import com.nononsenseapps.filepicker.FilePickerActivity
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupConst
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.popControllerWithTag
 | 
			
		||||
import eu.kanade.tachiyomi.util.getUriCompat
 | 
			
		||||
import eu.kanade.tachiyomi.util.registerLocalReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import eu.kanade.tachiyomi.util.unregisterLocalReceiver
 | 
			
		||||
import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.util.concurrent.TimeUnit
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
class SettingsBackupController : SettingsController() {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Flags containing information of what to backup.
 | 
			
		||||
     */
 | 
			
		||||
    private var backupFlags = 0
 | 
			
		||||
 | 
			
		||||
    private val receiver = BackupBroadcastReceiver()
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        preferences.context.registerLocalReceiver(receiver, IntentFilter(BackupConst.INTENT_FILTER))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedInstanceState)
 | 
			
		||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
			
		||||
            requestPermissions(arrayOf(WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE), 500)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroy() {
 | 
			
		||||
        super.onDestroy()
 | 
			
		||||
        preferences.context.unregisterLocalReceiver(receiver)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.backup
 | 
			
		||||
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.pref_create_backup
 | 
			
		||||
            summaryRes = R.string.pref_create_backup_summ
 | 
			
		||||
 | 
			
		||||
            onClick {
 | 
			
		||||
                val ctrl = CreateBackupDialog()
 | 
			
		||||
                ctrl.targetController = this@SettingsBackupController
 | 
			
		||||
                ctrl.showDialog(router)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.pref_restore_backup
 | 
			
		||||
            summaryRes = R.string.pref_restore_backup_summ
 | 
			
		||||
 | 
			
		||||
            onClick {
 | 
			
		||||
                val intent = Intent(Intent.ACTION_GET_CONTENT)
 | 
			
		||||
                intent.addCategory(Intent.CATEGORY_OPENABLE)
 | 
			
		||||
                intent.type = "application/*"
 | 
			
		||||
                val title = resources?.getString(R.string.file_select_backup)
 | 
			
		||||
                val chooser = Intent.createChooser(intent, title)
 | 
			
		||||
                startActivityForResult(chooser, CODE_BACKUP_RESTORE)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preferenceCategory {
 | 
			
		||||
            titleRes = R.string.pref_backup_service_category
 | 
			
		||||
 | 
			
		||||
            intListPreference {
 | 
			
		||||
                key = Keys.backupInterval
 | 
			
		||||
                titleRes = R.string.pref_backup_interval
 | 
			
		||||
                entriesRes = arrayOf(R.string.update_never, R.string.update_6hour,
 | 
			
		||||
                        R.string.update_12hour, R.string.update_24hour,
 | 
			
		||||
                        R.string.update_48hour, R.string.update_weekly)
 | 
			
		||||
                entryValues = arrayOf("0", "6", "12", "24", "168")
 | 
			
		||||
                defaultValue = "0"
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
 | 
			
		||||
                onChange { newValue ->
 | 
			
		||||
                    // Always cancel the previous task, it seems that sometimes they are not updated
 | 
			
		||||
                    BackupCreatorJob.cancelTask()
 | 
			
		||||
 | 
			
		||||
                    val interval = (newValue as String).toInt()
 | 
			
		||||
                    if (interval > 0) {
 | 
			
		||||
                        BackupCreatorJob.setupTask(interval)
 | 
			
		||||
                    }
 | 
			
		||||
                    true
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            val backupDir = preference {
 | 
			
		||||
                key = Keys.backupDirectory
 | 
			
		||||
                titleRes = R.string.pref_backup_directory
 | 
			
		||||
 | 
			
		||||
                onClick {
 | 
			
		||||
                    val currentDir = preferences.backupsDirectory().getOrDefault()
 | 
			
		||||
 | 
			
		||||
                    val intent = if (Build.VERSION.SDK_INT < 21) {
 | 
			
		||||
                        // Custom dir selected, open directory selector
 | 
			
		||||
                        val i = Intent(activity, CustomLayoutPickerActivity::class.java)
 | 
			
		||||
                        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, currentDir)
 | 
			
		||||
 | 
			
		||||
                    } else {
 | 
			
		||||
                        Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
 | 
			
		||||
                    }
 | 
			
		||||
                    startActivityForResult(intent, CODE_BACKUP_DIR)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                preferences.backupsDirectory().asObservable()
 | 
			
		||||
                        .subscribeUntilDestroy { path ->
 | 
			
		||||
                            val dir = UniFile.fromUri(context, Uri.parse(path))
 | 
			
		||||
                            summary = dir.filePath ?: path
 | 
			
		||||
                        }
 | 
			
		||||
            }
 | 
			
		||||
            val backupNumber = intListPreference {
 | 
			
		||||
                key = Keys.numberOfBackups
 | 
			
		||||
                titleRes = R.string.pref_backup_slots
 | 
			
		||||
                entries = arrayOf("1", "2", "3", "4", "5")
 | 
			
		||||
                entryValues = entries
 | 
			
		||||
                defaultValue = "1"
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            preferences.backupInterval().asObservable()
 | 
			
		||||
                    .subscribeUntilDestroy {
 | 
			
		||||
                        backupDir.isVisible = it > 0
 | 
			
		||||
                        backupNumber.isVisible = it > 0
 | 
			
		||||
                    }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        when (requestCode) {
 | 
			
		||||
            CODE_BACKUP_DIR -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val activity = activity ?: return
 | 
			
		||||
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
                    val uri = Uri.fromFile(File(data.data.path))
 | 
			
		||||
                    preferences.backupsDirectory().set(uri.toString())
 | 
			
		||||
                } else {
 | 
			
		||||
                    val uri = data.data
 | 
			
		||||
                    val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                            Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
                    activity.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
 | 
			
		||||
                    val file = UniFile.fromUri(activity, uri)
 | 
			
		||||
                    preferences.backupsDirectory().set(file.uri.toString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            CODE_BACKUP_CREATE -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val activity = activity ?: return
 | 
			
		||||
                val path = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
                    val dir = data.data.path
 | 
			
		||||
                    val file = File(dir, Backup.getDefaultFilename())
 | 
			
		||||
 | 
			
		||||
                    file.absolutePath
 | 
			
		||||
                } else {
 | 
			
		||||
                    val uri = data.data
 | 
			
		||||
                    val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                            Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
                    activity.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
                    val file = UniFile.fromUri(activity, uri)
 | 
			
		||||
 | 
			
		||||
                    file.uri.toString()
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                CreatingBackupDialog().showDialog(router, TAG_CREATING_BACKUP_DIALOG)
 | 
			
		||||
                BackupCreateService.makeBackup(activity, path, backupFlags)
 | 
			
		||||
            }
 | 
			
		||||
            CODE_BACKUP_RESTORE -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val uri = data.data
 | 
			
		||||
                RestoreBackupDialog(uri).showDialog(router)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun createBackup(flags: Int) {
 | 
			
		||||
        backupFlags = flags
 | 
			
		||||
 | 
			
		||||
        // If API lower as KitKat use custom dir picker
 | 
			
		||||
        val intent = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
            // Get dirs
 | 
			
		||||
            val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
            val currentDir = preferences.backupsDirectory().getOrDefault()
 | 
			
		||||
 | 
			
		||||
            Intent(activity, CustomLayoutPickerActivity::class.java)
 | 
			
		||||
                    .putExtra(FilePickerActivity.EXTRA_ALLOW_MULTIPLE, false)
 | 
			
		||||
                    .putExtra(FilePickerActivity.EXTRA_ALLOW_CREATE_DIR, true)
 | 
			
		||||
                    .putExtra(FilePickerActivity.EXTRA_MODE, FilePickerActivity.MODE_DIR)
 | 
			
		||||
                    .putExtra(FilePickerActivity.EXTRA_START_PATH, currentDir)
 | 
			
		||||
        } else {
 | 
			
		||||
            // Use Androids build in file creator
 | 
			
		||||
            Intent(Intent.ACTION_CREATE_DOCUMENT)
 | 
			
		||||
                    .addCategory(Intent.CATEGORY_OPENABLE)
 | 
			
		||||
                    .setType("application/*")
 | 
			
		||||
                    .putExtra(Intent.EXTRA_TITLE, Backup.getDefaultFilename())
 | 
			
		||||
        }
 | 
			
		||||
        startActivityForResult(intent, CODE_BACKUP_CREATE)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class CreateBackupDialog : DialogController() {
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.pref_create_backup)
 | 
			
		||||
                    .content(R.string.backup_choice)
 | 
			
		||||
                    .items(R.array.backup_options)
 | 
			
		||||
                    .itemsDisabledIndices(0)
 | 
			
		||||
                    .itemsCallbackMultiChoice(arrayOf(0, 1, 2, 3, 4), { _, positions, _ ->
 | 
			
		||||
                        var flags = 0
 | 
			
		||||
                        for (i in 1..positions.size - 1) {
 | 
			
		||||
                            when (positions[i]) {
 | 
			
		||||
                                1 -> flags = flags or BackupCreateService.BACKUP_CATEGORY
 | 
			
		||||
                                2 -> flags = flags or BackupCreateService.BACKUP_CHAPTER
 | 
			
		||||
                                3 -> flags = flags or BackupCreateService.BACKUP_TRACK
 | 
			
		||||
                                4 -> flags = flags or BackupCreateService.BACKUP_HISTORY
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        (targetController as? SettingsBackupController)?.createBackup(flags)
 | 
			
		||||
                        true
 | 
			
		||||
                    })
 | 
			
		||||
                    .positiveText(R.string.action_create)
 | 
			
		||||
                    .negativeText(android.R.string.cancel)
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class CreatingBackupDialog : DialogController() {
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.backup)
 | 
			
		||||
                    .content(R.string.creating_backup)
 | 
			
		||||
                    .progress(true, 0)
 | 
			
		||||
                    .cancelable(false)
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onRestoreInstanceState(savedInstanceState: Bundle) {
 | 
			
		||||
            super.onRestoreInstanceState(savedInstanceState)
 | 
			
		||||
            router.popController(this)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class CreatedBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
 | 
			
		||||
        constructor(uri: Uri) : this(Bundle().apply {
 | 
			
		||||
            putParcelable(KEY_URI, uri)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            val activity = activity!!
 | 
			
		||||
            val unifile = UniFile.fromUri(activity, args.getParcelable<Uri>(KEY_URI))
 | 
			
		||||
            return MaterialDialog.Builder(activity)
 | 
			
		||||
                    .title(R.string.backup_created)
 | 
			
		||||
                    .content(activity.getString(R.string.file_saved, unifile.filePath))
 | 
			
		||||
                    .positiveText(R.string.action_close)
 | 
			
		||||
                    .negativeText(R.string.action_export)
 | 
			
		||||
                    .onNegative { _, _ ->
 | 
			
		||||
                        val sendIntent = Intent(Intent.ACTION_SEND)
 | 
			
		||||
                        sendIntent.type = "application/json"
 | 
			
		||||
                        sendIntent.putExtra(Intent.EXTRA_STREAM, unifile.uri)
 | 
			
		||||
                        startActivity(Intent.createChooser(sendIntent, ""))
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private companion object {
 | 
			
		||||
            const val KEY_URI = "BackupCreatedDialog.uri"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class RestoreBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
 | 
			
		||||
        constructor(uri: Uri) : this(Bundle().apply {
 | 
			
		||||
            putParcelable(KEY_URI, uri)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.pref_restore_backup)
 | 
			
		||||
                    .content(R.string.backup_restore_content)
 | 
			
		||||
                    .positiveText(R.string.action_restore)
 | 
			
		||||
                    .onPositive { _, _ ->
 | 
			
		||||
                        val context = applicationContext
 | 
			
		||||
                        if (context != null) {
 | 
			
		||||
                            RestoringBackupDialog().showDialog(router, TAG_RESTORING_BACKUP_DIALOG)
 | 
			
		||||
                            BackupRestoreService.start(context, args.getParcelable<Uri>(KEY_URI))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private companion object {
 | 
			
		||||
            const val KEY_URI = "RestoreBackupDialog.uri"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class RestoringBackupDialog : DialogController() {
 | 
			
		||||
        private var materialDialog: MaterialDialog? = null
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            return MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.backup)
 | 
			
		||||
                    .content(R.string.restoring_backup)
 | 
			
		||||
                    .progress(false, 100, true)
 | 
			
		||||
                    .cancelable(false)
 | 
			
		||||
                    .negativeText(R.string.action_stop)
 | 
			
		||||
                    .onNegative { _, _ ->
 | 
			
		||||
                        applicationContext?.let { BackupRestoreService.stop(it) }
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
                    .also { materialDialog = it }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onDestroyView(view: View) {
 | 
			
		||||
            super.onDestroyView(view)
 | 
			
		||||
            materialDialog = null
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        override fun onRestoreInstanceState(savedInstanceState: Bundle) {
 | 
			
		||||
            super.onRestoreInstanceState(savedInstanceState)
 | 
			
		||||
            router.popController(this)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun updateProgress(content: String?, progress: Int, amount: Int) {
 | 
			
		||||
            val dialog = materialDialog ?: return
 | 
			
		||||
            dialog.setContent(content)
 | 
			
		||||
            dialog.setProgress(progress)
 | 
			
		||||
            dialog.maxProgress = amount
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class RestoredBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
 | 
			
		||||
        constructor(time: Long, errorCount: Int, path: String, file: String) : this(Bundle().apply {
 | 
			
		||||
            putLong(KEY_TIME, time)
 | 
			
		||||
            putInt(KEY_ERROR_COUNT, errorCount)
 | 
			
		||||
            putString(KEY_PATH, path)
 | 
			
		||||
            putString(KEY_FILE, file)
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            val activity = activity!!
 | 
			
		||||
            val time = args.getLong(KEY_TIME)
 | 
			
		||||
            val errors = args.getInt(KEY_ERROR_COUNT)
 | 
			
		||||
            val path = args.getString(KEY_PATH)
 | 
			
		||||
            val file = args.getString(KEY_FILE)
 | 
			
		||||
            val timeString = String.format("%02d min, %02d sec",
 | 
			
		||||
                    TimeUnit.MILLISECONDS.toMinutes(time),
 | 
			
		||||
                    TimeUnit.MILLISECONDS.toSeconds(time) - TimeUnit.MINUTES.toSeconds(
 | 
			
		||||
                            TimeUnit.MILLISECONDS.toMinutes(time))
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
            return MaterialDialog.Builder(activity)
 | 
			
		||||
                    .title(R.string.restore_completed)
 | 
			
		||||
                    .content(activity.getString(R.string.restore_completed_content, timeString,
 | 
			
		||||
                            if (errors > 0) "$errors" else activity.getString(android.R.string.no)))
 | 
			
		||||
                    .positiveText(R.string.action_close)
 | 
			
		||||
                    .negativeText(R.string.action_open_log)
 | 
			
		||||
                    .onNegative { _, _ ->
 | 
			
		||||
                        val context = applicationContext ?: return@onNegative
 | 
			
		||||
                        if (!path.isEmpty()) {
 | 
			
		||||
                            val destFile = File(path, file)
 | 
			
		||||
                            val uri = destFile.getUriCompat(context)
 | 
			
		||||
                            val sendIntent = Intent(Intent.ACTION_VIEW).apply {
 | 
			
		||||
                                setDataAndType(uri, "text/plain")
 | 
			
		||||
                                flags = Intent.FLAG_ACTIVITY_NEW_TASK or
 | 
			
		||||
                                        Intent.FLAG_GRANT_READ_URI_PERMISSION
 | 
			
		||||
                            }
 | 
			
		||||
                            startActivity(sendIntent)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            context.toast(context.getString(R.string.error_opening_log))
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private companion object {
 | 
			
		||||
            const val KEY_TIME = "RestoredBackupDialog.time"
 | 
			
		||||
            const val KEY_ERROR_COUNT = "RestoredBackupDialog.errors"
 | 
			
		||||
            const val KEY_PATH = "RestoredBackupDialog.path"
 | 
			
		||||
            const val KEY_FILE = "RestoredBackupDialog.file"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inner class BackupBroadcastReceiver : BroadcastReceiver() {
 | 
			
		||||
        override fun onReceive(context: Context, intent: Intent) {
 | 
			
		||||
            when (intent.getStringExtra(BackupConst.ACTION)) {
 | 
			
		||||
                BackupConst.ACTION_BACKUP_COMPLETED_DIALOG -> {
 | 
			
		||||
                    router.popControllerWithTag(TAG_CREATING_BACKUP_DIALOG)
 | 
			
		||||
                    val uri = Uri.parse(intent.getStringExtra(BackupConst.EXTRA_URI))
 | 
			
		||||
                    CreatedBackupDialog(uri).showDialog(router)
 | 
			
		||||
                }
 | 
			
		||||
                BackupConst.ACTION_SET_PROGRESS_DIALOG -> {
 | 
			
		||||
                    val progress = intent.getIntExtra(BackupConst.EXTRA_PROGRESS, 0)
 | 
			
		||||
                    val amount = intent.getIntExtra(BackupConst.EXTRA_AMOUNT, 0)
 | 
			
		||||
                    val content = intent.getStringExtra(BackupConst.EXTRA_CONTENT)
 | 
			
		||||
                    (router.getControllerWithTag(TAG_RESTORING_BACKUP_DIALOG)
 | 
			
		||||
                            as? RestoringBackupDialog)?.updateProgress(content, progress, amount)
 | 
			
		||||
                }
 | 
			
		||||
                BackupConst.ACTION_RESTORE_COMPLETED_DIALOG -> {
 | 
			
		||||
                    router.popControllerWithTag(TAG_RESTORING_BACKUP_DIALOG)
 | 
			
		||||
                    val time = intent.getLongExtra(BackupConst.EXTRA_TIME, 0)
 | 
			
		||||
                    val errors = intent.getIntExtra(BackupConst.EXTRA_ERRORS, 0)
 | 
			
		||||
                    val path = intent.getStringExtra(BackupConst.EXTRA_ERROR_FILE_PATH)
 | 
			
		||||
                    val file = intent.getStringExtra(BackupConst.EXTRA_ERROR_FILE)
 | 
			
		||||
                    if (errors > 0) {
 | 
			
		||||
                        RestoredBackupDialog(time, errors, path, file).showDialog(router)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                BackupConst.ACTION_ERROR_BACKUP_DIALOG -> {
 | 
			
		||||
                    router.popControllerWithTag(TAG_CREATING_BACKUP_DIALOG)
 | 
			
		||||
                    context.toast(intent.getStringExtra(BackupConst.EXTRA_ERROR_MESSAGE))
 | 
			
		||||
                }
 | 
			
		||||
                BackupConst.ACTION_ERROR_RESTORE_DIALOG -> {
 | 
			
		||||
                    router.popControllerWithTag(TAG_RESTORING_BACKUP_DIALOG)
 | 
			
		||||
                    context.toast(intent.getStringExtra(BackupConst.EXTRA_ERROR_MESSAGE))
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private companion object {
 | 
			
		||||
        const val CODE_BACKUP_CREATE = 501
 | 
			
		||||
        const val CODE_BACKUP_RESTORE = 502
 | 
			
		||||
        const val CODE_BACKUP_DIR = 503
 | 
			
		||||
 | 
			
		||||
        const val TAG_CREATING_BACKUP_DIALOG = "CreatingBackupDialog"
 | 
			
		||||
        const val TAG_RESTORING_BACKUP_DIALOG = "RestoringBackupDialog"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,407 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.content.BroadcastReceiver
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.content.IntentFilter
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import com.nononsenseapps.filepicker.FilePickerActivity
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreateService
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
 | 
			
		||||
import eu.kanade.tachiyomi.data.backup.models.Backup
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
 | 
			
		||||
import eu.kanade.tachiyomi.util.*
 | 
			
		||||
import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
import net.xpece.android.support.preference.Preference
 | 
			
		||||
import rx.subscriptions.Subscriptions
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
import java.util.concurrent.TimeUnit
 | 
			
		||||
import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Settings for [BackupCreateService] and [BackupRestoreService]
 | 
			
		||||
 */
 | 
			
		||||
class SettingsBackupFragment : SettingsFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val INTENT_FILTER = "SettingsBackupFragment"
 | 
			
		||||
        const val ACTION_BACKUP_COMPLETED_DIALOG = "$ID.$INTENT_FILTER.ACTION_BACKUP_COMPLETED_DIALOG"
 | 
			
		||||
        const val ACTION_SET_PROGRESS_DIALOG = "$ID.$INTENT_FILTER.ACTION_SET_PROGRESS_DIALOG"
 | 
			
		||||
        const val ACTION_ERROR_BACKUP_DIALOG = "$ID.$INTENT_FILTER.ACTION_ERROR_BACKUP_DIALOG"
 | 
			
		||||
        const val ACTION_ERROR_RESTORE_DIALOG = "$ID.$INTENT_FILTER.ACTION_ERROR_RESTORE_DIALOG"
 | 
			
		||||
        const val ACTION_RESTORE_COMPLETED_DIALOG = "$ID.$INTENT_FILTER.ACTION_RESTORE_COMPLETED_DIALOG"
 | 
			
		||||
        const val ACTION = "$ID.$INTENT_FILTER.ACTION"
 | 
			
		||||
        const val EXTRA_PROGRESS = "$ID.$INTENT_FILTER.EXTRA_PROGRESS"
 | 
			
		||||
        const val EXTRA_AMOUNT = "$ID.$INTENT_FILTER.EXTRA_AMOUNT"
 | 
			
		||||
        const val EXTRA_ERRORS = "$ID.$INTENT_FILTER.EXTRA_ERRORS"
 | 
			
		||||
        const val EXTRA_CONTENT = "$ID.$INTENT_FILTER.EXTRA_CONTENT"
 | 
			
		||||
        const val EXTRA_ERROR_MESSAGE = "$ID.$INTENT_FILTER.EXTRA_ERROR_MESSAGE"
 | 
			
		||||
        const val EXTRA_URI = "$ID.$INTENT_FILTER.EXTRA_URI"
 | 
			
		||||
        const val EXTRA_TIME = "$ID.$INTENT_FILTER.EXTRA_TIME"
 | 
			
		||||
        const val EXTRA_ERROR_FILE_PATH = "$ID.$INTENT_FILTER.EXTRA_ERROR_FILE_PATH"
 | 
			
		||||
        const val EXTRA_ERROR_FILE = "$ID.$INTENT_FILTER.EXTRA_ERROR_FILE"
 | 
			
		||||
 | 
			
		||||
        private const val BACKUP_CREATE = 201
 | 
			
		||||
        private const val BACKUP_RESTORE = 202
 | 
			
		||||
        private const val BACKUP_DIR = 203
 | 
			
		||||
 | 
			
		||||
        fun newInstance(rootKey: String): SettingsBackupFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsBackupFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preference selected to create backup
 | 
			
		||||
     */
 | 
			
		||||
    private val createBackup: Preference by bindPref(R.string.pref_create_local_backup_key)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preference selected to restore backup
 | 
			
		||||
     */
 | 
			
		||||
    private val restoreBackup: Preference by bindPref(R.string.pref_restore_local_backup_key)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preference which determines the frequency of automatic backups.
 | 
			
		||||
     */
 | 
			
		||||
    private val automaticBackup: IntListPreference by bindPref(R.string.pref_backup_interval_key)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preference containing number of automatic backups
 | 
			
		||||
     */
 | 
			
		||||
    private val backupSlots: IntListPreference by bindPref(R.string.pref_backup_slots_key)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preference containing interval of automatic backups
 | 
			
		||||
     */
 | 
			
		||||
    private val backupDirPref: Preference by bindPref(R.string.pref_backup_directory_key)
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Preferences
 | 
			
		||||
     */
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Value containing information on what to backup
 | 
			
		||||
     */
 | 
			
		||||
    private var backup_flags = 0
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The root directory for backups..
 | 
			
		||||
     */
 | 
			
		||||
    private var backupDir = preferences.backupsDirectory().getOrDefault().let {
 | 
			
		||||
        UniFile.fromUri(context, Uri.parse(it))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val restoreDialog: MaterialDialog by lazy {
 | 
			
		||||
        MaterialDialog.Builder(context)
 | 
			
		||||
                .title(R.string.backup)
 | 
			
		||||
                .content(R.string.restoring_backup)
 | 
			
		||||
                .progress(false, 100, true)
 | 
			
		||||
                .cancelable(false)
 | 
			
		||||
                .negativeText(R.string.action_stop)
 | 
			
		||||
                .onNegative { materialDialog, _ ->
 | 
			
		||||
                    BackupRestoreService.stop(context)
 | 
			
		||||
                    materialDialog.dismiss()
 | 
			
		||||
                }
 | 
			
		||||
                .build()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val backupDialog: MaterialDialog by lazy {
 | 
			
		||||
        MaterialDialog.Builder(context)
 | 
			
		||||
                .title(R.string.backup)
 | 
			
		||||
                .content(R.string.creating_backup)
 | 
			
		||||
                .progress(true, 0)
 | 
			
		||||
                .cancelable(false)
 | 
			
		||||
                .build()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val receiver = object : BroadcastReceiver() {
 | 
			
		||||
 | 
			
		||||
        override fun onReceive(context: Context, intent: Intent) {
 | 
			
		||||
            when (intent.getStringExtra(ACTION)) {
 | 
			
		||||
                ACTION_BACKUP_COMPLETED_DIALOG -> {
 | 
			
		||||
                    backupDialog.dismiss()
 | 
			
		||||
                    val uri = Uri.parse(intent.getStringExtra(EXTRA_URI))
 | 
			
		||||
                    val file = UniFile.fromUri(context, uri)
 | 
			
		||||
                    MaterialDialog.Builder(this@SettingsBackupFragment.context)
 | 
			
		||||
                            .title(getString(R.string.backup_created))
 | 
			
		||||
                            .content(getString(R.string.file_saved, file.filePath))
 | 
			
		||||
                            .positiveText(getString(R.string.action_close))
 | 
			
		||||
                            .negativeText(getString(R.string.action_export))
 | 
			
		||||
                            .onPositive { materialDialog, _ -> materialDialog.dismiss() }
 | 
			
		||||
                            .onNegative { _, _ ->
 | 
			
		||||
                                val sendIntent = Intent(Intent.ACTION_SEND)
 | 
			
		||||
                                sendIntent.type = "application/json"
 | 
			
		||||
                                sendIntent.putExtra(Intent.EXTRA_STREAM, file.uri)
 | 
			
		||||
                                startActivity(Intent.createChooser(sendIntent, ""))
 | 
			
		||||
                            }
 | 
			
		||||
                            .safeShow()
 | 
			
		||||
 | 
			
		||||
                }
 | 
			
		||||
                ACTION_SET_PROGRESS_DIALOG -> {
 | 
			
		||||
                    val progress = intent.getIntExtra(EXTRA_PROGRESS, 0)
 | 
			
		||||
                    val amount = intent.getIntExtra(EXTRA_AMOUNT, 0)
 | 
			
		||||
                    val content = intent.getStringExtra(EXTRA_CONTENT)
 | 
			
		||||
                    restoreDialog.setContent(content)
 | 
			
		||||
                    restoreDialog.setProgress(progress)
 | 
			
		||||
                    restoreDialog.maxProgress = amount
 | 
			
		||||
                }
 | 
			
		||||
                ACTION_RESTORE_COMPLETED_DIALOG -> {
 | 
			
		||||
                    restoreDialog.dismiss()
 | 
			
		||||
                    val time = intent.getLongExtra(EXTRA_TIME, 0)
 | 
			
		||||
                    val errors = intent.getIntExtra(EXTRA_ERRORS, 0)
 | 
			
		||||
                    val path = intent.getStringExtra(EXTRA_ERROR_FILE_PATH)
 | 
			
		||||
                    val file = intent.getStringExtra(EXTRA_ERROR_FILE)
 | 
			
		||||
                    val timeString = String.format("%02d min, %02d sec",
 | 
			
		||||
                            TimeUnit.MILLISECONDS.toMinutes(time),
 | 
			
		||||
                            TimeUnit.MILLISECONDS.toSeconds(time) -
 | 
			
		||||
                                    TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(time))
 | 
			
		||||
                    )
 | 
			
		||||
 | 
			
		||||
                    if (errors > 0) {
 | 
			
		||||
                        MaterialDialog.Builder(this@SettingsBackupFragment.context)
 | 
			
		||||
                                .title(getString(R.string.restore_completed))
 | 
			
		||||
                                .content(getString(R.string.restore_completed_content, timeString,
 | 
			
		||||
                                        if (errors > 0) "$errors" else getString(android.R.string.no)))
 | 
			
		||||
                                .positiveText(getString(R.string.action_close))
 | 
			
		||||
                                .negativeText(getString(R.string.action_open_log))
 | 
			
		||||
                                .onPositive { materialDialog, _ -> materialDialog.dismiss() }
 | 
			
		||||
                                .onNegative { materialDialog, _ ->
 | 
			
		||||
                                    if (!path.isEmpty()) {
 | 
			
		||||
                                        val destFile = File(path, file)
 | 
			
		||||
                                        val uri = destFile.getUriCompat(context)
 | 
			
		||||
                                        val sendIntent = Intent(Intent.ACTION_VIEW).apply {
 | 
			
		||||
                                            setDataAndType(uri, "text/plain")
 | 
			
		||||
                                            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_GRANT_READ_URI_PERMISSION
 | 
			
		||||
                                        }
 | 
			
		||||
                                        startActivity(sendIntent)
 | 
			
		||||
                                    } else {
 | 
			
		||||
                                        context.toast(getString(R.string.error_opening_log))
 | 
			
		||||
                                    }
 | 
			
		||||
                                    materialDialog.dismiss()
 | 
			
		||||
                                }
 | 
			
		||||
                                .safeShow()
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                ACTION_ERROR_BACKUP_DIALOG -> {
 | 
			
		||||
                    context.toast(intent.getStringExtra(EXTRA_ERROR_MESSAGE))
 | 
			
		||||
                    backupDialog.dismiss()
 | 
			
		||||
                }
 | 
			
		||||
                ACTION_ERROR_RESTORE_DIALOG -> {
 | 
			
		||||
                    context.toast(intent.getStringExtra(EXTRA_ERROR_MESSAGE))
 | 
			
		||||
                    restoreDialog.dismiss()
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onStart() {
 | 
			
		||||
        super.onStart()
 | 
			
		||||
        context.registerLocalReceiver(receiver, IntentFilter(INTENT_FILTER))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPause() {
 | 
			
		||||
        context.unregisterLocalReceiver(receiver)
 | 
			
		||||
        super.onPause()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        if (savedState != null) {
 | 
			
		||||
            if (BackupRestoreService.isRunning(context)) {
 | 
			
		||||
                restoreDialog.safeShow()
 | 
			
		||||
            }
 | 
			
		||||
            else if (BackupCreateService.isRunning(context)) {
 | 
			
		||||
                backupDialog.safeShow()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        (activity as BaseActivity).requestPermissionsOnMarshmallow()
 | 
			
		||||
 | 
			
		||||
        // Set onClickListeners
 | 
			
		||||
        createBackup.setOnPreferenceClickListener {
 | 
			
		||||
            MaterialDialog.Builder(context)
 | 
			
		||||
                    .title(R.string.pref_create_backup)
 | 
			
		||||
                    .content(R.string.backup_choice)
 | 
			
		||||
                    .items(R.array.backup_options)
 | 
			
		||||
                    .itemsCallbackMultiChoice(arrayOf(0, 1, 2, 3, 4 /*todo not hard code*/)) { _, positions, _ ->
 | 
			
		||||
                        // TODO not very happy with global value, but putExtra doesn't work
 | 
			
		||||
                        backup_flags = 0
 | 
			
		||||
                        for (i in 1..positions.size - 1) {
 | 
			
		||||
                            when (positions[i]) {
 | 
			
		||||
                                1 -> backup_flags = backup_flags or BackupCreateService.BACKUP_CATEGORY
 | 
			
		||||
                                2 -> backup_flags = backup_flags or BackupCreateService.BACKUP_CHAPTER
 | 
			
		||||
                                3 -> backup_flags = backup_flags or BackupCreateService.BACKUP_TRACK
 | 
			
		||||
                                4 -> backup_flags = backup_flags or BackupCreateService.BACKUP_HISTORY
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        // If API lower as KitKat use custom dir picker
 | 
			
		||||
                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
 | 
			
		||||
                            // Get dirs
 | 
			
		||||
                            val currentDir = preferences.backupsDirectory().getOrDefault()
 | 
			
		||||
 | 
			
		||||
                            val i = Intent(activity, CustomLayoutPickerActivity::class.java)
 | 
			
		||||
                            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, currentDir)
 | 
			
		||||
                            startActivityForResult(i, BACKUP_CREATE)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // Use Androids build in file creator
 | 
			
		||||
                            val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
 | 
			
		||||
                            intent.addCategory(Intent.CATEGORY_OPENABLE)
 | 
			
		||||
 | 
			
		||||
                            // TODO create custom MIME data type? Will make older backups deprecated
 | 
			
		||||
                            intent.type = "application/*"
 | 
			
		||||
                            intent.putExtra(Intent.EXTRA_TITLE, Backup.getDefaultFilename())
 | 
			
		||||
                            startActivityForResult(intent, BACKUP_CREATE)
 | 
			
		||||
                        }
 | 
			
		||||
                        true
 | 
			
		||||
                    }
 | 
			
		||||
                    .itemsDisabledIndices(0)
 | 
			
		||||
                    .positiveText(getString(R.string.action_create))
 | 
			
		||||
                    .negativeText(android.R.string.cancel)
 | 
			
		||||
                    .safeShow()
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        restoreBackup.setOnPreferenceClickListener {
 | 
			
		||||
            val intent = Intent(Intent.ACTION_GET_CONTENT)
 | 
			
		||||
            intent.addCategory(Intent.CATEGORY_OPENABLE)
 | 
			
		||||
            intent.type = "application/*"
 | 
			
		||||
            val chooser = Intent.createChooser(intent, getString(R.string.file_select_backup))
 | 
			
		||||
            startActivityForResult(chooser, BACKUP_RESTORE)
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        automaticBackup.setOnPreferenceChangeListener { _, newValue ->
 | 
			
		||||
            // Always cancel the previous task, it seems that sometimes they are not updated.
 | 
			
		||||
            BackupCreatorJob.cancelTask()
 | 
			
		||||
 | 
			
		||||
            val interval = (newValue as String).toInt()
 | 
			
		||||
            if (interval > 0) {
 | 
			
		||||
                BackupCreatorJob.setupTask(interval)
 | 
			
		||||
            }
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        backupSlots.setOnPreferenceChangeListener { preference, newValue ->
 | 
			
		||||
            preferences.numberOfBackups().set((newValue as String).toInt())
 | 
			
		||||
            preference.summary = newValue
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        backupDirPref.setOnPreferenceClickListener {
 | 
			
		||||
            val currentDir = preferences.backupsDirectory().getOrDefault()
 | 
			
		||||
 | 
			
		||||
            if (Build.VERSION.SDK_INT < 21) {
 | 
			
		||||
                // Custom dir selected, open directory selector
 | 
			
		||||
                val i = Intent(activity, CustomLayoutPickerActivity::class.java)
 | 
			
		||||
                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, currentDir)
 | 
			
		||||
 | 
			
		||||
                startActivityForResult(i, BACKUP_DIR)
 | 
			
		||||
            } else {
 | 
			
		||||
                val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
 | 
			
		||||
                startActivityForResult(i, BACKUP_DIR)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.backupsDirectory().asObservable()
 | 
			
		||||
                .subscribe { path ->
 | 
			
		||||
                    backupDir = UniFile.fromUri(context, Uri.parse(path))
 | 
			
		||||
                    backupDirPref.summary = backupDir.filePath ?: path
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.backupInterval().asObservable()
 | 
			
		||||
                .subscribe {
 | 
			
		||||
                    backupDirPref.isVisible = it > 0
 | 
			
		||||
                    backupSlots.isVisible = it > 0
 | 
			
		||||
                }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        when (requestCode) {
 | 
			
		||||
            BACKUP_DIR -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
 | 
			
		||||
                    val uri = Uri.fromFile(File(data.data.path))
 | 
			
		||||
                    preferences.backupsDirectory().set(uri.toString())
 | 
			
		||||
                } else {
 | 
			
		||||
                    val uri = data.data
 | 
			
		||||
                    val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                            Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
                    context.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
 | 
			
		||||
                    val file = UniFile.fromUri(context, uri)
 | 
			
		||||
                    preferences.backupsDirectory().set(file.uri.toString())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BACKUP_CREATE -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
 | 
			
		||||
                    val dir = data.data.path
 | 
			
		||||
                    val file = File(dir, Backup.getDefaultFilename())
 | 
			
		||||
 | 
			
		||||
                    backupDialog.safeShow()
 | 
			
		||||
                    BackupCreateService.makeBackup(context, file.toURI().toString(), backup_flags)
 | 
			
		||||
                } else {
 | 
			
		||||
                    val uri = data.data
 | 
			
		||||
                    val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                            Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
                    context.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
                    val file = UniFile.fromUri(context, uri)
 | 
			
		||||
 | 
			
		||||
                    backupDialog.safeShow()
 | 
			
		||||
                    BackupCreateService.makeBackup(context, file.uri.toString(), backup_flags)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            BACKUP_RESTORE -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val uri = data.data
 | 
			
		||||
 | 
			
		||||
                MaterialDialog.Builder(context)
 | 
			
		||||
                        .title(getString(R.string.pref_restore_backup))
 | 
			
		||||
                        .content(getString(R.string.backup_restore_content))
 | 
			
		||||
                        .positiveText(getString(R.string.action_restore))
 | 
			
		||||
                        .onPositive { _, _ ->
 | 
			
		||||
                            restoreDialog.safeShow()
 | 
			
		||||
                            BackupRestoreService.start(context, uri)
 | 
			
		||||
                        }
 | 
			
		||||
                        .safeShow()
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun MaterialDialog.Builder.safeShow(): Dialog {
 | 
			
		||||
        return build().safeShow()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun Dialog.safeShow(): Dialog {
 | 
			
		||||
        subscriptions += Subscriptions.create { dismiss() }
 | 
			
		||||
        show()
 | 
			
		||||
        return this
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.app.AppCompatActivity
 | 
			
		||||
import android.support.v7.preference.PreferenceController
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import android.util.TypedValue
 | 
			
		||||
import android.view.ContextThemeWrapper
 | 
			
		||||
import android.view.LayoutInflater
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.view.ViewGroup
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import rx.subscriptions.CompositeSubscription
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
abstract class SettingsController : PreferenceController() {
 | 
			
		||||
 | 
			
		||||
    val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    var untilDestroySubscriptions = CompositeSubscription()
 | 
			
		||||
        private set
 | 
			
		||||
 | 
			
		||||
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
 | 
			
		||||
        if (untilDestroySubscriptions.isUnsubscribed) {
 | 
			
		||||
            untilDestroySubscriptions = CompositeSubscription()
 | 
			
		||||
        }
 | 
			
		||||
        return super.onCreateView(inflater, container, savedInstanceState)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView(view: View) {
 | 
			
		||||
        super.onDestroyView(view)
 | 
			
		||||
        untilDestroySubscriptions.unsubscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
 | 
			
		||||
        val screen = preferenceManager.createPreferenceScreen(getThemedContext())
 | 
			
		||||
        preferenceScreen = screen
 | 
			
		||||
        setupPreferenceScreen(screen)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    abstract fun setupPreferenceScreen(screen: PreferenceScreen): Any?
 | 
			
		||||
 | 
			
		||||
    private fun getThemedContext(): Context {
 | 
			
		||||
        val tv = TypedValue()
 | 
			
		||||
        activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true)
 | 
			
		||||
        return ContextThemeWrapper(activity, tv.resourceId)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    open fun getTitle(): String? {
 | 
			
		||||
        return preferenceScreen?.title?.toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onAttach(view: View) {
 | 
			
		||||
        (activity as? AppCompatActivity)?.supportActionBar?.title = getTitle()
 | 
			
		||||
        super.onAttach(view)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun <T> Observable<T>.subscribeUntilDestroy(): Subscription {
 | 
			
		||||
        return subscribe().also { untilDestroySubscriptions.add(it) }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun <T> Observable<T>.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription {
 | 
			
		||||
        return subscribe(onNext).also { untilDestroySubscriptions.add(it) }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,186 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Environment
 | 
			
		||||
import android.support.v4.content.ContextCompat
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import com.nononsenseapps.filepicker.FilePickerActivity
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.util.DiskUtil
 | 
			
		||||
import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
class SettingsDownloadController : SettingsController() {
 | 
			
		||||
 | 
			
		||||
    private val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_downloads
 | 
			
		||||
 | 
			
		||||
        preference {
 | 
			
		||||
            key = Keys.downloadsDirectory
 | 
			
		||||
            titleRes = R.string.pref_download_directory
 | 
			
		||||
            onClick {
 | 
			
		||||
                showDownloadDirectoriesDialog()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            preferences.downloadsDirectory().asObservable()
 | 
			
		||||
                    .subscribeUntilDestroy { path ->
 | 
			
		||||
                        val dir = UniFile.fromUri(context, Uri.parse(path))
 | 
			
		||||
                        summary = dir.filePath ?: path
 | 
			
		||||
 | 
			
		||||
                        // Don't display downloaded chapters in gallery apps creating .nomedia
 | 
			
		||||
                        if (dir != null && dir.exists()) {
 | 
			
		||||
                            val nomedia = dir.findFile(".nomedia")
 | 
			
		||||
                            if (nomedia == null) {
 | 
			
		||||
                                dir.createFile(".nomedia")
 | 
			
		||||
                                applicationContext?.let { DiskUtil.scanMedia(it, dir.uri) }
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.downloadOnlyOverWifi
 | 
			
		||||
            titleRes = R.string.pref_download_only_over_wifi
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.downloadThreads
 | 
			
		||||
            titleRes = R.string.pref_download_slots
 | 
			
		||||
            entries = arrayOf("1", "2", "3")
 | 
			
		||||
            entryValues = arrayOf("1", "2", "3")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        preferenceCategory {
 | 
			
		||||
            titleRes = R.string.pref_remove_after_read
 | 
			
		||||
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.removeAfterMarkedAsRead
 | 
			
		||||
                titleRes = R.string.pref_remove_after_marked_as_read
 | 
			
		||||
                defaultValue = false
 | 
			
		||||
            }
 | 
			
		||||
            intListPreference {
 | 
			
		||||
                key = Keys.removeAfterReadSlots
 | 
			
		||||
                titleRes = R.string.pref_remove_after_read
 | 
			
		||||
                entriesRes = arrayOf(R.string.disabled, R.string.last_read_chapter,
 | 
			
		||||
                        R.string.second_to_last, R.string.third_to_last, R.string.fourth_to_last,
 | 
			
		||||
                        R.string.fifth_to_last)
 | 
			
		||||
                entryValues = arrayOf("-1", "0", "1", "2", "3", "4")
 | 
			
		||||
                defaultValue = "-1"
 | 
			
		||||
                summary = "%s"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val dbCategories = db.getCategories().executeAsBlocking()
 | 
			
		||||
 | 
			
		||||
        preferenceCategory {
 | 
			
		||||
            titleRes = R.string.pref_download_new
 | 
			
		||||
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.downloadNew
 | 
			
		||||
                titleRes = R.string.pref_download_new
 | 
			
		||||
                defaultValue = false
 | 
			
		||||
            }
 | 
			
		||||
            multiSelectListPreference {
 | 
			
		||||
                key = Keys.downloadNewCategories
 | 
			
		||||
                titleRes = R.string.pref_download_new_categories
 | 
			
		||||
                entries = dbCategories.map { it.name }.toTypedArray()
 | 
			
		||||
                entryValues = dbCategories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
 | 
			
		||||
                preferences.downloadNew().asObservable()
 | 
			
		||||
                        .subscribeUntilDestroy { isVisible = it }
 | 
			
		||||
 | 
			
		||||
                preferences.downloadNewCategories().asObservable()
 | 
			
		||||
                        .subscribe {
 | 
			
		||||
                            val selectedCategories = it
 | 
			
		||||
                                    .mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
 | 
			
		||||
                                    .sortedBy { it.order }
 | 
			
		||||
 | 
			
		||||
                            summary = if (selectedCategories.isEmpty())
 | 
			
		||||
                                resources?.getString(R.string.all)
 | 
			
		||||
                            else
 | 
			
		||||
                                selectedCategories.joinToString { it.name }
 | 
			
		||||
                        }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun showDownloadDirectoriesDialog() {
 | 
			
		||||
        val activity = activity ?: return
 | 
			
		||||
 | 
			
		||||
        val currentDir = preferences.downloadsDirectory().getOrDefault()
 | 
			
		||||
        val externalDirs = getExternalFilesDirs() + File(activity.getString(R.string.custom_dir))
 | 
			
		||||
        val selectedIndex = externalDirs.map(File::toString).indexOfFirst { it in currentDir }
 | 
			
		||||
 | 
			
		||||
        MaterialDialog.Builder(activity)
 | 
			
		||||
                .items(externalDirs)
 | 
			
		||||
                .itemsCallbackSingleChoice(selectedIndex, { _, _, which, text ->
 | 
			
		||||
                    if (which == externalDirs.lastIndex) {
 | 
			
		||||
                        if (Build.VERSION.SDK_INT < 21) {
 | 
			
		||||
                            // Custom dir selected, open directory selector
 | 
			
		||||
                            val i = Intent(activity, CustomLayoutPickerActivity::class.java)
 | 
			
		||||
                            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, currentDir)
 | 
			
		||||
 | 
			
		||||
                            startActivityForResult(i, DOWNLOAD_DIR_PRE_L)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
 | 
			
		||||
                            startActivityForResult(i, DOWNLOAD_DIR_L)
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        // One of the predefined folders was selected
 | 
			
		||||
                        val path = Uri.fromFile(File(text.toString()))
 | 
			
		||||
                        preferences.downloadsDirectory().set(path.toString())
 | 
			
		||||
                    }
 | 
			
		||||
                    true
 | 
			
		||||
                })
 | 
			
		||||
                .show()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getExternalFilesDirs(): List<File> {
 | 
			
		||||
        val defaultDir = Environment.getExternalStorageDirectory().absolutePath +
 | 
			
		||||
                File.separator + resources?.getString(R.string.app_name) +
 | 
			
		||||
                File.separator + "downloads"
 | 
			
		||||
 | 
			
		||||
        return mutableListOf(File(defaultDir)) +
 | 
			
		||||
                ContextCompat.getExternalFilesDirs(activity, "").filterNotNull()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        when (requestCode) {
 | 
			
		||||
            DOWNLOAD_DIR_PRE_L -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val uri = Uri.fromFile(File(data.data.path))
 | 
			
		||||
                preferences.downloadsDirectory().set(uri.toString())
 | 
			
		||||
            }
 | 
			
		||||
            DOWNLOAD_DIR_L -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val context = applicationContext ?: return
 | 
			
		||||
                val uri = data.data
 | 
			
		||||
                val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
                @Suppress("NewApi")
 | 
			
		||||
                context.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
 | 
			
		||||
                val file = UniFile.fromUri(context, uri)
 | 
			
		||||
                preferences.downloadsDirectory().set(file.uri.toString())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private companion object {
 | 
			
		||||
        const val DOWNLOAD_DIR_PRE_L = 103
 | 
			
		||||
        const val DOWNLOAD_DIR_L = 104
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,149 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.net.Uri
 | 
			
		||||
import android.os.Build
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.os.Environment
 | 
			
		||||
import android.support.v4.content.ContextCompat
 | 
			
		||||
import android.support.v7.preference.Preference
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.hippo.unifile.UniFile
 | 
			
		||||
import com.nononsenseapps.filepicker.FilePickerActivity
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.util.plusAssign
 | 
			
		||||
import eu.kanade.tachiyomi.widget.CustomLayoutPickerActivity
 | 
			
		||||
import net.xpece.android.support.preference.MultiSelectListPreference
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.io.File
 | 
			
		||||
 | 
			
		||||
class SettingsDownloadsFragment : SettingsFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val DOWNLOAD_DIR_PRE_L = 103
 | 
			
		||||
        const val DOWNLOAD_DIR_L = 104
 | 
			
		||||
 | 
			
		||||
        fun newInstance(rootKey: String): SettingsDownloadsFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsDownloadsFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    val downloadDirPref: Preference by bindPref(R.string.pref_download_directory_key)
 | 
			
		||||
 | 
			
		||||
    val downloadCategory: MultiSelectListPreference by bindPref(R.string.pref_download_new_categories_key)
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        downloadDirPref.setOnPreferenceClickListener {
 | 
			
		||||
 | 
			
		||||
            val currentDir = preferences.downloadsDirectory().getOrDefault()
 | 
			
		||||
            val externalDirs = getExternalFilesDirs() + File(getString(R.string.custom_dir))
 | 
			
		||||
            val selectedIndex = externalDirs.map(File::toString).indexOfFirst { it in currentDir }
 | 
			
		||||
 | 
			
		||||
            MaterialDialog.Builder(activity)
 | 
			
		||||
                    .items(externalDirs)
 | 
			
		||||
                    .itemsCallbackSingleChoice(selectedIndex, { dialog, view, which, text ->
 | 
			
		||||
                        if (which == externalDirs.lastIndex) {
 | 
			
		||||
                            if (Build.VERSION.SDK_INT < 21) {
 | 
			
		||||
                                // Custom dir selected, open directory selector
 | 
			
		||||
                                val i = Intent(activity, CustomLayoutPickerActivity::class.java)
 | 
			
		||||
                                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, currentDir)
 | 
			
		||||
 | 
			
		||||
                                startActivityForResult(i, DOWNLOAD_DIR_PRE_L)
 | 
			
		||||
                            } else {
 | 
			
		||||
                                val i = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
 | 
			
		||||
                                startActivityForResult(i, DOWNLOAD_DIR_L)
 | 
			
		||||
                            }
 | 
			
		||||
                        } else {
 | 
			
		||||
                            // One of the predefined folders was selected
 | 
			
		||||
                            val path = Uri.fromFile(File(text.toString()))
 | 
			
		||||
                            preferences.downloadsDirectory().set(path.toString())
 | 
			
		||||
                        }
 | 
			
		||||
                        true
 | 
			
		||||
                    })
 | 
			
		||||
                    .show()
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.downloadsDirectory().asObservable()
 | 
			
		||||
                .subscribe { path ->
 | 
			
		||||
                    val dir = UniFile.fromUri(context, Uri.parse(path))
 | 
			
		||||
 | 
			
		||||
                    downloadDirPref.summary = dir.filePath ?: path
 | 
			
		||||
 | 
			
		||||
                    // Don't display downloaded chapters in gallery apps creating a ".nomedia" file.
 | 
			
		||||
                    if (dir != null && dir.exists()) {
 | 
			
		||||
                        dir.createFile(".nomedia")
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.downloadNew().asObservable()
 | 
			
		||||
                .subscribe { downloadCategory.isVisible = it }
 | 
			
		||||
 | 
			
		||||
        val dbCategories = db.getCategories().executeAsBlocking()
 | 
			
		||||
        downloadCategory.apply {
 | 
			
		||||
            entries = dbCategories.map { it.name }.toTypedArray()
 | 
			
		||||
            entryValues = dbCategories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.downloadNewCategories().asObservable()
 | 
			
		||||
                .subscribe {
 | 
			
		||||
                    val selectedCategories = it
 | 
			
		||||
                            .mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
 | 
			
		||||
                            .sortedBy { it.order }
 | 
			
		||||
 | 
			
		||||
                    val summary = if (selectedCategories.isEmpty())
 | 
			
		||||
                        getString(R.string.all)
 | 
			
		||||
                    else
 | 
			
		||||
                        selectedCategories.joinToString { it.name }
 | 
			
		||||
 | 
			
		||||
                    downloadCategory.summary = summary
 | 
			
		||||
                }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fun getExternalFilesDirs(): List<File> {
 | 
			
		||||
        val defaultDir = Environment.getExternalStorageDirectory().absolutePath +
 | 
			
		||||
                File.separator + getString(R.string.app_name) +
 | 
			
		||||
                File.separator + "downloads"
 | 
			
		||||
 | 
			
		||||
        return mutableListOf(File(defaultDir)) +
 | 
			
		||||
                ContextCompat.getExternalFilesDirs(activity, "").filterNotNull()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        when (requestCode) {
 | 
			
		||||
            DOWNLOAD_DIR_PRE_L -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val uri = Uri.fromFile(File(data.data.path))
 | 
			
		||||
                preferences.downloadsDirectory().set(uri.toString())
 | 
			
		||||
            }
 | 
			
		||||
            DOWNLOAD_DIR_L -> if (data != null && resultCode == Activity.RESULT_OK) {
 | 
			
		||||
                val uri = data.data
 | 
			
		||||
                val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
 | 
			
		||||
                        Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
			
		||||
 | 
			
		||||
                @Suppress("NewApi")
 | 
			
		||||
                context.contentResolver.takePersistableUriPermission(uri, flags)
 | 
			
		||||
 | 
			
		||||
                val file = UniFile.fromUri(context, uri)
 | 
			
		||||
                preferences.downloadsDirectory().set(file.uri.toString())
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.annotation.CallSuper
 | 
			
		||||
import android.support.v7.preference.Preference
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import net.xpece.android.support.preference.PreferenceScreenNavigationStrategy
 | 
			
		||||
import rx.subscriptions.CompositeSubscription
 | 
			
		||||
 | 
			
		||||
open class SettingsFragment : XpPreferenceFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun newInstance(rootKey: String?): SettingsFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    lateinit var subscriptions: CompositeSubscription
 | 
			
		||||
 | 
			
		||||
    override final fun onCreatePreferences2(savedState: Bundle?, rootKey: String?) {
 | 
			
		||||
        subscriptions = CompositeSubscription()
 | 
			
		||||
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_general)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_reader)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_downloads)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_sources)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_tracking)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_backup)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_advanced)
 | 
			
		||||
        addPreferencesFromResource(R.xml.pref_about)
 | 
			
		||||
 | 
			
		||||
        // Setup root preference title.
 | 
			
		||||
        preferenceScreen.title = activity.title
 | 
			
		||||
 | 
			
		||||
        PreferenceScreenNavigationStrategy.ReplaceFragment.onCreatePreferences(this, rootKey)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @CallSuper
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
        listView.isFocusable = false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onStart() {
 | 
			
		||||
        super.onStart()
 | 
			
		||||
        activity.title = preferenceScreen.title
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDestroyView() {
 | 
			
		||||
        subscriptions.unsubscribe()
 | 
			
		||||
        super.onDestroyView()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected inline fun <reified T : Preference> bindPref(resId: Int): Lazy<T> {
 | 
			
		||||
        return lazy { findPreference(getString(resId)) as T }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,225 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.os.Handler
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
 | 
			
		||||
import eu.kanade.tachiyomi.util.LocaleHelper
 | 
			
		||||
import kotlinx.android.synthetic.main.pref_library_columns.view.*
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
class SettingsGeneralController : SettingsController() {
 | 
			
		||||
 | 
			
		||||
    private val db: DatabaseHelper = Injekt.get()
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_general
 | 
			
		||||
 | 
			
		||||
        listPreference {
 | 
			
		||||
            key = Keys.lang
 | 
			
		||||
            titleRes = R.string.pref_language
 | 
			
		||||
            entryValues = arrayOf("", "bg", "en", "es", "fr", "it", "pt", "ru", "vi")
 | 
			
		||||
            entries = entryValues.map { value ->
 | 
			
		||||
                val locale = LocaleHelper.getLocaleFromString(value.toString())
 | 
			
		||||
                locale?.getDisplayName(locale)?.capitalize() ?:
 | 
			
		||||
                        context.getString(R.string.system_default)
 | 
			
		||||
            }.toTypedArray()
 | 
			
		||||
            defaultValue = ""
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
 | 
			
		||||
            onChange { newValue ->
 | 
			
		||||
                val activity = activity ?: return@onChange false
 | 
			
		||||
                val app = activity.application
 | 
			
		||||
                LocaleHelper.changeLocale(newValue.toString())
 | 
			
		||||
                LocaleHelper.updateConfiguration(app, app.resources.configuration)
 | 
			
		||||
                activity.recreate()
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.theme
 | 
			
		||||
            titleRes = R.string.pref_theme
 | 
			
		||||
            entriesRes = arrayOf(R.string.light_theme, R.string.dark_theme)
 | 
			
		||||
            entryValues = arrayOf("1", "2")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
 | 
			
		||||
            onChange {
 | 
			
		||||
                activity?.recreate()
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            titleRes = R.string.pref_library_columns
 | 
			
		||||
            onClick {
 | 
			
		||||
                LibraryColumnsDialog().showDialog(router)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            fun getColumnValue(value: Int): String {
 | 
			
		||||
                return if (value == 0)
 | 
			
		||||
                    context.getString(R.string.default_columns)
 | 
			
		||||
                else
 | 
			
		||||
                    value.toString()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            Observable.combineLatest(
 | 
			
		||||
                    preferences.portraitColumns().asObservable(),
 | 
			
		||||
                    preferences.landscapeColumns().asObservable(),
 | 
			
		||||
                    { portraitCols, landscapeCols -> Pair(portraitCols, landscapeCols) })
 | 
			
		||||
                    .subscribeUntilDestroy { (portraitCols, landscapeCols) ->
 | 
			
		||||
                        val portrait = getColumnValue(portraitCols)
 | 
			
		||||
                        val landscape = getColumnValue(landscapeCols)
 | 
			
		||||
                        summary = "${context.getString(R.string.portrait)}: $portrait, " +
 | 
			
		||||
                                "${context.getString(R.string.landscape)}: $landscape"
 | 
			
		||||
                    }
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.startScreen
 | 
			
		||||
            titleRes = R.string.pref_start_screen
 | 
			
		||||
            entriesRes = arrayOf(R.string.label_library, R.string.label_recent_manga,
 | 
			
		||||
                    R.string.label_recent_updates)
 | 
			
		||||
            entryValues = arrayOf("1", "2", "3")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.libraryUpdateInterval
 | 
			
		||||
            titleRes = R.string.pref_library_update_interval
 | 
			
		||||
            entriesRes = arrayOf(R.string.update_never, R.string.update_1hour,
 | 
			
		||||
                    R.string.update_2hour, R.string.update_3hour, R.string.update_6hour,
 | 
			
		||||
                    R.string.update_12hour, R.string.update_24hour, R.string.update_48hour)
 | 
			
		||||
            entryValues = arrayOf("0", "1", "2", "3", "6", "12", "24", "48")
 | 
			
		||||
            defaultValue = "0"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
 | 
			
		||||
            onChange { newValue ->
 | 
			
		||||
                // Always cancel the previous task, it seems that sometimes they are not updated.
 | 
			
		||||
                LibraryUpdateJob.cancelTask()
 | 
			
		||||
 | 
			
		||||
                val interval = (newValue as String).toInt()
 | 
			
		||||
                if (interval > 0) {
 | 
			
		||||
                    LibraryUpdateJob.setupTask(interval)
 | 
			
		||||
                }
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        multiSelectListPreference {
 | 
			
		||||
            key = Keys.libraryUpdateRestriction
 | 
			
		||||
            titleRes = R.string.pref_library_update_restriction
 | 
			
		||||
            entriesRes = arrayOf(R.string.wifi, R.string.charging)
 | 
			
		||||
            entryValues = arrayOf("wifi", "ac")
 | 
			
		||||
            summaryRes = R.string.pref_library_update_restriction_summary
 | 
			
		||||
 | 
			
		||||
            preferences.libraryUpdateInterval().asObservable()
 | 
			
		||||
                    .subscribeUntilDestroy { isVisible = it > 0 }
 | 
			
		||||
 | 
			
		||||
            onChange {
 | 
			
		||||
                // Post to event looper to allow the preference to be updated.
 | 
			
		||||
                Handler().post { LibraryUpdateJob.setupTask() }
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.updateOnlyNonCompleted
 | 
			
		||||
            titleRes = R.string.pref_update_only_non_completed
 | 
			
		||||
            defaultValue = false
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val dbCategories = db.getCategories().executeAsBlocking()
 | 
			
		||||
 | 
			
		||||
        multiSelectListPreference {
 | 
			
		||||
            key = Keys.libraryUpdateCategories
 | 
			
		||||
            titleRes = R.string.pref_library_update_categories
 | 
			
		||||
            entries = dbCategories.map { it.name }.toTypedArray()
 | 
			
		||||
            entryValues = dbCategories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
 | 
			
		||||
            preferences.libraryUpdateCategories().asObservable()
 | 
			
		||||
                    .subscribeUntilDestroy {
 | 
			
		||||
                        val selectedCategories = it
 | 
			
		||||
                                .mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
 | 
			
		||||
                                .sortedBy { it.order }
 | 
			
		||||
 | 
			
		||||
                        summary = if (selectedCategories.isEmpty())
 | 
			
		||||
                            context.getString(R.string.all)
 | 
			
		||||
                        else
 | 
			
		||||
                            selectedCategories.joinToString { it.name }
 | 
			
		||||
                    }
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.defaultCategory
 | 
			
		||||
            titleRes = R.string.default_category
 | 
			
		||||
 | 
			
		||||
            val selectedCategory = dbCategories.find { it.id == preferences.defaultCategory() }
 | 
			
		||||
            entries = arrayOf(context.getString(R.string.default_category_summary)) +
 | 
			
		||||
                    dbCategories.map { it.name }.toTypedArray()
 | 
			
		||||
            entryValues = arrayOf("-1") + dbCategories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
            defaultValue = "-1"
 | 
			
		||||
            summary = selectedCategory?.name ?: context.getString(R.string.default_category_summary)
 | 
			
		||||
 | 
			
		||||
            onChange { newValue ->
 | 
			
		||||
                summary = dbCategories.find {
 | 
			
		||||
                    it.id == (newValue as String).toInt()
 | 
			
		||||
                }?.name ?: context.getString(R.string.default_category_summary)
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    class LibraryColumnsDialog : DialogController() {
 | 
			
		||||
 | 
			
		||||
        private val preferences: PreferencesHelper = Injekt.get()
 | 
			
		||||
 | 
			
		||||
        private var portrait = preferences.portraitColumns().getOrDefault()
 | 
			
		||||
        private var landscape = preferences.landscapeColumns().getOrDefault()
 | 
			
		||||
 | 
			
		||||
        override fun onCreateDialog(savedViewState: Bundle?): Dialog {
 | 
			
		||||
            val dialog = MaterialDialog.Builder(activity!!)
 | 
			
		||||
                    .title(R.string.pref_library_columns)
 | 
			
		||||
                    .customView(R.layout.pref_library_columns, false)
 | 
			
		||||
                    .positiveText(android.R.string.ok)
 | 
			
		||||
                    .negativeText(android.R.string.cancel)
 | 
			
		||||
                    .onPositive { _, _ ->
 | 
			
		||||
                        preferences.portraitColumns().set(portrait)
 | 
			
		||||
                        preferences.landscapeColumns().set(landscape)
 | 
			
		||||
                    }
 | 
			
		||||
                    .build()
 | 
			
		||||
 | 
			
		||||
            onViewCreated(dialog.view)
 | 
			
		||||
            return dialog
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        fun onViewCreated(view: View) {
 | 
			
		||||
            with(view.portrait_columns) {
 | 
			
		||||
                displayedValues = arrayOf(context.getString(R.string.default_columns)) +
 | 
			
		||||
                        IntRange(1, 10).map(Int::toString)
 | 
			
		||||
                value = portrait
 | 
			
		||||
 | 
			
		||||
                setOnValueChangedListener { _, _, newValue ->
 | 
			
		||||
                    portrait = newValue
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            with(view.landscape_columns) {
 | 
			
		||||
                displayedValues = arrayOf(context.getString(R.string.default_columns)) +
 | 
			
		||||
                        IntRange(1, 10).map(Int::toString)
 | 
			
		||||
                value = landscape
 | 
			
		||||
 | 
			
		||||
                setOnValueChangedListener { _, _, newValue ->
 | 
			
		||||
                    landscape = newValue
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,166 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.Preference
 | 
			
		||||
import android.support.v7.preference.PreferenceFragmentCompat
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.util.LocaleHelper
 | 
			
		||||
import eu.kanade.tachiyomi.util.plusAssign
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.LibraryColumnsDialog
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference
 | 
			
		||||
import net.xpece.android.support.preference.ListPreference
 | 
			
		||||
import net.xpece.android.support.preference.MultiSelectListPreference
 | 
			
		||||
import rx.Observable
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class SettingsGeneralFragment : SettingsFragment(),
 | 
			
		||||
        PreferenceFragmentCompat.OnPreferenceDisplayDialogCallback {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        fun newInstance(rootKey: String): SettingsGeneralFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsGeneralFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val db: DatabaseHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    val columnsPreference: SimpleDialogPreference by bindPref(R.string.pref_library_columns_dialog_key)
 | 
			
		||||
 | 
			
		||||
    val updateInterval: IntListPreference by bindPref(R.string.pref_library_update_interval_key)
 | 
			
		||||
 | 
			
		||||
    val updateRestriction: MultiSelectListPreference by bindPref(R.string.pref_library_update_restriction_key)
 | 
			
		||||
 | 
			
		||||
    val themePreference: IntListPreference by bindPref(R.string.pref_theme_key)
 | 
			
		||||
 | 
			
		||||
    val categoryUpdate: MultiSelectListPreference by bindPref(R.string.pref_library_update_categories_key)
 | 
			
		||||
 | 
			
		||||
    val defaultCategory: IntListPreference by bindPref(R.string.default_category_key)
 | 
			
		||||
 | 
			
		||||
    val langPreference: ListPreference by bindPref(R.string.pref_language_key)
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.libraryUpdateInterval().asObservable()
 | 
			
		||||
                .subscribe { updateRestriction.isVisible = it > 0 }
 | 
			
		||||
 | 
			
		||||
        subscriptions += Observable.combineLatest(
 | 
			
		||||
                preferences.portraitColumns().asObservable(),
 | 
			
		||||
                preferences.landscapeColumns().asObservable())
 | 
			
		||||
                { portraitColumns, landscapeColumns -> Pair(portraitColumns, landscapeColumns) }
 | 
			
		||||
                .subscribe { updateColumnsSummary(it.first, it.second) }
 | 
			
		||||
 | 
			
		||||
        updateInterval.setOnPreferenceChangeListener { preference, newValue ->
 | 
			
		||||
            // Always cancel the previous task, it seems that sometimes they are not updated.
 | 
			
		||||
            LibraryUpdateJob.cancelTask()
 | 
			
		||||
 | 
			
		||||
            val interval = (newValue as String).toInt()
 | 
			
		||||
            if (interval > 0) {
 | 
			
		||||
                LibraryUpdateJob.setupTask(interval)
 | 
			
		||||
            }
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        updateRestriction.setOnPreferenceChangeListener { preference, newValue ->
 | 
			
		||||
            // Post to event looper to allow the preference to be updated.
 | 
			
		||||
            subscriptions += Observable.fromCallable {
 | 
			
		||||
                LibraryUpdateJob.setupTask()
 | 
			
		||||
            }.subscribeOn(AndroidSchedulers.mainThread()).subscribe()
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val dbCategories = db.getCategories().executeAsBlocking()
 | 
			
		||||
        categoryUpdate.apply {
 | 
			
		||||
            entries = dbCategories.map { it.name }.toTypedArray()
 | 
			
		||||
            entryValues = dbCategories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        subscriptions += preferences.libraryUpdateCategories().asObservable()
 | 
			
		||||
                .subscribe {
 | 
			
		||||
                    val selectedCategories = it
 | 
			
		||||
                            .mapNotNull { id -> dbCategories.find { it.id == id.toInt() } }
 | 
			
		||||
                            .sortedBy { it.order }
 | 
			
		||||
 | 
			
		||||
                    val summary = if (selectedCategories.isEmpty())
 | 
			
		||||
                        getString(R.string.all)
 | 
			
		||||
                    else
 | 
			
		||||
                        selectedCategories.joinToString { it.name }
 | 
			
		||||
 | 
			
		||||
                    categoryUpdate.summary = summary
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
        defaultCategory.apply {
 | 
			
		||||
            val selectedCategory = dbCategories.find { it.id == preferences.defaultCategory()}
 | 
			
		||||
            value = selectedCategory?.id?.toString() ?: value
 | 
			
		||||
            entries += dbCategories.map { it.name }.toTypedArray()
 | 
			
		||||
            entryValues += dbCategories.map { it.id.toString() }.toTypedArray()
 | 
			
		||||
            summary = selectedCategory?.name ?: summary
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        defaultCategory.setOnPreferenceChangeListener { _, newValue ->
 | 
			
		||||
            defaultCategory.summary = dbCategories.find {
 | 
			
		||||
                it.id == (newValue as String).toInt()
 | 
			
		||||
            }?.name ?: getString(R.string.default_category_summary)
 | 
			
		||||
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        themePreference.setOnPreferenceChangeListener { preference, newValue ->
 | 
			
		||||
            (activity as SettingsActivity).parentFlags = SettingsActivity.FLAG_THEME_CHANGED
 | 
			
		||||
            activity.recreate()
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        val langValues = langPreference.entryValues.map { value ->
 | 
			
		||||
            val locale = LocaleHelper.getLocaleFromString(value.toString())
 | 
			
		||||
            locale?.getDisplayName(locale)?.capitalize() ?: context.getString(R.string.system_default)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        langPreference.entries = langValues.toTypedArray()
 | 
			
		||||
        langPreference.setOnPreferenceChangeListener { preference, newValue ->
 | 
			
		||||
            (activity as SettingsActivity).parentFlags = SettingsActivity.FLAG_LANG_CHANGED
 | 
			
		||||
            LocaleHelper.changeLocale(newValue.toString())
 | 
			
		||||
            val app = activity.application
 | 
			
		||||
            LocaleHelper.updateConfiguration(app, app.resources.configuration)
 | 
			
		||||
            activity.recreate()
 | 
			
		||||
            true
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPreferenceDisplayDialog(p0: PreferenceFragmentCompat?, p: Preference): Boolean {
 | 
			
		||||
        if (p === columnsPreference) {
 | 
			
		||||
            val fragment = LibraryColumnsDialog.newInstance(p)
 | 
			
		||||
            fragment.setTargetFragment(this, 0)
 | 
			
		||||
            fragment.show(fragmentManager, null)
 | 
			
		||||
            return true
 | 
			
		||||
        }
 | 
			
		||||
        return false
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updateColumnsSummary(portraitColumns: Int, landscapeColumns: Int) {
 | 
			
		||||
        val portrait = getColumnValue(portraitColumns)
 | 
			
		||||
        val landscape = getColumnValue(landscapeColumns)
 | 
			
		||||
        val msg = "${getString(R.string.portrait)}: $portrait, ${getString(R.string.landscape)}: $landscape"
 | 
			
		||||
 | 
			
		||||
        columnsPreference.summary = msg
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getColumnValue(value: Int): String {
 | 
			
		||||
        return if (value == 0) getString(R.string.default_columns) else value.toString()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,70 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import com.bluelinelabs.conductor.RouterTransaction
 | 
			
		||||
import com.bluelinelabs.conductor.changehandler.FadeChangeHandler
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceColor
 | 
			
		||||
 | 
			
		||||
class SettingsMainController : SettingsController() {
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.label_settings
 | 
			
		||||
 | 
			
		||||
        val tintColor = context.getResourceColor(R.attr.colorAccent)
 | 
			
		||||
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_tune_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_general
 | 
			
		||||
            onClick { navigateTo(SettingsGeneralController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_chrome_reader_mode_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_reader
 | 
			
		||||
            onClick { navigateTo(SettingsReaderController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_file_download_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_downloads
 | 
			
		||||
            onClick { navigateTo(SettingsDownloadController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_language_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_sources
 | 
			
		||||
            onClick { navigateTo(SettingsSourcesController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_sync_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_tracking
 | 
			
		||||
            onClick { navigateTo(SettingsTrackingController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_backup_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.backup
 | 
			
		||||
            onClick { navigateTo(SettingsBackupController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_code_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_advanced
 | 
			
		||||
            onClick { navigateTo(SettingsAdvancedController()) }
 | 
			
		||||
        }
 | 
			
		||||
        preference {
 | 
			
		||||
            iconRes = R.drawable.ic_help_black_24dp
 | 
			
		||||
            iconTint = tintColor
 | 
			
		||||
            titleRes = R.string.pref_category_about
 | 
			
		||||
            onClick { navigateTo(SettingsAboutController()) }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun navigateTo(controller: SettingsController) {
 | 
			
		||||
        router.pushController(RouterTransaction.with(controller)
 | 
			
		||||
                .pushChangeHandler(FadeChangeHandler())
 | 
			
		||||
                .popChangeHandler(FadeChangeHandler()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,106 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
class SettingsReaderController : SettingsController() {
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_reader
 | 
			
		||||
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.defaultViewer
 | 
			
		||||
            titleRes = R.string.pref_viewer_type
 | 
			
		||||
            entriesRes = arrayOf(R.string.left_to_right_viewer, R.string.right_to_left_viewer,
 | 
			
		||||
                    R.string.vertical_viewer, R.string.webtoon_viewer)
 | 
			
		||||
            entryValues = arrayOf("1", "2", "3", "4")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.imageScaleType
 | 
			
		||||
            titleRes = R.string.pref_image_scale_type
 | 
			
		||||
            entriesRes = arrayOf(R.string.scale_type_fit_screen, R.string.scale_type_stretch,
 | 
			
		||||
                    R.string.scale_type_fit_width, R.string.scale_type_fit_height,
 | 
			
		||||
                    R.string.scale_type_original_size, R.string.scale_type_smart_fit)
 | 
			
		||||
            entryValues = arrayOf("1", "2", "3", "4", "5", "6")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.zoomStart
 | 
			
		||||
            titleRes = R.string.pref_zoom_start
 | 
			
		||||
            entriesRes = arrayOf(R.string.zoom_start_automatic, R.string.zoom_start_left,
 | 
			
		||||
                    R.string.zoom_start_right, R.string.zoom_start_center)
 | 
			
		||||
            entryValues = arrayOf("1", "2", "3", "4")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.rotation
 | 
			
		||||
            titleRes = R.string.pref_rotation_type
 | 
			
		||||
            entriesRes = arrayOf(R.string.rotation_free, R.string.rotation_lock,
 | 
			
		||||
                    R.string.rotation_force_portrait, R.string.rotation_force_landscape)
 | 
			
		||||
            entryValues = arrayOf("1", "2", "3", "4")
 | 
			
		||||
            defaultValue = "1"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.readerTheme
 | 
			
		||||
            titleRes = R.string.pref_reader_theme
 | 
			
		||||
            entriesRes = arrayOf(R.string.white_background, R.string.black_background)
 | 
			
		||||
            entryValues = arrayOf("0", "1")
 | 
			
		||||
            defaultValue = "0"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        intListPreference {
 | 
			
		||||
            key = Keys.imageDecoder
 | 
			
		||||
            titleRes = R.string.pref_image_decoder
 | 
			
		||||
            entries = arrayOf("Image", "Rapid", "Skia")
 | 
			
		||||
            entryValues = arrayOf("0", "1", "2")
 | 
			
		||||
            defaultValue = "0"
 | 
			
		||||
            summary = "%s"
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.fullscreen
 | 
			
		||||
            titleRes = R.string.pref_fullscreen
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.enableTransitions
 | 
			
		||||
            titleRes = R.string.pref_page_transitions
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.showPageNumber
 | 
			
		||||
            titleRes = R.string.pref_show_page_number
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.cropBorders
 | 
			
		||||
            titleRes = R.string.pref_crop_borders
 | 
			
		||||
            defaultValue = false
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.keepScreenOn
 | 
			
		||||
            titleRes = R.string.pref_keep_screen_on
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        preferenceCategory {
 | 
			
		||||
            titleRes = R.string.pref_reader_navigation
 | 
			
		||||
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.readWithTapping
 | 
			
		||||
                titleRes = R.string.pref_read_with_tapping
 | 
			
		||||
                defaultValue = true
 | 
			
		||||
            }
 | 
			
		||||
            switchPreference {
 | 
			
		||||
                key = Keys.readWithVolumeKeys
 | 
			
		||||
                titleRes = R.string.pref_read_with_volume_keys
 | 
			
		||||
                defaultValue = false
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,47 +1,27 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.graphics.drawable.Drawable
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import android.support.v7.preference.PreferenceGroup
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.getOrDefault
 | 
			
		||||
import eu.kanade.tachiyomi.source.SourceManager
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.HttpSource
 | 
			
		||||
import eu.kanade.tachiyomi.source.online.LoginSource
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.LoginCheckBoxPreference
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.SourceLoginDialog
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.SwitchPreferenceCategory
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import java.util.*
 | 
			
		||||
 | 
			
		||||
class SettingsSourcesFragment : SettingsFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val SOURCE_CHANGE_REQUEST = 120
 | 
			
		||||
 | 
			
		||||
        fun newInstance(rootKey: String?): SettingsSourcesFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsSourcesFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
class SettingsSourcesController : SettingsController(),
 | 
			
		||||
        SourceLoginDialog.Listener {
 | 
			
		||||
 | 
			
		||||
    private val onlineSources by lazy { Injekt.get<SourceManager>().getOnlineSources() }
 | 
			
		||||
 | 
			
		||||
    override fun setDivider(divider: Drawable?) {
 | 
			
		||||
        super.setDivider(null)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        // Remove dummy preference
 | 
			
		||||
        preferenceScreen.removeAll()
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_sources
 | 
			
		||||
 | 
			
		||||
        // Get the list of active language codes.
 | 
			
		||||
        val activeLangsCodes = preferences.enabledLanguages().getOrDefault()
 | 
			
		||||
@@ -66,8 +46,8 @@ class SettingsSourcesFragment : SettingsFragment() {
 | 
			
		||||
                    addLanguageSources(this, sources)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                setOnPreferenceChangeListener { preference, any ->
 | 
			
		||||
                    val checked = any as Boolean
 | 
			
		||||
                onChange { newValue ->
 | 
			
		||||
                    val checked = newValue as Boolean
 | 
			
		||||
                    val current = preferences.enabledLanguages().getOrDefault()
 | 
			
		||||
                    if (!checked) {
 | 
			
		||||
                        preferences.enabledLanguages().set(current - lang)
 | 
			
		||||
@@ -82,24 +62,28 @@ class SettingsSourcesFragment : SettingsFragment() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun setDivider(divider: Drawable?) {
 | 
			
		||||
        super.setDivider(null)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Adds the source list for the given group (language).
 | 
			
		||||
     *
 | 
			
		||||
     * @param group the language category.
 | 
			
		||||
     */
 | 
			
		||||
    private fun addLanguageSources(group: SwitchPreferenceCategory, sources: List<HttpSource>) {
 | 
			
		||||
    private fun addLanguageSources(group: PreferenceGroup, sources: List<HttpSource>) {
 | 
			
		||||
        val hiddenCatalogues = preferences.hiddenCatalogues().getOrDefault()
 | 
			
		||||
 | 
			
		||||
        sources.forEach { source ->
 | 
			
		||||
            val sourcePreference = LoginCheckBoxPreference(context, source).apply {
 | 
			
		||||
            val sourcePreference = LoginCheckBoxPreference(group.context, source).apply {
 | 
			
		||||
                val id = source.id.toString()
 | 
			
		||||
                title = source.name
 | 
			
		||||
                key = getSourceKey(source.id)
 | 
			
		||||
                isPersistent = false
 | 
			
		||||
                isChecked = id !in hiddenCatalogues
 | 
			
		||||
 | 
			
		||||
                setOnPreferenceChangeListener { preference, any ->
 | 
			
		||||
                    val checked = any as Boolean
 | 
			
		||||
                onChange { newValue ->
 | 
			
		||||
                    val checked = newValue as Boolean
 | 
			
		||||
                    val current = preferences.hiddenCatalogues().getOrDefault()
 | 
			
		||||
 | 
			
		||||
                    preferences.hiddenCatalogues().set(if (checked)
 | 
			
		||||
@@ -111,27 +95,23 @@ class SettingsSourcesFragment : SettingsFragment() {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                setOnLoginClickListener {
 | 
			
		||||
                    val fragment = SourceLoginDialog.newInstance(source)
 | 
			
		||||
                    fragment.setTargetFragment(this@SettingsSourcesFragment, SOURCE_CHANGE_REQUEST)
 | 
			
		||||
                    fragment.show(fragmentManager, null)
 | 
			
		||||
                    val dialog = SourceLoginDialog(source)
 | 
			
		||||
                    dialog.targetController = this@SettingsSourcesController
 | 
			
		||||
                    dialog.showDialog(router)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            group.addPreference(sourcePreference)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        if (requestCode == SOURCE_CHANGE_REQUEST && data != null) {
 | 
			
		||||
            val sourceId = data.getLongExtra("key", -1L)
 | 
			
		||||
            val pref = findPreference(getSourceKey(sourceId)) as? LoginCheckBoxPreference
 | 
			
		||||
            pref?.notifyChanged()
 | 
			
		||||
        }
 | 
			
		||||
    override fun loginDialogClosed(source: LoginSource) {
 | 
			
		||||
        val pref = findPreference(getSourceKey(source.id)) as? LoginCheckBoxPreference
 | 
			
		||||
        pref?.notifyChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun getSourceKey(sourceId: Long): String {
 | 
			
		||||
        return "source_$sourceId"
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,91 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.support.customtabs.CustomTabsIntent
 | 
			
		||||
import android.support.v7.preference.PreferenceScreen
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackService
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceColor
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.LoginPreference
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 | 
			
		||||
 | 
			
		||||
class SettingsTrackingController : SettingsController(),
 | 
			
		||||
        TrackLoginDialog.Listener {
 | 
			
		||||
 | 
			
		||||
    private val trackManager: TrackManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    override fun setupPreferenceScreen(screen: PreferenceScreen) = with(screen) {
 | 
			
		||||
        titleRes = R.string.pref_category_tracking
 | 
			
		||||
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.autoUpdateTrack
 | 
			
		||||
            titleRes = R.string.pref_auto_update_manga_sync
 | 
			
		||||
            defaultValue = true
 | 
			
		||||
        }
 | 
			
		||||
        switchPreference {
 | 
			
		||||
            key = Keys.askUpdateTrack
 | 
			
		||||
            titleRes = R.string.pref_ask_update_manga_sync
 | 
			
		||||
            defaultValue = false
 | 
			
		||||
        }.apply {
 | 
			
		||||
            dependency = Keys.autoUpdateTrack // the preference needs to be attached.
 | 
			
		||||
        }
 | 
			
		||||
        preferenceCategory {
 | 
			
		||||
            titleRes = R.string.services
 | 
			
		||||
 | 
			
		||||
            trackPreference(trackManager.myAnimeList) {
 | 
			
		||||
                onClick {
 | 
			
		||||
                    val dialog = TrackLoginDialog(trackManager.myAnimeList)
 | 
			
		||||
                    dialog.targetController = this@SettingsTrackingController
 | 
			
		||||
                    dialog.showDialog(router)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            trackPreference(trackManager.aniList) {
 | 
			
		||||
                onClick {
 | 
			
		||||
                    val tabsIntent = CustomTabsIntent.Builder()
 | 
			
		||||
                            .setToolbarColor(context.getResourceColor(R.attr.colorPrimary))
 | 
			
		||||
                            .build()
 | 
			
		||||
                    tabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
 | 
			
		||||
                    tabsIntent.launchUrl(activity, AnilistApi.authUrl())
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            trackPreference(trackManager.kitsu) {
 | 
			
		||||
                onClick {
 | 
			
		||||
                    val dialog = TrackLoginDialog(trackManager.kitsu)
 | 
			
		||||
                    dialog.targetController = this@SettingsTrackingController
 | 
			
		||||
                    dialog.showDialog(router)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline fun PreferenceScreen.trackPreference(
 | 
			
		||||
            service: TrackService,
 | 
			
		||||
            block: (@DSL LoginPreference).() -> Unit
 | 
			
		||||
    ): LoginPreference {
 | 
			
		||||
        return initThenAdd(LoginPreference(context).apply {
 | 
			
		||||
            key = Keys.trackUsername(service.id)
 | 
			
		||||
            title = service.name
 | 
			
		||||
        }, block)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResumed(activity: Activity) {
 | 
			
		||||
        super.onActivityResumed(activity)
 | 
			
		||||
        // Manually refresh anilist holder
 | 
			
		||||
        updatePreference(trackManager.aniList.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updatePreference(id: Int) {
 | 
			
		||||
        val pref = findPreference(Keys.trackUsername(id)) as? LoginPreference
 | 
			
		||||
        pref?.notifyChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun trackDialogClosed(service: TrackService) {
 | 
			
		||||
        updatePreference(service.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,95 +0,0 @@
 | 
			
		||||
package eu.kanade.tachiyomi.ui.setting
 | 
			
		||||
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.customtabs.CustomTabsIntent
 | 
			
		||||
import android.support.v7.preference.PreferenceCategory
 | 
			
		||||
import android.support.v7.preference.XpPreferenceFragment
 | 
			
		||||
import android.view.View
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackManager
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.TrackService
 | 
			
		||||
import eu.kanade.tachiyomi.data.track.anilist.AnilistApi
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceColor
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.LoginPreference
 | 
			
		||||
import eu.kanade.tachiyomi.widget.preference.TrackLoginDialog
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
class SettingsTrackingFragment : SettingsFragment() {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val SYNC_CHANGE_REQUEST = 121
 | 
			
		||||
 | 
			
		||||
        fun newInstance(rootKey: String): SettingsTrackingFragment {
 | 
			
		||||
            val args = Bundle()
 | 
			
		||||
            args.putString(XpPreferenceFragment.ARG_PREFERENCE_ROOT, rootKey)
 | 
			
		||||
            return SettingsTrackingFragment().apply { arguments = args }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val trackManager: TrackManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    private val preferences: PreferencesHelper by injectLazy()
 | 
			
		||||
 | 
			
		||||
    val syncCategory: PreferenceCategory by bindPref(R.string.pref_category_tracking_accounts_key)
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        super.onViewCreated(view, savedState)
 | 
			
		||||
 | 
			
		||||
        registerService(trackManager.myAnimeList)
 | 
			
		||||
 | 
			
		||||
        registerService(trackManager.aniList) {
 | 
			
		||||
            val intent = CustomTabsIntent.Builder()
 | 
			
		||||
                    .setToolbarColor(activity.getResourceColor(R.attr.colorPrimary))
 | 
			
		||||
                    .build()
 | 
			
		||||
            intent.intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
 | 
			
		||||
            intent.launchUrl(activity, AnilistApi.authUrl())
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        registerService(trackManager.kitsu)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun <T : TrackService> registerService(
 | 
			
		||||
            service: T,
 | 
			
		||||
            onPreferenceClick: (T) -> Unit = defaultOnPreferenceClick) {
 | 
			
		||||
 | 
			
		||||
        LoginPreference(preferenceManager.context).apply {
 | 
			
		||||
            key = preferences.keys.trackUsername(service.id)
 | 
			
		||||
            title = service.name
 | 
			
		||||
 | 
			
		||||
            setOnPreferenceClickListener {
 | 
			
		||||
                onPreferenceClick(service)
 | 
			
		||||
                true
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            syncCategory.addPreference(this)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private val defaultOnPreferenceClick: (TrackService) -> Unit
 | 
			
		||||
        get() = {
 | 
			
		||||
            val fragment = TrackLoginDialog.newInstance(it)
 | 
			
		||||
            fragment.setTargetFragment(this, SYNC_CHANGE_REQUEST)
 | 
			
		||||
            fragment.show(fragmentManager, null)
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    override fun onResume() {
 | 
			
		||||
        super.onResume()
 | 
			
		||||
        // Manually refresh anilist holder
 | 
			
		||||
        updatePreference(trackManager.aniList.id)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
 | 
			
		||||
        if (requestCode == SYNC_CHANGE_REQUEST && data != null) {
 | 
			
		||||
            val serviceId = data.getIntExtra("key", -1)
 | 
			
		||||
            updatePreference(serviceId)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun updatePreference(id: Int) {
 | 
			
		||||
        val pref = findPreference(preferences.keys.trackUsername(id)) as? LoginPreference
 | 
			
		||||
        pref?.notifyChanged()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -107,13 +107,20 @@ object DiskUtil {
 | 
			
		||||
     * Scans the given file so that it can be shown in gallery apps, for example.
 | 
			
		||||
     */
 | 
			
		||||
    fun scanMedia(context: Context, file: File) {
 | 
			
		||||
        scanMedia(context, Uri.fromFile(file))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Scans the given file so that it can be shown in gallery apps, for example.
 | 
			
		||||
     */
 | 
			
		||||
    fun scanMedia(context: Context, uri: Uri) {
 | 
			
		||||
        val action = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
 | 
			
		||||
            Intent.ACTION_MEDIA_MOUNTED
 | 
			
		||||
        } else {
 | 
			
		||||
            Intent.ACTION_MEDIA_SCANNER_SCAN_FILE
 | 
			
		||||
        }
 | 
			
		||||
        val mediaScanIntent = Intent(action)
 | 
			
		||||
        mediaScanIntent.data = Uri.fromFile(file)
 | 
			
		||||
        mediaScanIntent.data = uri
 | 
			
		||||
        context.sendBroadcast(mediaScanIntent)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.widget.preference
 | 
			
		||||
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.graphics.Color
 | 
			
		||||
import android.support.v7.preference.CheckBoxPreference
 | 
			
		||||
import android.support.v7.preference.PreferenceViewHolder
 | 
			
		||||
import android.util.AttributeSet
 | 
			
		||||
import android.view.View
 | 
			
		||||
@@ -11,7 +12,6 @@ import eu.kanade.tachiyomi.source.online.LoginSource
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceColor
 | 
			
		||||
import eu.kanade.tachiyomi.util.setVectorCompat
 | 
			
		||||
import kotlinx.android.synthetic.main.pref_item_source.view.*
 | 
			
		||||
import net.xpece.android.support.preference.CheckBoxPreference
 | 
			
		||||
 | 
			
		||||
class LoginCheckBoxPreference @JvmOverloads constructor(
 | 
			
		||||
        context: Context,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,22 @@
 | 
			
		||||
package eu.kanade.tachiyomi.widget.preference
 | 
			
		||||
 | 
			
		||||
import android.app.Activity
 | 
			
		||||
import android.app.Dialog
 | 
			
		||||
import android.content.DialogInterface
 | 
			
		||||
import android.content.Intent
 | 
			
		||||
import android.os.Bundle
 | 
			
		||||
import android.support.v4.app.DialogFragment
 | 
			
		||||
import android.text.method.PasswordTransformationMethod
 | 
			
		||||
import android.view.View
 | 
			
		||||
import com.afollestad.materialdialogs.MaterialDialog
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeHandler
 | 
			
		||||
import com.bluelinelabs.conductor.ControllerChangeType
 | 
			
		||||
import com.dd.processbutton.iml.ActionProcessButton
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 | 
			
		||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
 | 
			
		||||
import eu.kanade.tachiyomi.widget.SimpleTextWatcher
 | 
			
		||||
import kotlinx.android.synthetic.main.pref_account_login.view.*
 | 
			
		||||
import rx.Subscription
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
 | 
			
		||||
abstract class LoginDialogPreference : DialogFragment() {
 | 
			
		||||
abstract class LoginDialogPreference(bundle: Bundle? = null) : DialogController(bundle) {
 | 
			
		||||
 | 
			
		||||
    var v: View? = null
 | 
			
		||||
        private set
 | 
			
		||||
@@ -27,7 +26,7 @@ abstract class LoginDialogPreference : DialogFragment() {
 | 
			
		||||
    var requestSubscription: Subscription? = null
 | 
			
		||||
 | 
			
		||||
    override fun onCreateDialog(savedState: Bundle?): Dialog {
 | 
			
		||||
        val dialog = MaterialDialog.Builder(activity)
 | 
			
		||||
        val dialog = MaterialDialog.Builder(activity!!)
 | 
			
		||||
                .customView(R.layout.pref_account_login, false)
 | 
			
		||||
                .negativeText(android.R.string.cancel)
 | 
			
		||||
                .build()
 | 
			
		||||
@@ -37,7 +36,7 @@ abstract class LoginDialogPreference : DialogFragment() {
 | 
			
		||||
        return dialog
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
    fun onViewCreated(view: View, savedState: Bundle?) {
 | 
			
		||||
        v = view.apply {
 | 
			
		||||
            show_password.setOnCheckedChangeListener { v, isChecked ->
 | 
			
		||||
                if (isChecked)
 | 
			
		||||
@@ -55,7 +54,7 @@ abstract class LoginDialogPreference : DialogFragment() {
 | 
			
		||||
 | 
			
		||||
            password.addTextChangedListener(object : SimpleTextWatcher() {
 | 
			
		||||
                override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
 | 
			
		||||
                    if (s.length == 0) {
 | 
			
		||||
                    if (s.isEmpty()) {
 | 
			
		||||
                        show_password.isEnabled = true
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -64,15 +63,15 @@ abstract class LoginDialogPreference : DialogFragment() {
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onPause() {
 | 
			
		||||
        super.onPause()
 | 
			
		||||
        requestSubscription?.unsubscribe()
 | 
			
		||||
    override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
 | 
			
		||||
        super.onChangeStarted(handler, type)
 | 
			
		||||
        if (!type.isEnter) {
 | 
			
		||||
            onDialogClosed()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDismiss(dialog: DialogInterface) {
 | 
			
		||||
        super.onDismiss(dialog)
 | 
			
		||||
        val intent = Intent().putExtras(arguments)
 | 
			
		||||
        targetFragment?.onActivityResult(targetRequestCode, Activity.RESULT_OK, intent)
 | 
			
		||||
    open fun onDialogClosed() {
 | 
			
		||||
        requestSubscription?.unsubscribe()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected abstract fun checkLogin()
 | 
			
		||||
 
 | 
			
		||||
@@ -10,34 +10,17 @@ import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import kotlinx.android.synthetic.main.pref_account_login.view.*
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class SourceLoginDialog : LoginDialogPreference() {
 | 
			
		||||
class SourceLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle) {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
    private val source = Injekt.get<SourceManager>().get(args.getLong("key")) as LoginSource
 | 
			
		||||
 | 
			
		||||
        fun newInstance(source: Source): LoginDialogPreference {
 | 
			
		||||
            val fragment = SourceLoginDialog()
 | 
			
		||||
            val bundle = Bundle(1)
 | 
			
		||||
            bundle.putLong("key", source.id)
 | 
			
		||||
            fragment.arguments = bundle
 | 
			
		||||
            return fragment
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val sourceManager: SourceManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    lateinit var source: LoginSource
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
 | 
			
		||||
        val sourceId = arguments.getLong("key")
 | 
			
		||||
        source = sourceManager.get(sourceId) as LoginSource
 | 
			
		||||
    }
 | 
			
		||||
    constructor(source: Source) : this(Bundle().apply { putLong("key", source.id) })
 | 
			
		||||
 | 
			
		||||
    override fun setCredentialsOnView(view: View) = with(view) {
 | 
			
		||||
        dialog_title.text = getString(R.string.login_title, source.toString())
 | 
			
		||||
        dialog_title.text = context.getString(R.string.login_title, source.toString())
 | 
			
		||||
        username.setText(preferences.sourceUsername(source))
 | 
			
		||||
        password.setText(preferences.sourcePassword(source))
 | 
			
		||||
    }
 | 
			
		||||
@@ -60,7 +43,7 @@ class SourceLoginDialog : LoginDialogPreference() {
 | 
			
		||||
                                    username.text.toString(),
 | 
			
		||||
                                    password.text.toString())
 | 
			
		||||
 | 
			
		||||
                            dialog.dismiss()
 | 
			
		||||
                            dialog?.dismiss()
 | 
			
		||||
                            context.toast(R.string.login_success)
 | 
			
		||||
                        } else {
 | 
			
		||||
                            preferences.setSourceCredentials(source, "", "")
 | 
			
		||||
@@ -74,4 +57,13 @@ class SourceLoginDialog : LoginDialogPreference() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDialogClosed() {
 | 
			
		||||
        super.onDialogClosed()
 | 
			
		||||
        (targetController as? Listener)?.loginDialogClosed(source)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface Listener {
 | 
			
		||||
        fun loginDialogClosed(source: LoginSource)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,15 +4,16 @@ import android.annotation.TargetApi
 | 
			
		||||
import android.content.Context
 | 
			
		||||
import android.content.res.TypedArray
 | 
			
		||||
import android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH
 | 
			
		||||
import android.support.v7.preference.PreferenceCategory
 | 
			
		||||
import android.support.v7.preference.PreferenceViewHolder
 | 
			
		||||
import android.support.v7.widget.SwitchCompat
 | 
			
		||||
import android.util.AttributeSet
 | 
			
		||||
import android.view.View
 | 
			
		||||
import android.widget.Checkable
 | 
			
		||||
import android.widget.CompoundButton
 | 
			
		||||
import android.widget.TextView
 | 
			
		||||
import eu.kanade.tachiyomi.R
 | 
			
		||||
import eu.kanade.tachiyomi.util.getResourceColor
 | 
			
		||||
import net.xpece.android.support.preference.PreferenceCategory
 | 
			
		||||
import net.xpece.android.support.preference.R
 | 
			
		||||
 | 
			
		||||
class SwitchPreferenceCategory @JvmOverloads constructor(
 | 
			
		||||
        context: Context,
 | 
			
		||||
@@ -20,20 +21,17 @@ class SwitchPreferenceCategory @JvmOverloads constructor(
 | 
			
		||||
: PreferenceCategory(
 | 
			
		||||
        context,
 | 
			
		||||
        attrs,
 | 
			
		||||
        R.attr.switchPreferenceCompatStyle,
 | 
			
		||||
        R.style.Preference_Material_SwitchPreferenceCompat),
 | 
			
		||||
        R.attr.switchPreferenceCompatStyle),
 | 
			
		||||
CompoundButton.OnCheckedChangeListener {
 | 
			
		||||
 | 
			
		||||
    init {
 | 
			
		||||
        setTitleTextColor(context.getResourceColor(R.attr.colorAccent))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private var mChecked = false
 | 
			
		||||
 | 
			
		||||
    private var mCheckedSet = false
 | 
			
		||||
 | 
			
		||||
    override fun onBindViewHolder(holder: PreferenceViewHolder) {
 | 
			
		||||
        super.onBindViewHolder(holder)
 | 
			
		||||
        val titleView = holder.findViewById(android.R.id.title) as TextView
 | 
			
		||||
        titleView.setTextColor(context.getResourceColor(R.attr.colorAccent))
 | 
			
		||||
        syncSwitchView(holder)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -9,36 +9,19 @@ import eu.kanade.tachiyomi.util.toast
 | 
			
		||||
import kotlinx.android.synthetic.main.pref_account_login.view.*
 | 
			
		||||
import rx.android.schedulers.AndroidSchedulers
 | 
			
		||||
import rx.schedulers.Schedulers
 | 
			
		||||
import uy.kohesive.injekt.injectLazy
 | 
			
		||||
import uy.kohesive.injekt.Injekt
 | 
			
		||||
import uy.kohesive.injekt.api.get
 | 
			
		||||
 | 
			
		||||
class TrackLoginDialog : LoginDialogPreference() {
 | 
			
		||||
class TrackLoginDialog(bundle: Bundle? = null) : LoginDialogPreference(bundle) {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
    private val service = Injekt.get<TrackManager>().getService(args.getInt("key"))!!
 | 
			
		||||
 | 
			
		||||
        fun newInstance(sync: TrackService): LoginDialogPreference {
 | 
			
		||||
            val fragment = TrackLoginDialog()
 | 
			
		||||
            val bundle = Bundle(1)
 | 
			
		||||
            bundle.putInt("key", sync.id)
 | 
			
		||||
            fragment.arguments = bundle
 | 
			
		||||
            return fragment
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val trackManager: TrackManager by injectLazy()
 | 
			
		||||
 | 
			
		||||
    lateinit var sync: TrackService
 | 
			
		||||
 | 
			
		||||
    override fun onCreate(savedInstanceState: Bundle?) {
 | 
			
		||||
        super.onCreate(savedInstanceState)
 | 
			
		||||
 | 
			
		||||
        val syncId = arguments.getInt("key")
 | 
			
		||||
        sync = trackManager.getService(syncId)!!
 | 
			
		||||
    }
 | 
			
		||||
    constructor(service: TrackService) : this(Bundle().apply { putInt("key", service.id) })
 | 
			
		||||
 | 
			
		||||
    override fun setCredentialsOnView(view: View) = with(view) {
 | 
			
		||||
        dialog_title.text = getString(R.string.login_title, sync.name)
 | 
			
		||||
        username.setText(sync.getUsername())
 | 
			
		||||
        password.setText(sync.getPassword())
 | 
			
		||||
        dialog_title.text = context.getString(R.string.login_title, service.name)
 | 
			
		||||
        username.setText(service.getUsername())
 | 
			
		||||
        password.setText(service.getPassword())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun checkLogin() {
 | 
			
		||||
@@ -52,11 +35,11 @@ class TrackLoginDialog : LoginDialogPreference() {
 | 
			
		||||
            val user = username.text.toString()
 | 
			
		||||
            val pass = password.text.toString()
 | 
			
		||||
 | 
			
		||||
            requestSubscription = sync.login(user, pass)
 | 
			
		||||
            requestSubscription = service.login(user, pass)
 | 
			
		||||
                    .subscribeOn(Schedulers.io())
 | 
			
		||||
                    .observeOn(AndroidSchedulers.mainThread())
 | 
			
		||||
                    .subscribe({
 | 
			
		||||
                        dialog.dismiss()
 | 
			
		||||
                        dialog?.dismiss()
 | 
			
		||||
                        context.toast(R.string.login_success)
 | 
			
		||||
                    }, { error ->
 | 
			
		||||
                        login.progress = -1
 | 
			
		||||
@@ -67,4 +50,13 @@ class TrackLoginDialog : LoginDialogPreference() {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun onDialogClosed() {
 | 
			
		||||
        super.onDialogClosed()
 | 
			
		||||
        (targetController as? Listener)?.trackDialogClosed(service)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    interface Listener {
 | 
			
		||||
        fun trackDialogClosed(service: TrackService)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -25,6 +25,7 @@
 | 
			
		||||
            android:id="@+id/portrait_columns"
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:descendantFocusability="blocksDescendants"
 | 
			
		||||
            app:max="10"
 | 
			
		||||
            app:min="0"/>
 | 
			
		||||
 | 
			
		||||
@@ -46,6 +47,7 @@
 | 
			
		||||
            android:id="@+id/landscape_columns"
 | 
			
		||||
            android:layout_width="wrap_content"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            android:descendantFocusability="blocksDescendants"
 | 
			
		||||
            app:max="10"
 | 
			
		||||
            app:min="0"/>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,81 +1,5 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<resources>
 | 
			
		||||
    <string name="pref_category_general_key" translatable="false">pref_category_general_key</string>
 | 
			
		||||
    <string name="pref_category_reader_key" translatable="false">pref_category_reader_key</string>
 | 
			
		||||
    <string name="pref_category_tracking_key" translatable="false">pref_category_tracking_key</string>
 | 
			
		||||
    <string name="pref_category_downloads_key" translatable="false">pref_category_downloads_key</string>
 | 
			
		||||
    <string name="pref_category_advanced_key" translatable="false">pref_category_advanced_key</string>
 | 
			
		||||
    <string name="pref_category_about_key" translatable="false">pref_category_about_key</string>
 | 
			
		||||
    <string name="pref_category_sources_key" translatable="false">pref_category_sources_key</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_display_library_as_list" translatable="false">pref_display_library_as_list</string>
 | 
			
		||||
    <string name="pref_library_columns_dialog_key" translatable="false">pref_library_columns_dialog_key</string>
 | 
			
		||||
    <string name="pref_library_columns_portrait_key" translatable="false">pref_library_columns_portrait_key</string>
 | 
			
		||||
    <string name="pref_library_columns_landscape_key" translatable="false">pref_library_columns_landscape_key</string>
 | 
			
		||||
    <string name="pref_library_update_interval_key" translatable="false">pref_library_update_interval_key</string>
 | 
			
		||||
    <string name="pref_library_update_categories_key" translatable="false">library_update_categories</string>
 | 
			
		||||
    <string name="pref_update_only_non_completed_key" translatable="false">pref_update_only_non_completed_key</string>
 | 
			
		||||
    <string name="pref_auto_update_manga_sync_key" translatable="false">pref_auto_update_manga_sync_key</string>
 | 
			
		||||
    <string name="pref_ask_update_manga_sync_key" translatable="false">pref_ask_update_manga_sync_key</string>
 | 
			
		||||
    <string name="pref_theme_key" translatable="false">pref_theme_key</string>
 | 
			
		||||
    <string name="pref_library_update_restriction_key" translatable="false">library_update_restriction</string>
 | 
			
		||||
    <string name="pref_start_screen_key" translatable="false">start_screen</string>
 | 
			
		||||
    <string name="pref_language_key" translatable="false">app_language</string>
 | 
			
		||||
    <string name="default_category_key" translatable="false">default_category</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_default_viewer_key" translatable="false">pref_default_viewer_key</string>
 | 
			
		||||
    <string name="pref_image_scale_type_key" translatable="false">pref_image_scale_type_key</string>
 | 
			
		||||
    <string name="pref_zoom_start_key" translatable="false">pref_zoom_start_key</string>
 | 
			
		||||
    <string name="pref_fullscreen_key" translatable="false">fullscreen</string>
 | 
			
		||||
    <string name="pref_rotation_type_key" translatable="false">pref_rotation_type_key</string>
 | 
			
		||||
    <string name="pref_enable_transitions_key" translatable="false">pref_enable_transitions_key</string>
 | 
			
		||||
    <string name="pref_show_page_number_key" translatable="false">pref_show_page_number_key</string>
 | 
			
		||||
    <string name="pref_keep_screen_on_key" translatable="false">pref_keep_screen_on_key</string>
 | 
			
		||||
    <string name="pref_custom_brightness_key" translatable="false">pref_custom_brightness_key</string>
 | 
			
		||||
    <string name="pref_custom_brightness_value_key" translatable="false">custom_brightness_value</string>
 | 
			
		||||
    <string name="pref_color_filter_key" translatable="false">pref_color_filter_key</string>
 | 
			
		||||
    <string name="pref_color_filter_value_key" translatable="false">color_filter_value</string>
 | 
			
		||||
    <string name="pref_red_filter_value_key" translatable="false">pref_red_filter_value</string>
 | 
			
		||||
    <string name="pref_reader_theme_key" translatable="false">pref_reader_theme_key</string>
 | 
			
		||||
    <string name="pref_image_decoder_key" translatable="false">image_decoder</string>
 | 
			
		||||
    <string name="pref_crop_borders_key" translatable="false">crop_borders</string>
 | 
			
		||||
    <string name="pref_read_with_volume_keys_key" translatable="false">reader_volume_keys</string>
 | 
			
		||||
    <string name="pref_read_with_tapping_key" translatable="false">reader_tap</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_filter_downloaded_key" translatable="false">pref_filter_downloaded_key</string>
 | 
			
		||||
    <string name="pref_filter_unread_key" translatable="false">pref_filter_unread_key</string>
 | 
			
		||||
    <string name="pref_library_sorting_mode_key" translatable="false">library_sorting_mode</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_download_directory_key" translatable="false">download_directory</string>
 | 
			
		||||
    <string name="pref_download_slots_key" translatable="false">pref_download_slots_key</string>
 | 
			
		||||
    <string name="pref_remove_after_read_slots_key" translatable="false">remove_after_read_slots</string>
 | 
			
		||||
    <string name="pref_download_only_over_wifi_key" translatable="false">pref_download_only_over_wifi_key</string>
 | 
			
		||||
    <string name="pref_remove_after_marked_as_read_key" translatable="false">pref_remove_after_marked_as_read_key</string>
 | 
			
		||||
    <string name="pref_last_used_category_key" translatable="false">last_used_category</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_create_local_backup_key" translatable="false">create_local_backup</string>
 | 
			
		||||
    <string name="pref_restore_local_backup_key" translatable="false">restore_local_backup</string>
 | 
			
		||||
    <string name="pref_backup_interval_key" translatable="false">backup_interval</string>
 | 
			
		||||
    <string name="pref_backup_directory_key" translatable="false">backup_directory</string>
 | 
			
		||||
    <string name="pref_backup_slots_key" translatable="false">backup_slots</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_source_languages" translatable="false">source_languages</string>
 | 
			
		||||
    <string name="pref_category_tracking_accounts_key" translatable="false">category_tracking_accounts</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_clear_chapter_cache_key" translatable="false">pref_clear_chapter_cache_key</string>
 | 
			
		||||
    <string name="pref_clear_database_key" translatable="false">pref_clear_database_key</string>
 | 
			
		||||
    <string name="pref_clear_cookies_key" translatable="false">pref_clear_cookies_key</string>
 | 
			
		||||
    <string name="pref_refresh_library_metadata_key" translatable="false">refresh_library_metadata</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_version" translatable="false">pref_version</string>
 | 
			
		||||
    <string name="pref_build_time" translatable="false">pref_build_time</string>
 | 
			
		||||
    <string name="pref_enable_automatic_updates_key" translatable="false">automatic_updates</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_display_catalogue_as_list" translatable="false">pref_display_catalogue_as_list</string>
 | 
			
		||||
    <string name="pref_last_catalogue_source_key" translatable="false">last_catalogue_source</string>
 | 
			
		||||
 | 
			
		||||
    <string name="pref_download_new_key" translatable="false">download_new</string>
 | 
			
		||||
    <string name="pref_download_new_categories_key" translatable="false">download_new_categories</string>
 | 
			
		||||
 | 
			
		||||
    <!-- String Fonts -->
 | 
			
		||||
    <string name="font_roboto_medium" translatable="false">sans-serif</string>
 | 
			
		||||
 
 | 
			
		||||
@@ -36,8 +36,6 @@
 | 
			
		||||
        <item name="selectable_library_drawable">@drawable/library_item_selector_light</item>
 | 
			
		||||
        <item name="text_color_primary">@color/textColorPrimaryLight</item>
 | 
			
		||||
        <item name="background_card">@color/dialogLight</item>
 | 
			
		||||
        <item name="asp_preferenceIconTint">?colorAccent</item>
 | 
			
		||||
        <item name="asp_preferenceDialogIconTint">?colorAccent</item>
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <style name="Theme.Tachiyomi" parent="Theme.Base">
 | 
			
		||||
@@ -76,8 +74,6 @@
 | 
			
		||||
        <item name="selectable_library_drawable">@drawable/library_item_selector_dark</item>
 | 
			
		||||
        <item name="text_color_primary">@color/textColorPrimaryDark</item>
 | 
			
		||||
        <item name="background_card">@color/dialogDark</item>
 | 
			
		||||
        <item name="asp_preferenceIconTint">?colorAccent</item>
 | 
			
		||||
        <item name="asp_preferenceDialogIconTint">?colorAccent</item>
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <style name="Theme.Tachiyomi.Dark" parent="Theme.Base.Dark">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,37 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_help_black_24dp"
 | 
			
		||||
        android:key="about_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_about"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:defaultValue="true"
 | 
			
		||||
            android:key="acra.enable"
 | 
			
		||||
            android:summary="@string/pref_acra_summary"
 | 
			
		||||
            android:title="@string/pref_enable_acra" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:defaultValue="false"
 | 
			
		||||
            android:key="@string/pref_enable_automatic_updates_key"
 | 
			
		||||
            android:summary="@string/pref_enable_automatic_updates_summary"
 | 
			
		||||
            android:title="@string/pref_enable_automatic_updates" />
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_version"
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            android:title="@string/version"/>
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_build_time"
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            android:title="@string/build_time"/>
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_code_black_24dp"
 | 
			
		||||
        android:key="advanced_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_advanced"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_clear_chapter_cache_key"
 | 
			
		||||
            android:title="@string/pref_clear_chapter_cache"/>
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_clear_cookies_key"
 | 
			
		||||
            android:title="@string/pref_clear_cookies"/>
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_clear_database_key"
 | 
			
		||||
            android:summary="@string/pref_clear_database_summary"
 | 
			
		||||
            android:title="@string/pref_clear_database"/>
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_refresh_library_metadata_key"
 | 
			
		||||
            android:summary="@string/pref_refresh_library_metadata_summary"
 | 
			
		||||
            android:title="@string/pref_refresh_library_metadata"/>
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,48 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_backup_black_24dp"
 | 
			
		||||
        android:key="backup_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="Backup"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_create_local_backup_key"
 | 
			
		||||
            android:summary="@string/pref_create_backup_summ"
 | 
			
		||||
            android:title="@string/pref_create_backup" />
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_restore_local_backup_key"
 | 
			
		||||
            android:summary="@string/pref_restore_backup_summ"
 | 
			
		||||
            android:title="@string/pref_restore_backup" />
 | 
			
		||||
 | 
			
		||||
        <PreferenceCategory
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            android:title="@string/pref_backup_service_category" />
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="0"
 | 
			
		||||
            android:entries="@array/backup_update_interval"
 | 
			
		||||
            android:entryValues="@array/backup_update_interval_values"
 | 
			
		||||
            android:key="@string/pref_backup_interval_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_backup_interval"/>
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_backup_directory_key"
 | 
			
		||||
            android:title="@string/pref_backup_directory" />
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:entries="@array/backup_slots"
 | 
			
		||||
            android:entryValues="@array/backup_slots"
 | 
			
		||||
            android:key="@string/pref_backup_slots_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_backup_slots" />
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,62 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_file_download_black_24dp"
 | 
			
		||||
        android:key="downloads_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_downloads"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <Preference
 | 
			
		||||
            android:key="@string/pref_download_directory_key"
 | 
			
		||||
            android:title="@string/pref_download_directory"/>
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:defaultValue="true"
 | 
			
		||||
            android:key="@string/pref_download_only_over_wifi_key"
 | 
			
		||||
            android:title="@string/pref_download_only_over_wifi" />
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:entries="@array/download_slots"
 | 
			
		||||
            android:entryValues="@array/download_slots"
 | 
			
		||||
            android:key="@string/pref_download_slots_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_download_slots"/>
 | 
			
		||||
 | 
			
		||||
        <PreferenceCategory
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            android:title="@string/pref_remove_after_read" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:defaultValue="false"
 | 
			
		||||
            android:key="@string/pref_remove_after_marked_as_read_key"
 | 
			
		||||
            android:title="@string/pref_remove_after_marked_as_read" />
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="-1"
 | 
			
		||||
            android:entries="@array/remove_after_read_slots"
 | 
			
		||||
            android:entryValues="@array/remove_after_read_slots_values"
 | 
			
		||||
            android:key="@string/pref_remove_after_read_slots_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_remove_after_read" />
 | 
			
		||||
 | 
			
		||||
        <PreferenceCategory
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            android:title="@string/pref_download_new" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:defaultValue="false"
 | 
			
		||||
            android:key="@string/pref_download_new_key"
 | 
			
		||||
            android:title="@string/pref_download_new"/>
 | 
			
		||||
 | 
			
		||||
        <MultiSelectListPreference
 | 
			
		||||
            android:key="@string/pref_download_new_categories_key"
 | 
			
		||||
            android:title="@string/pref_download_new_categories" />
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,76 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_tune_black_24dp"
 | 
			
		||||
        android:key="general_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_general"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <ListPreference
 | 
			
		||||
            android:defaultValue=""
 | 
			
		||||
            android:entryValues="@array/languages_values"
 | 
			
		||||
            android:key="@string/pref_language_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_language" />
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:entries="@array/themes"
 | 
			
		||||
            android:entryValues="@array/themes_values"
 | 
			
		||||
            android:key="@string/pref_theme_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_theme"/>
 | 
			
		||||
        
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
                android:title="@string/pref_start_screen"
 | 
			
		||||
                android:key="@string/pref_start_screen_key"
 | 
			
		||||
                android:entries="@array/start_screen_selection"
 | 
			
		||||
                android:entryValues="@array/start_screen_selection_values"
 | 
			
		||||
                android:defaultValue="1"
 | 
			
		||||
                android:summary="%s"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.SimpleDialogPreference
 | 
			
		||||
            android:dialogLayout="@layout/pref_library_columns"
 | 
			
		||||
            android:key="@string/pref_library_columns_dialog_key"
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            android:title="@string/pref_library_columns"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="0"
 | 
			
		||||
            android:entries="@array/library_update_interval"
 | 
			
		||||
            android:entryValues="@array/library_update_interval_values"
 | 
			
		||||
            android:key="@string/pref_library_update_interval_key"
 | 
			
		||||
            android:summary="%s"
 | 
			
		||||
            android:title="@string/pref_library_update_interval"/>
 | 
			
		||||
 | 
			
		||||
        <MultiSelectListPreference
 | 
			
		||||
            android:entries="@array/library_update_restrictions"
 | 
			
		||||
            android:entryValues="@array/library_update_restrictions_values"
 | 
			
		||||
            android:key="@string/pref_library_update_restriction_key"
 | 
			
		||||
            android:summary="@string/pref_library_update_restriction_summary"
 | 
			
		||||
            android:title="@string/pref_library_update_restriction" />
 | 
			
		||||
 | 
			
		||||
        <MultiSelectListPreference
 | 
			
		||||
            android:key="@string/pref_library_update_categories_key"
 | 
			
		||||
            android:title="@string/pref_library_update_categories"/>
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:defaultValue="false"
 | 
			
		||||
            android:key="@string/pref_update_only_non_completed_key"
 | 
			
		||||
            android:title="@string/pref_update_only_non_completed" />
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:defaultValue="-1"
 | 
			
		||||
            android:entries="@array/default_category_entry"
 | 
			
		||||
            android:entryValues="@array/default_category_entry_value"
 | 
			
		||||
            android:key="@string/default_category_key"
 | 
			
		||||
            android:title="@string/default_category"
 | 
			
		||||
            android:summary="@string/default_category_summary"/>
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,103 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_chrome_reader_mode_black_24dp"
 | 
			
		||||
        android:key="reader_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_reader"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:title="@string/pref_viewer_type"
 | 
			
		||||
            android:key="@string/pref_default_viewer_key"
 | 
			
		||||
            android:entries="@array/viewers"
 | 
			
		||||
            android:entryValues="@array/viewers_values"
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:summary="%s"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:title="@string/pref_image_scale_type"
 | 
			
		||||
            android:key="@string/pref_image_scale_type_key"
 | 
			
		||||
            android:entries="@array/image_scale_type"
 | 
			
		||||
            android:entryValues="@array/image_scale_type_values"
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:summary="%s"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:title="@string/pref_zoom_start"
 | 
			
		||||
            android:key="@string/pref_zoom_start_key"
 | 
			
		||||
            android:entries="@array/zoom_start"
 | 
			
		||||
            android:entryValues="@array/zoom_start_values"
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:summary="%s"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:title="@string/pref_rotation_type"
 | 
			
		||||
            android:key="@string/pref_rotation_type_key"
 | 
			
		||||
            android:entries="@array/rotation_type"
 | 
			
		||||
            android:entryValues="@array/rotation_type_values"
 | 
			
		||||
            android:defaultValue="1"
 | 
			
		||||
            android:summary="%s"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:title="@string/pref_reader_theme"
 | 
			
		||||
            android:key="@string/pref_reader_theme_key"
 | 
			
		||||
            android:entries="@array/reader_themes"
 | 
			
		||||
            android:entryValues="@array/reader_themes_values"
 | 
			
		||||
            android:defaultValue="0"
 | 
			
		||||
            android:summary="%s"/>
 | 
			
		||||
 | 
			
		||||
        <eu.kanade.tachiyomi.widget.preference.IntListPreference
 | 
			
		||||
            android:title="@string/pref_image_decoder"
 | 
			
		||||
            android:key="@string/pref_image_decoder_key"
 | 
			
		||||
            android:entries="@array/image_decoders"
 | 
			
		||||
            android:entryValues="@array/image_decoders_values"
 | 
			
		||||
            android:defaultValue="0"
 | 
			
		||||
            android:summary="%s" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:title="@string/pref_fullscreen"
 | 
			
		||||
            android:key="@string/pref_fullscreen_key"
 | 
			
		||||
            android:defaultValue="true" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:title="@string/pref_page_transitions"
 | 
			
		||||
            android:key="@string/pref_enable_transitions_key"
 | 
			
		||||
            android:defaultValue="true" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:title="@string/pref_show_page_number"
 | 
			
		||||
            android:key="@string/pref_show_page_number_key"
 | 
			
		||||
            android:defaultValue="true" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:title="@string/pref_crop_borders"
 | 
			
		||||
            android:key="@string/pref_crop_borders_key"
 | 
			
		||||
            android:defaultValue="false" />
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:title="@string/pref_keep_screen_on"
 | 
			
		||||
            android:key="@string/pref_keep_screen_on_key"
 | 
			
		||||
            android:defaultValue="true" />
 | 
			
		||||
 | 
			
		||||
        <PreferenceCategory
 | 
			
		||||
            android:title="@string/pref_reader_navigation">
 | 
			
		||||
 | 
			
		||||
            <SwitchPreference
 | 
			
		||||
                android:title="@string/pref_read_with_tapping"
 | 
			
		||||
                android:key="@string/pref_read_with_tapping_key"
 | 
			
		||||
                android:defaultValue="true" />
 | 
			
		||||
 | 
			
		||||
            <SwitchPreference
 | 
			
		||||
                android:title="@string/pref_read_with_volume_keys"
 | 
			
		||||
                android:key="@string/pref_read_with_volume_keys_key"
 | 
			
		||||
                android:defaultValue="false" />
 | 
			
		||||
 | 
			
		||||
        </PreferenceCategory>
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,18 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_language_black_24dp"
 | 
			
		||||
        android:key="sources_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_sources"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <!-- Dummy preference, it's needed at least one  -->
 | 
			
		||||
        <Preference/>
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
@@ -1,33 +0,0 @@
 | 
			
		||||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto">
 | 
			
		||||
 | 
			
		||||
    <PreferenceScreen
 | 
			
		||||
        android:icon="@drawable/ic_sync_black_24dp"
 | 
			
		||||
        android:key="tracking_screen"
 | 
			
		||||
        android:persistent="false"
 | 
			
		||||
        android:title="@string/pref_category_tracking"
 | 
			
		||||
        app:asp_tintEnabled="true">
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:key="@string/pref_auto_update_manga_sync_key"
 | 
			
		||||
            android:title="@string/pref_auto_update_manga_sync"
 | 
			
		||||
            android:defaultValue="true"
 | 
			
		||||
            app:showText="false"/>
 | 
			
		||||
 | 
			
		||||
        <SwitchPreference
 | 
			
		||||
            android:key="@string/pref_ask_update_manga_sync_key"
 | 
			
		||||
            android:title="@string/pref_ask_update_manga_sync"
 | 
			
		||||
            android:defaultValue="false"
 | 
			
		||||
            android:dependency="@string/pref_auto_update_manga_sync_key"
 | 
			
		||||
            app:showText="false"/>
 | 
			
		||||
 | 
			
		||||
        <PreferenceCategory
 | 
			
		||||
            android:key="@string/pref_category_tracking_accounts_key"
 | 
			
		||||
            android:title="@string/services"
 | 
			
		||||
            android:persistent="false"
 | 
			
		||||
            app:showText="false"/>
 | 
			
		||||
 | 
			
		||||
    </PreferenceScreen>
 | 
			
		||||
 | 
			
		||||
</PreferenceScreen>
 | 
			
		||||
		Reference in New Issue
	
	Block a user