Remove dead code
Mostly from settings rewrite, but some other things too. (cherry picked from commit 69cdba71eb842865586309d8549de78480cdbe0e) # Conflicts: # app/src/main/java/eu/kanade/presentation/more/settings/database/ClearDatabaseScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAdvancedController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsAppearanceController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBackupController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBrowseController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsGeneralController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsReaderController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsSecurityController.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabasePresenter.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/search/SettingsSearchHelper.kt # app/src/main/java/eu/kanade/tachiyomi/ui/setting/track/TrackLoginDialog.kt # app/src/main/java/eu/kanade/tachiyomi/widget/materialdialogs/MaterialAlertDialogBuilderExtensions.kt # app/src/main/java/eu/kanade/tachiyomi/widget/preference/IntListPreference.kt # app/src/main/java/eu/kanade/tachiyomi/widget/preference/LoginDialogPreference.kt # app/src/main/res/drawable/ic_done_green_24dp.xml # app/src/main/res/layout/pref_account_login.xml # app/src/main/res/layout/pref_library_columns.xml
This commit is contained in:
parent
a2f6b90547
commit
2144221250
@ -240,7 +240,6 @@ dependencies {
|
||||
|
||||
// UI libraries
|
||||
implementation(libs.material)
|
||||
implementation(libs.androidprocessbutton)
|
||||
implementation(libs.flexible.adapter.core)
|
||||
implementation(libs.flexible.adapter.ui)
|
||||
implementation(libs.photoview)
|
||||
|
@ -1,62 +0,0 @@
|
||||
package eu.kanade.presentation.more.settings
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Search
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import eu.kanade.presentation.components.AppBar
|
||||
import eu.kanade.presentation.components.AppBarActions
|
||||
import eu.kanade.presentation.components.PreferenceRow
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
@Composable
|
||||
fun SettingsMainScreen(
|
||||
navigateUp: () -> Unit,
|
||||
sections: List<SettingsSection>,
|
||||
onClickSearch: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
AppBar(
|
||||
title = stringResource(R.string.label_settings),
|
||||
navigateUp = navigateUp,
|
||||
actions = {
|
||||
AppBarActions(
|
||||
listOf(
|
||||
AppBar.Action(
|
||||
title = stringResource(R.string.action_search),
|
||||
icon = Icons.Outlined.Search,
|
||||
onClick = onClickSearch,
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
sections.map {
|
||||
item {
|
||||
PreferenceRow(
|
||||
title = stringResource(it.titleRes),
|
||||
painter = it.painter,
|
||||
onClick = it.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class SettingsSection(
|
||||
@StringRes val titleRes: Int,
|
||||
val painter: Painter,
|
||||
val onClick: () -> Unit,
|
||||
)
|
@ -1,116 +0,0 @@
|
||||
package eu.kanade.presentation.more.settings
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.text.KeyboardActions
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.components.ScrollbarLazyColumn
|
||||
import eu.kanade.presentation.components.SearchToolbar
|
||||
import eu.kanade.presentation.util.horizontalPadding
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsController
|
||||
import eu.kanade.tachiyomi.ui.setting.search.SettingsSearchHelper
|
||||
import eu.kanade.tachiyomi.ui.setting.search.SettingsSearchPresenter
|
||||
import kotlin.reflect.full.createInstance
|
||||
|
||||
@Composable
|
||||
fun SettingsSearchScreen(
|
||||
navigateUp: () -> Unit,
|
||||
presenter: SettingsSearchPresenter,
|
||||
onClickResult: (SettingsController) -> Unit,
|
||||
) {
|
||||
val results by presenter.state.collectAsState()
|
||||
var query by remember { mutableStateOf("") }
|
||||
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
val focusManager = LocalFocusManager.current
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
SearchToolbar(
|
||||
searchQuery = query,
|
||||
onChangeSearchQuery = {
|
||||
query = it
|
||||
presenter.searchSettings(it)
|
||||
},
|
||||
placeholderText = stringResource(R.string.action_search_settings),
|
||||
onClickCloseSearch = navigateUp,
|
||||
onClickResetSearch = { query = "" },
|
||||
scrollBehavior = scrollBehavior,
|
||||
keyboardOptions = KeyboardOptions.Default.copy(
|
||||
imeAction = ImeAction.Search,
|
||||
),
|
||||
keyboardActions = KeyboardActions(
|
||||
onSearch = {
|
||||
focusManager.clearFocus()
|
||||
keyboardController?.hide()
|
||||
},
|
||||
),
|
||||
)
|
||||
},
|
||||
) { contentPadding ->
|
||||
ScrollbarLazyColumn(
|
||||
contentPadding = contentPadding,
|
||||
) {
|
||||
items(
|
||||
items = results,
|
||||
key = { it.key.toString() },
|
||||
) { result ->
|
||||
SearchResult(result, onClickResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SearchResult(
|
||||
result: SettingsSearchHelper.SettingsSearchResult,
|
||||
onClickResult: (SettingsController) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
// Must pass a new Controller instance to avoid this error
|
||||
// https://github.com/bluelinelabs/Conductor/issues/446
|
||||
val controller = result.searchController::class.createInstance()
|
||||
controller.preferenceKey = result.key
|
||||
onClickResult(controller)
|
||||
}
|
||||
.padding(horizontal = horizontalPadding, vertical = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = result.title,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = result.summary,
|
||||
style = MaterialTheme.typography.bodySmall.copy(
|
||||
color = MaterialTheme.colorScheme.outline,
|
||||
),
|
||||
)
|
||||
|
||||
Text(
|
||||
text = result.breadcrumb,
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
)
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
package eu.kanade.presentation.more.settings.database
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import eu.kanade.presentation.components.Scaffold
|
||||
import eu.kanade.presentation.more.settings.database.components.ClearDatabaseContent
|
||||
import eu.kanade.presentation.more.settings.database.components.ClearDatabaseDeleteDialog
|
||||
import eu.kanade.presentation.more.settings.database.components.ClearDatabaseToolbar
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.setting.database.ClearDatabasePresenter
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
|
||||
@Composable
|
||||
fun ClearDatabaseScreen(
|
||||
presenter: ClearDatabasePresenter,
|
||||
navigateUp: () -> Unit,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
ClearDatabaseToolbar(
|
||||
state = presenter,
|
||||
navigateUp = navigateUp,
|
||||
onClickSelectAll = { presenter.selectAll() },
|
||||
onClickInvertSelection = { presenter.invertSelection() },
|
||||
scrollBehavior = scrollBehavior,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
ClearDatabaseContent(
|
||||
state = presenter,
|
||||
contentPadding = paddingValues,
|
||||
onClickSelection = { source ->
|
||||
presenter.toggleSelection(source)
|
||||
},
|
||||
onClickDelete = {
|
||||
presenter.dialog = ClearDatabasePresenter.Dialog.Delete(presenter.selection)
|
||||
},
|
||||
)
|
||||
}
|
||||
val dialog = presenter.dialog
|
||||
if (dialog is ClearDatabasePresenter.Dialog.Delete) {
|
||||
ClearDatabaseDeleteDialog(
|
||||
onDismissRequest = { presenter.dialog = null },
|
||||
onDelete = {
|
||||
presenter.removeMangaBySourceId(dialog.sourceIds, /* SY --> */ it /* SY <-- */)
|
||||
presenter.clearSelection()
|
||||
presenter.dialog = null
|
||||
context.toast(R.string.clear_database_completed)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
@ -6,14 +6,13 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import eu.kanade.domain.source.model.SourceWithCount
|
||||
import eu.kanade.tachiyomi.ui.setting.database.ClearDatabasePresenter
|
||||
|
||||
@Stable
|
||||
interface ClearDatabaseState {
|
||||
val items: List<SourceWithCount>
|
||||
val selection: List<Long>
|
||||
val isEmpty: Boolean
|
||||
var dialog: ClearDatabasePresenter.Dialog?
|
||||
var dialog: Dialog?
|
||||
}
|
||||
|
||||
fun ClearDatabaseState(): ClearDatabaseState {
|
||||
@ -24,5 +23,9 @@ class ClearDatabaseStateImpl : ClearDatabaseState {
|
||||
override var items: List<SourceWithCount> by mutableStateOf(emptyList())
|
||||
override var selection: List<Long> by mutableStateOf(emptyList())
|
||||
override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
|
||||
override var dialog: ClearDatabasePresenter.Dialog? by mutableStateOf(null)
|
||||
override var dialog: Dialog? by mutableStateOf(null)
|
||||
}
|
||||
|
||||
sealed class Dialog {
|
||||
data class Delete(val sourceIds: List<Long>) : Dialog()
|
||||
}
|
||||
|
@ -1,663 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.webkit.WebStorage
|
||||
import android.webkit.WebView
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
|
||||
import eu.kanade.domain.chapter.model.toDbChapter
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.domain.manga.interactor.GetAllManga
|
||||
import eu.kanade.domain.manga.repository.MangaRepository
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
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.cache.PagePreviewCache
|
||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||
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.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.SourceManager.Companion.DELEGATED_SOURCES
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
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.launchIO
|
||||
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.storage.DiskUtil
|
||||
import eu.kanade.tachiyomi.util.system.DeviceUtil
|
||||
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 exh.debug.SettingsDebugController
|
||||
import exh.log.EHLogLevel
|
||||
import exh.pref.DelegateSourcePreferences
|
||||
import exh.source.BlacklistedSources
|
||||
import exh.source.EH_SOURCE_ID
|
||||
import exh.source.EXH_SOURCE_ID
|
||||
import kotlinx.coroutines.Job
|
||||
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()
|
||||
private val sourcePreferences: SourcePreferences by injectLazy()
|
||||
private val delegateSourcePreferences: DelegateSourcePreferences by injectLazy()
|
||||
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||
private val getAllManga: GetAllManga by injectLazy()
|
||||
private val getChapterByMangaId: GetChapterByMangaId by injectLazy()
|
||||
private val pagePreviewCache: PagePreviewCache by injectLazy()
|
||||
|
||||
@SuppressLint("BatteryLife")
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_advanced
|
||||
|
||||
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() }
|
||||
}
|
||||
// SY -->
|
||||
preference {
|
||||
key = CLEAR_PREVIEW_CACHE_KEY
|
||||
titleRes = R.string.pref_clear_page_preview_cache
|
||||
summary = context.getString(R.string.used_cache, pagePreviewCache.readableSize)
|
||||
|
||||
onClick { clearPagePreviewCache() }
|
||||
}
|
||||
// SY <--
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --> EXH
|
||||
preferenceCategory {
|
||||
titleRes = R.string.download_notifier_downloader_title
|
||||
|
||||
preference {
|
||||
key = "clean_up_downloaded_chapters"
|
||||
titleRes = R.string.clean_up_downloaded_chapters
|
||||
summaryRes = R.string.delete_unused_chapters
|
||||
|
||||
onClick {
|
||||
val ctrl = CleanupDownloadsDialogController()
|
||||
ctrl.targetController = this@SettingsAdvancedController
|
||||
ctrl.showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.data_saver
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.dataSaver())
|
||||
titleRes = R.string.data_saver
|
||||
summaryRes = R.string.data_saver_summary
|
||||
}
|
||||
|
||||
editTextPreference {
|
||||
bindTo(sourcePreferences.dataSaverServer())
|
||||
titleRes = R.string.data_saver_server
|
||||
summaryRes = R.string.data_saver_server_summary
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.dataSaverDownloader())
|
||||
titleRes = R.string.data_saver_downloader
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.dataSaverIgnoreJpeg())
|
||||
titleRes = R.string.data_saver_ignore_jpeg
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.dataSaverIgnoreGif())
|
||||
titleRes = R.string.data_saver_ignore_gif
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
bindTo(sourcePreferences.dataSaverImageQuality())
|
||||
titleRes = R.string.data_saver_image_quality
|
||||
entries = arrayOf("10%", "20%", "40%", "50%", "70%", "80%", "90%", "95%")
|
||||
entryValues = entries.map { it.trimEnd('%') }.toTypedArray()
|
||||
summaryRes = R.string.data_saver_image_quality_summary
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.dataSaverImageFormatJpeg())
|
||||
titleRes = R.string.data_saver_image_format
|
||||
summaryOn = context.getString(R.string.data_saver_image_format_summary_on)
|
||||
summaryOff = context.getString(R.string.data_saver_image_format_summary_off)
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.dataSaverColorBW())
|
||||
titleRes = R.string.data_saver_color_bw
|
||||
|
||||
visibleIf(sourcePreferences.dataSaver()) { it }
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.developer_tools
|
||||
isPersistent = false
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.isHentaiEnabled())
|
||||
titleRes = R.string.toggle_hentai_features
|
||||
summaryRes = R.string.toggle_hentai_features_summary
|
||||
|
||||
onChange {
|
||||
if (unsortedPreferences.isHentaiEnabled().get()) {
|
||||
BlacklistedSources.HIDDEN_SOURCES += EH_SOURCE_ID
|
||||
BlacklistedSources.HIDDEN_SOURCES += EXH_SOURCE_ID
|
||||
} else {
|
||||
BlacklistedSources.HIDDEN_SOURCES -= EH_SOURCE_ID
|
||||
BlacklistedSources.HIDDEN_SOURCES -= EXH_SOURCE_ID
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(delegateSourcePreferences.delegateSources())
|
||||
titleRes = R.string.toggle_delegated_sources
|
||||
summary = context.getString(R.string.toggle_delegated_sources_summary, context.getString(R.string.app_name), DELEGATED_SOURCES.values.map { it.sourceName }.distinct().joinToString())
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
bindTo(unsortedPreferences.logLevel())
|
||||
titleRes = R.string.log_level
|
||||
|
||||
entries = EHLogLevel.values().map {
|
||||
"${context.getString(it.nameRes)} (${context.getString(it.description)})"
|
||||
}.toTypedArray()
|
||||
entryValues = EHLogLevel.values().mapIndexed { index, _ -> "$index" }.toTypedArray()
|
||||
|
||||
summaryRes = R.string.log_level_summary
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.enableSourceBlacklist())
|
||||
titleRes = R.string.enable_source_blacklist
|
||||
summary = context.getString(R.string.enable_source_blacklist_summary, context.getString(R.string.app_name))
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_open_debug_menu"
|
||||
titleRes = R.string.open_debug_menu
|
||||
summary = HtmlCompat.fromHtml(context.getString(R.string.open_debug_menu_summary), HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
onClick { router.pushController(SettingsDebugController()) }
|
||||
}
|
||||
}
|
||||
// <-- EXH
|
||||
}
|
||||
|
||||
// SY -->
|
||||
class CleanupDownloadsDialogController : DialogController() {
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val options = activity!!.resources.getStringArray(R.array.clean_up_downloads)
|
||||
val selected = options.map { true }.toBooleanArray()
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.clean_up_downloaded_chapters)
|
||||
.setMultiChoiceItems(options, selected) { dialog, which, checked ->
|
||||
if (which == 0) {
|
||||
(dialog as AlertDialog).listView.setItemChecked(which, true)
|
||||
} else {
|
||||
selected[which] = checked
|
||||
}
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
(targetController as? SettingsAdvancedController)?.cleanupDownloads(
|
||||
selected[1],
|
||||
selected[2],
|
||||
)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
private fun cleanupDownloads(removeRead: Boolean, removeNonFavorite: Boolean) {
|
||||
if (job?.isActive == true) return
|
||||
activity?.toast(R.string.starting_cleanup)
|
||||
job = launchIO {
|
||||
val mangaList = getAllManga.await()
|
||||
val downloadManager: DownloadManager = Injekt.get()
|
||||
var foldersCleared = 0
|
||||
Injekt.get<SourceManager>().getOnlineSources().forEach { source ->
|
||||
val mangaFolders = downloadManager.getMangaFolders(source)
|
||||
val sourceManga = mangaList
|
||||
.asSequence()
|
||||
.filter { it.source == source.id }
|
||||
.map { it to DiskUtil.buildValidFilename(it.ogTitle) }
|
||||
.toList()
|
||||
|
||||
mangaFolders.forEach mangaFolder@{ mangaFolder ->
|
||||
val manga = sourceManga.find { (_, folderName) -> folderName == mangaFolder.name }?.first
|
||||
if (manga == null) {
|
||||
// download is orphaned delete it
|
||||
foldersCleared += 1 + (mangaFolder.listFiles().orEmpty().size)
|
||||
mangaFolder.delete()
|
||||
} else {
|
||||
val chapterList = getChapterByMangaId.await(manga.id)
|
||||
foldersCleared += downloadManager.cleanupChapters(chapterList.map { it.toDbChapter() }, manga, source, removeRead, removeNonFavorite)
|
||||
}
|
||||
}
|
||||
}
|
||||
withUIContext {
|
||||
val activity = activity ?: return@withUIContext
|
||||
val cleanupString =
|
||||
if (foldersCleared == 0) {
|
||||
activity.getString(R.string.no_folders_to_cleanup)
|
||||
} else {
|
||||
resources!!.getQuantityString(
|
||||
R.plurals.cleanup_done,
|
||||
foldersCleared,
|
||||
foldersCleared,
|
||||
)
|
||||
}
|
||||
activity.toast(cleanupString, Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearPagePreviewCache() {
|
||||
val activity = activity ?: return
|
||||
launchIO {
|
||||
try {
|
||||
val deletedFiles = pagePreviewCache.clear()
|
||||
withUIContext {
|
||||
activity.toast(resources?.getString(R.string.cache_deleted, deletedFiles))
|
||||
findPreference(CLEAR_PREVIEW_CACHE_KEY)?.summary =
|
||||
resources?.getString(R.string.used_cache, pagePreviewCache.readableSize)
|
||||
}
|
||||
} catch (e: Throwable) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
withUIContext { activity.toast(R.string.cache_delete_error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
|
||||
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 companion object {
|
||||
// SY -->
|
||||
private var job: Job? = null
|
||||
// SY <--
|
||||
}
|
||||
}
|
||||
|
||||
private const val CLEAR_CACHE_KEY = "pref_clear_cache_key"
|
||||
|
||||
// SY -->
|
||||
private const val CLEAR_PREVIEW_CACHE_KEY = "pref_clear_preview_cache_key"
|
||||
// SY <--
|
@ -1,190 +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"
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_navbar
|
||||
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.showNavUpdates())
|
||||
titleRes = R.string.pref_hide_updates_button
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.showNavHistory())
|
||||
titleRes = R.string.pref_hide_history_button
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.bottomBarLabels())
|
||||
titleRes = R.string.pref_show_bottom_bar_labels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,314 +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,
|
||||
// SY -->
|
||||
R.string.custom_manga_info,
|
||||
R.string.all_read_manga,
|
||||
// SY <--
|
||||
)
|
||||
.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
|
||||
// SY -->
|
||||
5 -> flags = flags or BackupConst.BACKUP_CUSTOM_INFO
|
||||
6 -> flags = flags or BackupConst.BACKUP_READ_MANGA
|
||||
// SY <--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(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,148 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.PreferenceScreen
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.extension.ExtensionUpdateJob
|
||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||
import eu.kanade.tachiyomi.ui.category.repos.RepoController
|
||||
import eu.kanade.tachiyomi.ui.category.sources.SourceCategoryController
|
||||
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.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
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()
|
||||
|
||||
// SY -->
|
||||
private val uiPreferences: UiPreferences by injectLazy()
|
||||
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||
// SY <--
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.browse
|
||||
|
||||
// SY -->
|
||||
preferenceCategory {
|
||||
titleRes = R.string.label_sources
|
||||
|
||||
preference {
|
||||
key = "pref_edit_source_categories"
|
||||
titleRes = R.string.action_edit_categories
|
||||
|
||||
val catCount = sourcePreferences.sourcesTabCategories().get().count()
|
||||
summary = context.resources.getQuantityString(R.plurals.num_categories, catCount, catCount)
|
||||
|
||||
onClick {
|
||||
router.pushController(SourceCategoryController())
|
||||
}
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(sourcePreferences.sourcesTabCategoriesFilter())
|
||||
titleRes = R.string.pref_source_source_filtering
|
||||
summaryRes = R.string.pref_source_source_filtering_summery
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.useNewSourceNavigation())
|
||||
titleRes = R.string.pref_source_navigation
|
||||
summaryRes = R.string.pref_source_navigation_summery
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.allowLocalSourceHiddenFolders())
|
||||
titleRes = R.string.pref_local_source_hidden_folders
|
||||
summaryRes = R.string.pref_local_source_hidden_folders_summery
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.feed
|
||||
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.feedTabInFront())
|
||||
titleRes = R.string.pref_feed_position
|
||||
summaryRes = R.string.pref_feed_position_summery
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
// SY -->
|
||||
preference {
|
||||
key = "pref_edit_extension_repos"
|
||||
titleRes = R.string.action_edit_repos
|
||||
|
||||
val catCount = unsortedPreferences.extensionRepos().get().count()
|
||||
summary = context.resources.getQuantityString(R.plurals.num_repos, catCount, catCount)
|
||||
|
||||
onClick {
|
||||
router.pushController(RepoController())
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
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,614 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.text.InputType
|
||||
import android.view.LayoutInflater
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.domain.manga.interactor.DeleteFavoriteEntries
|
||||
import eu.kanade.domain.manga.interactor.GetExhFavoriteMangaWithMetadata
|
||||
import eu.kanade.domain.manga.interactor.GetFlatMetadataById
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.core.preference.Preference
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
|
||||
import eu.kanade.tachiyomi.data.preference.DEVICE_ONLY_ON_WIFI
|
||||
import eu.kanade.tachiyomi.databinding.DialogStubTextinputBinding
|
||||
import eu.kanade.tachiyomi.ui.setting.eh.FrontPageCategoriesDialog
|
||||
import eu.kanade.tachiyomi.ui.setting.eh.LanguagesDialog
|
||||
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
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.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.util.system.toast
|
||||
import exh.eh.EHentaiUpdateWorker
|
||||
import exh.eh.EHentaiUpdateWorkerConstants
|
||||
import exh.eh.EHentaiUpdaterStats
|
||||
import exh.favorites.FavoritesIntroDialog
|
||||
import exh.log.xLogD
|
||||
import exh.metadata.metadata.EHentaiSearchMetadata
|
||||
import exh.uconfig.WarnConfigureDialogController
|
||||
import exh.ui.login.EhLoginActivity
|
||||
import exh.util.nullIfBlank
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.days
|
||||
import kotlin.time.Duration.Companion.hours
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.minutes
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* EH Settings fragment
|
||||
*/
|
||||
|
||||
class SettingsEhController : SettingsController() {
|
||||
val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||
private val getFlatMetadataById: GetFlatMetadataById by injectLazy()
|
||||
private val deleteFavoriteEntries: DeleteFavoriteEntries by injectLazy()
|
||||
private val getExhFavoriteMangaWithMetadata: GetExhFavoriteMangaWithMetadata by injectLazy()
|
||||
|
||||
fun Preference<*>.reconfigure(): Boolean {
|
||||
// Listen for change commit
|
||||
changes()
|
||||
.take(1) // Only listen for first commit
|
||||
.onEach {
|
||||
// Only listen for first change commit
|
||||
WarnConfigureDialogController.uploadSettings(router)
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
|
||||
// Always return true to save changes
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.pref_category_eh
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.ehentai_prefs_account_settings
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.enableExhentai())
|
||||
titleRes = R.string.enable_exhentai
|
||||
summaryOff = context.getString(R.string.requires_login)
|
||||
isPersistent = false
|
||||
unsortedPreferences.enableExhentai()
|
||||
.changes()
|
||||
.onEach {
|
||||
isChecked = it
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
|
||||
onChange { newVal ->
|
||||
newVal as Boolean
|
||||
if (!newVal) {
|
||||
unsortedPreferences.enableExhentai().set(false)
|
||||
true
|
||||
} else {
|
||||
startActivityForResult(EhLoginActivity.newIntent(activity!!), LOGIN_RESULT)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
bindTo(unsortedPreferences.useHentaiAtHome())
|
||||
titleRes = R.string.use_hentai_at_home
|
||||
summaryRes = R.string.use_hentai_at_home_summary
|
||||
entriesRes = arrayOf(
|
||||
R.string.use_hentai_at_home_option_1,
|
||||
R.string.use_hentai_at_home_option_2,
|
||||
)
|
||||
entryValues = arrayOf("0", "1")
|
||||
|
||||
onChange { unsortedPreferences.useHentaiAtHome().reconfigure() }
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.useJapaneseTitle())
|
||||
titleRes = R.string.show_japanese_titles
|
||||
summaryOn = context.getString(R.string.show_japanese_titles_option_1)
|
||||
summaryOff = context.getString(R.string.show_japanese_titles_option_2)
|
||||
|
||||
onChange { unsortedPreferences.useJapaneseTitle().reconfigure() }
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.exhUseOriginalImages())
|
||||
titleRes = R.string.use_original_images
|
||||
summaryOn = context.getString(R.string.use_original_images_on)
|
||||
summaryOff = context.getString(R.string.use_original_images_off)
|
||||
|
||||
onChange { unsortedPreferences.exhUseOriginalImages().reconfigure() }
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_watched_tags"
|
||||
titleRes = R.string.watched_tags
|
||||
summaryRes = R.string.watched_tags_summary
|
||||
onClick {
|
||||
val intent = if (unsortedPreferences.enableExhentai().get()) {
|
||||
WebViewActivity.newIntent(activity!!, url = "https://exhentai.org/mytags", title = context.getString(R.string.watched_tags_exh))
|
||||
} else {
|
||||
WebViewActivity.newIntent(activity!!, url = "https://e-hentai.org/mytags", title = context.getString(R.string.watched_tags_eh))
|
||||
}
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
preference {
|
||||
bindTo(unsortedPreferences.ehTagFilterValue())
|
||||
titleRes = R.string.tag_filtering_threshold
|
||||
summary = context.getString(R.string.tag_filtering_threshhold_summary, unsortedPreferences.ehTagFilterValue().get())
|
||||
|
||||
onClick {
|
||||
var value: Int? = unsortedPreferences.ehTagFilterValue().get()
|
||||
MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.tag_filtering_threshold)
|
||||
.let { builder ->
|
||||
val binding = DialogStubTextinputBinding.inflate(LayoutInflater.from(builder.context))
|
||||
binding.textField.editText?.apply {
|
||||
inputType = InputType.TYPE_NUMBER_FLAG_SIGNED
|
||||
setText(value.toString(), TextView.BufferType.EDITABLE)
|
||||
doAfterTextChanged {
|
||||
value = it?.toString()?.toIntOrNull()
|
||||
this@SettingsEhController.xLogD(value)
|
||||
error = if (value in -9999..0 || it.toString() == "-") {
|
||||
null
|
||||
} else {
|
||||
context.getString(R.string.tag_filtering_threshhold_error)
|
||||
}
|
||||
}
|
||||
post {
|
||||
requestFocusFromTouch()
|
||||
context.getSystemService<InputMethodManager>()?.showSoftInput(this, 0)
|
||||
}
|
||||
}
|
||||
builder.setView(binding.root)
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
unsortedPreferences.ehTagFilterValue().set(value ?: return@setPositiveButton)
|
||||
summary = context.getString(R.string.tag_filtering_threshhold_summary, unsortedPreferences.ehTagFilterValue().get())
|
||||
unsortedPreferences.ehTagFilterValue().reconfigure()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
preference {
|
||||
bindTo(unsortedPreferences.ehTagWatchingValue())
|
||||
titleRes = R.string.tag_watching_threshhold
|
||||
summary = context.getString(R.string.tag_watching_threshhold_summary, unsortedPreferences.ehTagWatchingValue().get())
|
||||
|
||||
onClick {
|
||||
var value: Int? = unsortedPreferences.ehTagWatchingValue().get()
|
||||
MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.tag_watching_threshhold)
|
||||
.let { builder ->
|
||||
val binding = DialogStubTextinputBinding.inflate(LayoutInflater.from(builder.context))
|
||||
binding.textField.editText?.apply {
|
||||
inputType = InputType.TYPE_NUMBER_FLAG_SIGNED
|
||||
|
||||
setText(value.toString(), TextView.BufferType.EDITABLE)
|
||||
doAfterTextChanged {
|
||||
value = it?.toString()?.toIntOrNull()
|
||||
|
||||
error = if (value in 0..9999) {
|
||||
null
|
||||
} else {
|
||||
context.getString(R.string.tag_watching_threshhold_error)
|
||||
}
|
||||
}
|
||||
post {
|
||||
requestFocusFromTouch()
|
||||
context.getSystemService<InputMethodManager>()?.showSoftInput(this, 0)
|
||||
}
|
||||
}
|
||||
builder.setView(binding.root)
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
unsortedPreferences.ehTagWatchingValue().set(value ?: return@setPositiveButton)
|
||||
summary = context.getString(R.string.tag_watching_threshhold_summary, unsortedPreferences.ehTagWatchingValue().get())
|
||||
unsortedPreferences.ehTagWatchingValue().reconfigure()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
preference {
|
||||
bindTo(unsortedPreferences.exhSettingsLanguages())
|
||||
titleRes = R.string.language_filtering
|
||||
summaryRes = R.string.language_filtering_summary
|
||||
|
||||
onClick {
|
||||
val dialog = LanguagesDialog()
|
||||
dialog.targetController = this@SettingsEhController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
preference {
|
||||
bindTo(unsortedPreferences.exhEnabledCategories())
|
||||
titleRes = R.string.frong_page_categories
|
||||
summaryRes = R.string.fromt_page_categories_summary
|
||||
|
||||
onClick {
|
||||
val dialog = FrontPageCategoriesDialog()
|
||||
dialog.targetController = this@SettingsEhController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.exhWatchedListDefaultState())
|
||||
titleRes = R.string.watched_list_default
|
||||
summaryRes = R.string.watched_list_state_summary
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
listPreference {
|
||||
bindTo(unsortedPreferences.imageQuality())
|
||||
summaryRes = R.string.eh_image_quality_summary
|
||||
titleRes = R.string.eh_image_quality
|
||||
entriesRes = arrayOf(
|
||||
R.string.eh_image_quality_auto,
|
||||
R.string.eh_image_quality_2400,
|
||||
R.string.eh_image_quality_1600,
|
||||
R.string.eh_image_quality_1280,
|
||||
R.string.eh_image_quality_980,
|
||||
R.string.eh_image_quality_780,
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
"auto",
|
||||
"ovrs_2400",
|
||||
"ovrs_1600",
|
||||
"high",
|
||||
"med",
|
||||
"low",
|
||||
)
|
||||
|
||||
onChange { unsortedPreferences.imageQuality().reconfigure() }
|
||||
|
||||
visibleIf(unsortedPreferences.enableExhentai()) { it }
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.enhancedEHentaiView())
|
||||
titleRes = R.string.pref_enhanced_e_hentai_view
|
||||
summaryRes = R.string.pref_enhanced_e_hentai_view_summary
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.favorites_sync
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.exhReadOnlySync())
|
||||
titleRes = R.string.disable_favorites_uploading
|
||||
summaryRes = R.string.disable_favorites_uploading_summary
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_show_sync_favorite_notes"
|
||||
titleRes = R.string.show_favorite_sync_notes
|
||||
summaryRes = R.string.show_favorite_sync_notes_summary
|
||||
|
||||
onClick {
|
||||
activity?.let {
|
||||
FavoritesIntroDialog().show(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.exhLenientSync())
|
||||
titleRes = R.string.ignore_sync_errors
|
||||
summaryRes = R.string.ignore_sync_errors_summary
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_force_sync_reset"
|
||||
titleRes = R.string.force_sync_state_reset
|
||||
summaryRes = R.string.force_sync_state_reset_summary
|
||||
|
||||
onClick {
|
||||
activity?.let { activity ->
|
||||
MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.favorites_sync_reset)
|
||||
.setMessage(R.string.favorites_sync_reset_message)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
launchIO {
|
||||
deleteFavoriteEntries.await()
|
||||
withUIContext {
|
||||
activity.toast(context.getString(R.string.sync_state_reset), Toast.LENGTH_LONG)
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.setCancelable(false)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.gallery_update_checker
|
||||
|
||||
intListPreference {
|
||||
bindTo(unsortedPreferences.exhAutoUpdateFrequency())
|
||||
titleRes = R.string.time_between_batches
|
||||
entriesRes = arrayOf(
|
||||
R.string.time_between_batches_never,
|
||||
R.string.time_between_batches_1_hour,
|
||||
R.string.time_between_batches_2_hours,
|
||||
R.string.time_between_batches_3_hours,
|
||||
R.string.time_between_batches_6_hours,
|
||||
R.string.time_between_batches_12_hours,
|
||||
R.string.time_between_batches_24_hours,
|
||||
R.string.time_between_batches_48_hours,
|
||||
)
|
||||
entryValues = arrayOf("0", "1", "2", "3", "6", "12", "24", "48")
|
||||
|
||||
unsortedPreferences.exhAutoUpdateFrequency().changes()
|
||||
.onEach { newVal ->
|
||||
summary = if (newVal == 0) {
|
||||
context.getString(R.string.time_between_batches_summary_1, context.getString(R.string.app_name))
|
||||
} else {
|
||||
context.getString(R.string.time_between_batches_summary_2, context.getString(R.string.app_name), newVal, EHentaiUpdateWorkerConstants.UPDATES_PER_ITERATION)
|
||||
}
|
||||
}
|
||||
.launchIn(viewScope)
|
||||
|
||||
onChange { newValue ->
|
||||
val interval = (newValue as String).toInt()
|
||||
EHentaiUpdateWorker.scheduleBackground(context, interval)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
multiSelectListPreference {
|
||||
bindTo(unsortedPreferences.exhAutoUpdateRequirements())
|
||||
titleRes = R.string.auto_update_restrictions
|
||||
entriesRes = arrayOf(R.string.connected_to_wifi, R.string.charging)
|
||||
entryValues = arrayOf(DEVICE_ONLY_ON_WIFI, DEVICE_CHARGING)
|
||||
|
||||
fun updateSummary() {
|
||||
val restrictions = unsortedPreferences.exhAutoUpdateRequirements().get()
|
||||
.sorted()
|
||||
.map {
|
||||
when (it) {
|
||||
DEVICE_ONLY_ON_WIFI -> context.getString(R.string.connected_to_wifi)
|
||||
DEVICE_CHARGING -> context.getString(R.string.charging)
|
||||
else -> it
|
||||
}
|
||||
}
|
||||
val restrictionsText = if (restrictions.isEmpty()) {
|
||||
context.getString(R.string.none)
|
||||
} else {
|
||||
restrictions.joinToString()
|
||||
}
|
||||
|
||||
summary = context.getString(R.string.restrictions, restrictionsText)
|
||||
}
|
||||
|
||||
visibleIf(unsortedPreferences.exhAutoUpdateFrequency()) { it > 0 }
|
||||
|
||||
onChange {
|
||||
// Post to event looper to allow the preference to be updated.
|
||||
ContextCompat.getMainExecutor(context).execute { EHentaiUpdateWorker.scheduleBackground(context) }
|
||||
true
|
||||
}
|
||||
|
||||
unsortedPreferences.exhAutoUpdateRequirements().changes()
|
||||
.onEach { updateSummary() }
|
||||
.launchIn(viewScope)
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_show_updater_statistics"
|
||||
titleRes = R.string.show_updater_statistics
|
||||
|
||||
onClick {
|
||||
val progress = MaterialAlertDialogBuilder(context)
|
||||
.setMessage(R.string.gallery_updater_statistics_collection)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
progress.show()
|
||||
|
||||
viewScope.launch(Dispatchers.IO) {
|
||||
val updateInfo = try {
|
||||
val stats =
|
||||
unsortedPreferences.exhAutoUpdateStats().get().nullIfBlank()?.let {
|
||||
Json.decodeFromString<EHentaiUpdaterStats>(it)
|
||||
}
|
||||
|
||||
val statsText = if (stats != null) {
|
||||
context.getString(R.string.gallery_updater_stats_text, getRelativeTimeString(getRelativeTimeFromNow(stats.startTime.milliseconds), context), stats.updateCount, stats.possibleUpdates)
|
||||
} else {
|
||||
context.getString(R.string.gallery_updater_not_ran_yet)
|
||||
}
|
||||
|
||||
val allMeta = getExhFavoriteMangaWithMetadata.await()
|
||||
.mapNotNull {
|
||||
getFlatMetadataById.await(it.id)
|
||||
?.raise<EHentaiSearchMetadata>()
|
||||
}
|
||||
|
||||
fun metaInRelativeDuration(duration: Duration): Int {
|
||||
val durationMs = duration.inWholeMilliseconds
|
||||
return allMeta.asSequence().filter {
|
||||
System.currentTimeMillis() - it.lastUpdateCheck < durationMs
|
||||
}.count()
|
||||
}
|
||||
|
||||
statsText + "\n\n" + context.getString(
|
||||
R.string.gallery_updater_stats_time,
|
||||
metaInRelativeDuration(1.hours),
|
||||
metaInRelativeDuration(6.hours),
|
||||
metaInRelativeDuration(12.hours),
|
||||
metaInRelativeDuration(1.days),
|
||||
metaInRelativeDuration(2.days),
|
||||
metaInRelativeDuration(7.days),
|
||||
metaInRelativeDuration(30.days),
|
||||
metaInRelativeDuration(365.days),
|
||||
)
|
||||
} finally {
|
||||
progress.dismiss()
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.gallery_updater_statistics)
|
||||
.setMessage(updateInfo)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getRelativeTimeFromNow(then: Duration): RelativeTime {
|
||||
val now = System.currentTimeMillis().milliseconds
|
||||
var period: Duration = now - then
|
||||
val relativeTime = RelativeTime()
|
||||
while (period > 0.milliseconds) {
|
||||
when {
|
||||
period >= 365.days -> {
|
||||
(period.inWholeDays / 365).let {
|
||||
relativeTime.years = it
|
||||
period -= (it * 365).days
|
||||
}
|
||||
continue
|
||||
}
|
||||
period >= 30.days -> {
|
||||
(period.inWholeDays / 30).let {
|
||||
relativeTime.months = it
|
||||
period -= (it * 30).days
|
||||
}
|
||||
}
|
||||
period >= 7.days -> {
|
||||
(period.inWholeDays / 7).let {
|
||||
relativeTime.weeks = it
|
||||
period -= (it * 7).days
|
||||
}
|
||||
}
|
||||
period >= 1.days -> {
|
||||
period.inWholeDays.let {
|
||||
relativeTime.days = it
|
||||
period -= it.days
|
||||
}
|
||||
}
|
||||
period >= 1.hours -> {
|
||||
period.inWholeHours.let {
|
||||
relativeTime.hours = it
|
||||
period -= it.hours
|
||||
}
|
||||
}
|
||||
period >= 1.minutes -> {
|
||||
period.inWholeMinutes.let {
|
||||
relativeTime.minutes = it
|
||||
period -= it.minutes
|
||||
}
|
||||
}
|
||||
period >= 1.seconds -> {
|
||||
period.inWholeSeconds.let {
|
||||
relativeTime.seconds = it
|
||||
period -= it.seconds
|
||||
}
|
||||
}
|
||||
period >= 1.milliseconds -> {
|
||||
period.inWholeMilliseconds.let {
|
||||
relativeTime.milliseconds = it
|
||||
}
|
||||
period = Duration.ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
return relativeTime
|
||||
}
|
||||
|
||||
private fun getRelativeTimeString(relativeTime: RelativeTime, context: Context): String {
|
||||
return relativeTime.years?.let { context.resources.getQuantityString(R.plurals.humanize_year, it.toInt(), it) }
|
||||
?: relativeTime.months?.let { context.resources.getQuantityString(R.plurals.humanize_month, it.toInt(), it) }
|
||||
?: relativeTime.weeks?.let { context.resources.getQuantityString(R.plurals.humanize_week, it.toInt(), it) }
|
||||
?: relativeTime.days?.let { context.resources.getQuantityString(R.plurals.humanize_day, it.toInt(), it) }
|
||||
?: relativeTime.hours?.let { context.resources.getQuantityString(R.plurals.humanize_hour, it.toInt(), it) }
|
||||
?: relativeTime.minutes?.let { context.resources.getQuantityString(R.plurals.humanize_minute, it.toInt(), it) }
|
||||
?: relativeTime.seconds?.let { context.resources.getQuantityString(R.plurals.humanize_second, it.toInt(), it) }
|
||||
?: context.getString(R.string.humanize_fallback)
|
||||
}
|
||||
|
||||
data class RelativeTime(
|
||||
var years: Long? = null,
|
||||
var months: Long? = null,
|
||||
var weeks: Long? = null,
|
||||
var days: Long? = null,
|
||||
var hours: Long? = null,
|
||||
var minutes: Long? = null,
|
||||
var seconds: Long? = null,
|
||||
var milliseconds: Long? = null,
|
||||
)
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (resultCode == Activity.RESULT_OK) {
|
||||
if (requestCode == LOGIN_RESULT) {
|
||||
// Upload settings
|
||||
WarnConfigureDialogController.uploadSettings(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LOGIN_RESULT = 500
|
||||
}
|
||||
}
|
@ -1,129 +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.UnsortedPreferences
|
||||
import eu.kanade.domain.library.service.LibraryPreferences
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
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.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.LocaleHelper
|
||||
import org.xmlpull.v1.XmlPullParser
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsGeneralController : SettingsController() {
|
||||
|
||||
private val libraryPreferences: LibraryPreferences by injectLazy()
|
||||
|
||||
// SY -->
|
||||
private val uiPreferences: UiPreferences by injectLazy()
|
||||
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||
// SY <--
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
// --> EXH
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_fork
|
||||
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.expandFilters())
|
||||
titleRes = R.string.toggle_expand_search_filters
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.autoSolveCaptcha())
|
||||
titleRes = R.string.auto_solve_captchas
|
||||
summaryRes = R.string.auto_solve_captchas_summary
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.recommendsInOverflow())
|
||||
titleRes = R.string.put_recommends_in_overflow
|
||||
summaryRes = R.string.put_recommends_in_overflow_summary
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(uiPreferences.mergeInOverflow())
|
||||
titleRes = R.string.put_merge_in_overflow
|
||||
summaryRes = R.string.put_merge_in_overflow_summary
|
||||
}
|
||||
}
|
||||
// <-- EXH
|
||||
}
|
||||
}
|
@ -1,441 +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.UnsortedPreferences
|
||||
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.model.GroupLibraryMode
|
||||
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.ui.category.genre.SortTagController
|
||||
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.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()
|
||||
|
||||
// SY -->
|
||||
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||
// SY <--
|
||||
|
||||
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)
|
||||
}
|
||||
// SY -->
|
||||
listPreference {
|
||||
bindTo(libraryPreferences.groupLibraryUpdateType())
|
||||
titleRes = R.string.library_group_updates
|
||||
entriesRes = arrayOf(
|
||||
R.string.library_group_updates_global,
|
||||
R.string.library_group_updates_all_but_ungrouped,
|
||||
R.string.library_group_updates_all,
|
||||
)
|
||||
entryValues = arrayOf(
|
||||
GroupLibraryMode.GLOBAL.name,
|
||||
GroupLibraryMode.ALL_BUT_UNGROUPED.name,
|
||||
GroupLibraryMode.ALL.name,
|
||||
)
|
||||
summary = "%s"
|
||||
}
|
||||
// SY <--
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_sorting_settings
|
||||
preference {
|
||||
key = "pref_tag_sorting"
|
||||
titleRes = R.string.pref_tag_sorting
|
||||
val count = libraryPreferences.sortTagsForLibrary().get().size
|
||||
summary = context.resources.getQuantityString(R.plurals.pref_tag_sorting_desc, count, count)
|
||||
onClick {
|
||||
router.pushController(SortTagController())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unsortedPreferences.skipPreMigration().get() || unsortedPreferences.migrationSources().get()
|
||||
.isNotEmpty()
|
||||
) {
|
||||
preferenceCategory {
|
||||
titleRes = R.string.migration
|
||||
|
||||
switchPreference {
|
||||
bindTo(unsortedPreferences.skipPreMigration())
|
||||
titleRes = R.string.skip_pre_migration
|
||||
summaryRes = R.string.pref_skip_pre_migration_summary
|
||||
}
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
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,124 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.library.LibraryUpdateService
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.util.preference.bindTo
|
||||
import eu.kanade.tachiyomi.util.preference.listPreference
|
||||
import eu.kanade.tachiyomi.util.preference.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
import eu.kanade.tachiyomi.util.preference.summaryRes
|
||||
import eu.kanade.tachiyomi.util.preference.titleRes
|
||||
import exh.md.utils.MdUtil
|
||||
import exh.widget.preference.MangaDexLoginPreference
|
||||
import exh.widget.preference.MangadexLoginDialog
|
||||
import exh.widget.preference.MangadexLogoutDialog
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class SettingsMangaDexController :
|
||||
SettingsController(),
|
||||
MangadexLoginDialog.Listener,
|
||||
MangadexLogoutDialog.Listener {
|
||||
|
||||
private val sourcePreferences: SourcePreferences by injectLazy()
|
||||
private val unsortedPreferences: UnsortedPreferences by injectLazy()
|
||||
private val mdex by lazy { MdUtil.getEnabledMangaDex(unsortedPreferences, sourcePreferences) }
|
||||
|
||||
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
|
||||
titleRes = R.string.mangadex_specific_settings
|
||||
val mdex = mdex ?: return@apply
|
||||
val sourcePreference = MangaDexLoginPreference(context, mdex).apply {
|
||||
title = mdex.name + " Login"
|
||||
key = getSourceKey(source.id)
|
||||
setOnLoginClickListener {
|
||||
if (mdex.isLogged()) {
|
||||
val dialog = MangadexLogoutDialog(source)
|
||||
dialog.targetController = this@SettingsMangaDexController
|
||||
dialog.showDialog(router)
|
||||
} else {
|
||||
val dialog = MangadexLoginDialog(source)
|
||||
dialog.targetController = this@SettingsMangaDexController
|
||||
dialog.showDialog(router)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addPreference(sourcePreference)
|
||||
|
||||
listPreference {
|
||||
bindTo(unsortedPreferences.preferredMangaDexId())
|
||||
titleRes = R.string.mangadex_preffered_source
|
||||
summaryRes = R.string.mangadex_preffered_source_summary
|
||||
val mangaDexs = MdUtil.getEnabledMangaDexs(sourcePreferences)
|
||||
entries = mangaDexs.map { it.toString() }.toTypedArray()
|
||||
entryValues = mangaDexs.map { it.id.toString() }.toTypedArray()
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "pref_sync_mangadex_into_this"
|
||||
titleRes = R.string.mangadex_sync_follows_to_library
|
||||
summaryRes = R.string.mangadex_sync_follows_to_library_summary
|
||||
|
||||
onClick {
|
||||
val items = context.resources.getStringArray(R.array.md_follows_options)
|
||||
.drop(1)
|
||||
.toTypedArray()
|
||||
val selection = items.mapIndexed { index, _ ->
|
||||
index == 0 || index == 5
|
||||
}.toBooleanArray()
|
||||
MaterialAlertDialogBuilder(context)
|
||||
.setTitle(R.string.mangadex_sync_follows_to_library)
|
||||
// .setMessage(R.string.mangadex_sync_follows_to_library_message)
|
||||
.setMultiChoiceItems(
|
||||
items,
|
||||
selection,
|
||||
) { _, which, selected ->
|
||||
selection[which] = selected
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
unsortedPreferences.mangadexSyncToLibraryIndexes().set(
|
||||
items.filterIndexed { index, _ -> selection[index] }
|
||||
.mapIndexed { index, _ -> (index + 1).toString() }
|
||||
.toSet(),
|
||||
)
|
||||
LibraryUpdateService.start(
|
||||
context,
|
||||
target = LibraryUpdateService.Target.SYNC_FOLLOWS,
|
||||
)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
preference {
|
||||
titleRes = R.string.mangadex_push_favorites_to_mangadex
|
||||
summaryRes = R.string.mangadex_push_favorites_to_mangadex_summary
|
||||
|
||||
onClick {
|
||||
LibraryUpdateService.start(
|
||||
context,
|
||||
target = LibraryUpdateService.Target.PUSH_FAVORITES,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun siteLoginDialogClosed(source: Source) {
|
||||
val pref = findPreference(getSourceKey(source.id)) as? MangaDexLoginPreference
|
||||
pref?.notifyChanged()
|
||||
}
|
||||
|
||||
override fun siteLogoutDialogClosed(source: Source) {
|
||||
val pref = findPreference(getSourceKey(source.id)) as? MangaDexLoginPreference
|
||||
pref?.notifyChanged()
|
||||
}
|
||||
|
||||
private fun getSourceKey(sourceId: Long): String {
|
||||
return "source_$sourceId"
|
||||
}
|
||||
}
|
@ -1,563 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
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.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderBottomButton
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
|
||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
|
||||
import eu.kanade.tachiyomi.ui.reader.viewer.pager.PagerConfig
|
||||
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.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.hasDisplayCutout
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
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
|
||||
}
|
||||
// SY -->
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.forceHorizontalSeekbar())
|
||||
titleRes = R.string.pref_force_horz_seekbar
|
||||
summaryRes = R.string.pref_force_horz_seekbar_summary
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.landscapeVerticalSeekbar())
|
||||
titleRes = R.string.pref_show_vert_seekbar_landscape
|
||||
summaryRes = R.string.pref_show_vert_seekbar_landscape_summary
|
||||
visibleIf(readerPreferences.forceHorizontalSeekbar()) { !it }
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.leftVerticalSeekbar())
|
||||
titleRes = R.string.pref_left_handed_vertical_seekbar
|
||||
summaryRes = R.string.pref_left_handed_vertical_seekbar_summary
|
||||
visibleIf(readerPreferences.forceHorizontalSeekbar()) { !it }
|
||||
}
|
||||
// SY <--
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
// SY -->
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.pageTransitionsPager())
|
||||
titleRes = R.string.pref_page_transitions
|
||||
}
|
||||
// SY <--
|
||||
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
|
||||
}
|
||||
// SY -->
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.pageTransitionsWebtoon())
|
||||
titleRes = R.string.pref_page_transitions
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.webtoonEnableZoomOut())
|
||||
titleRes = R.string.enable_zoom_out
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
// SY -->
|
||||
preferenceCategory {
|
||||
titleRes = R.string.vertical_plus_viewer
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.continuousVerticalTappingByPage())
|
||||
titleRes = R.string.tap_scroll_page
|
||||
summaryRes = R.string.tap_scroll_page_summary
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.cropBordersContinuousVertical())
|
||||
titleRes = R.string.pref_crop_borders
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
preferenceCategory {
|
||||
titleRes = R.string.page_downloading
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.preloadSize())
|
||||
titleRes = R.string.reader_preload_amount
|
||||
entryValues = arrayOf(
|
||||
"4",
|
||||
"6",
|
||||
"8",
|
||||
"10",
|
||||
"12",
|
||||
"14",
|
||||
"16",
|
||||
"20",
|
||||
)
|
||||
entriesRes = arrayOf(
|
||||
R.string.reader_preload_amount_4_pages,
|
||||
R.string.reader_preload_amount_6_pages,
|
||||
R.string.reader_preload_amount_8_pages,
|
||||
R.string.reader_preload_amount_10_pages,
|
||||
R.string.reader_preload_amount_12_pages,
|
||||
R.string.reader_preload_amount_14_pages,
|
||||
R.string.reader_preload_amount_16_pages,
|
||||
R.string.reader_preload_amount_20_pages,
|
||||
)
|
||||
summaryRes = R.string.reader_preload_amount_summary
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.readerThreads())
|
||||
titleRes = R.string.download_threads
|
||||
entries = arrayOf("1", "2", "3", "4", "5")
|
||||
entryValues = entries
|
||||
summaryRes = R.string.download_threads_summary
|
||||
}
|
||||
|
||||
listPreference {
|
||||
bindTo(readerPreferences.cacheSize())
|
||||
titleRes = R.string.reader_cache_size
|
||||
entryValues = arrayOf(
|
||||
"50",
|
||||
"75",
|
||||
"100",
|
||||
"150",
|
||||
"250",
|
||||
"500",
|
||||
"750",
|
||||
"1000",
|
||||
"1500",
|
||||
"2000",
|
||||
"2500",
|
||||
"3000",
|
||||
"3500",
|
||||
"4000",
|
||||
"4500",
|
||||
"5000",
|
||||
)
|
||||
entries = arrayOf(
|
||||
"50 MB",
|
||||
"75 MB",
|
||||
"100 MB",
|
||||
"150 MB",
|
||||
"250 MB",
|
||||
"500 MB",
|
||||
"750 MB",
|
||||
"1 GB",
|
||||
"1.5 GB",
|
||||
"2 GB",
|
||||
"2.5 GB",
|
||||
"3 GB",
|
||||
"3.5 GB",
|
||||
"4 GB",
|
||||
"4.5 GB",
|
||||
"5 GB",
|
||||
)
|
||||
summaryRes = R.string.reader_cache_size_summary
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.aggressivePageLoading())
|
||||
titleRes = R.string.aggressively_load_pages
|
||||
summaryRes = R.string.aggressively_load_pages_summary
|
||||
}
|
||||
}
|
||||
|
||||
preferenceCategory {
|
||||
titleRes = R.string.pref_category_fork
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.readerInstantRetry())
|
||||
titleRes = R.string.skip_queue_on_retry
|
||||
summaryRes = R.string.skip_queue_on_retry_summary
|
||||
}
|
||||
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.preserveReadingPosition())
|
||||
titleRes = R.string.preserve_reading_position
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.useAutoWebtoon())
|
||||
titleRes = R.string.auto_webtoon_mode
|
||||
summaryRes = R.string.auto_webtoon_mode_summary
|
||||
}
|
||||
|
||||
preference {
|
||||
key = "reader_bottom_buttons_pref"
|
||||
titleRes = R.string.reader_bottom_buttons
|
||||
summaryRes = R.string.reader_bottom_buttons_summary
|
||||
|
||||
onClick {
|
||||
ReaderBottomButtonsDialog().showDialog(router)
|
||||
}
|
||||
}
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.pageLayout())
|
||||
titleRes = R.string.page_layout
|
||||
summaryRes = R.string.automatic_can_still_switch
|
||||
entriesRes = arrayOf(
|
||||
R.string.single_page,
|
||||
R.string.double_pages,
|
||||
R.string.automatic_orientation,
|
||||
)
|
||||
entryValues = arrayOf("0", "1", "2")
|
||||
}
|
||||
switchPreference {
|
||||
bindTo(readerPreferences.invertDoublePages())
|
||||
titleRes = R.string.invert_double_pages
|
||||
visibleIf(readerPreferences.pageLayout()) { it != PagerConfig.PageLayout.SINGLE_PAGE }
|
||||
}
|
||||
|
||||
intListPreference {
|
||||
bindTo(readerPreferences.centerMarginType())
|
||||
titleRes = R.string.center_margin
|
||||
entriesRes = arrayOf(
|
||||
R.string.center_margin_none,
|
||||
R.string.center_margin_double_page,
|
||||
R.string.center_margin_wide_page,
|
||||
R.string.center_margin_double_and_wide_page,
|
||||
)
|
||||
entryValues = arrayOf("0", "1", "2", "3")
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
}
|
||||
|
||||
// SY -->
|
||||
class ReaderBottomButtonsDialog : DialogController() {
|
||||
|
||||
private val readerPreferences: ReaderPreferences = Injekt.get()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val oldSelection = readerPreferences.readerBottomButtons().get()
|
||||
val values = ReaderBottomButton.values()
|
||||
|
||||
val selection = values.map { it.value in oldSelection }
|
||||
.toBooleanArray()
|
||||
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.reader_bottom_buttons)
|
||||
.setMultiChoiceItems(
|
||||
values.map { activity!!.getString(it.stringRes) }.toTypedArray(),
|
||||
selection,
|
||||
) { _, which, selected ->
|
||||
selection[which] = selected
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val included = values
|
||||
.filterIndexed { index, _ ->
|
||||
selection[index]
|
||||
}
|
||||
.map { it.value }
|
||||
.toSet()
|
||||
|
||||
readerPreferences.readerBottomButtons().set(included)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
}
|
@ -1,203 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.core.security.SecurityPreferences
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||
import eu.kanade.tachiyomi.ui.base.delegate.SecureActivityDelegate
|
||||
import eu.kanade.tachiyomi.ui.category.biometric.BiometricTimesController
|
||||
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.onClick
|
||||
import eu.kanade.tachiyomi.util.preference.preference
|
||||
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
|
||||
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()
|
||||
}
|
||||
|
||||
// SY -->
|
||||
preference {
|
||||
key = "pref_edit_lock_times"
|
||||
titleRes = R.string.action_edit_biometric_lock_times
|
||||
|
||||
val timeRanges = securityPreferences.authenticatorTimeRanges().get().size
|
||||
summary = context.resources.getQuantityString(R.plurals.num_lock_times, timeRanges, timeRanges)
|
||||
|
||||
visibleIf(securityPreferences.useAuthenticator()) { it }
|
||||
|
||||
onClick {
|
||||
router.pushController(BiometricTimesController())
|
||||
}
|
||||
}
|
||||
preference {
|
||||
key = "pref_edit_lock_days"
|
||||
titleRes = R.string.biometric_lock_days
|
||||
summaryRes = R.string.biometric_lock_days_summary
|
||||
|
||||
visibleIf(securityPreferences.useAuthenticator()) { it }
|
||||
|
||||
onClick {
|
||||
SetLockedDaysDialog().showDialog(router)
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
|
||||
infoPreference(R.string.secure_screen_summary)
|
||||
}
|
||||
|
||||
// SY -->
|
||||
class SetLockedDaysDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
val securityPreferences: SecurityPreferences by injectLazy()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val activity = activity!!
|
||||
val options = listOf(
|
||||
R.string.sunday,
|
||||
R.string.monday,
|
||||
R.string.tuesday,
|
||||
R.string.wednesday,
|
||||
R.string.thursday,
|
||||
R.string.friday,
|
||||
R.string.saturday,
|
||||
)
|
||||
.map { activity.getString(it) }
|
||||
.toTypedArray()
|
||||
|
||||
val lockDays = securityPreferences.authenticatorDays().get()
|
||||
val selection = BooleanArray(7) {
|
||||
when (it) {
|
||||
0 -> (lockDays and SecureActivityDelegate.LOCK_SUNDAY) == SecureActivityDelegate.LOCK_SUNDAY
|
||||
1 -> (lockDays and SecureActivityDelegate.LOCK_MONDAY) == SecureActivityDelegate.LOCK_MONDAY
|
||||
2 -> (lockDays and SecureActivityDelegate.LOCK_TUESDAY) == SecureActivityDelegate.LOCK_TUESDAY
|
||||
3 -> (lockDays and SecureActivityDelegate.LOCK_WEDNESDAY) == SecureActivityDelegate.LOCK_WEDNESDAY
|
||||
4 -> (lockDays and SecureActivityDelegate.LOCK_THURSDAY) == SecureActivityDelegate.LOCK_THURSDAY
|
||||
5 -> (lockDays and SecureActivityDelegate.LOCK_FRIDAY) == SecureActivityDelegate.LOCK_FRIDAY
|
||||
6 -> (lockDays and SecureActivityDelegate.LOCK_SATURDAY) == SecureActivityDelegate.LOCK_SATURDAY
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(activity)
|
||||
.setTitle(R.string.biometric_lock_days)
|
||||
.setMultiChoiceItems(
|
||||
options,
|
||||
selection,
|
||||
) { _, which, selected ->
|
||||
selection[which] = selected
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
var flags = 0
|
||||
selection.forEachIndexed { index, checked ->
|
||||
if (checked) {
|
||||
when (index) {
|
||||
0 -> flags = flags or SecureActivityDelegate.LOCK_SUNDAY
|
||||
1 -> flags = flags or SecureActivityDelegate.LOCK_MONDAY
|
||||
2 -> flags = flags or SecureActivityDelegate.LOCK_TUESDAY
|
||||
3 -> flags = flags or SecureActivityDelegate.LOCK_WEDNESDAY
|
||||
4 -> flags = flags or SecureActivityDelegate.LOCK_THURSDAY
|
||||
5 -> flags = flags or SecureActivityDelegate.LOCK_FRIDAY
|
||||
6 -> flags = flags or SecureActivityDelegate.LOCK_SATURDAY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
securityPreferences.authenticatorDays().set(flags)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
}
|
@ -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,68 +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>, /* SY --> */ keepReadManga: Boolean /* SY <-- */) {
|
||||
// SY -->
|
||||
if (keepReadManga) {
|
||||
database.mangasQueries.deleteMangasNotInLibraryAndNotReadBySourceIds(sourceIds)
|
||||
} else {
|
||||
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sourceIds)
|
||||
}
|
||||
// SY <--
|
||||
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,93 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.eh
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ScrollView
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.EhDialogCategoriesBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsEhController
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class FrontPageCategoriesDialog(
|
||||
bundle: Bundle? = null,
|
||||
) : DialogController(bundle) {
|
||||
|
||||
var binding: EhDialogCategoriesBinding? = null
|
||||
private set
|
||||
|
||||
val preferences: UnsortedPreferences by injectLazy()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = EhDialogCategoriesBinding.inflate(LayoutInflater.from(activity!!))
|
||||
val view = ScrollView(binding!!.root.context).apply {
|
||||
addView(binding!!.root)
|
||||
}
|
||||
onViewCreated()
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.language_filtering)
|
||||
.setMessage(R.string.language_filtering_summary)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
onPositive()
|
||||
}
|
||||
.setOnDismissListener {
|
||||
onPositive()
|
||||
}
|
||||
.setOnCancelListener {
|
||||
onPositive()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
fun onViewCreated() {
|
||||
with(binding!!) {
|
||||
val list = preferences.exhEnabledCategories().get().split(",").map { !it.toBoolean() }
|
||||
doujinshiCheckbox.isChecked = list[0]
|
||||
mangaCheckbox.isChecked = list[1]
|
||||
artistCgCheckbox.isChecked = list[2]
|
||||
gameCgCheckbox.isChecked = list[3]
|
||||
westernCheckbox.isChecked = list[4]
|
||||
nonHCheckbox.isChecked = list[5]
|
||||
imageSetCheckbox.isChecked = list[6]
|
||||
cosplayCheckbox.isChecked = list[7]
|
||||
asianPornCheckbox.isChecked = list[8]
|
||||
miscCheckbox.isChecked = list[9]
|
||||
}
|
||||
}
|
||||
|
||||
fun onPositive() {
|
||||
with(binding!!) {
|
||||
preferences.exhEnabledCategories().set(
|
||||
listOf(
|
||||
(!doujinshiCheckbox.isChecked),
|
||||
(!mangaCheckbox.isChecked),
|
||||
(!artistCgCheckbox.isChecked),
|
||||
(!gameCgCheckbox.isChecked),
|
||||
(!westernCheckbox.isChecked),
|
||||
(!nonHCheckbox.isChecked),
|
||||
(!imageSetCheckbox.isChecked),
|
||||
(!cosplayCheckbox.isChecked),
|
||||
(!asianPornCheckbox.isChecked),
|
||||
(!miscCheckbox.isChecked),
|
||||
).joinToString(separator = ",") { it.toString() },
|
||||
)
|
||||
}
|
||||
with(targetController as? SettingsEhController ?: return) {
|
||||
unsortedPreferences.exhSettingsLanguages().reconfigure()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (!type.isEnter) {
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
}
|
@ -1,183 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.eh
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.ScrollView
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.EhDialogLanguagesBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsEhController
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class LanguagesDialog(
|
||||
bundle: Bundle? = null,
|
||||
) : DialogController(bundle) {
|
||||
|
||||
var binding: EhDialogLanguagesBinding? = null
|
||||
private set
|
||||
|
||||
val preferences: UnsortedPreferences by injectLazy()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = EhDialogLanguagesBinding.inflate(LayoutInflater.from(activity!!))
|
||||
val view = ScrollView(binding!!.root.context).apply {
|
||||
addView(binding!!.root)
|
||||
}
|
||||
onViewCreated()
|
||||
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.language_filtering)
|
||||
.setMessage(R.string.language_filtering_summary)
|
||||
.setView(view)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
onPositive()
|
||||
}
|
||||
.setOnDismissListener {
|
||||
onPositive()
|
||||
}
|
||||
.setOnCancelListener {
|
||||
onPositive()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
fun onViewCreated() {
|
||||
val settingsLanguages = preferences.exhSettingsLanguages().get().split("\n")
|
||||
|
||||
val japanese = settingsLanguages[0].split("*").map { it.toBoolean() }
|
||||
val english = settingsLanguages[1].split("*").map { it.toBoolean() }
|
||||
val chinese = settingsLanguages[2].split("*").map { it.toBoolean() }
|
||||
val dutch = settingsLanguages[3].split("*").map { it.toBoolean() }
|
||||
val french = settingsLanguages[4].split("*").map { it.toBoolean() }
|
||||
val german = settingsLanguages[5].split("*").map { it.toBoolean() }
|
||||
val hungarian = settingsLanguages[6].split("*").map { it.toBoolean() }
|
||||
val italian = settingsLanguages[7].split("*").map { it.toBoolean() }
|
||||
val korean = settingsLanguages[8].split("*").map { it.toBoolean() }
|
||||
val polish = settingsLanguages[9].split("*").map { it.toBoolean() }
|
||||
val portuguese = settingsLanguages[10].split("*").map { it.toBoolean() }
|
||||
val russian = settingsLanguages[11].split("*").map { it.toBoolean() }
|
||||
val spanish = settingsLanguages[12].split("*").map { it.toBoolean() }
|
||||
val thai = settingsLanguages[13].split("*").map { it.toBoolean() }
|
||||
val vietnamese = settingsLanguages[14].split("*").map { it.toBoolean() }
|
||||
val notAvailable =
|
||||
settingsLanguages[15].split("*").map { it.toBoolean() }
|
||||
val other = settingsLanguages[16].split("*").map { it.toBoolean() }
|
||||
|
||||
with(binding!!) {
|
||||
japaneseOriginal.isChecked = japanese[0]
|
||||
japaneseTranslated.isChecked = japanese[1]
|
||||
japaneseRewrite.isChecked = japanese[2]
|
||||
|
||||
japaneseOriginal.isChecked = japanese[0]
|
||||
japaneseTranslated.isChecked = japanese[1]
|
||||
japaneseRewrite.isChecked = japanese[2]
|
||||
|
||||
englishOriginal.isChecked = english[0]
|
||||
englishTranslated.isChecked = english[1]
|
||||
englishRewrite.isChecked = english[2]
|
||||
|
||||
chineseOriginal.isChecked = chinese[0]
|
||||
chineseTranslated.isChecked = chinese[1]
|
||||
chineseRewrite.isChecked = chinese[2]
|
||||
|
||||
dutchOriginal.isChecked = dutch[0]
|
||||
dutchTranslated.isChecked = dutch[1]
|
||||
dutchRewrite.isChecked = dutch[2]
|
||||
|
||||
frenchOriginal.isChecked = french[0]
|
||||
frenchTranslated.isChecked = french[1]
|
||||
frenchRewrite.isChecked = french[2]
|
||||
|
||||
germanOriginal.isChecked = german[0]
|
||||
germanTranslated.isChecked = german[1]
|
||||
germanRewrite.isChecked = german[2]
|
||||
|
||||
hungarianOriginal.isChecked = hungarian[0]
|
||||
hungarianTranslated.isChecked = hungarian[1]
|
||||
hungarianRewrite.isChecked = hungarian[2]
|
||||
|
||||
italianOriginal.isChecked = italian[0]
|
||||
italianTranslated.isChecked = italian[1]
|
||||
italianRewrite.isChecked = italian[2]
|
||||
|
||||
koreanOriginal.isChecked = korean[0]
|
||||
koreanTranslated.isChecked = korean[1]
|
||||
koreanRewrite.isChecked = korean[2]
|
||||
|
||||
polishOriginal.isChecked = polish[0]
|
||||
polishTranslated.isChecked = polish[1]
|
||||
polishRewrite.isChecked = polish[2]
|
||||
|
||||
portugueseOriginal.isChecked = portuguese[0]
|
||||
portugueseTranslated.isChecked = portuguese[1]
|
||||
portugueseRewrite.isChecked = portuguese[2]
|
||||
|
||||
russianOriginal.isChecked = russian[0]
|
||||
russianTranslated.isChecked = russian[1]
|
||||
russianRewrite.isChecked = russian[2]
|
||||
|
||||
spanishOriginal.isChecked = spanish[0]
|
||||
spanishTranslated.isChecked = spanish[1]
|
||||
spanishRewrite.isChecked = spanish[2]
|
||||
|
||||
thaiOriginal.isChecked = thai[0]
|
||||
thaiTranslated.isChecked = thai[1]
|
||||
thaiRewrite.isChecked = thai[2]
|
||||
|
||||
vietnameseOriginal.isChecked = vietnamese[0]
|
||||
vietnameseTranslated.isChecked = vietnamese[1]
|
||||
vietnameseRewrite.isChecked = vietnamese[2]
|
||||
|
||||
notAvailableOriginal.isChecked = notAvailable[0]
|
||||
notAvailableTranslated.isChecked = notAvailable[1]
|
||||
notAvailableRewrite.isChecked = notAvailable[2]
|
||||
|
||||
otherOriginal.isChecked = other[0]
|
||||
otherTranslated.isChecked = other[1]
|
||||
otherRewrite.isChecked = other[2]
|
||||
}
|
||||
}
|
||||
|
||||
fun onPositive() {
|
||||
val languages = with(binding!!) {
|
||||
listOf(
|
||||
"${japaneseOriginal.isChecked}*${japaneseTranslated.isChecked}*${japaneseRewrite.isChecked}",
|
||||
"${englishOriginal.isChecked}*${englishTranslated.isChecked}*${englishRewrite.isChecked}",
|
||||
"${chineseOriginal.isChecked}*${chineseTranslated.isChecked}*${chineseRewrite.isChecked}",
|
||||
"${dutchOriginal.isChecked}*${dutchTranslated.isChecked}*${dutchRewrite.isChecked}",
|
||||
"${frenchOriginal.isChecked}*${frenchTranslated.isChecked}*${frenchRewrite.isChecked}",
|
||||
"${germanOriginal.isChecked}*${germanTranslated.isChecked}*${germanRewrite.isChecked}",
|
||||
"${hungarianOriginal.isChecked}*${hungarianTranslated.isChecked}*${hungarianRewrite.isChecked}",
|
||||
"${italianOriginal.isChecked}*${italianTranslated.isChecked}*${italianRewrite.isChecked}",
|
||||
"${koreanOriginal.isChecked}*${koreanTranslated.isChecked}*${koreanRewrite.isChecked}",
|
||||
"${polishOriginal.isChecked}*${polishTranslated.isChecked}*${polishRewrite.isChecked}",
|
||||
"${portugueseOriginal.isChecked}*${portugueseTranslated.isChecked}*${portugueseRewrite.isChecked}",
|
||||
"${russianOriginal.isChecked}*${russianTranslated.isChecked}*${russianRewrite.isChecked}",
|
||||
"${spanishOriginal.isChecked}*${spanishTranslated.isChecked}*${spanishRewrite.isChecked}",
|
||||
"${thaiOriginal.isChecked}*${thaiTranslated.isChecked}*${thaiRewrite.isChecked}",
|
||||
"${vietnameseOriginal.isChecked}*${vietnameseTranslated.isChecked}*${vietnameseRewrite.isChecked}",
|
||||
"${notAvailableOriginal.isChecked}*${notAvailableTranslated.isChecked}*${notAvailableRewrite.isChecked}",
|
||||
"${otherOriginal.isChecked}*${otherTranslated.isChecked}*${otherRewrite.isChecked}",
|
||||
).joinToString("\n")
|
||||
}
|
||||
|
||||
preferences.exhSettingsLanguages().set(languages)
|
||||
|
||||
with(targetController as? SettingsEhController ?: return) {
|
||||
unsortedPreferences.exhSettingsLanguages().reconfigure()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (!type.isEnter) {
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
}
|
@ -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,159 +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.domain.UnsortedPreferences
|
||||
import eu.kanade.domain.source.service.SourcePreferences
|
||||
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.SettingsEhController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsGeneralController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsLibraryController
|
||||
import eu.kanade.tachiyomi.ui.setting.SettingsMangaDexController
|
||||
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 exh.md.utils.MdUtil
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
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.
|
||||
*/
|
||||
// SY -->
|
||||
private val settingControllersList: List<KClass<out SettingsController>> = run {
|
||||
val controllers = mutableListOf(
|
||||
SettingsAdvancedController::class,
|
||||
SettingsAppearanceController::class,
|
||||
SettingsBackupController::class,
|
||||
SettingsBrowseController::class,
|
||||
SettingsDownloadController::class,
|
||||
SettingsGeneralController::class,
|
||||
SettingsLibraryController::class,
|
||||
SettingsReaderController::class,
|
||||
SettingsSecurityController::class,
|
||||
SettingsTrackingController::class,
|
||||
)
|
||||
val preferences = Injekt.get<UnsortedPreferences>()
|
||||
val sourcePreferences = Injekt.get<SourcePreferences>()
|
||||
if (MdUtil.getEnabledMangaDexs(sourcePreferences).isNotEmpty()) {
|
||||
controllers += SettingsMangaDexController::class
|
||||
}
|
||||
if (preferences.isHentaiEnabled().get()) {
|
||||
controllers += SettingsEhController::class
|
||||
}
|
||||
controllers
|
||||
}
|
||||
|
||||
// SY <--
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
@ -1,198 +0,0 @@
|
||||
@file:Suppress("NOTHING_TO_INLINE")
|
||||
|
||||
package eu.kanade.tachiyomi.util.preference
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.preference.CheckBoxPreference
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.MultiSelectListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceGroup
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.preference.PreferenceScreen
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
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.getResourceColor
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.preference.AdaptiveTitlePreferenceCategory
|
||||
import eu.kanade.tachiyomi.widget.preference.IntListPreference
|
||||
|
||||
@DslMarker
|
||||
@Target(AnnotationTarget.TYPE)
|
||||
annotation class DSL
|
||||
|
||||
inline fun PreferenceManager.newScreen(block: (@DSL PreferenceScreen).() -> Unit): PreferenceScreen {
|
||||
return createPreferenceScreen(context).also { it.block() }
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.preference(block: (@DSL Preference).() -> Unit): Preference {
|
||||
return initThenAdd(Preference(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.infoPreference(@StringRes infoRes: Int): Preference {
|
||||
return add(
|
||||
Preference(context).apply {
|
||||
iconRes = R.drawable.ic_info_24dp
|
||||
iconTint = context.getResourceColor(android.R.attr.textColorHint)
|
||||
summaryRes = infoRes
|
||||
isSelectable = false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.switchPreference(block: (@DSL SwitchPreferenceCompat).() -> Unit): SwitchPreferenceCompat {
|
||||
return initThenAdd(SwitchPreferenceCompat(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.checkBoxPreference(block: (@DSL CheckBoxPreference).() -> Unit): CheckBoxPreference {
|
||||
return initThenAdd(CheckBoxPreference(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.editTextPreference(block: (@DSL EditTextPreference).() -> Unit): EditTextPreference {
|
||||
return initThenAdd(EditTextPreference(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.listPreference(block: (@DSL ListPreference).() -> Unit): ListPreference {
|
||||
return initThenAdd(ListPreference(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.intListPreference(block: (@DSL IntListPreference).() -> Unit): IntListPreference {
|
||||
return initThenAdd(IntListPreference(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceGroup.multiSelectListPreference(block: (@DSL MultiSelectListPreference).() -> Unit): MultiSelectListPreference {
|
||||
return initThenAdd(MultiSelectListPreference(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceScreen.preferenceCategory(block: (@DSL PreferenceCategory).() -> Unit): PreferenceCategory {
|
||||
return addThenInit(AdaptiveTitlePreferenceCategory(context), block)
|
||||
}
|
||||
|
||||
inline fun PreferenceScreen.preferenceScreen(block: (@DSL PreferenceScreen).() -> Unit): PreferenceScreen {
|
||||
return addThenInit(preferenceManager.createPreferenceScreen(context), block)
|
||||
}
|
||||
|
||||
inline fun <P : Preference> PreferenceGroup.add(p: P): P {
|
||||
return p.apply {
|
||||
this.isIconSpaceReserved = false
|
||||
this.isSingleLineTitle = false
|
||||
addPreference(this)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <P : Preference> PreferenceGroup.initThenAdd(p: P, block: P.() -> Unit): P {
|
||||
return p.apply {
|
||||
block()
|
||||
this.isIconSpaceReserved = false
|
||||
this.isSingleLineTitle = false
|
||||
addPreference(this)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <P : Preference> PreferenceGroup.addThenInit(p: P, block: P.() -> Unit): P {
|
||||
return p.apply {
|
||||
this.isIconSpaceReserved = false
|
||||
this.isSingleLineTitle = false
|
||||
addPreference(this)
|
||||
block()
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Preference.bindTo(preference: eu.kanade.tachiyomi.core.preference.Preference<T>) {
|
||||
key = preference.key()
|
||||
defaultValue = preference.defaultValue()
|
||||
}
|
||||
|
||||
inline fun <T> ListPreference.bindTo(preference: eu.kanade.tachiyomi.core.preference.Preference<T>) {
|
||||
key = preference.key()
|
||||
defaultValue = preference.defaultValue().toString()
|
||||
}
|
||||
|
||||
inline fun Preference.onClick(crossinline block: () -> Unit) {
|
||||
setOnPreferenceClickListener { block(); true }
|
||||
}
|
||||
|
||||
inline fun Preference.onChange(crossinline block: (Any?) -> Boolean) {
|
||||
setOnPreferenceChangeListener { _, newValue -> block(newValue) }
|
||||
}
|
||||
|
||||
inline fun SwitchPreferenceCompat.requireAuthentication(activity: FragmentActivity?, title: String, subtitle: String?) {
|
||||
onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
|
||||
if (context.isAuthenticationSupported()) {
|
||||
activity?.startAuthentication(
|
||||
title,
|
||||
subtitle,
|
||||
callback = object : AuthenticatorUtil.AuthenticationCallback() {
|
||||
override fun onAuthenticationSucceeded(
|
||||
activity: FragmentActivity?,
|
||||
result: BiometricPrompt.AuthenticationResult,
|
||||
) {
|
||||
super.onAuthenticationSucceeded(activity, result)
|
||||
isChecked = newValue as Boolean
|
||||
}
|
||||
|
||||
override fun onAuthenticationError(
|
||||
activity: FragmentActivity?,
|
||||
errorCode: Int,
|
||||
errString: CharSequence,
|
||||
) {
|
||||
super.onAuthenticationError(activity, errorCode, errString)
|
||||
activity?.toast(errString.toString())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
var Preference.defaultValue: Any?
|
||||
get() = null // set only
|
||||
set(value) {
|
||||
setDefaultValue(value)
|
||||
}
|
||||
|
||||
var Preference.titleRes: Int
|
||||
get() = 0 // set only
|
||||
set(value) {
|
||||
setTitle(value)
|
||||
}
|
||||
|
||||
var Preference.iconRes: Int
|
||||
get() = 0 // set only
|
||||
set(value) {
|
||||
icon = AppCompatResources.getDrawable(context, value)
|
||||
}
|
||||
|
||||
var Preference.summaryRes: Int
|
||||
get() = 0 // set only
|
||||
set(value) {
|
||||
setSummary(value)
|
||||
}
|
||||
|
||||
var Preference.iconTint: Int
|
||||
get() = 0 // set only
|
||||
set(value) {
|
||||
icon?.setTint(value)
|
||||
}
|
||||
|
||||
var ListPreference.entriesRes: Array<Int>
|
||||
get() = emptyArray() // set only
|
||||
set(value) {
|
||||
entries = value.map { context.getString(it) }.toTypedArray()
|
||||
}
|
||||
|
||||
var MultiSelectListPreference.entriesRes: Array<Int>
|
||||
get() = emptyArray() // set only
|
||||
set(value) {
|
||||
entries = value.map { context.getString(it) }.toTypedArray()
|
||||
}
|
@ -3,17 +3,10 @@ package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
import android.view.LayoutInflater
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.widget.doAfterTextChanged
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.tachiyomi.databinding.DialogStubQuadstatemultichoiceBinding
|
||||
import eu.kanade.tachiyomi.databinding.DialogStubTextinputBinding
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import kotlin.coroutines.resume
|
||||
|
||||
fun MaterialAlertDialogBuilder.setTextInput(
|
||||
hint: String? = null,
|
||||
@ -34,59 +27,3 @@ fun MaterialAlertDialogBuilder.setTextInput(
|
||||
}
|
||||
return setView(binding.root)
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a list of items with checkboxes that supports 4 states.
|
||||
*
|
||||
* @see eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
*/
|
||||
fun MaterialAlertDialogBuilder.setQuadStateMultiChoiceItems(
|
||||
@StringRes message: Int? = null,
|
||||
isActionList: Boolean = true,
|
||||
items: List<CharSequence>,
|
||||
initialSelected: IntArray,
|
||||
disabledIndices: IntArray? = null,
|
||||
selection: QuadStateMultiChoiceListener,
|
||||
): MaterialAlertDialogBuilder {
|
||||
val binding = DialogStubQuadstatemultichoiceBinding.inflate(LayoutInflater.from(context))
|
||||
binding.list.layoutManager = LinearLayoutManager(context)
|
||||
binding.list.adapter = QuadStateMultiChoiceDialogAdapter(
|
||||
items = items,
|
||||
disabledItems = disabledIndices,
|
||||
initialSelected = initialSelected,
|
||||
isActionList = isActionList,
|
||||
listener = selection,
|
||||
)
|
||||
val updateScrollIndicators = {
|
||||
binding.scrollIndicatorUp.isVisible = binding.list.canScrollVertically(-1)
|
||||
binding.scrollIndicatorDown.isVisible = binding.list.canScrollVertically(1)
|
||||
}
|
||||
binding.list.setOnScrollChangeListener { _, _, _, _, _ ->
|
||||
updateScrollIndicators()
|
||||
}
|
||||
binding.list.post {
|
||||
updateScrollIndicators()
|
||||
}
|
||||
|
||||
if (message != null) {
|
||||
binding.message.setText(message)
|
||||
binding.message.isVisible = true
|
||||
}
|
||||
return setView(binding.root)
|
||||
}
|
||||
|
||||
suspend fun MaterialAlertDialogBuilder.await(
|
||||
@StringRes positiveLabelId: Int,
|
||||
@StringRes negativeLabelId: Int,
|
||||
@StringRes neutralLabelId: Int? = null,
|
||||
) = suspendCancellableCoroutine { cont ->
|
||||
setPositiveButton(positiveLabelId) { _, _ -> cont.resume(AlertDialog.BUTTON_POSITIVE) }
|
||||
setNegativeButton(negativeLabelId) { _, _ -> cont.resume(AlertDialog.BUTTON_NEGATIVE) }
|
||||
if (neutralLabelId != null) {
|
||||
setNeutralButton(neutralLabelId) { _, _ -> cont.resume(AlertDialog.BUTTON_NEUTRAL) }
|
||||
}
|
||||
setOnDismissListener { cont.cancel() }
|
||||
|
||||
val dialog = show()
|
||||
cont.invokeOnCancellation { dialog.dismiss() }
|
||||
}
|
||||
|
@ -1,128 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.databinding.DialogQuadstatemultichoiceItemBinding
|
||||
|
||||
private object CheckPayload
|
||||
private object InverseCheckPayload
|
||||
private object UncheckPayload
|
||||
private object IndeterminatePayload
|
||||
|
||||
typealias QuadStateMultiChoiceListener = (indices: IntArray) -> Unit
|
||||
|
||||
// isAction state: Uncheck-> Check-> Invert else Uncheck-> Indeterminate (only if initial so)-> Check
|
||||
// isAction for list of action to operate on like filter include, exclude
|
||||
internal class QuadStateMultiChoiceDialogAdapter(
|
||||
internal var items: List<CharSequence>,
|
||||
disabledItems: IntArray?,
|
||||
private var initialSelected: IntArray,
|
||||
internal var listener: QuadStateMultiChoiceListener,
|
||||
val isActionList: Boolean = true,
|
||||
) : RecyclerView.Adapter<QuadStateMultiChoiceViewHolder>() {
|
||||
|
||||
private val states = QuadStateTextView.State.values()
|
||||
|
||||
private var currentSelection: IntArray = initialSelected
|
||||
set(value) {
|
||||
val previousSelection = field
|
||||
field = value
|
||||
previousSelection.forEachIndexed { index, previous ->
|
||||
val current = value[index]
|
||||
when {
|
||||
current == QuadStateTextView.State.CHECKED.ordinal && previous != QuadStateTextView.State.CHECKED.ordinal -> {
|
||||
// This value was selected
|
||||
notifyItemChanged(index, CheckPayload)
|
||||
}
|
||||
current == QuadStateTextView.State.INVERSED.ordinal && previous != QuadStateTextView.State.INVERSED.ordinal -> {
|
||||
// This value was inverse selected
|
||||
notifyItemChanged(index, InverseCheckPayload)
|
||||
}
|
||||
current == QuadStateTextView.State.UNCHECKED.ordinal && previous != QuadStateTextView.State.UNCHECKED.ordinal -> {
|
||||
// This value was unselected
|
||||
notifyItemChanged(index, UncheckPayload)
|
||||
}
|
||||
current == QuadStateTextView.State.INDETERMINATE.ordinal && previous != QuadStateTextView.State.INDETERMINATE.ordinal -> {
|
||||
// This value was set back to Indeterminate
|
||||
notifyItemChanged(index, IndeterminatePayload)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private var disabledIndices: IntArray = disabledItems ?: IntArray(0)
|
||||
internal fun itemActionClicked(index: Int) {
|
||||
val newSelection = this.currentSelection.toMutableList()
|
||||
newSelection[index] = when (currentSelection[index]) {
|
||||
QuadStateTextView.State.CHECKED.ordinal -> QuadStateTextView.State.INVERSED.ordinal
|
||||
QuadStateTextView.State.INVERSED.ordinal -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
// INDETERMINATE or UNCHECKED
|
||||
else -> QuadStateTextView.State.CHECKED.ordinal
|
||||
}
|
||||
this.currentSelection = newSelection.toIntArray()
|
||||
listener(currentSelection)
|
||||
}
|
||||
|
||||
internal fun itemDisplayClicked(index: Int) {
|
||||
val newSelection = this.currentSelection.toMutableList()
|
||||
newSelection[index] = when (currentSelection[index]) {
|
||||
QuadStateTextView.State.UNCHECKED.ordinal -> QuadStateTextView.State.CHECKED.ordinal
|
||||
QuadStateTextView.State.CHECKED.ordinal -> when (initialSelected[index]) {
|
||||
QuadStateTextView.State.INDETERMINATE.ordinal -> QuadStateTextView.State.INDETERMINATE.ordinal
|
||||
else -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
}
|
||||
// INDETERMINATE or UNCHECKED
|
||||
else -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
}
|
||||
this.currentSelection = newSelection.toIntArray()
|
||||
listener(currentSelection)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): QuadStateMultiChoiceViewHolder {
|
||||
return QuadStateMultiChoiceViewHolder(
|
||||
itemBinding = DialogQuadstatemultichoiceItemBinding
|
||||
.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||
adapter = this,
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: QuadStateMultiChoiceViewHolder,
|
||||
position: Int,
|
||||
) {
|
||||
holder.isEnabled = !disabledIndices.contains(position)
|
||||
holder.controlView.state = states[currentSelection[position]]
|
||||
holder.controlView.text = items[position]
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: QuadStateMultiChoiceViewHolder,
|
||||
position: Int,
|
||||
payloads: MutableList<Any>,
|
||||
) {
|
||||
when (payloads.firstOrNull()) {
|
||||
CheckPayload -> {
|
||||
holder.controlView.state = QuadStateTextView.State.CHECKED
|
||||
return
|
||||
}
|
||||
InverseCheckPayload -> {
|
||||
holder.controlView.state = QuadStateTextView.State.INVERSED
|
||||
return
|
||||
}
|
||||
UncheckPayload -> {
|
||||
holder.controlView.state = QuadStateTextView.State.UNCHECKED
|
||||
return
|
||||
}
|
||||
IndeterminatePayload -> {
|
||||
holder.controlView.state = QuadStateTextView.State.INDETERMINATE
|
||||
return
|
||||
}
|
||||
}
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.tachiyomi.databinding.DialogQuadstatemultichoiceItemBinding
|
||||
|
||||
internal class QuadStateMultiChoiceViewHolder(
|
||||
itemBinding: DialogQuadstatemultichoiceItemBinding,
|
||||
private val adapter: QuadStateMultiChoiceDialogAdapter,
|
||||
) : RecyclerView.ViewHolder(itemBinding.root), View.OnClickListener {
|
||||
init {
|
||||
itemView.setOnClickListener(this)
|
||||
}
|
||||
|
||||
val controlView = itemBinding.quadStateControl
|
||||
|
||||
var isEnabled: Boolean
|
||||
get() = itemView.isEnabled
|
||||
set(value) {
|
||||
itemView.isEnabled = value
|
||||
controlView.isEnabled = value
|
||||
}
|
||||
|
||||
override fun onClick(view: View) = when (adapter.isActionList) {
|
||||
true -> adapter.itemActionClicked(bindingAdapterPosition)
|
||||
false -> adapter.itemDisplayClicked(bindingAdapterPosition)
|
||||
}
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.materialdialogs
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.ColorStateList
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.getThemeColor
|
||||
|
||||
class QuadStateTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
AppCompatTextView(context, attrs) {
|
||||
|
||||
var state: State = State.UNCHECKED
|
||||
set(value) {
|
||||
field = value
|
||||
updateDrawable()
|
||||
}
|
||||
|
||||
private fun updateDrawable() {
|
||||
val drawableStartId = when (state) {
|
||||
State.UNCHECKED -> R.drawable.ic_check_box_outline_blank_24dp
|
||||
State.INDETERMINATE -> R.drawable.ic_indeterminate_check_box_24dp
|
||||
State.CHECKED -> R.drawable.ic_check_box_24dp
|
||||
State.INVERSED -> R.drawable.ic_check_box_x_24dp
|
||||
}
|
||||
setCompoundDrawablesRelativeWithIntrinsicBounds(drawableStartId, 0, 0, 0)
|
||||
|
||||
val tint = if (state == State.UNCHECKED) {
|
||||
context.getThemeColor(R.attr.colorControlNormal)
|
||||
} else {
|
||||
context.getThemeColor(R.attr.colorPrimary)
|
||||
}
|
||||
if (tint != 0) {
|
||||
TextViewCompat.setCompoundDrawableTintList(this, ColorStateList.valueOf(tint))
|
||||
}
|
||||
}
|
||||
|
||||
enum class State {
|
||||
UNCHECKED,
|
||||
INDETERMINATE,
|
||||
CHECKED,
|
||||
INVERSED,
|
||||
;
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.content.Context
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
/**
|
||||
* PreferenceCategory that hides the title placeholder layout if the title is unset
|
||||
*/
|
||||
class AdaptiveTitlePreferenceCategory(context: Context) : PreferenceCategory(context) {
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
if (title.isNullOrBlank()) {
|
||||
holder.itemView.updateLayoutParams<RecyclerView.LayoutParams> {
|
||||
height = 0
|
||||
topMargin = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.ListPreference
|
||||
|
||||
class IntListPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
ListPreference(context, attrs) {
|
||||
|
||||
override fun persistString(value: String?): Boolean {
|
||||
return value != null && persistInt(value.toInt())
|
||||
}
|
||||
|
||||
override fun getPersistedString(defaultReturnValue: String?): String? {
|
||||
// When the underlying preference is using a PreferenceDataStore, there's no way (for now)
|
||||
// to check if a value is in the store, so we use a most likely unused value as workaround
|
||||
val defaultIntValue = Int.MIN_VALUE + 1
|
||||
|
||||
val value = getPersistedInt(defaultIntValue)
|
||||
return if (value != defaultIntValue) {
|
||||
value.toString()
|
||||
} else {
|
||||
defaultReturnValue
|
||||
}
|
||||
}
|
||||
}
|
@ -1,68 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import androidx.annotation.StringRes
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.dd.processbutton.iml.ActionProcessButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.base.BasePreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.PrefAccountLoginBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
abstract class LoginDialogPreference(
|
||||
@StringRes private val usernameLabelRes: Int? = null,
|
||||
bundle: Bundle? = null,
|
||||
) : DialogController(bundle) {
|
||||
|
||||
var binding: PrefAccountLoginBinding? = null
|
||||
private set
|
||||
|
||||
val preferences: BasePreferences by injectLazy()
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = PrefAccountLoginBinding.inflate(LayoutInflater.from(activity!!))
|
||||
onViewCreated(binding!!.root)
|
||||
val titleName = activity!!.getString(getTitleName())
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(activity!!.getString(R.string.login_title, titleName))
|
||||
.setView(binding!!.root)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
/* SY --> */
|
||||
open /* SY <-- */ fun onViewCreated(view: View) {
|
||||
if (usernameLabelRes != null) {
|
||||
binding!!.usernameLabel.hint = view.context.getString(usernameLabelRes)
|
||||
}
|
||||
|
||||
binding!!.login.setMode(ActionProcessButton.Mode.ENDLESS)
|
||||
binding!!.login.setOnClickListener { checkLogin() }
|
||||
|
||||
setCredentialsOnView(view)
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (!type.isEnter) {
|
||||
onDialogClosed()
|
||||
}
|
||||
}
|
||||
|
||||
open fun onDialogClosed() {
|
||||
binding = null
|
||||
}
|
||||
|
||||
@StringRes
|
||||
protected abstract fun getTitleName(): Int
|
||||
|
||||
protected abstract fun checkLogin()
|
||||
|
||||
protected abstract fun setCredentialsOnView(view: View)
|
||||
}
|
@ -1,76 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.domain.ui.model.AppTheme
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.util.system.dpToPx
|
||||
|
||||
class ThemesPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
ListPreference(context, attrs),
|
||||
ThemesPreferenceAdapter.OnItemClickListener {
|
||||
|
||||
private var recycler: RecyclerView? = null
|
||||
private val adapter = ThemesPreferenceAdapter(this)
|
||||
|
||||
var lastScrollPosition: Int? = null
|
||||
|
||||
var entries: List<AppTheme> = emptyList()
|
||||
set(value) {
|
||||
field = value
|
||||
adapter.setItems(value)
|
||||
}
|
||||
|
||||
init {
|
||||
layoutResource = R.layout.pref_themes_list
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
|
||||
recycler = holder.findViewById(R.id.themes_list) as RecyclerView
|
||||
recycler?.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false)
|
||||
recycler?.adapter = adapter
|
||||
|
||||
// Retain scroll position on activity recreate after changing theme
|
||||
recycler?.addOnScrollListener(
|
||||
object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
lastScrollPosition = recyclerView.computeHorizontalScrollOffset()
|
||||
}
|
||||
},
|
||||
)
|
||||
lastScrollPosition?.let { scrollToOffset(it) }
|
||||
}
|
||||
|
||||
override fun onItemClick(position: Int) {
|
||||
if (position !in 0..entries.size) {
|
||||
return
|
||||
}
|
||||
|
||||
callChangeListener(value)
|
||||
value = entries[position].name
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
// no-op; not actually a DialogPreference
|
||||
}
|
||||
|
||||
private fun scrollToOffset(lX: Int) {
|
||||
recycler?.let {
|
||||
(it.layoutManager as LinearLayoutManager).apply {
|
||||
scrollToPositionWithOffset(
|
||||
// 114dp is the width of the pref_theme_item layout
|
||||
lX / 114.dpToPx,
|
||||
-lX % 114.dpToPx,
|
||||
)
|
||||
}
|
||||
lastScrollPosition = it.computeHorizontalScrollOffset()
|
||||
}
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.kanade.domain.ui.UiPreferences
|
||||
import eu.kanade.domain.ui.model.AppTheme
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.PrefThemeItemBinding
|
||||
import eu.kanade.tachiyomi.ui.base.delegate.ThemingDelegate
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class ThemesPreferenceAdapter(private val clickListener: OnItemClickListener) :
|
||||
RecyclerView.Adapter<ThemesPreferenceAdapter.ThemeViewHolder>() {
|
||||
|
||||
private val preferences: UiPreferences by injectLazy()
|
||||
|
||||
private var themes = emptyList<AppTheme>()
|
||||
|
||||
private lateinit var binding: PrefThemeItemBinding
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ThemeViewHolder {
|
||||
val themeResIds = ThemingDelegate.getThemeResIds(themes[viewType], preferences.themeDarkAmoled().get())
|
||||
val themedContext = themeResIds.fold(parent.context) {
|
||||
context, themeResId ->
|
||||
ContextThemeWrapper(context, themeResId)
|
||||
}
|
||||
|
||||
binding = PrefThemeItemBinding.inflate(LayoutInflater.from(themedContext), parent, false)
|
||||
return ThemeViewHolder(binding.root)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int = position
|
||||
|
||||
override fun getItemCount(): Int = themes.size
|
||||
|
||||
override fun onBindViewHolder(holder: ThemesPreferenceAdapter.ThemeViewHolder, position: Int) {
|
||||
holder.bind(themes[position])
|
||||
}
|
||||
|
||||
fun setItems(themes: List<AppTheme>) {
|
||||
this.themes = themes
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
inner class ThemeViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val selectedColor = view.context.getResourceColor(R.attr.colorAccent)
|
||||
private val unselectedColor = view.context.getResourceColor(android.R.attr.divider)
|
||||
|
||||
fun bind(appTheme: AppTheme) {
|
||||
binding.name.text = view.context.getString(appTheme.titleResId!!)
|
||||
|
||||
// For rounded corners
|
||||
binding.badges.clipToOutline = true
|
||||
|
||||
val isSelected = preferences.appTheme().get() == appTheme
|
||||
binding.themeCard.isChecked = isSelected
|
||||
binding.themeCard.strokeColor = if (isSelected) selectedColor else unselectedColor
|
||||
|
||||
listOf(binding.root, binding.themeCard).forEach {
|
||||
it.setOnClickListener {
|
||||
clickListener.onItemClick(bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnItemClickListener {
|
||||
fun onItemClick(position: Int)
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
package eu.kanade.tachiyomi.widget.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.widget.ImageView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import com.google.android.material.card.MaterialCardView
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
||||
class TrackerPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
Preference(context, attrs) {
|
||||
|
||||
init {
|
||||
layoutResource = R.layout.pref_tracker_item
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
|
||||
val logoContainer = holder.findViewById(R.id.logo_container) as MaterialCardView
|
||||
val checkedIcon = holder.findViewById(R.id.checked_icon) as ImageView
|
||||
|
||||
logoContainer.setCardBackgroundColor(iconColor)
|
||||
checkedIcon.isVisible = !getPersistedString("").isNullOrEmpty()
|
||||
}
|
||||
|
||||
@ColorInt
|
||||
var iconColor: Int = Color.TRANSPARENT
|
||||
set(value) {
|
||||
field = value
|
||||
notifyChanged()
|
||||
}
|
||||
|
||||
public override fun notifyChanged() {
|
||||
super.notifyChanged()
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package exh.widget.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.PrefItemMangadexBinding
|
||||
import eu.kanade.tachiyomi.source.online.all.MangaDex
|
||||
import eu.kanade.tachiyomi.util.system.getResourceColor
|
||||
|
||||
class MangaDexLoginPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
val source: MangaDex,
|
||||
attrs: AttributeSet? = null,
|
||||
) : Preference(context, attrs) {
|
||||
|
||||
init {
|
||||
layoutResource = R.layout.pref_item_mangadex
|
||||
}
|
||||
|
||||
var binding: PrefItemMangadexBinding? = null
|
||||
|
||||
private var onLoginClick: () -> Unit = {}
|
||||
|
||||
override fun onBindViewHolder(holder: PreferenceViewHolder) {
|
||||
super.onBindViewHolder(holder)
|
||||
binding = PrefItemMangadexBinding.bind(holder.itemView)
|
||||
holder.itemView.setOnClickListener {
|
||||
onLoginClick()
|
||||
}
|
||||
val loginFrame = binding?.loginFrame
|
||||
val color = if (source.isLogged()) {
|
||||
context.getResourceColor(R.attr.colorAccent)
|
||||
} else {
|
||||
context.getResourceColor(R.attr.colorSecondary)
|
||||
}
|
||||
|
||||
binding?.login?.setImageResource(R.drawable.ic_outline_people_alt_24dp)
|
||||
binding?.login?.drawable?.setTint(color)
|
||||
|
||||
loginFrame?.isVisible = true
|
||||
loginFrame?.setOnClickListener {
|
||||
onLoginClick()
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnLoginClickListener(block: () -> Unit) {
|
||||
onLoginClick = block
|
||||
}
|
||||
|
||||
// Make method public
|
||||
public override fun notifyChanged() {
|
||||
super.notifyChanged()
|
||||
}
|
||||
}
|
@ -1,158 +0,0 @@
|
||||
package exh.widget.preference
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import com.dd.processbutton.iml.ActionProcessButton
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.UnsortedPreferences
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.PrefSiteLoginTwoFactorAuthBinding
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.LoginSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import exh.log.xLogW
|
||||
import exh.source.getMainSource
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.launch
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MangadexLoginDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
|
||||
val source = Injekt.get<SourceManager>().get(args.getLong("key", 0))?.getMainSource() as LoginSource
|
||||
|
||||
val preferences: UnsortedPreferences by injectLazy()
|
||||
|
||||
val scope = CoroutineScope(Job() + Dispatchers.Main)
|
||||
|
||||
lateinit var binding: PrefSiteLoginTwoFactorAuthBinding
|
||||
|
||||
constructor(source: LoginSource) : this(
|
||||
bundleOf(
|
||||
"key" to source.id,
|
||||
),
|
||||
)
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
binding = PrefSiteLoginTwoFactorAuthBinding.inflate(LayoutInflater.from(activity!!))
|
||||
onViewCreated()
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setView(binding.root)
|
||||
.create()
|
||||
}
|
||||
|
||||
fun onViewCreated() {
|
||||
binding.login.setMode(ActionProcessButton.Mode.ENDLESS)
|
||||
binding.login.setOnClickListener { checkLogin() }
|
||||
|
||||
setCredentialsOnView()
|
||||
|
||||
when (source.twoFactorAuth) {
|
||||
LoginSource.AuthSupport.REQUIRED -> {
|
||||
binding.twoFactorCheck.isVisible = false
|
||||
binding.twoFactorHolder.isVisible = true
|
||||
}
|
||||
LoginSource.AuthSupport.SUPPORTED -> {
|
||||
binding.twoFactorCheck.setOnCheckedChangeListener { _, isChecked ->
|
||||
binding.twoFactorHolder.isVisible = isChecked
|
||||
}
|
||||
}
|
||||
LoginSource.AuthSupport.NOT_SUPPORTED -> {
|
||||
binding.twoFactorCheck.isVisible = false
|
||||
binding.twoFactorHolder.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCredentialsOnView() {
|
||||
binding.username.setText(source.getUsername())
|
||||
binding.password.setText(source.getPassword())
|
||||
}
|
||||
|
||||
private fun checkLogin() {
|
||||
val username = binding.username.text?.toString()
|
||||
val password = binding.password.text?.toString()
|
||||
val twoFactor = binding.twoFactorEdit.text?.toString()
|
||||
if (username.isNullOrBlank() || password.isNullOrBlank() || (binding.twoFactorCheck.isChecked && twoFactor.isNullOrBlank())) {
|
||||
errorResult()
|
||||
launchUI {
|
||||
binding.root.context.toast(R.string.fields_cannot_be_blank)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
binding.login.progress = 1
|
||||
|
||||
dialog?.setCancelable(false)
|
||||
dialog?.setCanceledOnTouchOutside(false)
|
||||
|
||||
scope.launch {
|
||||
try {
|
||||
val result = source.login(
|
||||
username,
|
||||
password,
|
||||
twoFactor.toString(),
|
||||
)
|
||||
if (result) {
|
||||
dialog?.dismiss()
|
||||
withUIContext {
|
||||
binding.root.context.toast(R.string.login_success)
|
||||
}
|
||||
} else {
|
||||
errorResult()
|
||||
}
|
||||
} catch (error: Exception) {
|
||||
errorResult()
|
||||
xLogW("Login to Mangadex error", error)
|
||||
error.message?.let { launchUI { binding.root.context.toast(it) } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun errorResult() {
|
||||
with(binding) {
|
||||
dialog?.setCancelable(true)
|
||||
dialog?.setCanceledOnTouchOutside(true)
|
||||
login.progress = -1
|
||||
login.setText(R.string.unknown_error)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onChangeStarted(handler: ControllerChangeHandler, type: ControllerChangeType) {
|
||||
super.onChangeStarted(handler, type)
|
||||
if (!type.isEnter) {
|
||||
onDialogClosed()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onDialogClosed() {
|
||||
scope.cancel()
|
||||
if (activity != null) {
|
||||
(activity as? Listener)?.siteLoginDialogClosed(source)
|
||||
} else {
|
||||
(targetController as? Listener)?.siteLoginDialogClosed(source)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
scope.cancel()
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun siteLoginDialogClosed(source: Source)
|
||||
}
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
package exh.widget.preference
|
||||
|
||||
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.source.Source
|
||||
import eu.kanade.tachiyomi.source.SourceManager
|
||||
import eu.kanade.tachiyomi.source.online.LoginSource
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import exh.source.getMainSource
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
|
||||
class MangadexLogoutDialog(bundle: Bundle? = null) : DialogController(bundle) {
|
||||
|
||||
val source = Injekt.get<SourceManager>().get(args.getLong("key", 0))?.getMainSource() as LoginSource
|
||||
|
||||
val trackManager: TrackManager by injectLazy()
|
||||
|
||||
constructor(source: Source) : this(
|
||||
bundleOf(
|
||||
"key" to source.id,
|
||||
),
|
||||
)
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.logout)
|
||||
.setPositiveButton(R.string.logout) { _, _ ->
|
||||
launchIO {
|
||||
if (source.logout()) {
|
||||
withUIContext {
|
||||
activity?.toast(R.string.logout_success)
|
||||
(targetController as? Listener)?.siteLogoutDialogClosed(source)
|
||||
}
|
||||
} else {
|
||||
withUIContext {
|
||||
activity?.toast(R.string.unknown_error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun siteLogoutDialogClosed(source: Source)
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4c-1.48,0 -2.85,0.43 -4.01,1.17l1.46,1.46C10.21,6.23 11.08,6 12,6c3.04,0 5.5,2.46 5.5,5.5v0.5H19c1.66,0 3,1.34 3,3 0,1.13 -0.64,2.11 -1.56,2.62l1.45,1.45C23.16,18.16 24,16.68 24,15c0,-2.64 -2.05,-4.78 -4.65,-4.96zM3,5.27l2.75,2.74C2.56,8.15 0,10.77 0,14c0,3.31 2.69,6 6,6h11.73l2,2L21,20.73 4.27,4 3,5.27zM7.73,10l8,8H6c-2.21,0 -4,-1.79 -4,-4s1.79,-4 4,-4h1.73z" />
|
||||
</vector>
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M9,7L7,7v2h2L9,7zM9,11L7,11v2h2v-2zM9,3c-1.11,0 -2,0.9 -2,2h2L9,3zM13,15h-2v2h2v-2zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM13,3h-2v2h2L13,3zM9,17v-2L7,15c0,1.1 0.89,2 2,2zM19,13h2v-2h-2v2zM19,9h2L21,7h-2v2zM19,17c1.1,0 2,-0.9 2,-2h-2v2zM5,7L3,7v12c0,1.1 0.89,2 2,2h12v-2L5,19L5,7zM15,5h2L17,3h-2v2zM15,17h2v-2h-2v2z" />
|
||||
</vector>
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M7,13H17V11H7ZM5,21Q4.175,21 3.587,20.413Q3,19.825 3,19V5Q3,4.175 3.587,3.587Q4.175,3 5,3H19Q19.825,3 20.413,3.587Q21,4.175 21,5V19Q21,19.825 20.413,20.413Q19.825,21 19,21Z"/>
|
||||
</vector>
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z" />
|
||||
</vector>
|
@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/black"
|
||||
android:pathData="M4,11h5L9,5L4,5v6zM4,18h5v-6L4,12v6zM10,18h5v-6h-5v6zM16,18h5v-6h-5v6zM10,11h5L15,5h-5v6zM16,5v6h5L21,5h-5z" />
|
||||
</vector>
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="#00ffffff"
|
||||
android:startColor="#ffffffff" />
|
||||
|
||||
<corners android:radius="0dp" />
|
||||
</shape>
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="#00000000"
|
||||
android:centerColor="#CC000000"
|
||||
android:startColor="#E6000000" />
|
||||
|
||||
<corners android:radius="0dp" />
|
||||
</shape>
|
@ -1,14 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:type="radial"
|
||||
android:gradientRadius="18dp"
|
||||
android:startColor="#CC000000"
|
||||
android:centerColor="#CC000000"
|
||||
android:endColor="#0D000000" />
|
||||
|
||||
<corners android:radius="0dp" />
|
||||
|
||||
</shape>
|
@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
style="?attr/materialAlertDialogBodyTextStyle"
|
||||
android:id="@+id/quad_state_control"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackground"
|
||||
android:minHeight="?attr/listPreferredItemHeightSmall"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
android:paddingStart="@dimen/abc_select_dialog_padding_start_material"
|
||||
android:paddingEnd="?attr/dialogPreferredPadding"
|
||||
android:drawablePadding="20dp"
|
||||
android:ellipsize="marquee"
|
||||
app:drawableStartCompat="@drawable/ic_check_box_outline_blank_24dp"
|
||||
tools:text="Quad-state item" />
|
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:minHeight="48dp">
|
||||
|
||||
<Space
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/abc_dialog_title_divider_material" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
style="?attr/materialAlertDialogBodyTextStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/abc_dialog_title_divider_material"
|
||||
android:paddingHorizontal="?attr/dialogPreferredPadding"
|
||||
android:visibility="gone"
|
||||
tools:text="Dialog Message for quad-state dialog"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:id="@+id/scrollIndicatorUp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="top" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollIndicators="none"
|
||||
tools:listitem="@layout/dialog_quadstatemultichoice_item" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:id="@+id/scrollIndicatorDown"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,238 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_row="0"
|
||||
android:layout_column="0"
|
||||
android:divider="?attr/colorOnBackground"
|
||||
android:padding="16dp"
|
||||
android:showDividers="middle">
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:text="Category" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:text="Enabled" />
|
||||
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/doujinshi"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Doujinshi" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/doujinshi_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manga"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Manga" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/manga_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/artist_cg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Artist CG" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/artist_cg_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/game_cg"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Game CG" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/game_cg_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/western"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Western" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/western_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/non_h"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Non-H" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/non_h_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/image_set"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Image Set" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/image_set_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cosplay"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Cosplay" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/cosplay_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/asian_porn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Asian Porn" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/asian_porn_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/misc"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Misc" />
|
||||
|
||||
<com.google.android.material.materialswitch.MaterialSwitch
|
||||
android:id="@+id/misc_checkbox"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="16dp" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
@ -1,565 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_row="0"
|
||||
android:layout_column="0"
|
||||
android:padding="16dp" >
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="4dp"
|
||||
android:text="Language" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:text="Original" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:text="Translated" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="8dp"
|
||||
android:text="Rewrite" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/japanese"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Japanese" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/japanese_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="invisible" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/japanese_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/japanese_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/english"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="English" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/english_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/english_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/english_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/chinese"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Chinese" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/chinese_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/chinese_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/chinese_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dutch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Dutch" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/dutch_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/dutch_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/dutch_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/french"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="French" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/french_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/french_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/french_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/german"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="German" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/german_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/german_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/german_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hungarian"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Hungarian" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/hungarian_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/hungarian_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/hungarian_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/italian"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Italian" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/italian_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/italian_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/italian_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/korean"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Korean" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/korean_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/korean_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/korean_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/polish"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Polish" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/polish_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/polish_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/polish_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/portuguese"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Portuguese" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/portuguese_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/portuguese_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/portuguese_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/russian"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Russian" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/russian_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/russian_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/russian_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/spanish"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Spanish" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/spanish_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/spanish_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/spanish_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/thai"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Thai" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/thai_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/thai_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/thai_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/vietnamese"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Vietnamese" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/vietnamese_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/vietnamese_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/vietnamese_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/not_available"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="N/A" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/not_available_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/not_available_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/not_available_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
|
||||
<TableRow
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/other"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:text="Other" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/other_original"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/other_translated"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/other_rewrite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
</TableRow>
|
||||
</TableLayout>
|
@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="24dp"
|
||||
android:paddingEnd="24dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/username_label"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/username">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="username"
|
||||
android:inputType="text" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
app:endIconMode="password_toggle">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="password"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.dd.processbutton.iml.ActionProcessButton
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/login"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:pb_colorNormal="?attr/colorPrimary"
|
||||
app:pb_colorPressed="?attr/colorPrimary"
|
||||
app:pb_textComplete="@string/login_success"
|
||||
app:pb_textError="@string/invalid_login"
|
||||
app:pb_textProgress="@string/loading" />
|
||||
|
||||
</LinearLayout>
|
@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?selectableItemBackground"
|
||||
android:baselineAligned="false"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="center_vertical"
|
||||
android:minHeight="42dp"
|
||||
android:paddingStart="?listPreferredItemPaddingStart"
|
||||
android:paddingEnd="?listPreferredItemPaddingEnd"
|
||||
tools:ignore="RtlHardcoded">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@android:id/widget_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"/>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:singleLine="true"
|
||||
android:textAppearance="?textAppearanceListItem"/>
|
||||
|
||||
<!-- Hidden view -->
|
||||
<TextView
|
||||
android:id="@android:id/summary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/login_frame"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="-16dp"
|
||||
android:clipToPadding="false"
|
||||
android:gravity="end|center_vertical"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/login" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:baselineAligned="false"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/portrait" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
||||
android:id="@+id/portrait_columns"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:max="10"
|
||||
app:min="0" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/landscape" />
|
||||
|
||||
<eu.kanade.tachiyomi.widget.MinMaxNumberPicker
|
||||
android:id="@+id/landscape_columns"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:max="10"
|
||||
app:min="0" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@ -1,86 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="24dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dialog_title"
|
||||
style="@style/TextAppearance.MaterialComponents.Headline6"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
tools:text="Log in to MangaDex" />
|
||||
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/username_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/username">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
|
||||
android:id="@+id/username"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="username"
|
||||
android:inputType="text" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/password"
|
||||
app:endIconMode="password_toggle">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
|
||||
android:id="@+id/password"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:autofillHints="password"
|
||||
android:inputType="textPassword" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/two_factor_holder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="12dp"
|
||||
android:hint="@string/two_factor"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<eu.kanade.tachiyomi.widget.TachiyomiTextInputEditText
|
||||
android:id="@+id/two_factor_edit"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textNoSuggestions" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<com.google.android.material.checkbox.MaterialCheckBox
|
||||
android:id="@+id/two_factor_check"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/two_factor" />
|
||||
|
||||
<com.dd.processbutton.iml.ActionProcessButton
|
||||
android:id="@+id/login"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="20dp"
|
||||
android:text="@string/login"
|
||||
android:textColor="?attr/colorOnPrimary"
|
||||
app:pb_colorNormal="?attr/colorPrimary"
|
||||
app:pb_colorPressed="?attr/colorPrimary"
|
||||
app:pb_textComplete="@string/login_success"
|
||||
app:pb_textError="@string/invalid_login"
|
||||
app:pb_textProgress="@string/loading" />
|
||||
|
||||
</LinearLayout>
|
@ -1,148 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="114dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="4dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/theme_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkable="true"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:importantForAccessibility="no"
|
||||
app:cardCornerRadius="17dp"
|
||||
app:strokeColor="?attr/colorAccent"
|
||||
app:strokeWidth="4dp"
|
||||
app:cardElevation="0dp">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="170dp"
|
||||
android:background="?android:attr/colorBackground">
|
||||
|
||||
<View
|
||||
android:id="@+id/top_nav"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/top_nav_text"
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="17dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginStart="12dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/top_nav"
|
||||
app:layout_constraintStart_toStartOf="@+id/top_nav"
|
||||
app:layout_constraintTop_toTopOf="@+id/top_nav"
|
||||
app:cardBackgroundColor="?attr/colorOnSurface"
|
||||
app:cardCornerRadius="4dp"/>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/cover_container"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintTop_toBottomOf="@+id/top_nav"
|
||||
app:layout_constraintDimensionRatio="2:2.7"
|
||||
app:layout_constraintStart_toStartOf="@id/top_nav_text"
|
||||
app:layout_constraintEnd_toStartOf="@id/center_guideline"
|
||||
app:cardBackgroundColor="?android:attr/divider"
|
||||
app:cardElevation="0dp" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginTop="4dp"
|
||||
app:layout_constraintStart_toStartOf="@+id/cover_container"
|
||||
app:layout_constraintTop_toTopOf="@+id/cover_container"
|
||||
app:cardCornerRadius="6dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/badges"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/rounded_rectangle">
|
||||
|
||||
<View
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="?attr/colorTertiary" />
|
||||
|
||||
<View
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="?attr/colorSecondary" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/bottom_nav"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="40dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:cardCornerRadius="0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="12dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/bottom_nav_selected_item"
|
||||
android:layout_width="17dp"
|
||||
android:layout_height="17dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
app:cardBackgroundColor="?attr/colorPrimary"
|
||||
app:cardCornerRadius="100dp"/>
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/bottom_nav_unselected_item"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="17dp"
|
||||
android:alpha="0.6"
|
||||
app:cardBackgroundColor="?attr/colorOnSurface"
|
||||
app:cardCornerRadius="4dp"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/center_guideline"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintGuide_percent="0.5" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32sp"
|
||||
android:maxLines="2"
|
||||
android:layout_marginTop="4dp"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="?android:attr/textAppearanceListItemSecondary"
|
||||
android:scrollbars="none"
|
||||
tools:text="Theme Name" />
|
||||
|
||||
</LinearLayout>
|
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingTop="4dp"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Menu"
|
||||
tools:text="App theme" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/themes_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="12dp"
|
||||
android:paddingVertical="8dp"
|
||||
android:clipToPadding="false"
|
||||
tools:listitem="@layout/pref_theme_item" />
|
||||
|
||||
</LinearLayout>
|
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/logo_container"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
app:cardBackgroundColor="#2E51A2"
|
||||
app:cardElevation="0dp"
|
||||
app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.MaterialCardView.Tracker">
|
||||
|
||||
<ImageView
|
||||
android:id="@android:id/icon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:importantForAccessibility="no"
|
||||
android:padding="4dp"
|
||||
tools:src="@drawable/ic_tracker_mal" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
tools:text="MyAnimeList" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/checked_icon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:src="@drawable/ic_done_green_24dp" />
|
||||
|
||||
</LinearLayout>
|
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_select_all"
|
||||
android:icon="@drawable/ic_select_all_24dp"
|
||||
android:title="@string/action_select_all"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_select_inverse"
|
||||
android:icon="@drawable/ic_flip_to_back_24dp"
|
||||
android:title="@string/action_select_inverse"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
@ -1,11 +0,0 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_backup_help"
|
||||
android:icon="@drawable/ic_help_24dp"
|
||||
android:title="@string/label_help"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
@ -1,11 +0,0 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_tracking_help"
|
||||
android:icon="@drawable/ic_help_24dp"
|
||||
android:title="@string/tracking_guide"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
</menu>
|
@ -56,7 +56,6 @@ natural-comparator = "com.github.gpanther:java-nat-sort:natural-comparator-1.1"
|
||||
markwon = "io.noties.markwon:core:4.6.2"
|
||||
|
||||
material = "com.google.android.material:material:1.7.0-rc01"
|
||||
androidprocessbutton = "com.github.dmytrodanylyk.android-process-button:library:1.0.4"
|
||||
flexible-adapter-core = "com.github.arkon.FlexibleAdapter:flexible-adapter:c8013533"
|
||||
flexible-adapter-ui = "com.github.arkon.FlexibleAdapter:flexible-adapter-ui:c8013533"
|
||||
photoview = "com.github.chrisbanes:PhotoView:2.3.0"
|
||||
|
Loading…
x
Reference in New Issue
Block a user