Search through settings

This commit is contained in:
Koitharu
2024-10-20 17:01:17 +03:00
parent 100073f45e
commit 3d285104a4
28 changed files with 434 additions and 12 deletions

View File

@@ -9,7 +9,10 @@ import androidx.annotation.CallSuper
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceScreen
import androidx.preference.get
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@@ -20,9 +23,11 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner import org.koitharu.kotatsu.core.ui.util.RecyclerViewOwner
import org.koitharu.kotatsu.core.ui.util.WindowInsetsDelegate import org.koitharu.kotatsu.core.ui.util.WindowInsetsDelegate
import org.koitharu.kotatsu.core.util.ext.getThemeColor import org.koitharu.kotatsu.core.util.ext.getThemeColor
import org.koitharu.kotatsu.core.util.ext.getThemeDrawable
import org.koitharu.kotatsu.core.util.ext.parentView import org.koitharu.kotatsu.core.util.ext.parentView
import org.koitharu.kotatsu.settings.SettingsActivity import org.koitharu.kotatsu.settings.SettingsActivity
import javax.inject.Inject import javax.inject.Inject
import com.google.android.material.R as materialR
@AndroidEntryPoint @AndroidEntryPoint
abstract class BasePreferenceFragment(@StringRes private val titleId: Int) : abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
@@ -67,6 +72,10 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setTitle(if (titleId != 0) getString(titleId) else null) setTitle(if (titleId != 0) getString(titleId) else null)
arguments?.getString(SettingsActivity.ARG_PREF_KEY)?.let {
focusPreference(it)
arguments?.remove(SettingsActivity.ARG_PREF_KEY)
}
} }
@CallSuper @CallSuper
@@ -87,4 +96,31 @@ abstract class BasePreferenceFragment(@StringRes private val titleId: Int) :
Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show() Snackbar.make(listView, R.string.operation_not_supported, Snackbar.LENGTH_SHORT).show()
false false
} }
private fun focusPreference(key: String) {
val pref = findPreference<Preference>(key)
if (pref == null) {
scrollToPreference(key)
return
}
scrollToPreference(pref)
val prefIndex = preferenceScreen.indexOf(key)
val view = if (prefIndex >= 0) {
listView.findViewHolderForAdapterPosition(prefIndex)?.itemView ?: return
} else {
return
}
view.context.getThemeDrawable(materialR.attr.colorTertiaryContainer)?.let {
view.background = it
}
}
private fun PreferenceScreen.indexOf(key: String): Int {
for (i in 0 until preferenceCount) {
if (get(i).key == key) {
return i
}
}
return -1
}
} }

View File

