mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-10 11:08:56 +01:00
Remove dead code
Mostly from settings rewrite, but some other things too.
This commit is contained in:
@@ -1,401 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.provider.Settings
|
||||
import android.webkit.WebStorage
|
||||
import android.webkit.WebView
|
||||
import androidx.core.net.toUri
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.domain.manga.repository.MangaRepository
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.TabletUiMode
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.cache.ChapterCache
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService.Target
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import eu.kanade.tachiyomi.network.NetworkPreferences
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_360
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_ADGUARD
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_ALIDNS
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_CLOUDFLARE
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_CONTROLD
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_DNSPOD
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_GOOGLE
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_MULLVAD
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_NJALLA
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_QUAD101
|
||||
import eu.kanade.tachiyomi.network.PREF_DOH_QUAD9
|
||||
import eu.kanade.tachiyomi.ui.base.controller.openInBrowser
|
||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||
import eu.kanade.tachiyomi.ui.setting.database.ClearDatabaseController
|
||||
import eu.kanade.tachiyomi.util.CrashLogUtil
|
||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.defaultValue
|
||||
import eu.kanade.tachiyomi.util.preference.editTextPreference
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.isDevFlavor
|
||||
import eu.kanade.tachiyomi.util.system.isPackageInstalled
|
||||
import eu.kanade.tachiyomi.util.system.logcat
|
||||
import eu.kanade.tachiyomi.util.system.powerManager
|
||||
import eu.kanade.tachiyomi.util.system.setDefaultSettings
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import logcat.LogPriority
|
||||
import rikka.sui.Sui
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
|
||||
class SettingsAdvancedController(
|
||||
private val mangaRepository: MangaRepository = Injekt.get(),
|
||||
) : SettingsController() {
|
||||
|
||||
private val network: NetworkHelper by injectLazy()
|
||||
private val chapterCache: ChapterCache by injectLazy()
|
||||
private val trackManager: TrackManager by injectLazy()
|
||||
private val networkPreferences: NetworkPreferences by injectLazy()
|
||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
private val uiPreferences: UiPreferences by injectLazy()
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_advanced
|
||||
|
||||
if (isDevFlavor.not()) {
|
||||
switchPreference {
|
||||
key = "acra.enable"
|
||||
titleRes = R.string.pref_enable_acra
|
||||
summaryRes = R.string.pref_acra_summary
|
||||
defaultValue = true
|
||||
}
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "dump_crash_logs"
|
||||
titleRes = R.string.pref_dump_crash_logs
|
||||
summaryRes = R.string.pref_dump_crash_logs_summary
|
||||
|
||||
onClick {
|
||||
viewScope.launchNonCancellable {
|
||||
CrashLogUtil(context).dumpLogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
key = networkPreferences.verboseLogging().key()
|
||||
titleRes = R.string.pref_verbose_logging
|
||||
summaryRes = R.string.pref_verbose_logging_summary
|
||||
defaultValue = isDevFlavor
|
||||
|
||||
onChange {
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_background_activity
|
||||
|
||||
preference {
|
||||
key = "pref_disable_battery_optimization"
|
||||
titleRes = R.string.pref_disable_battery_optimization
|
||||
summaryRes = R.string.pref_disable_battery_optimization_summary
|
||||
|
||||
onClick {
|
||||
val packageName: String = context.packageName
|
||||
if (!context.powerManager.isIgnoringBatteryOptimizations(packageName)) {
|
||||
try {
|
||||
val intent = Intent().apply {
|
||||
action = Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
|
||||
data = "package:$packageName".toUri()
|
||||
}
|
||||
startActivity(intent)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
context.toast(R.string.battery_optimization_setting_activity_not_found)
|
||||
}
|
||||
} else {
|
||||
context.toast(R.string.battery_optimization_disabled)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_dont_kill_my_app"
|
||||
title = "Don't kill my app!"
|
||||
summaryRes = R.string.about_dont_kill_my_app
|
||||
|
||||
onClick {
|
||||
openInBrowser("https://dontkillmyapp.com/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_data
|
||||
|
||||
preference {
|
||||
key = CLEAR_CACHE_KEY
|
||||
titleRes = R.string.pref_clear_chapter_cache
|
||||
summary = context.getString(R.string.used_cache, chapterCache.readableSize)
|
||||
|
||||
onClick { clearChapterCache() }
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(libraryPreferences.autoClearChapterCache())
|
||||
titleRes = R.string.pref_auto_clear_chapter_cache
|
||||
}
|
||||
preference {
|
||||
key = "pref_clear_database"
|
||||
titleRes = R.string.pref_clear_database
|
||||
summaryRes = R.string.pref_clear_database_summary
|
||||
|
||||
onClick {
|
||||
router.pushController(ClearDatabaseController())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_network
|
||||
|
||||
preference {
|
||||
key = "pref_clear_cookies"
|
||||
titleRes = R.string.pref_clear_cookies
|
||||
|
||||
onClick {
|
||||
network.cookieManager.removeAll()
|
||||
activity?.toast(R.string.cookies_cleared)
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_clear_webview_data"
|
||||
titleRes = R.string.pref_clear_webview_data
|
||||
|
||||
onClick { clearWebViewData() }
|
||||
}
|
||||
intListPreference {
|
||||
key = networkPreferences.dohProvider().key()
|
||||
titleRes = R.string.pref_dns_over_https
|
||||
entries = arrayOf(
|
||||
context.getString(R.string.disabled),
|
||||
"Cloudflare",
|
||||
"Google",
|
||||
"AdGuard",
|
||||
"Quad9",
|
||||
"AliDNS",
|
||||
"DNSPod",
|
||||
"360",
|
||||
"Quad 101",
|
||||
"Mullvad",
|
||||
"Control D",
|
||||
"Njalla",
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
"-1",
|
||||
PREF_DOH_CLOUDFLARE.toString(),
|
||||
PREF_DOH_GOOGLE.toString(),
|
||||
PREF_DOH_ADGUARD.toString(),
|
||||
PREF_DOH_QUAD9.toString(),
|
||||
PREF_DOH_ALIDNS.toString(),
|
||||
PREF_DOH_DNSPOD.toString(),
|
||||
PREF_DOH_360.toString(),
|
||||
PREF_DOH_QUAD101.toString(),
|
||||
PREF_DOH_MULLVAD.toString(),
|
||||
PREF_DOH_CONTROLD.toString(),
|
||||
PREF_DOH_NJALLA.toString(),
|
||||
)
|
||||
defaultValue = "-1"
|
||||
summary = "%s"
|
||||
|
||||
onChange {
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
true
|
||||
}
|
||||
}
|
||||
val defaultUserAgent = networkPreferences.defaultUserAgent()
|
||||
editTextPreference {
|
||||
key = defaultUserAgent.key()
|
||||
titleRes = R.string.pref_user_agent_string
|
||||
text = defaultUserAgent.get()
|
||||
summary = network.defaultUserAgent
|
||||
|
||||
onChange {
|
||||
if (it.toString().isBlank()) {
|
||||
activity?.toast(R.string.error_user_agent_string_blank)
|
||||
} else {
|
||||
text = it.toString().trim()
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_reset_user_agent"
|
||||
titleRes = R.string.pref_reset_user_agent_string
|
||||
|
||||
visibleIf(defaultUserAgent) { it != defaultUserAgent.defaultValue() }
|
||||
|
||||
onClick {
|
||||
defaultUserAgent.delete()
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_library
|
||||
|
||||
preference {
|
||||
key = "pref_refresh_library_covers"
|
||||
titleRes = R.string.pref_refresh_library_covers
|
||||
|
||||
onClick { LibraryUpdateService.start(context, target = Target.COVERS) }
|
||||
}
|
||||
if (trackManager.hasLoggedServices()) {
|
||||
preference {
|
||||
key = "pref_refresh_library_tracking"
|
||||
titleRes = R.string.pref_refresh_library_tracking
|
||||
summaryRes = R.string.pref_refresh_library_tracking_summary
|
||||
|
||||
onClick { LibraryUpdateService.start(context, target = Target.TRACKING) }
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_reset_viewer_flags"
|
||||
titleRes = R.string.pref_reset_viewer_flags
|
||||
summaryRes = R.string.pref_reset_viewer_flags_summary
|
||||
|
||||
onClick { resetViewerFlags() }
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_extensions
|
||||
|
||||
listPreference {
|
||||
bindTo(preferences.extensionInstaller())
|
||||
titleRes = R.string.ext_installer_pref
|
||||
summary = "%s"
|
||||
|
||||
// PackageInstaller doesn't work on MIUI properly for non-allowlisted apps
|
||||
val values = if (DeviceUtil.isMiui) {
|
||||
PreferenceValues.ExtensionInstaller.values()
|
||||
.filter { it != PreferenceValues.ExtensionInstaller.PACKAGEINSTALLER }
|
||||
} else {
|
||||
PreferenceValues.ExtensionInstaller.values().toList()
|
||||
}
|
||||
|
||||
entriesRes = values.map { it.titleResId }.toTypedArray()
|
||||
entryValues = values.map { it.name }.toTypedArray()
|
||||
|
||||
onChange {
|
||||
if (it == PreferenceValues.ExtensionInstaller.SHIZUKU.name &&
|
||||
!(context.isPackageInstalled("moe.shizuku.privileged.api") || Sui.isSui())
|
||||
) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.ext_installer_shizuku)
|
||||
.setMessage(R.string.ext_installer_shizuku_unavailable_dialog)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
openInBrowser("https://shizuku.rikka.app/download")
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_display
|
||||
|
||||
listPreference {
|
||||
bindTo(uiPreferences.tabletUiMode())
|
||||
titleRes = R.string.pref_tablet_ui_mode
|
||||
summary = "%s"
|
||||
entriesRes = TabletUiMode.values().map { it.titleResId }.toTypedArray()
|
||||
entryValues = TabletUiMode.values().map { it.name }.toTypedArray()
|
||||
|
||||
onChange {
|
||||
activity?.toast(R.string.requires_app_restart)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearChapterCache() {
|
||||
val activity = activity ?: return
|
||||
viewScope.launchNonCancellable {
|
||||
try {
|
||||
val deletedFiles = chapterCache.clear()
|
||||
withUIContext {
|
||||
activity.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
|
||||
findPreference(CLEAR_CACHE_KEY)?.summary =
|
||||
resources?.getString(R.string.used_cache, chapterCache.readableSize)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
withUIContext { activity.toast(R.string.cache_delete_error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearWebViewData() {
|
||||
val activity = activity ?: return
|
||||
try {
|
||||
WebView(activity).run {
|
||||
setDefaultSettings()
|
||||
clearCache(true)
|
||||
clearFormData()
|
||||
clearHistory()
|
||||
clearSslPreferences()
|
||||
}
|
||||
WebStorage.getInstance().deleteAllData()
|
||||
activity.applicationInfo?.dataDir?.let { File("$it/app_webview/").deleteRecursively() }
|
||||
activity.toast(R.string.webview_data_deleted)
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
activity.toast(R.string.cache_delete_error)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetViewerFlags() {
|
||||
val activity = activity ?: return
|
||||
viewScope.launchNonCancellable {
|
||||
val success = mangaRepository.resetViewerFlags()
|
||||
withUIContext {
|
||||
val message = if (success) {
|
||||
R.string.pref_reset_viewer_flags_success
|
||||
} else {
|
||||
R.string.pref_reset_viewer_flags_error
|
||||
}
|
||||
activity.toast(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val CLEAR_CACHE_KEY = "pref_clear_cache_key"
|
||||
@@ -1,173 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.AppTheme
|
||||
import eu.kanade.domain.ui.model.ThemeMode
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.initThenAdd
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.isDynamicColorAvailable
|
||||
import eu.kanade.tachiyomi.util.system.isTablet
|
||||
import eu.kanade.tachiyomi.widget.preference.ThemesPreference
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.Date
|
||||
|
||||
class SettingsAppearanceController : SettingsController() {
|
||||
|
||||
private var themesPreference: ThemesPreference? = null
|
||||
private val uiPreferences: UiPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_appearance
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_theme
|
||||
|
||||
listPreference {
|
||||
bindTo(uiPreferences.themeMode())
|
||||
titleRes = R.string.pref_theme_mode
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
entriesRes = arrayOf(
|
||||
R.string.theme_system,
|
||||
R.string.theme_light,
|
||||
R.string.theme_dark,
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
ThemeMode.SYSTEM.name,
|
||||
ThemeMode.LIGHT.name,
|
||||
ThemeMode.DARK.name,
|
||||
)
|
||||
} else {
|
||||
entriesRes = arrayOf(
|
||||
R.string.theme_light,
|
||||
R.string.theme_dark,
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
ThemeMode.LIGHT.name,
|
||||
ThemeMode.DARK.name,
|
||||
)
|
||||
}
|
||||
|
||||
summary = "%s"
|
||||
}
|
||||
themesPreference = initThenAdd(ThemesPreference(context)) {
|
||||
bindTo(uiPreferences.appTheme())
|
||||
titleRes = R.string.pref_app_theme
|
||||
|
||||
val appThemes = AppTheme.values().filter {
|
||||
val monetFilter = if (it == AppTheme.MONET) {
|
||||
DeviceUtil.isDynamicColorAvailable
|
||||
} else {
|
||||
true
|
||||
}
|
||||
it.titleResId != null && monetFilter
|
||||
}
|
||||
entries = appThemes
|
||||
|
||||
onChange {
|
||||
activity?.let { ActivityCompat.recreate(it) }
|
||||
true
|
||||
}
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.themeDarkAmoled())
|
||||
titleRes = R.string.pref_dark_theme_pure_black
|
||||
|
||||
visibleIf(uiPreferences.themeMode()) { it != ThemeMode.LIGHT }
|
||||
|
||||
onChange {
|
||||
activity?.let { ActivityCompat.recreate(it) }
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context.isTablet()) {
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_navigation
|
||||
|
||||
intListPreference {
|
||||
bindTo(uiPreferences.sideNavIconAlignment())
|
||||
titleRes = R.string.pref_side_nav_icon_alignment
|
||||
entriesRes = arrayOf(
|
||||
R.string.alignment_top,
|
||||
R.string.alignment_center,
|
||||
R.string.alignment_bottom,
|
||||
)
|
||||
entryValues = arrayOf("0", "1", "2")
|
||||
summary = "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_timestamps
|
||||
|
||||
intListPreference {
|
||||
bindTo(uiPreferences.relativeTime())
|
||||
titleRes = R.string.pref_relative_format
|
||||
val values = arrayOf("0", "2", "7")
|
||||
entryValues = values
|
||||
entries = values.map {
|
||||
when (it) {
|
||||
"0" -> context.getString(R.string.off)
|
||||
"2" -> context.getString(R.string.pref_relative_time_short)
|
||||
else -> context.getString(R.string.pref_relative_time_long)
|
||||
}
|
||||
}.toTypedArray()
|
||||
summary = "%s"
|
||||
}
|
||||
|
||||
listPreference {
|
||||
bindTo(uiPreferences.dateFormat())
|
||||
titleRes = R.string.pref_date_format
|
||||
entryValues = arrayOf("", "MM/dd/yy", "dd/MM/yy", "yyyy-MM-dd", "dd MMM yyyy", "MMM dd, yyyy")
|
||||
|
||||
val now = Date().time
|
||||
entries = entryValues.map { value ->
|
||||
val formattedDate = UiPreferences.dateFormat(value.toString()).format(now)
|
||||
if (value == "") {
|
||||
"${context.getString(R.string.label_default)} ($formattedDate)"
|
||||
} else {
|
||||
"$value ($formattedDate)"
|
||||
}
|
||||
}.toTypedArray()
|
||||
|
||||
summary = "%s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveViewState(view: View, outState: Bundle) {
|
||||
themesPreference?.let {
|
||||
outState.putInt(THEMES_SCROLL_POSITION, it.lastScrollPosition ?: 0)
|
||||
}
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onRestoreViewState(view: View, savedViewState: Bundle) {
|
||||
super.onRestoreViewState(view, savedViewState)
|
||||
themesPreference?.lastScrollPosition = savedViewState.getInt(THEMES_SCROLL_POSITION, 0)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
themesPreference = null
|
||||
}
|
||||
}
|
||||
|
||||
private const val THEMES_SCROLL_POSITION = "themesScrollPosition"
|
||||
@@ -1,306 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.domain.backup.service.BackupPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.backup.BackupConst
|
||||
import eu.kanade.tachiyomi.data.backup.BackupCreatorJob
|
||||
import eu.kanade.tachiyomi.data.backup.BackupFileValidator
|
||||
import eu.kanade.tachiyomi.data.backup.BackupRestoreService
|
||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.requestPermissionsSafe
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.infoPreference
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
import eu.kanade.tachiyomi.util.system.getParcelableCompat
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsBackupController : SettingsController() {
|
||||
|
||||
/**
|
||||
* Flags containing information of what to backup.
|
||||
*/
|
||||
private var backupFlags = 0
|
||||
|
||||
private val backupPreferences: BackupPreferences by injectLazy()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
requestPermissionsSafe(arrayOf(WRITE_EXTERNAL_STORAGE), 500)
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.label_backup
|
||||
|
||||
preference {
|
||||
key = "pref_create_backup"
|
||||
titleRes = R.string.pref_create_backup
|
||||
summaryRes = R.string.pref_create_backup_summ
|
||||
|
||||
onClick {
|
||||
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
|
||||
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
if (!BackupCreatorJob.isManualJobRunning(context)) {
|
||||
val ctrl = CreateBackupDialog()
|
||||
ctrl.targetController = this@SettingsBackupController
|
||||
ctrl.showDialog(router)
|
||||
} else {
|
||||
context.toast(R.string.backup_in_progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_restore_backup"
|
||||
titleRes = R.string.pref_restore_backup
|
||||
summaryRes = R.string.pref_restore_backup_summ
|
||||
|
||||
onClick {
|
||||
if (DeviceUtil.isMiui && DeviceUtil.isMiuiOptimizationDisabled()) {
|
||||
context.toast(R.string.restore_miui_warning, Toast.LENGTH_LONG)
|
||||
}
|
||||
|
||||
if (!BackupRestoreService.isRunning(context)) {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/*"
|
||||
}
|
||||
val title = resources?.getString(R.string.file_select_backup)
|
||||
val chooser = Intent.createChooser(intent, title)
|
||||
startActivityForResult(chooser, CODE_BACKUP_RESTORE)
|
||||
} else {
|
||||
context.toast(R.string.restore_in_progress)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_backup_service_category
|
||||
|
||||
intListPreference {
|
||||
bindTo(backupPreferences.backupInterval())
|
||||
titleRes = R.string.pref_backup_interval
|
||||
entriesRes = arrayOf(
|
||||
R.string.update_6hour,
|
||||
R.string.update_12hour,
|
||||
R.string.update_24hour,
|
||||
R.string.update_48hour,
|
||||
R.string.update_weekly,
|
||||
)
|
||||
entryValues = arrayOf("6", "12", "24", "48", "168")
|
||||
summary = "%s"
|
||||
|
||||
onChange { newValue ->
|
||||
val interval = (newValue as String).toInt()
|
||||
BackupCreatorJob.setupTask(context, interval)
|
||||
true
|
||||
}
|
||||
}
|
||||
preference {
|
||||
bindTo(backupPreferences.backupsDirectory())
|
||||
titleRes = R.string.pref_backup_directory
|
||||
|
||||
onClick {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
startActivityForResult(intent, CODE_BACKUP_DIR)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
activity?.toast(R.string.file_picker_error)
|
||||
}
|
||||
}
|
||||
|
||||
backupPreferences.backupsDirectory().changes()
|
||||
.onEach { path ->
|
||||
val dir = UniFile.fromUri(context, path.toUri())
|
||||
summary = dir.filePath + "/automatic"
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(backupPreferences.numberOfBackups())
|
||||
titleRes = R.string.pref_backup_slots
|
||||
entries = arrayOf("2", "3", "4", "5")
|
||||
entryValues = entries
|
||||
summary = "%s"
|
||||
}
|
||||
}
|
||||
|
||||
infoPreference(R.string.backup_info)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.settings_backup, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_backup_help -> activity?.openInBrowser(HELP_URL)
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (data != null && resultCode == Activity.RESULT_OK) {
|
||||
val activity = activity ?: return
|
||||
val uri = data.data
|
||||
|
||||
if (uri == null) {
|
||||
activity.toast(R.string.backup_restore_invalid_uri)
|
||||
return
|
||||
}
|
||||
|
||||
when (requestCode) {
|
||||
CODE_BACKUP_DIR -> {
|
||||
// Get UriPermission so it's possible to write files
|
||||
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
|
||||
activity.contentResolver.takePersistableUriPermission(uri, flags)
|
||||
backupPreferences.backupsDirectory().set(uri.toString())
|
||||
}
|
||||
CODE_BACKUP_CREATE -> {
|
||||
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
|
||||
activity.contentResolver.takePersistableUriPermission(uri, flags)
|
||||
BackupCreatorJob.startNow(activity, uri, backupFlags)
|
||||
}
|
||||
CODE_BACKUP_RESTORE -> {
|
||||
RestoreBackupDialog(uri).showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createBackup(flags: Int) {
|
||||
backupFlags = flags
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
.setType("application/*")
|
||||
.putExtra(Intent.EXTRA_TITLE, Backup.getBackupFilename())
|
||||
|
||||
startActivityForResult(intent, CODE_BACKUP_CREATE)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
activity?.toast(R.string.file_picker_error)
|
||||
}
|
||||
}
|
||||
|
||||
class CreateBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val activity = activity!!
|
||||
val options = arrayOf(
|
||||
R.string.manga,
|
||||
R.string.categories,
|
||||
R.string.chapters,
|
||||
R.string.track,
|
||||
R.string.history,
|
||||
)
|
||||
.map { activity.getString(it) }
|
||||
val selected = options.map { true }.toBooleanArray()
|
||||
|
||||
return MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.backup_choice)
|
||||
.setMultiChoiceItems(options.toTypedArray(), selected) { dialog, which, checked ->
|
||||
if (which == 0) {
|
||||
(dialog as AlertDialog).listView.setItemChecked(which, true)
|
||||
} else {
|
||||
selected[which] = checked
|
||||
}
|
||||
}
|
||||
.setPositiveButton(R.string.action_create) { _, _ ->
|
||||
var flags = 0
|
||||
selected.forEachIndexed { i, checked ->
|
||||
if (checked) {
|
||||
when (i) {
|
||||
1 -> flags = flags or BackupConst.BACKUP_CATEGORY
|
||||
2 -> flags = flags or BackupConst.BACKUP_CHAPTER
|
||||
3 -> flags = flags or BackupConst.BACKUP_TRACK
|
||||
4 -> flags = flags or BackupConst.BACKUP_HISTORY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(targetController as? SettingsBackupController)?.createBackup(flags)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
class RestoreBackupDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
constructor(uri: Uri) : this(
|
||||
bundleOf(KEY_URI to uri),
|
||||
)
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val activity = activity!!
|
||||
val uri = args.getParcelableCompat<Uri>(KEY_URI)!!
|
||||
|
||||
return try {
|
||||
val results = BackupFileValidator().validate(activity, uri)
|
||||
|
||||
var message = activity.getString(R.string.backup_restore_content_full)
|
||||
if (results.missingSources.isNotEmpty()) {
|
||||
message += "\n\n${activity.getString(R.string.backup_restore_missing_sources)}\n${results.missingSources.joinToString("\n") { "- $it" }}"
|
||||
}
|
||||
if (results.missingTrackers.isNotEmpty()) {
|
||||
message += "\n\n${activity.getString(R.string.backup_restore_missing_trackers)}\n${results.missingTrackers.joinToString("\n") { "- $it" }}"
|
||||
}
|
||||
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.pref_restore_backup)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.action_restore) { _, _ ->
|
||||
BackupRestoreService.start(activity, uri)
|
||||
}
|
||||
.create()
|
||||
} catch (e: Exception) {
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.invalid_backup_file)
|
||||
.setMessage(e.message)
|
||||
.setPositiveButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val KEY_URI = "RestoreBackupDialog.uri"
|
||||
|
||||
private const val CODE_BACKUP_DIR = 503
|
||||
private const val CODE_BACKUP_CREATE = 504
|
||||
private const val CODE_BACKUP_RESTORE = 505
|
||||
|
||||
private const val HELP_URL = "https://tachiyomi.org/help/guides/backups/"
|
||||
@@ -1,80 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.infoPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.requireAuthentication
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsBrowseController : SettingsController() {
|
||||
|
||||
private val sourcePreferences: SourcePreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.browse
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_sources
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.duplicatePinnedSources())
|
||||
titleRes = R.string.pref_duplicate_pinned_sources
|
||||
summaryRes = R.string.pref_duplicate_pinned_sources_summary
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_extensions
|
||||
|
||||
switchPreference {
|
||||
bindTo(preferences.automaticExtUpdates())
|
||||
titleRes = R.string.pref_enable_automatic_extension_updates
|
||||
|
||||
onChange { newValue ->
|
||||
val checked = newValue as Boolean
|
||||
ExtensionUpdateJob.setupTask(activity!!, checked)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.action_global_search
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.searchPinnedSourcesOnly())
|
||||
titleRes = R.string.pref_search_pinned_sources_only
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_nsfw_content
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.showNsfwSource())
|
||||
titleRes = R.string.pref_show_nsfw_source
|
||||
summaryRes = R.string.requires_app_restart
|
||||
|
||||
if (context.isAuthenticationSupported() && activity != null) {
|
||||
requireAuthentication(
|
||||
activity as? FragmentActivity,
|
||||
context.getString(R.string.pref_category_nsfw_content),
|
||||
context.getString(R.string.confirm_lock_change),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
infoPreference(R.string.parental_controls_info)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.core.animation.doOnEnd
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceController
|
||||
import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.preference.asHotFlow
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
abstract class SettingsController : PreferenceController() {
|
||||
|
||||
var preferenceKey: String? = null
|
||||
val preferences: BasePreferences = Injekt.get()
|
||||
val viewScope: CoroutineScope = MainScope()
|
||||
private var themedContext: Context? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup, savedInstanceState: Bundle?): View {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
||||
listView.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
padding()
|
||||
}
|
||||
}
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onAttach(view: View) {
|
||||
super.onAttach(view)
|
||||
|
||||
preferenceKey?.let { prefKey ->
|
||||
val adapter = listView.adapter
|
||||
scrollToPreference(prefKey)
|
||||
|
||||
listView.post {
|
||||
if (adapter is PreferenceGroup.PreferencePositionCallback) {
|
||||
val pos = adapter.getPreferenceAdapterPosition(prefKey)
|
||||
listView.findViewHolderForAdapterPosition(pos)?.let {
|
||||
animatePreferenceHighlight(it.itemView)
|
||||
}
|
||||
}
|
||||
|
||||
// Explicitly clear it to avoid re-scrolling/animating on activity recreations
|
||||
preferenceKey = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
if (type.isEnter) {
|
||||
setTitle()
|
||||
}
|
||||
setHasOptionsMenu(type.isEnter)
|
||||
super.onChangeStarted(handler, type)
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
super.onDestroyView(view)
|
||||
viewScope.cancel()
|
||||
themedContext = null
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
val tv = TypedValue()
|
||||
activity!!.theme.resolveAttribute(R.attr.preferenceTheme, tv, true)
|
||||
themedContext = ContextThemeWrapper(activity, tv.resourceId)
|
||||
|
||||
val screen = preferenceManager.createPreferenceScreen(themedContext!!)
|
||||
preferenceScreen = screen
|
||||
setupPreferenceScreen(screen)
|
||||
}
|
||||
|
||||
abstract fun setupPreferenceScreen(screen: PreferenceScreen): PreferenceScreen
|
||||
|
||||
private fun animatePreferenceHighlight(view: View) {
|
||||
val origBackground = view.background
|
||||
ValueAnimator
|
||||
.ofObject(ArgbEvaluator(), Color.TRANSPARENT, view.context.getResourceColor(R.attr.colorControlHighlight))
|
||||
.apply {
|
||||
duration = 200L
|
||||
repeatCount = 5
|
||||
repeatMode = ValueAnimator.REVERSE
|
||||
addUpdateListener { animator -> view.setBackgroundColor(animator.animatedValue as Int) }
|
||||
start()
|
||||
}
|
||||
.doOnEnd {
|
||||
// Restore original ripple
|
||||
view.background = origBackground
|
||||
}
|
||||
}
|
||||
|
||||
private fun setTitle() {
|
||||
(activity as? AppCompatActivity)?.supportActionBar?.title = preferenceScreen?.title?.toString()
|
||||
}
|
||||
|
||||
inline fun <T> Preference.visibleIf(preference: eu.kanade.tachiyomi.core.preference.Preference<T>, crossinline block: (T) -> Boolean) {
|
||||
preference.asHotFlow { isVisible = block(it) }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
}
|
||||
@@ -1,315 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.domain.category.interactor.GetCategories
|
||||
import eu.kanade.domain.download.service.DownloadPreferences
|
||||
import eu.kanade.presentation.category.visualName
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.infoPreference
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.multiSelectListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.File
|
||||
|
||||
class SettingsDownloadController : SettingsController() {
|
||||
|
||||
private val getCategories: GetCategories by injectLazy()
|
||||
private val downloadPreferences: DownloadPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_downloads
|
||||
|
||||
val categories = runBlocking { getCategories.await() }
|
||||
|
||||
preference {
|
||||
bindTo(downloadPreferences.downloadsDirectory())
|
||||
titleRes = R.string.pref_download_directory
|
||||
onClick {
|
||||
val ctrl = DownloadDirectoriesDialog()
|
||||
ctrl.targetController = this@SettingsDownloadController
|
||||
ctrl.showDialog(router)
|
||||
}
|
||||
|
||||
downloadPreferences.downloadsDirectory().changes()
|
||||
.onEach { path ->
|
||||
val dir = UniFile.fromUri(context, path.toUri())
|
||||
summary = dir.filePath ?: path
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(downloadPreferences.downloadOnlyOverWifi())
|
||||
titleRes = R.string.connected_to_wifi
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(downloadPreferences.saveChaptersAsCBZ())
|
||||
titleRes = R.string.save_chapter_as_cbz
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(downloadPreferences.splitTallImages())
|
||||
titleRes = R.string.split_tall_images
|
||||
summaryRes = R.string.split_tall_images_summary
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_delete_chapters
|
||||
|
||||
switchPreference {
|
||||
bindTo(downloadPreferences.removeAfterMarkedAsRead())
|
||||
titleRes = R.string.pref_remove_after_marked_as_read
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(downloadPreferences.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")
|
||||
summary = "%s"
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(downloadPreferences.removeBookmarkedChapters())
|
||||
titleRes = R.string.pref_remove_bookmarked_chapters
|
||||
}
|
||||
multiSelectListPreference {
|
||||
bindTo(downloadPreferences.removeExcludeCategories())
|
||||
titleRes = R.string.pref_remove_exclude_categories
|
||||
entries = categories.map { it.visualName(context) }.toTypedArray()
|
||||
entryValues = categories.map { it.id.toString() }.toTypedArray()
|
||||
|
||||
downloadPreferences.removeExcludeCategories().changes()
|
||||
.onEach { mutable ->
|
||||
val selected = mutable
|
||||
.mapNotNull { id -> categories.find { it.id == id.toLong() } }
|
||||
.sortedBy { it.order }
|
||||
|
||||
summary = if (selected.isEmpty()) {
|
||||
resources?.getString(R.string.none)
|
||||
} else {
|
||||
selected.joinToString { it.visualName(context) }
|
||||
}
|
||||
}.launchIn(viewScope)
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_download_new
|
||||
|
||||
switchPreference {
|
||||
bindTo(downloadPreferences.downloadNewChapters())
|
||||
titleRes = R.string.pref_download_new
|
||||
}
|
||||
preference {
|
||||
bindTo(downloadPreferences.downloadNewChapterCategories())
|
||||
titleRes = R.string.categories
|
||||
onClick {
|
||||
DownloadCategoriesDialog().showDialog(router)
|
||||
}
|
||||
|
||||
visibleIf(downloadPreferences.downloadNewChapters()) { it }
|
||||
|
||||
fun updateSummary() {
|
||||
val selectedCategories = downloadPreferences.downloadNewChapterCategories().get()
|
||||
.mapNotNull { id -> categories.find { it.id == id.toLong() } }
|
||||
.sortedBy { it.order }
|
||||
val includedItemsText = if (selectedCategories.isEmpty()) {
|
||||
context.getString(R.string.all)
|
||||
} else {
|
||||
selectedCategories.joinToString { it.visualName(context) }
|
||||
}
|
||||
|
||||
val excludedCategories = downloadPreferences.downloadNewChapterCategoriesExclude().get()
|
||||
.mapNotNull { id -> categories.find { it.id == id.toLong() } }
|
||||
.sortedBy { it.order }
|
||||
val excludedItemsText = if (excludedCategories.isEmpty()) {
|
||||
context.getString(R.string.none)
|
||||
} else {
|
||||
excludedCategories.joinToString { it.visualName(context) }
|
||||
}
|
||||
|
||||
summary = buildSpannedString {
|
||||
append(context.getString(R.string.include, includedItemsText))
|
||||
appendLine()
|
||||
append(context.getString(R.string.exclude, excludedItemsText))
|
||||
}
|
||||
}
|
||||
|
||||
downloadPreferences.downloadNewChapterCategories().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
downloadPreferences.downloadNewChapterCategoriesExclude().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.download_ahead
|
||||
|
||||
intListPreference {
|
||||
bindTo(downloadPreferences.autoDownloadWhileReading())
|
||||
titleRes = R.string.auto_download_while_reading
|
||||
entries = arrayOf(
|
||||
context.getString(R.string.disabled),
|
||||
context.resources.getQuantityString(R.plurals.next_unread_chapters, 2, 2),
|
||||
context.resources.getQuantityString(R.plurals.next_unread_chapters, 3, 3),
|
||||
context.resources.getQuantityString(R.plurals.next_unread_chapters, 5, 5),
|
||||
context.resources.getQuantityString(R.plurals.next_unread_chapters, 10, 10),
|
||||
)
|
||||
entryValues = arrayOf("0", "2", "3", "5", "10")
|
||||
summary = "%s"
|
||||
}
|
||||
infoPreference(R.string.download_ahead_info)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
when (requestCode) {
|
||||
DOWNLOAD_DIR -> 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
|
||||
|
||||
if (uri != null) {
|
||||
@Suppress("NewApi")
|
||||
context.contentResolver.takePersistableUriPermission(uri, flags)
|
||||
}
|
||||
|
||||
val file = UniFile.fromUri(context, uri)
|
||||
downloadPreferences.downloadsDirectory().set(file.uri.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun predefinedDirectorySelected(selectedDir: String) {
|
||||
val path = File(selectedDir).toUri()
|
||||
downloadPreferences.downloadsDirectory().set(path.toString())
|
||||
}
|
||||
|
||||
fun customDirectorySelected() {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
try {
|
||||
startActivityForResult(intent, DOWNLOAD_DIR)
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
activity?.toast(R.string.file_picker_error)
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadDirectoriesDialog : DialogController() {
|
||||
|
||||
private val downloadPreferences: DownloadPreferences = Injekt.get()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val activity = activity!!
|
||||
val currentDir = downloadPreferences.downloadsDirectory().get()
|
||||
val externalDirs = listOf(getDefaultDownloadDir(), File(activity.getString(R.string.custom_dir))).map(File::toString)
|
||||
var selectedIndex = externalDirs.indexOfFirst { it in currentDir }
|
||||
|
||||
return MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.pref_download_directory)
|
||||
.setSingleChoiceItems(externalDirs.toTypedArray(), selectedIndex) { _, which ->
|
||||
selectedIndex = which
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val target = targetController as? SettingsDownloadController
|
||||
if (selectedIndex == externalDirs.lastIndex) {
|
||||
target?.customDirectorySelected()
|
||||
} else {
|
||||
target?.predefinedDirectorySelected(externalDirs[selectedIndex])
|
||||
}
|
||||
}
|
||||
.create()
|
||||
}
|
||||
|
||||
private fun getDefaultDownloadDir(): File {
|
||||
val defaultDir = Environment.getExternalStorageDirectory().absolutePath +
|
||||
File.separator + resources?.getString(R.string.app_name) +
|
||||
File.separator + "downloads"
|
||||
|
||||
return File(defaultDir)
|
||||
}
|
||||
}
|
||||
|
||||
class DownloadCategoriesDialog : DialogController() {
|
||||
|
||||
private val downloadPreferences: DownloadPreferences = Injekt.get()
|
||||
private val getCategories: GetCategories = Injekt.get()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val categories = runBlocking { getCategories.await() }
|
||||
|
||||
val items = categories.map { it.visualName(activity!!) }
|
||||
var selected = categories
|
||||
.map {
|
||||
when (it.id.toString()) {
|
||||
in downloadPreferences.downloadNewChapterCategories().get() -> QuadStateTextView.State.CHECKED.ordinal
|
||||
in downloadPreferences.downloadNewChapterCategoriesExclude().get() -> QuadStateTextView.State.INVERSED.ordinal
|
||||
else -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
}
|
||||
}
|
||||
.toIntArray()
|
||||
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.categories)
|
||||
.setQuadStateMultiChoiceItems(
|
||||
message = R.string.pref_download_new_categories_details,
|
||||
items = items,
|
||||
initialSelected = selected,
|
||||
) { selections ->
|
||||
selected = selections
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val included = selected
|
||||
.mapIndexed { index, value -> if (value == QuadStateTextView.State.CHECKED.ordinal) index else null }
|
||||
.filterNotNull()
|
||||
.map { categories[it].id.toString() }
|
||||
.toSet()
|
||||
val excluded = selected
|
||||
.mapIndexed { index, value -> if (value == QuadStateTextView.State.INVERSED.ordinal) index else null }
|
||||
.filterNotNull()
|
||||
.map { categories[it].id.toString() }
|
||||
.toSet()
|
||||
|
||||
downloadPreferences.downloadNewChapterCategories().set(included)
|
||||
downloadPreferences.downloadNewChapterCategoriesExclude().set(excluded)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val DOWNLOAD_DIR = 104
|
||||
@@ -1,92 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.provider.Settings
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsGeneralController : SettingsController() {
|
||||
|
||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_general
|
||||
|
||||
switchPreference {
|
||||
bindTo(libraryPreferences.showUpdatesNavBadge())
|
||||
titleRes = R.string.pref_library_update_show_tab_badge
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(preferences.confirmExit())
|
||||
titleRes = R.string.pref_confirm_exit
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
preference {
|
||||
key = "pref_manage_notifications"
|
||||
titleRes = R.string.pref_manage_notifications
|
||||
onClick {
|
||||
val intent = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
|
||||
putExtra(Settings.EXTRA_APP_PACKAGE, context.packageName)
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
}
|
||||
listPreference {
|
||||
key = "app_lang"
|
||||
isPersistent = false
|
||||
titleRes = R.string.pref_app_language
|
||||
|
||||
val langs = mutableListOf<Pair<String, String>>()
|
||||
|
||||
val parser = context.resources.getXml(R.xml.locales_config)
|
||||
var eventType = parser.eventType
|
||||
while (eventType != XmlPullParser.END_DOCUMENT) {
|
||||
if (eventType == XmlPullParser.START_TAG && parser.name == "locale") {
|
||||
for (i in 0 until parser.attributeCount) {
|
||||
if (parser.getAttributeName(i) == "name") {
|
||||
val langTag = parser.getAttributeValue(i)
|
||||
val displayName = LocaleHelper.getDisplayName(langTag)
|
||||
if (displayName.isNotEmpty()) {
|
||||
langs.add(Pair(langTag, displayName))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
eventType = parser.next()
|
||||
}
|
||||
|
||||
langs.sortBy { it.second }
|
||||
langs.add(0, Pair("", context.getString(R.string.label_default)))
|
||||
|
||||
entryValues = langs.map { it.first }.toTypedArray()
|
||||
entries = langs.map { it.second }.toTypedArray()
|
||||
summary = "%s"
|
||||
value = AppCompatDelegate.getApplicationLocales().get(0)?.toLanguageTag() ?: ""
|
||||
|
||||
onChange { newValue ->
|
||||
val locale = if ((newValue as String).isEmpty()) {
|
||||
LocaleListCompat.getEmptyLocaleList()
|
||||
} else {
|
||||
LocaleListCompat.forLanguageTags(newValue)
|
||||
}
|
||||
AppCompatDelegate.setApplicationLocales(locale)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,388 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.text.buildSpannedString
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.category.interactor.GetCategories
|
||||
import eu.kanade.domain.category.interactor.ResetCategoryFlags
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.presentation.category.visualName
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_BATTERY_NOT_LOW
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_NETWORK_NOT_METERED
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_HAS_UNREAD
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_NON_COMPLETED
|
||||
import eu.kanade.tachiyomi.data.preference.MANGA_NON_READ
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.databinding.PrefLibraryColumnsBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryController
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.defaultValue
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.multiSelectListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onChange
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsLibraryController : SettingsController() {
|
||||
|
||||
private val getCategories: GetCategories by injectLazy()
|
||||
private val trackManager: TrackManager by injectLazy()
|
||||
private val resetCategoryFlags: ResetCategoryFlags by injectLazy()
|
||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_library
|
||||
|
||||
val allCategories = runBlocking { getCategories.await() }
|
||||
val userCategories = allCategories.filterNot(Category::isSystemCategory)
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_display
|
||||
|
||||
preference {
|
||||
key = "pref_library_columns"
|
||||
titleRes = R.string.pref_library_columns
|
||||
onClick {
|
||||
LibraryColumnsDialog().showDialog(router)
|
||||
}
|
||||
|
||||
fun getColumnValue(value: Int): String {
|
||||
return if (value == 0) {
|
||||
context.getString(R.string.label_default)
|
||||
} else {
|
||||
value.toString()
|
||||
}
|
||||
}
|
||||
|
||||
combine(libraryPreferences.portraitColumns().changes(), libraryPreferences.landscapeColumns().changes()) { portraitCols, landscapeCols -> Pair(portraitCols, landscapeCols) }
|
||||
.onEach { (portraitCols, landscapeCols) ->
|
||||
val portrait = getColumnValue(portraitCols)
|
||||
val landscape = getColumnValue(landscapeCols)
|
||||
summary = "${context.getString(R.string.portrait)}: $portrait, " +
|
||||
"${context.getString(R.string.landscape)}: $landscape"
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.categories
|
||||
|
||||
preference {
|
||||
key = "pref_action_edit_categories"
|
||||
titleRes = R.string.action_edit_categories
|
||||
|
||||
val catCount = userCategories.size
|
||||
summary = context.resources.getQuantityString(R.plurals.num_categories, catCount, catCount)
|
||||
|
||||
onClick {
|
||||
router.pushController(CategoryController())
|
||||
}
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
val defaultCategory = libraryPreferences.defaultCategory()
|
||||
bindTo(defaultCategory)
|
||||
titleRes = R.string.default_category
|
||||
|
||||
entries = arrayOf(context.getString(R.string.default_category_summary)) +
|
||||
allCategories.map { it.visualName(context) }.toTypedArray()
|
||||
entryValues = arrayOf(defaultCategory.defaultValue().toString()) + allCategories.map { it.id.toString() }.toTypedArray()
|
||||
|
||||
val selectedCategory = allCategories.find { it.id == defaultCategory.get().toLong() }
|
||||
summary = selectedCategory?.visualName(context)
|
||||
?: context.getString(R.string.default_category_summary)
|
||||
onChange { newValue ->
|
||||
summary = allCategories.find {
|
||||
it.id == (newValue as String).toLong()
|
||||
}?.visualName(context) ?: context.getString(R.string.default_category_summary)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(libraryPreferences.categorizedDisplaySettings())
|
||||
titleRes = R.string.categorized_display_settings
|
||||
|
||||
libraryPreferences.categorizedDisplaySettings().changes()
|
||||
.onEach {
|
||||
if (it.not()) {
|
||||
resetCategoryFlags.await()
|
||||
}
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_library_update
|
||||
|
||||
intListPreference {
|
||||
bindTo(libraryPreferences.libraryUpdateInterval())
|
||||
titleRes = R.string.pref_library_update_interval
|
||||
entriesRes = arrayOf(
|
||||
R.string.update_never,
|
||||
R.string.update_12hour,
|
||||
R.string.update_24hour,
|
||||
R.string.update_48hour,
|
||||
R.string.update_72hour,
|
||||
R.string.update_weekly,
|
||||
)
|
||||
entryValues = arrayOf("0", "12", "24", "48", "72", "168")
|
||||
summary = "%s"
|
||||
|
||||
onChange { newValue ->
|
||||
val interval = (newValue as String).toInt()
|
||||
LibraryUpdateJob.setupTask(context, interval)
|
||||
true
|
||||
}
|
||||
}
|
||||
multiSelectListPreference {
|
||||
bindTo(libraryPreferences.libraryUpdateDeviceRestriction())
|
||||
titleRes = R.string.pref_library_update_restriction
|
||||
entriesRes = arrayOf(R.string.connected_to_wifi, R.string.network_not_metered, R.string.charging, R.string.battery_not_low)
|
||||
entryValues = arrayOf(DEVICE_ONLY_ON_WIFI, DEVICE_NETWORK_NOT_METERED, DEVICE_CHARGING, DEVICE_BATTERY_NOT_LOW)
|
||||
|
||||
visibleIf(libraryPreferences.libraryUpdateInterval()) { it > 0 }
|
||||
|
||||
onChange {
|
||||
// Post to event looper to allow the preference to be updated.
|
||||
ContextCompat.getMainExecutor(context).execute { LibraryUpdateJob.setupTask(context) }
|
||||
true
|
||||
}
|
||||
|
||||
fun updateSummary() {
|
||||
val restrictions = libraryPreferences.libraryUpdateDeviceRestriction().get()
|
||||
.sorted()
|
||||
.map {
|
||||
when (it) {
|
||||
DEVICE_ONLY_ON_WIFI -> context.getString(R.string.connected_to_wifi)
|
||||
DEVICE_NETWORK_NOT_METERED -> context.getString(R.string.network_not_metered)
|
||||
DEVICE_CHARGING -> context.getString(R.string.charging)
|
||||
DEVICE_BATTERY_NOT_LOW -> context.getString(R.string.battery_not_low)
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
val restrictionsText = if (restrictions.isEmpty()) {
|
||||
context.getString(R.string.none)
|
||||
} else {
|
||||
restrictions.joinToString()
|
||||
}
|
||||
|
||||
summary = context.getString(R.string.restrictions, restrictionsText)
|
||||
}
|
||||
|
||||
libraryPreferences.libraryUpdateDeviceRestriction().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
multiSelectListPreference {
|
||||
bindTo(libraryPreferences.libraryUpdateMangaRestriction())
|
||||
titleRes = R.string.pref_library_update_manga_restriction
|
||||
entriesRes = arrayOf(R.string.pref_update_only_completely_read, R.string.pref_update_only_started, R.string.pref_update_only_non_completed)
|
||||
entryValues = arrayOf(MANGA_HAS_UNREAD, MANGA_NON_READ, MANGA_NON_COMPLETED)
|
||||
|
||||
fun updateSummary() {
|
||||
val restrictions = libraryPreferences.libraryUpdateMangaRestriction().get().sorted()
|
||||
.map {
|
||||
when (it) {
|
||||
MANGA_NON_READ -> context.getString(R.string.pref_update_only_started)
|
||||
MANGA_HAS_UNREAD -> context.getString(R.string.pref_update_only_completely_read)
|
||||
MANGA_NON_COMPLETED -> context.getString(R.string.pref_update_only_non_completed)
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
val restrictionsText = if (restrictions.isEmpty()) {
|
||||
context.getString(R.string.none)
|
||||
} else {
|
||||
restrictions.joinToString()
|
||||
}
|
||||
|
||||
summary = restrictionsText
|
||||
}
|
||||
|
||||
libraryPreferences.libraryUpdateMangaRestriction().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
preference {
|
||||
bindTo(libraryPreferences.libraryUpdateCategories())
|
||||
titleRes = R.string.categories
|
||||
|
||||
onClick {
|
||||
LibraryGlobalUpdateCategoriesDialog().showDialog(router)
|
||||
}
|
||||
|
||||
fun updateSummary() {
|
||||
val includedCategories = libraryPreferences.libraryUpdateCategories().get()
|
||||
.mapNotNull { id -> allCategories.find { it.id == id.toLong() } }
|
||||
.sortedBy { it.order }
|
||||
val excludedCategories = libraryPreferences.libraryUpdateCategoriesExclude().get()
|
||||
.mapNotNull { id -> allCategories.find { it.id == id.toLong() } }
|
||||
.sortedBy { it.order }
|
||||
|
||||
val allExcluded = excludedCategories.size == allCategories.size
|
||||
|
||||
val includedItemsText = when {
|
||||
// Some selected, but not all
|
||||
includedCategories.isNotEmpty() && includedCategories.size != allCategories.size -> includedCategories.joinToString { it.visualName(context) }
|
||||
// All explicitly selected
|
||||
includedCategories.size == allCategories.size -> context.getString(R.string.all)
|
||||
allExcluded -> context.getString(R.string.none)
|
||||
else -> context.getString(R.string.all)
|
||||
}
|
||||
val excludedItemsText = when {
|
||||
excludedCategories.isEmpty() -> context.getString(R.string.none)
|
||||
allExcluded -> context.getString(R.string.all)
|
||||
else -> excludedCategories.joinToString { it.visualName(context) }
|
||||
}
|
||||
|
||||
summary = buildSpannedString {
|
||||
append(context.getString(R.string.include, includedItemsText))
|
||||
appendLine()
|
||||
append(context.getString(R.string.exclude, excludedItemsText))
|
||||
}
|
||||
}
|
||||
|
||||
libraryPreferences.libraryUpdateCategories().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
libraryPreferences.libraryUpdateCategoriesExclude().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(libraryPreferences.autoUpdateMetadata())
|
||||
titleRes = R.string.pref_library_update_refresh_metadata
|
||||
summaryRes = R.string.pref_library_update_refresh_metadata_summary
|
||||
}
|
||||
if (trackManager.hasLoggedServices()) {
|
||||
switchPreference {
|
||||
bindTo(libraryPreferences.autoUpdateTrackers())
|
||||
titleRes = R.string.pref_library_update_refresh_trackers
|
||||
summaryRes = R.string.pref_library_update_refresh_trackers_summary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryColumnsDialog : DialogController() {
|
||||
|
||||
private val preferences: LibraryPreferences = Injekt.get()
|
||||
|
||||
private var portrait = preferences.portraitColumns().get()
|
||||
private var landscape = preferences.landscapeColumns().get()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val binding = PrefLibraryColumnsBinding.inflate(LayoutInflater.from(activity!!))
|
||||
onViewCreated(binding)
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.pref_library_columns)
|
||||
.setView(binding.root)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
preferences.portraitColumns().set(portrait)
|
||||
preferences.landscapeColumns().set(landscape)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
fun onViewCreated(binding: PrefLibraryColumnsBinding) {
|
||||
with(binding.portraitColumns) {
|
||||
displayedValues = arrayOf(context.getString(R.string.label_default)) +
|
||||
IntRange(1, 10).map(Int::toString)
|
||||
value = portrait
|
||||
|
||||
setOnValueChangedListener { _, _, newValue ->
|
||||
portrait = newValue
|
||||
}
|
||||
}
|
||||
with(binding.landscapeColumns) {
|
||||
displayedValues = arrayOf(context.getString(R.string.label_default)) +
|
||||
IntRange(1, 10).map(Int::toString)
|
||||
value = landscape
|
||||
|
||||
setOnValueChangedListener { _, _, newValue ->
|
||||
landscape = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryGlobalUpdateCategoriesDialog : DialogController() {
|
||||
|
||||
private val preferences: LibraryPreferences = Injekt.get()
|
||||
private val getCategories: GetCategories = Injekt.get()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val categories = runBlocking { getCategories.await() }
|
||||
|
||||
val items = categories.map { it.visualName(activity!!) }
|
||||
var selected = categories
|
||||
.map {
|
||||
when (it.id.toString()) {
|
||||
in preferences.libraryUpdateCategories()
|
||||
.get(),
|
||||
-> QuadStateTextView.State.CHECKED.ordinal
|
||||
in preferences.libraryUpdateCategoriesExclude()
|
||||
.get(),
|
||||
-> QuadStateTextView.State.INVERSED.ordinal
|
||||
else -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
}
|
||||
}
|
||||
.toIntArray()
|
||||
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.categories)
|
||||
.setQuadStateMultiChoiceItems(
|
||||
message = R.string.pref_library_update_categories_details,
|
||||
items = items,
|
||||
initialSelected = selected,
|
||||
) { selections ->
|
||||
selected = selections
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val included = selected
|
||||
.mapIndexed { index, value -> if (value == QuadStateTextView.State.CHECKED.ordinal) index else null }
|
||||
.filterNotNull()
|
||||
.map { categories[it].id.toString() }
|
||||
.toSet()
|
||||
val excluded = selected
|
||||
.mapIndexed { index, value -> if (value == QuadStateTextView.State.INVERSED.ordinal) index else null }
|
||||
.filterNotNull()
|
||||
.map { categories[it].id.toString() }
|
||||
.toSet()
|
||||
|
||||
preferences.libraryUpdateCategories().set(included)
|
||||
preferences.libraryUpdateCategoriesExclude().set(excluded)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,325 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.os.Build
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues
|
||||
import eu.kanade.tachiyomi.data.preference.PreferenceValues.TappingInvertMode
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.hasDisplayCutout
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsReaderController : SettingsController() {
|
||||
|
||||
private val readerPreferences: ReaderPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_reader
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.defaultReadingMode())
|
||||
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,
|
||||
R.string.vertical_plus_viewer,
|
||||
)
|
||||
entryValues = ReadingModeType.values().drop(1)
|
||||
.map { value -> "${value.flagValue}" }.toTypedArray()
|
||||
summary = "%s"
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.doubleTapAnimSpeed())
|
||||
titleRes = R.string.pref_double_tap_anim_speed
|
||||
entries = arrayOf(context.getString(R.string.double_tap_anim_speed_0), context.getString(R.string.double_tap_anim_speed_normal), context.getString(R.string.double_tap_anim_speed_fast))
|
||||
entryValues = arrayOf("1", "500", "250") // using a value of 0 breaks the image viewer, so min is 1
|
||||
summary = "%s"
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.showReadingMode())
|
||||
titleRes = R.string.pref_show_reading_mode
|
||||
summaryRes = R.string.pref_show_reading_mode_summary
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.showNavigationOverlayOnStart())
|
||||
titleRes = R.string.pref_show_navigation_mode
|
||||
summaryRes = R.string.pref_show_navigation_mode_summary
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.trueColor())
|
||||
titleRes = R.string.pref_true_color
|
||||
summaryRes = R.string.pref_true_color_summary
|
||||
}
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.pageTransitions())
|
||||
titleRes = R.string.pref_page_transitions
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_display
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.defaultOrientationType())
|
||||
titleRes = R.string.pref_rotation_type
|
||||
entriesRes = arrayOf(
|
||||
R.string.rotation_free,
|
||||
R.string.rotation_portrait,
|
||||
R.string.rotation_reverse_portrait,
|
||||
R.string.rotation_landscape,
|
||||
R.string.rotation_force_portrait,
|
||||
R.string.rotation_force_landscape,
|
||||
)
|
||||
entryValues = OrientationType.values().drop(1)
|
||||
.map { value -> "${value.flagValue}" }.toTypedArray()
|
||||
summary = "%s"
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.readerTheme())
|
||||
titleRes = R.string.pref_reader_theme
|
||||
entriesRes = arrayOf(R.string.black_background, R.string.gray_background, R.string.white_background, R.string.automatic_background)
|
||||
entryValues = arrayOf("1", "2", "0", "3")
|
||||
summary = "%s"
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.fullscreen())
|
||||
titleRes = R.string.pref_fullscreen
|
||||
}
|
||||
|
||||
if (activity?.hasDisplayCutout() == true) {
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.cutoutShort())
|
||||
titleRes = R.string.pref_cutout_short
|
||||
|
||||
visibleIf(readerPreferences.fullscreen()) { it }
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.keepScreenOn())
|
||||
titleRes = R.string.pref_keep_screen_on
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.showPageNumber())
|
||||
titleRes = R.string.pref_show_page_number
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_reading
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.skipRead())
|
||||
titleRes = R.string.pref_skip_read_chapters
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.skipFiltered())
|
||||
titleRes = R.string.pref_skip_filtered_chapters
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.alwaysShowChapterTransition())
|
||||
titleRes = R.string.pref_always_show_chapter_transition
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pager_viewer
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.navigationModePager())
|
||||
titleRes = R.string.pref_viewer_nav
|
||||
entries = context.resources.getStringArray(R.array.pager_nav).also { values ->
|
||||
entryValues = values.indices.map { index -> "$index" }.toTypedArray()
|
||||
}
|
||||
summary = "%s"
|
||||
}
|
||||
listPreference {
|
||||
bindTo(readerPreferences.pagerNavInverted())
|
||||
titleRes = R.string.pref_read_with_tapping_inverted
|
||||
entriesRes = arrayOf(
|
||||
R.string.tapping_inverted_none,
|
||||
R.string.tapping_inverted_horizontal,
|
||||
R.string.tapping_inverted_vertical,
|
||||
R.string.tapping_inverted_both,
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
TappingInvertMode.NONE.name,
|
||||
TappingInvertMode.HORIZONTAL.name,
|
||||
TappingInvertMode.VERTICAL.name,
|
||||
TappingInvertMode.BOTH.name,
|
||||
)
|
||||
summary = "%s"
|
||||
visibleIf(readerPreferences.navigationModePager()) { it != 5 }
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.navigateToPan())
|
||||
titleRes = R.string.pref_navigate_pan
|
||||
visibleIf(readerPreferences.navigationModePager()) { it != 5 }
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.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")
|
||||
summary = "%s"
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.landscapeZoom())
|
||||
titleRes = R.string.pref_landscape_zoom
|
||||
visibleIf(readerPreferences.imageScaleType()) { it == 1 }
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.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")
|
||||
summary = "%s"
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.cropBorders())
|
||||
titleRes = R.string.pref_crop_borders
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.dualPageSplitPaged())
|
||||
titleRes = R.string.pref_dual_page_split
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.dualPageInvertPaged())
|
||||
titleRes = R.string.pref_dual_page_invert
|
||||
summaryRes = R.string.pref_dual_page_invert_summary
|
||||
visibleIf(readerPreferences.dualPageSplitPaged()) { it }
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.webtoon_viewer
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.navigationModeWebtoon())
|
||||
titleRes = R.string.pref_viewer_nav
|
||||
entries = context.resources.getStringArray(R.array.webtoon_nav).also { values ->
|
||||
entryValues = values.indices.map { index -> "$index" }.toTypedArray()
|
||||
}
|
||||
summary = "%s"
|
||||
}
|
||||
listPreference {
|
||||
bindTo(readerPreferences.webtoonNavInverted())
|
||||
titleRes = R.string.pref_read_with_tapping_inverted
|
||||
entriesRes = arrayOf(
|
||||
R.string.tapping_inverted_none,
|
||||
R.string.tapping_inverted_horizontal,
|
||||
R.string.tapping_inverted_vertical,
|
||||
R.string.tapping_inverted_both,
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
TappingInvertMode.NONE.name,
|
||||
TappingInvertMode.HORIZONTAL.name,
|
||||
TappingInvertMode.VERTICAL.name,
|
||||
TappingInvertMode.BOTH.name,
|
||||
)
|
||||
summary = "%s"
|
||||
visibleIf(readerPreferences.navigationModeWebtoon()) { it != 5 }
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.webtoonSidePadding())
|
||||
titleRes = R.string.pref_webtoon_side_padding
|
||||
entriesRes = arrayOf(
|
||||
R.string.webtoon_side_padding_0,
|
||||
R.string.webtoon_side_padding_5,
|
||||
R.string.webtoon_side_padding_10,
|
||||
R.string.webtoon_side_padding_15,
|
||||
R.string.webtoon_side_padding_20,
|
||||
R.string.webtoon_side_padding_25,
|
||||
)
|
||||
entryValues = arrayOf("0", "5", "10", "15", "20", "25")
|
||||
summary = "%s"
|
||||
}
|
||||
listPreference {
|
||||
bindTo(readerPreferences.readerHideThreshold())
|
||||
titleRes = R.string.pref_hide_threshold
|
||||
entriesRes = arrayOf(
|
||||
R.string.pref_highest,
|
||||
R.string.pref_high,
|
||||
R.string.pref_low,
|
||||
R.string.pref_lowest,
|
||||
)
|
||||
entryValues = PreferenceValues.ReaderHideThreshold.values()
|
||||
.map { it.name }
|
||||
.toTypedArray()
|
||||
summary = "%s"
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.cropBordersWebtoon())
|
||||
titleRes = R.string.pref_crop_borders
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.dualPageSplitWebtoon())
|
||||
titleRes = R.string.pref_dual_page_split
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.dualPageInvertWebtoon())
|
||||
titleRes = R.string.pref_dual_page_invert
|
||||
summaryRes = R.string.pref_dual_page_invert_summary
|
||||
visibleIf(readerPreferences.dualPageSplitWebtoon()) { it }
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.longStripSplitWebtoon())
|
||||
titleRes = R.string.pref_long_strip_split
|
||||
summaryRes = R.string.split_tall_images_summary
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_reader_navigation
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.readWithVolumeKeys())
|
||||
titleRes = R.string.pref_read_with_volume_keys
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.readWithVolumeKeysInverted())
|
||||
titleRes = R.string.pref_read_with_volume_keys_inverted
|
||||
visibleIf(readerPreferences.readWithVolumeKeys()) { it }
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_reader_actions
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.readWithLongTap())
|
||||
titleRes = R.string.pref_read_with_long_tap
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.folderPerManga())
|
||||
titleRes = R.string.pref_create_folder_per_manga
|
||||
summaryRes = R.string.pref_create_folder_per_manga_summary
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.entriesRes
|
||||
import eu.kanade.tachiyomi.util.preference.infoPreference
|
||||
import eu.kanade.tachiyomi.util.preference.intListPreference
|
||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||
import eu.kanade.tachiyomi.util.preference.requireAuthentication
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.isAuthenticationSupported
|
||||
import eu.kanade.tachiyomi.util.system.AuthenticatorUtil.startAuthentication
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsSecurityController : SettingsController() {
|
||||
|
||||
private val securityPreferences: SecurityPreferences by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_security
|
||||
|
||||
if (context.isAuthenticationSupported()) {
|
||||
switchPreference {
|
||||
bindTo(securityPreferences.useAuthenticator())
|
||||
titleRes = R.string.lock_with_biometrics
|
||||
|
||||
requireAuthentication(
|
||||
activity as? FragmentActivity,
|
||||
context.getString(R.string.lock_with_biometrics),
|
||||
context.getString(R.string.confirm_lock_change),
|
||||
)
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
bindTo(securityPreferences.lockAppAfter())
|
||||
titleRes = R.string.lock_when_idle
|
||||
val values = arrayOf("0", "1", "2", "5", "10", "-1")
|
||||
entries = values.mapNotNull {
|
||||
when (it) {
|
||||
"-1" -> context.getString(R.string.lock_never)
|
||||
"0" -> context.getString(R.string.lock_always)
|
||||
else -> resources?.getQuantityString(R.plurals.lock_after_mins, it.toInt(), it)
|
||||
}
|
||||
}.toTypedArray()
|
||||
entryValues = values
|
||||
summary = "%s"
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
if (value == newValue) return@OnPreferenceChangeListener false
|
||||
|
||||
(activity as? FragmentActivity)?.startAuthentication(
|
||||
activity!!.getString(R.string.lock_when_idle),
|
||||
activity!!.getString(R.string.confirm_lock_change),
|
||||
callback = object : AuthenticatorUtil.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
activity: FragmentActivity?,
|
||||
result: BiometricPrompt.AuthenticationResult,
|
||||
) {
|
||||
super.onAuthenticationSucceeded(activity, result)
|
||||
value = newValue as String
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(
|
||||
activity: FragmentActivity?,
|
||||
errorCode: Int,
|
||||
errString: CharSequence,
|
||||
) {
|
||||
super.onAuthenticationError(activity, errorCode, errString)
|
||||
activity?.toast(errString.toString())
|
||||
}
|
||||
},
|
||||
)
|
||||
false
|
||||
}
|
||||
|
||||
visibleIf(securityPreferences.useAuthenticator()) { it }
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(securityPreferences.hideNotificationContent())
|
||||
titleRes = R.string.hide_notification_content
|
||||
}
|
||||
|
||||
listPreference {
|
||||
bindTo(securityPreferences.secureScreen())
|
||||
titleRes = R.string.secure_screen
|
||||
summary = "%s"
|
||||
entriesRes = SecurityPreferences.SecureScreenMode.values().map { it.titleResId }.toTypedArray()
|
||||
entryValues = SecurityPreferences.SecureScreenMode.values().map { it.name }.toTypedArray()
|
||||
}
|
||||
|
||||
infoPreference(R.string.secure_screen_summary)
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.widget.Toast
|
||||
import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.domain.track.service.TrackPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.track.NoLoginTrackService
|
||||
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.data.track.bangumi.BangumiApi
|
||||
import eu.kanade.tachiyomi.data.track.myanimelist.MyAnimeListApi
|
||||
import eu.kanade.tachiyomi.data.track.shikimori.ShikimoriApi
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.ui.setting.track.TrackLoginDialog
|
||||
import eu.kanade.tachiyomi.ui.setting.track.TrackLogoutDialog
|
||||
import eu.kanade.tachiyomi.util.preference.add
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.iconRes
|
||||
import eu.kanade.tachiyomi.util.preference.infoPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preferenceCategory
|
||||
import eu.kanade.tachiyomi.util.preference.switchPreference
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import eu.kanade.tachiyomi.util.system.openInBrowser
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.preference.TrackerPreference
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsTrackingController :
|
||||
SettingsController(),
|
||||
TrackLoginDialog.Listener,
|
||||
TrackLogoutDialog.Listener {
|
||||
|
||||
private val trackManager: TrackManager by injectLazy()
|
||||
private val trackPreferences: TrackPreferences by injectLazy()
|
||||
private val sourceManager: SourceManager by injectLazy()
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_tracking
|
||||
|
||||
switchPreference {
|
||||
bindTo(trackPreferences.autoUpdateTrack())
|
||||
titleRes = R.string.pref_auto_update_manga_sync
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.services
|
||||
|
||||
trackPreference(trackManager.myAnimeList) {
|
||||
activity?.openInBrowser(MyAnimeListApi.authUrl(), forceDefaultBrowser = true)
|
||||
}
|
||||
trackPreference(trackManager.aniList) {
|
||||
activity?.openInBrowser(AnilistApi.authUrl(), forceDefaultBrowser = true)
|
||||
}
|
||||
trackPreference(trackManager.kitsu) {
|
||||
val dialog = TrackLoginDialog(trackManager.kitsu, R.string.email)
|
||||
dialog.targetController = this@SettingsTrackingController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
trackPreference(trackManager.mangaUpdates) {
|
||||
val dialog = TrackLoginDialog(trackManager.mangaUpdates, R.string.username)
|
||||
dialog.targetController = this@SettingsTrackingController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
trackPreference(trackManager.shikimori) {
|
||||
activity?.openInBrowser(ShikimoriApi.authUrl(), forceDefaultBrowser = true)
|
||||
}
|
||||
trackPreference(trackManager.bangumi) {
|
||||
activity?.openInBrowser(BangumiApi.authUrl(), forceDefaultBrowser = true)
|
||||
}
|
||||
infoPreference(R.string.tracking_info)
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.enhanced_services
|
||||
|
||||
trackPreference(trackManager.komga) {
|
||||
val acceptedSources = trackManager.komga.getAcceptedSources()
|
||||
val hasValidSourceInstalled = sourceManager.getCatalogueSources()
|
||||
.any { it::class.qualifiedName in acceptedSources }
|
||||
|
||||
if (hasValidSourceInstalled) {
|
||||
trackManager.komga.loginNoop()
|
||||
updatePreference(trackManager.komga.id)
|
||||
} else {
|
||||
context.toast(R.string.tracker_komga_warning, Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
|
||||
infoPreference(R.string.enhanced_tracking_info)
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun PreferenceGroup.trackPreference(
|
||||
service: TrackService,
|
||||
crossinline login: () -> Unit,
|
||||
): TrackerPreference {
|
||||
return add(
|
||||
TrackerPreference(context).apply {
|
||||
key = TrackPreferences.trackUsername(service.id)
|
||||
titleRes = service.nameRes()
|
||||
iconRes = service.getLogo()
|
||||
iconColor = service.getLogoColor()
|
||||
onClick {
|
||||
if (service.isLogged) {
|
||||
if (service is NoLoginTrackService) {
|
||||
service.logout()
|
||||
updatePreference(service.id)
|
||||
} else {
|
||||
val dialog = TrackLogoutDialog(service)
|
||||
dialog.targetController = this@SettingsTrackingController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
} else {
|
||||
login()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onActivityResumed(activity: Activity) {
|
||||
super.onActivityResumed(activity)
|
||||
|
||||
// Manually refresh OAuth trackers' holders
|
||||
updatePreference(trackManager.myAnimeList.id)
|
||||
updatePreference(trackManager.aniList.id)
|
||||
updatePreference(trackManager.shikimori.id)
|
||||
updatePreference(trackManager.bangumi.id)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.settings_tracking, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.action_tracking_help -> activity?.openInBrowser(HELP_URL)
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
private fun updatePreference(id: Long) {
|
||||
val pref = findPreference(TrackPreferences.trackUsername(id)) as? TrackerPreference
|
||||
pref?.notifyChanged()
|
||||
}
|
||||
|
||||
override fun trackLoginDialogClosed(service: TrackService) {
|
||||
updatePreference(service.id)
|
||||
}
|
||||
|
||||
override fun trackLogoutDialogClosed(service: TrackService) {
|
||||
updatePreference(service.id)
|
||||
}
|
||||
}
|
||||
|
||||
private const val HELP_URL = "https://tachiyomi.org/help/guides/tracking/"
|
||||
@@ -1,20 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.kanade.presentation.more.settings.database.ClearDatabaseScreen
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||
|
||||
class ClearDatabaseController : FullComposeController<ClearDatabasePresenter>() {
|
||||
|
||||
override fun createPresenter(): ClearDatabasePresenter {
|
||||
return ClearDatabasePresenter()
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun ComposeContent() {
|
||||
ClearDatabaseScreen(
|
||||
presenter = presenter,
|
||||
navigateUp = { router.popCurrentController() },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
|
||||
import eu.kanade.domain.source.model.Source
|
||||
import eu.kanade.presentation.more.settings.database.ClearDatabaseState
|
||||
import eu.kanade.presentation.more.settings.database.ClearDatabaseStateImpl
|
||||
import eu.kanade.tachiyomi.Database
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ClearDatabasePresenter(
|
||||
private val state: ClearDatabaseStateImpl = ClearDatabaseState() as ClearDatabaseStateImpl,
|
||||
private val database: Database = Injekt.get(),
|
||||
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get(),
|
||||
) : BasePresenter<ClearDatabaseController>(), ClearDatabaseState by state {
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
presenterScope.launchIO {
|
||||
getSourcesWithNonLibraryManga.subscribe()
|
||||
.collectLatest { list ->
|
||||
state.items = list.sortedBy { it.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun removeMangaBySourceId(sourceIds: List<Long>) {
|
||||
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sourceIds)
|
||||
database.historyQueries.removeResettedHistory()
|
||||
}
|
||||
|
||||
fun toggleSelection(source: Source) {
|
||||
val mutableList = state.selection.toMutableList()
|
||||
if (mutableList.contains(source.id)) {
|
||||
mutableList.remove(source.id)
|
||||
} else {
|
||||
mutableList.add(source.id)
|
||||
}
|
||||
state.selection = mutableList
|
||||
}
|
||||
|
||||
fun clearSelection() {
|
||||
state.selection = emptyList()
|
||||
}
|
||||
|
||||
fun selectAll() {
|
||||
state.selection = state.items.map { it.id }
|
||||
}
|
||||
|
||||
fun invertSelection() {
|
||||
state.selection = state.items.map { it.id }.filterNot { it in state.selection }
|
||||
}
|
||||
|
||||
sealed class Dialog {
|
||||
data class Delete(val sourceIds: List<Long>) : Dialog()
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.search
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.kanade.presentation.more.settings.SettingsSearchScreen
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||
|
||||
class SettingsSearchController : FullComposeController<SettingsSearchPresenter>() {
|
||||
|
||||
override fun createPresenter() = SettingsSearchPresenter()
|
||||
|
||||
@Composable
|
||||
override fun ComposeContent() {
|
||||
SettingsSearchScreen(
|
||||
navigateUp = router::popCurrentController,
|
||||
presenter = presenter,
|
||||
onClickResult = { router.pushController(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.search
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.forEach
|
||||
import androidx.preference.get
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsAdvancedController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsAppearanceController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsBackupController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsBrowseController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsDownloadController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsGeneralController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsLibraryController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsReaderController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsSecurityController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsTrackingController
|
||||
import eu.kanade.tachiyomi.util.lang.launchNow
|
||||
import eu.kanade.tachiyomi.util.system.isLTR
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.full.createInstance
|
||||
|
||||
object SettingsSearchHelper {
|
||||
private var prefSearchResultList: MutableList<SettingsSearchResult> = mutableListOf()
|
||||
|
||||
/**
|
||||
* All subclasses of `SettingsController` should be listed here, in order to have their preferences searchable.
|
||||
*/
|
||||
private val settingControllersList: List<KClass<out SettingsController>> = listOf(
|
||||
SettingsAdvancedController::class,
|
||||
SettingsAppearanceController::class,
|
||||
SettingsBackupController::class,
|
||||
SettingsBrowseController::class,
|
||||
SettingsDownloadController::class,
|
||||
SettingsGeneralController::class,
|
||||
SettingsLibraryController::class,
|
||||
SettingsReaderController::class,
|
||||
SettingsSecurityController::class,
|
||||
SettingsTrackingController::class,
|
||||
)
|
||||
|
||||
/**
|
||||
* Must be called to populate `prefSearchResultList`
|
||||
*/
|
||||
@SuppressLint("RestrictedApi")
|
||||
fun initPreferenceSearchResults(context: Context) {
|
||||
val preferenceManager = PreferenceManager(context)
|
||||
prefSearchResultList.clear()
|
||||
|
||||
launchNow {
|
||||
settingControllersList.forEach { kClass ->
|
||||
val ctrl = kClass.createInstance()
|
||||
val settingsPrefScreen = ctrl.setupPreferenceScreen(preferenceManager.createPreferenceScreen(context))
|
||||
val prefCount = settingsPrefScreen.preferenceCount
|
||||
for (i in 0 until prefCount) {
|
||||
val rootPref = settingsPrefScreen[i]
|
||||
if (rootPref.title == null) continue // no title, not a preference. (note: only info notes appear to not have titles)
|
||||
getSettingSearchResult(ctrl, rootPref, "${settingsPrefScreen.title}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getFilteredResults(query: String): List<SettingsSearchResult> {
|
||||
return prefSearchResultList.filter {
|
||||
val inTitle = it.title.contains(query, true)
|
||||
val inSummary = it.summary.contains(query, true)
|
||||
val inBreadcrumb = it.breadcrumb.contains(query, true)
|
||||
|
||||
return@filter inTitle || inSummary || inBreadcrumb
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the data needed from a `Preference` to create a `SettingsSearchResult`, and then adds it to `prefSearchResultList`
|
||||
* Future enhancement: make bold the text matched by the search query.
|
||||
*/
|
||||
private fun getSettingSearchResult(
|
||||
ctrl: SettingsController,
|
||||
pref: Preference,
|
||||
breadcrumbs: String = "",
|
||||
) {
|
||||
when {
|
||||
pref is PreferenceGroup -> {
|
||||
val breadcrumbsStr = addLocalizedBreadcrumb(breadcrumbs, "${pref.title}")
|
||||
pref.forEach {
|
||||
getSettingSearchResult(ctrl, it, breadcrumbsStr) // recursion
|
||||
}
|
||||
}
|
||||
pref is PreferenceCategory -> {
|
||||
val breadcrumbsStr = addLocalizedBreadcrumb(breadcrumbs, "${pref.title}")
|
||||
pref.forEach {
|
||||
getSettingSearchResult(ctrl, it, breadcrumbsStr) // recursion
|
||||
}
|
||||
}
|
||||
(pref.title != null && pref.isVisible) -> {
|
||||
// Is an actual preference
|
||||
val title = pref.title.toString()
|
||||
// ListPreferences occasionally run into ArrayIndexOutOfBoundsException issues
|
||||
val summary = try { pref.summary?.toString() ?: "" } catch (e: Throwable) { "" }
|
||||
val breadcrumbsStr = addLocalizedBreadcrumb(breadcrumbs, "${pref.title}")
|
||||
|
||||
prefSearchResultList.add(
|
||||
SettingsSearchResult(
|
||||
key = pref.key,
|
||||
title = title,
|
||||
summary = summary,
|
||||
breadcrumb = breadcrumbsStr,
|
||||
searchController = ctrl,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLocalizedBreadcrumb(path: String, node: String): String {
|
||||
return if (Resources.getSystem().isLTR) {
|
||||
// This locale reads left to right.
|
||||
"$path > $node"
|
||||
} else {
|
||||
// This locale reads right to left.
|
||||
"$node < $path"
|
||||
}
|
||||
}
|
||||
|
||||
data class SettingsSearchResult(
|
||||
val key: String?,
|
||||
val title: String,
|
||||
val summary: String,
|
||||
val breadcrumb: String,
|
||||
val searchController: SettingsController,
|
||||
)
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.search
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class SettingsSearchPresenter(
|
||||
private val preferences: BasePreferences = Injekt.get(),
|
||||
) : BasePresenter<SettingsSearchController>() {
|
||||
|
||||
private val _state: MutableStateFlow<List<SettingsSearchHelper.SettingsSearchResult>> =
|
||||
MutableStateFlow(emptyList())
|
||||
val state: StateFlow<List<SettingsSearchHelper.SettingsSearchResult>> = _state.asStateFlow()
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
SettingsSearchHelper.initPreferenceSearchResults(preferences.context)
|
||||
}
|
||||
|
||||
fun searchSettings(query: String?) {
|
||||
_state.value = if (!query.isNullOrBlank()) {
|
||||
SettingsSearchHelper.getFilteredResults(query)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.track
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.core.os.bundleOf
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.preference.LoginDialogPreference
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class TrackLoginDialog(
|
||||
@StringRes usernameLabelRes: Int? = null,
|
||||
bundle: Bundle? = null,
|
||||
) : LoginDialogPreference(usernameLabelRes, bundle) {
|
||||
|
||||
private val service = Injekt.get<TrackManager>().getService(args.getLong("serviceId"))!!
|
||||
|
||||
constructor(service: TrackService, @StringRes usernameLabelRes: Int?) :
|
||||
this(usernameLabelRes, bundleOf("serviceId" to service.id))
|
||||
|
||||
@StringRes
|
||||
override fun getTitleName(): Int = service.nameRes()
|
||||
|
||||
override fun setCredentialsOnView(view: View) {
|
||||
binding?.username?.setText(service.getUsername())
|
||||
binding?.password?.setText(service.getPassword())
|
||||
}
|
||||
|
||||
override fun checkLogin() {
|
||||
if (binding!!.username.text.isNullOrEmpty() || binding!!.password.text.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
binding!!.login.progress = 1
|
||||
val user = binding!!.username.text.toString()
|
||||
val pass = binding!!.password.text.toString()
|
||||
|
||||
launchIO {
|
||||
try {
|
||||
service.login(user, pass)
|
||||
dialog?.dismiss()
|
||||
withUIContext { view?.context?.toast(R.string.login_success) }
|
||||
} catch (e: Throwable) {
|
||||
service.logout()
|
||||
binding?.login?.progress = -1
|
||||
binding?.login?.setText(R.string.unknown_error)
|
||||
withUIContext { e.message?.let { view?.context?.toast(it) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogClosed() {
|
||||
super.onDialogClosed()
|
||||
(targetController as? Listener)?.trackLoginDialogClosed(service)
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun trackLoginDialogClosed(service: TrackService)
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.track
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.core.os.bundleOf
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||
import eu.kanade.tachiyomi.data.track.TrackService
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class TrackLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
|
||||
private val service = Injekt.get<TrackManager>().getService(args.getLong("serviceId"))!!
|
||||
|
||||
constructor(service: TrackService) : this(bundleOf("serviceId" to service.id))
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val serviceName = activity!!.getString(service.nameRes())
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(activity!!.getString(R.string.logout_title, serviceName))
|
||||
.setPositiveButton(R.string.logout) { _, _ ->
|
||||
service.logout()
|
||||
(targetController as? Listener)?.trackLogoutDialogClosed(service)
|
||||
activity?.toast(R.string.logout_success)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun trackLogoutDialogClosed(service: TrackService)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user