(),
actionMode = null
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt
index b49282531a..af0b6ffc78 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/AnilistLoginActivity.kt
@@ -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)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt
new file mode 100644
index 0000000000..0c496f0eb9
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/PreferenceDSL.kt
@@ -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 PreferenceGroup.initThenAdd(p: P, block: P.() -> Unit): P {
+ return p.apply { block(); addPreference(this); }
+}
+
+inline fun
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
+ get() = emptyArray() // set only
+ set(value) { entries = value.map { context.getString(it) }.toTypedArray() }
+
+var MultiSelectListPreference.entriesRes: Array
+ get() = emptyArray() // set only
+ set(value) { entries = value.map { context.getString(it) }.toTypedArray() }
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt
new file mode 100644
index 0000000000..3aa9398e95
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutController.kt
@@ -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
+ }
+ }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutFragment.kt
deleted file mode 100644
index 1f445cd5d6..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAboutFragment.kt
+++ /dev/null
@@ -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)
- })
- }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsActivity.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsActivity.kt
deleted file mode 100644
index 21304dae57..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsActivity.kt
+++ /dev/null
@@ -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
- }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt
new file mode 100644
index 0000000000..da58c80927
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt
@@ -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"
+ }
+}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt
deleted file mode 100644
index e8576414d2..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedFragment.kt
+++ /dev/null
@@ -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()
- }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt
new file mode 100644
index 0000000000..8c6d28dd25
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt
@@ -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(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(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"
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupFragment.kt
deleted file mode 100644
index 7ef9a70cd8..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupFragment.kt
+++ /dev/null
@@ -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
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt
new file mode 100644
index 0000000000..f78a4c98cd
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsController.kt
@@ -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 Observable.subscribeUntilDestroy(): Subscription {
+ return subscribe().also { untilDestroySubscriptions.add(it) }
+ }
+
+ fun Observable.subscribeUntilDestroy(onNext: (T) -> Unit): Subscription {
+ return subscribe(onNext).also { untilDestroySubscriptions.add(it) }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt
new file mode 100644
index 0000000000..71a6409d0b
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt
@@ -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 {
+ 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
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadsFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadsFragment.kt
deleted file mode 100644
index 09d56d5989..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadsFragment.kt
+++ /dev/null
@@ -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 {
- 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())
- }
- }
- }
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsFragment.kt
deleted file mode 100644
index 291db41798..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsFragment.kt
+++ /dev/null
@@ -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 bindPref(resId: Int): Lazy {
- return lazy { findPreference(getString(resId)) as T }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt
new file mode 100644
index 0000000000..294e858349
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt
@@ -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
+ }
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt
deleted file mode 100644
index bc872fb140..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralFragment.kt
+++ /dev/null
@@ -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()
- }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt
new file mode 100644
index 0000000000..4953429fb8
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsMainController.kt
@@ -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()))
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
new file mode 100644
index 0000000000..229a709a62
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt
@@ -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
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt
similarity index 63%
rename from app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt
rename to app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt
index 6f9349a5db..c7982d0d88 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesFragment.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSourcesController.kt
@@ -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().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) {
+ private fun addLanguageSources(group: PreferenceGroup, sources: List) {
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"
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt
new file mode 100644
index 0000000000..0f7251bbf4
--- /dev/null
+++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingController.kt
@@ -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)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingFragment.kt
deleted file mode 100644
index 922d83958b..0000000000
--- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsTrackingFragment.kt
+++ /dev/null
@@ -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 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()
- }
-
-}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt b/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt
index 1f1587fe68..ce231756c5 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/util/DiskUtil.kt
@@ -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)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt
index a905b9f59b..ac45bd4afc 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginCheckBoxPreference.kt
@@ -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,
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt
index a83d9712b2..1b355d4905 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt
@@ -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()
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt
index b197ba6a86..42cf0b18ec 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SourceLoginDialog.kt
@@ -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().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)
+ }
+
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt
index f07ccbc726..cff0847c10 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/SwitchPreferenceCategory.kt
@@ -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)
}
diff --git a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt
index 58da50ff20..f9fc355c0c 100644
--- a/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt
+++ b/app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt
@@ -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().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)
+ }
+
}
diff --git a/app/src/main/res/layout/pref_library_columns.xml b/app/src/main/res/layout/pref_library_columns.xml
index 7db9c0163b..eab84cc2e7 100644
--- a/app/src/main/res/layout/pref_library_columns.xml
+++ b/app/src/main/res/layout/pref_library_columns.xml
@@ -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"/>
diff --git a/app/src/main/res/values/keys.xml b/app/src/main/res/values/keys.xml
index bee25b223b..6f662810f2 100644
--- a/app/src/main/res/values/keys.xml
+++ b/app/src/main/res/values/keys.xml
@@ -1,81 +1,5 @@
- pref_category_general_key
- pref_category_reader_key
- pref_category_tracking_key
- pref_category_downloads_key
- pref_category_advanced_key
- pref_category_about_key
- pref_category_sources_key
-
- pref_display_library_as_list
- pref_library_columns_dialog_key
- pref_library_columns_portrait_key
- pref_library_columns_landscape_key
- pref_library_update_interval_key
- library_update_categories
- pref_update_only_non_completed_key
- pref_auto_update_manga_sync_key
- pref_ask_update_manga_sync_key
- pref_theme_key
- library_update_restriction
- start_screen
- app_language
- default_category
-
- pref_default_viewer_key
- pref_image_scale_type_key
- pref_zoom_start_key
- fullscreen
- pref_rotation_type_key
- pref_enable_transitions_key
- pref_show_page_number_key
- pref_keep_screen_on_key
- pref_custom_brightness_key
- custom_brightness_value
- pref_color_filter_key
- color_filter_value
- pref_red_filter_value
- pref_reader_theme_key
- image_decoder
- crop_borders
- reader_volume_keys
- reader_tap
-
- pref_filter_downloaded_key
- pref_filter_unread_key
- library_sorting_mode
-
- download_directory
- pref_download_slots_key
- remove_after_read_slots
- pref_download_only_over_wifi_key
- pref_remove_after_marked_as_read_key
- last_used_category
-
- create_local_backup
- restore_local_backup
- backup_interval
- backup_directory
- backup_slots
-
- source_languages
- category_tracking_accounts
-
- pref_clear_chapter_cache_key
- pref_clear_database_key
- pref_clear_cookies_key
- refresh_library_metadata
-
- pref_version
- pref_build_time
- automatic_updates
-
- pref_display_catalogue_as_list
- last_catalogue_source
-
- download_new
- download_new_categories
sans-serif
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index f4c0714a05..8a82972a37 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -36,8 +36,6 @@
- @drawable/library_item_selector_light
- @color/textColorPrimaryLight
- @color/dialogLight
- - ?colorAccent
- - ?colorAccent