@@ -28,6 +28,8 @@ class AdapterDelegateClickListenerAdapter<I, O>(
private fun mappedItem(): O = itemMapper.apply(adapterDelegate.item) private fun mappedItem(): O = itemMapper.apply(adapterDelegate.item)
fun attach() = attach(adapterDelegate.itemView)
fun attach(itemView: View) { fun attach(itemView: View) {
itemView.setOnClickListener(this) itemView.setOnClickListener(this)
itemView.setOnLongClickListener(this) itemView.setOnLongClickListener(this)

View File

@@ -74,6 +74,12 @@ class SearchSuggestionFragment :
companion object { companion object {
@Deprecated("",
ReplaceWith(
"SearchSuggestionFragment()",
"org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionFragment"
)
)
fun newInstance() = SearchSuggestionFragment() fun newInstance() = SearchSuggestionFragment()
} }
} }

View File

@@ -6,7 +6,9 @@ import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.provider.Settings import android.provider.Settings
import android.view.ViewGroup.MarginLayoutParams import android.view.ViewGroup.MarginLayoutParams
import androidx.activity.viewModels
import androidx.core.graphics.Insets import androidx.core.graphics.Insets
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@@ -23,11 +25,17 @@ import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.MangaSourceInfo import org.koitharu.kotatsu.core.model.MangaSourceInfo
import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource import org.koitharu.kotatsu.core.parser.external.ExternalMangaSource
import org.koitharu.kotatsu.core.ui.BaseActivity import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.observeEvent
import org.koitharu.kotatsu.core.util.ext.textAndVisible import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ActivitySettingsBinding import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.about.AboutSettingsFragment import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
import org.koitharu.kotatsu.settings.search.SettingsItem
import org.koitharu.kotatsu.settings.search.SettingsSearchFragment
import org.koitharu.kotatsu.settings.search.SettingsSearchMenuProvider
import org.koitharu.kotatsu.settings.search.SettingsSearchViewModel
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
import org.koitharu.kotatsu.settings.sources.manage.SourcesManageFragment import org.koitharu.kotatsu.settings.sources.manage.SourcesManageFragment
@@ -48,6 +56,8 @@ class SettingsActivity :
private var screenPadding = 0 private var screenPadding = 0
private val viewModel: SettingsSearchViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(ActivitySettingsBinding.inflate(layoutInflater)) setContentView(ActivitySettingsBinding.inflate(layoutInflater))
@@ -64,6 +74,9 @@ class SettingsActivity :
replace(R.id.container_master, RootSettingsFragment()) replace(R.id.container_master, RootSettingsFragment())
} }
} }
viewModel.isSearchActive.observe(this, ::toggleSearchMode)
viewModel.onNavigateToPreference.observeEvent(this, ::navigateToPreference)
addMenuProvider(SettingsSearchMenuProvider(viewModel))
addMenuProvider(SettingsMenuProvider(this)) addMenuProvider(SettingsMenuProvider(this))
} }
@@ -97,6 +110,7 @@ class SettingsActivity :
} }
fun openFragment(fragmentClass: Class<out Fragment>, args: Bundle?, isFromRoot: Boolean) { fun openFragment(fragmentClass: Class<out Fragment>, args: Bundle?, isFromRoot: Boolean) {
viewModel.discardSearch()
val hasFragment = supportFragmentManager.findFragmentById(R.id.container) != null val hasFragment = supportFragmentManager.findFragmentById(R.id.container) != null
supportFragmentManager.commit { supportFragmentManager.commit {
setReorderingAllowed(true) setReorderingAllowed(true)
@@ -108,6 +122,27 @@ class SettingsActivity :
} }
} }
private fun toggleSearchMode(isEnabled: Boolean) {
viewBinding.containerSearch.isVisible = isEnabled
val searchFragment = supportFragmentManager.findFragmentById(R.id.container_search)
if (searchFragment != null) {
if (!isEnabled) {
invalidateOptionsMenu()
supportFragmentManager.commit {
setReorderingAllowed(true)
remove(searchFragment)
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE)
}
}
} else if (isEnabled) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add(R.id.container_search, SettingsSearchFragment::class.java, null)
setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
}
}
}
private fun openDefaultFragment() { private fun openDefaultFragment() {
val fragment = when (intent?.action) { val fragment = when (intent?.action) {
ACTION_READER -> ReaderSettingsFragment() ACTION_READER -> ReaderSettingsFragment()
@@ -138,6 +173,12 @@ class SettingsActivity :
} }
} }
private fun navigateToPreference(item: SettingsItem) {
val args = Bundle(1)
args.putString(ARG_PREF_KEY, item.key)
openFragment(item.fragmentClass, args, true)
}
companion object { companion object {
private const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS" private const val ACTION_READER = "${BuildConfig.APPLICATION_ID}.action.MANAGE_READER_SETTINGS"
@@ -152,6 +193,7 @@ class SettingsActivity :
private const val EXTRA_SOURCE = "source" private const val EXTRA_SOURCE = "source"
private const val HOST_ABOUT = "about" private const val HOST_ABOUT = "about"
private const val HOST_SYNC_SETTINGS = "sync-settings" private const val HOST_SYNC_SETTINGS = "sync-settings"
const val ARG_PREF_KEY = "pref_key"
fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java) fun newIntent(context: Context) = Intent(context, SettingsActivity::class.java)

View File

@@ -0,0 +1,16 @@
package org.koitharu.kotatsu.settings.search
import androidx.preference.PreferenceFragmentCompat
import org.koitharu.kotatsu.list.ui.model.ListModel
data class SettingsItem(
val key: String,
val title: CharSequence,
val breadcrumbs: List<String>,
val fragmentClass: Class<out PreferenceFragmentCompat>,
) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is SettingsItem && other.key == key
}
}

View File

@@ -0,0 +1,23 @@
package org.koitharu.kotatsu.settings.search
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.list.AdapterDelegateClickListenerAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ItemPreferenceBinding
fun settingsItemAD(
listener: OnListItemClickListener<SettingsItem>,
) = adapterDelegateViewBinding<SettingsItem, SettingsItem, ItemPreferenceBinding>(
{ layoutInflater, parent -> ItemPreferenceBinding.inflate(layoutInflater, parent, false) },
) {
AdapterDelegateClickListenerAdapter(this, listener).attach()
val breadcrumbsSeparator = getString(R.string.breadcrumbs_separator)
bind {
binding.textViewTitle.text = item.title
binding.textViewSummary.textAndVisible = item.breadcrumbs.joinToString(breadcrumbsSeparator)
}
}

View File

@@ -0,0 +1,48 @@
package org.koitharu.kotatsu.settings.search
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.Insets
import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.BaseFragment
import org.koitharu.kotatsu.core.ui.BaseListAdapter
import org.koitharu.kotatsu.core.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.FragmentSearchSuggestionBinding
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
@AndroidEntryPoint
class SettingsSearchFragment : BaseFragment<FragmentSearchSuggestionBinding>(), OnListItemClickListener<SettingsItem> {
private val viewModel: SettingsSearchViewModel by activityViewModels()
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentSearchSuggestionBinding {
return FragmentSearchSuggestionBinding.inflate(inflater, container, false)
}
override fun onViewBindingCreated(binding: FragmentSearchSuggestionBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
val adapter = BaseListAdapter<SettingsItem>()
.addDelegate(ListItemType.NAV_ITEM, settingsItemAD(this))
binding.root.adapter = adapter
binding.root.setHasFixedSize(true)
viewModel.content.observe(viewLifecycleOwner, adapter)
}
override fun onWindowInsetsChanged(insets: Insets) {
val extraPadding = resources.getDimensionPixelOffset(R.dimen.list_spacing)
requireViewBinding().root.updatePadding(
top = extraPadding,
right = insets.right,
left = insets.left,
bottom = insets.bottom,
)
}
override fun onItemClick(item: SettingsItem, view: View) = viewModel.navigateToPreference(item)
}

View File

@@ -0,0 +1,84 @@
package org.koitharu.kotatsu.settings.search
import android.annotation.SuppressLint
import android.content.Context
import androidx.annotation.XmlRes
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.PreferenceManager
import androidx.preference.PreferenceScreen
import androidx.preference.get
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.settings.AppearanceSettingsFragment
import org.koitharu.kotatsu.settings.DownloadsSettingsFragment
import org.koitharu.kotatsu.settings.NetworkSettingsFragment
import org.koitharu.kotatsu.settings.ReaderSettingsFragment
import org.koitharu.kotatsu.settings.ServicesSettingsFragment
import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
import org.koitharu.kotatsu.settings.userdata.UserDataSettingsFragment
import javax.inject.Inject
@Reusable
@SuppressLint("RestrictedApi")
class SettingsSearchHelper @Inject constructor(
@ApplicationContext private val context: Context,
) {
fun inflatePreferences(): List<SettingsItem> {
val preferenceManager = PreferenceManager(context)
val result = ArrayList<SettingsItem>()
preferenceManager.inflateTo(result, R.xml.pref_appearance, emptyList(), AppearanceSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_sources, emptyList(), SourcesSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_reader, emptyList(), ReaderSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_network, emptyList(), NetworkSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_user_data, emptyList(), UserDataSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_downloads, emptyList(), DownloadsSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_tracker, emptyList(), TrackerSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_services, emptyList(), ServicesSettingsFragment::class.java)
preferenceManager.inflateTo(result, R.xml.pref_about, emptyList(), AboutSettingsFragment::class.java)
return result
}
private fun PreferenceManager.inflateTo(
result: MutableList<SettingsItem>,
@XmlRes resId: Int,
breadcrumbs: List<String>,
fragmentClass: Class<out PreferenceFragmentCompat>
) {
val screen = inflateFromResource(context, resId, null)
val screenTitle = screen.title?.toString()
screen.inflateTo(
result = result,
breadcrumbs = if (screenTitle.isNullOrEmpty()) breadcrumbs else breadcrumbs + screenTitle,
fragmentClass = fragmentClass,
)
}
private fun PreferenceScreen.inflateTo(
result: MutableList<SettingsItem>,
breadcrumbs: List<String>,
fragmentClass: Class<out PreferenceFragmentCompat>
): Unit = repeat(preferenceCount) { i ->
val pref = this[i]
if (pref is PreferenceScreen) {
val screenTitle = pref.title?.toString()
pref.inflateTo(
result = result,
breadcrumbs = if (screenTitle.isNullOrEmpty()) breadcrumbs else breadcrumbs + screenTitle,
fragmentClass = fragmentClass,
)
} else {
result.add(
SettingsItem(
key = pref.key ?: return@repeat,
title = pref.title ?: return@repeat,
breadcrumbs = breadcrumbs,
fragmentClass = fragmentClass,
),
)
}
}
}

View File

@@ -0,0 +1,51 @@
package org.koitharu.kotatsu.settings.search
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import androidx.appcompat.widget.SearchView
import androidx.core.view.MenuProvider
import org.koitharu.kotatsu.R
class SettingsSearchMenuProvider(
private val viewModel: SettingsSearchViewModel,
) : MenuProvider, MenuItem.OnActionExpandListener, SearchView.OnQueryTextListener {
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.opt_search, menu)
val menuItem = menu.findItem(R.id.action_search)
menuItem.setOnActionExpandListener(this)
val searchView = menuItem.actionView as SearchView
searchView.setOnQueryTextListener(this)
searchView.queryHint = menuItem.title
}
override fun onPrepareMenu(menu: Menu) {
super.onPrepareMenu(menu)
val currentQuery = viewModel.currentQuery
if (currentQuery.isNotEmpty()) {
val menuItem = menu.findItem(R.id.action_search)
menuItem.expandActionView()
val searchView = menuItem.actionView as SearchView
searchView.setQuery(currentQuery, false)
}
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = false
override fun onMenuItemActionExpand(item: MenuItem): Boolean = true
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
viewModel.discardSearch()
return true
}
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
viewModel.onQueryChanged(newText.orEmpty())
return true
}
}

View File

@@ -0,0 +1,47 @@
package org.koitharu.kotatsu.settings.search
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
import org.koitharu.kotatsu.core.util.ext.call
import javax.inject.Inject
@HiltViewModel
class SettingsSearchViewModel @Inject constructor(
private val searchHelper: SettingsSearchHelper,
) : BaseViewModel() {
private val query = MutableStateFlow("")
private val allSettings by lazy {
searchHelper.inflatePreferences()
}
val content = query.map { q ->
allSettings.filter { it.title.contains(q, ignoreCase = true) }
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, emptyList())
val isSearchActive = query.map {
it.isNotEmpty()
}.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false)
val onNavigateToPreference = MutableEventFlow<SettingsItem>()
val currentQuery: String
get() = query.value
fun onQueryChanged(value: String) {
query.value = value
}
fun discardSearch() = onQueryChanged("")
fun navigateToPreference(item: SettingsItem) {
onNavigateToPreference.call(item)
}
}

View File

@@ -72,4 +72,15 @@
app:layout_constraintStart_toEndOf="@id/container_master" app:layout_constraintStart_toEndOf="@id/container_master"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container_search"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="gone"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/appbar" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -26,4 +26,11 @@
android:layout_height="match_parent" android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" /> app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/container_search"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@@ -0,0 +1,31 @@
<?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:clipToPadding="false"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight"
android:orientation="vertical"
android:padding="4dp"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd">
<TextView
android:id="@+id/textView_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItem"
tools:text="@string/too_many_requests_message" />
<TextView
android:id="@+id/textView_summary"
style="@style/PreferenceSummaryTextStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="2dp"
tools:text="@string/tap_to_try_again" />
</LinearLayout>

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="breadcrumbs_separator" translatable="false"><![CDATA[" < "]]></string>
</resources>

View File

@@ -757,4 +757,5 @@
<string name="screen_orientation">Screen orientation</string> <string name="screen_orientation">Screen orientation</string>
<string name="portrait">Portrait</string> <string name="portrait">Portrait</string>
<string name="landscape">Landscape</string> <string name="landscape">Landscape</string>
<string name="breadcrumbs_separator" translatable="false"><![CDATA[" > "]]></string>
</resources> </resources>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/about">
<Preference <Preference
android:key="app_version" android:key="app_version"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/appearance">
<org.koitharu.kotatsu.settings.utils.ThemeChooserPreference <org.koitharu.kotatsu.settings.utils.ThemeChooserPreference
android:key="color_theme" android:key="color_theme"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/periodic_backups">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="false" android:defaultValue="false"

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:title="@string/downloads"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<Preference <Preference

View File

@@ -2,6 +2,7 @@
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/network"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<ListPreference <ListPreference

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/notifications">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"
@@ -35,4 +36,4 @@
app:allowDividerAbove="true" app:allowDividerAbove="true"
app:isPreferenceVisible="false" /> app:isPreferenceVisible="false" />
</PreferenceScreen> </PreferenceScreen>

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/proxy">
<ListPreference <ListPreference
android:defaultValue="DIRECT" android:defaultValue="DIRECT"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/reader_settings">
<ListPreference <ListPreference
android:entries="@array/reader_modes" android:entries="@array/reader_modes"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/services">
<Preference <Preference
android:enabled="@bool/is_sync_enabled" android:enabled="@bool/is_sync_enabled"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen <androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/remote_sources">
<ListPreference <ListPreference
android:key="sources_sort_order" android:key="sources_sort_order"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/suggestions">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="false" android:defaultValue="false"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/check_for_new_chapters">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:defaultValue="true" android:defaultValue="true"

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen <PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto"
android:title="@string/data_and_privacy">
<SwitchPreferenceCompat <SwitchPreferenceCompat
android:key="protect_app" android:key="protect_app"