Compare commits

...

14 Commits

Author SHA1 Message Date
afd56c02e6 Update parsers 2023-12-12 19:13:29 +02:00
dcf1ffc976 Translated using Weblate (Russian)
Currently translated at 99.8% (543 of 544 strings)

Co-authored-by: Koitharu <nvasya95@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ru/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
Nayuki
91b7028b1a Translated using Weblate (Thai)
Currently translated at 70.4% (382 of 542 strings)

Co-authored-by: Nayuki <me@nayuki.cyou>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/th/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
Anon
734c217c03 Translated using Weblate (Serbian)
Currently translated at 100.0% (542 of 542 strings)

Co-authored-by: Anon <anonymousprivate76@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/sr/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
SENPAi-03
de18c6eb71 Translated using Weblate (Arabic)
Currently translated at 34.3% (186 of 542 strings)

Co-authored-by: SENPAi-03 <naemamro1@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ar/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
Oğuz Ersen
034be6b44e Translated using Weblate (Turkish)
Currently translated at 100.0% (544 of 544 strings)

Translated using Weblate (Turkish)

Currently translated at 100.0% (542 of 542 strings)

Co-authored-by: Oğuz Ersen <oguz@ersen.moe>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/tr/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
gallegonovato
995ff5a764 Translated using Weblate (Spanish)
Currently translated at 100.0% (542 of 542 strings)

Co-authored-by: gallegonovato <fran-carro@hotmail.es>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/es/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
Макар Разин
102bec04d6 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (542 of 542 strings)

Translated using Weblate (Russian)

Currently translated at 100.0% (542 of 542 strings)

Translated using Weblate (Belarusian)

Currently translated at 100.0% (542 of 542 strings)

Co-authored-by: Макар Разин <makarrazin14@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/be/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/ru/
Translate-URL: https://hosted.weblate.org/projects/kotatsu/strings/uk/
Translation: Kotatsu/Strings
2023-12-12 19:13:17 +02:00
d05d807614 Slightly improve welcome screen 2023-12-12 20:05:59 +03:00
bffd75f4d9 Fix crashes 2023-12-12 18:55:22 +02:00
bdaf3da7e0 New welcome screen 2023-12-12 15:58:45 +02:00
353d856bf5 Fix chips style 2023-12-12 14:18:49 +02:00
fca9ba98cd Update parsers 2023-12-11 20:02:09 +03:00
5df76fd881 Fix crashes 2023-12-11 18:46:35 +03:00
28 changed files with 439 additions and 271 deletions

View File

@@ -16,8 +16,8 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 34
versionCode = 603
versionName = '6.5-b1'
versionCode = 604
versionName = '6.5-b2'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
ksp {
@@ -82,7 +82,7 @@ afterEvaluate {
}
dependencies {
//noinspection GradleDependency
implementation('com.github.KotatsuApp:kotatsu-parsers:d6b578acb0') {
implementation('com.github.KotatsuApp:kotatsu-parsers:97338f31c5') {
exclude group: 'org.json', module: 'json'
}

View File

@@ -9,7 +9,6 @@ import androidx.core.view.children
import com.google.android.material.chip.Chip
import com.google.android.material.chip.ChipDrawable
import com.google.android.material.chip.ChipGroup
import com.google.android.material.theme.overlay.MaterialThemeOverlay
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.castOrNull
@@ -42,9 +41,10 @@ class ChipsView @JvmOverloads constructor(
}
init {
chipStyle = context.obtainStyledAttributes(attrs, R.styleable.ChipsView, defStyleAttr, 0).use {
it.getResourceId(R.styleable.ChipsView_chipStyle, R.style.Widget_Kotatsu_Chip)
}
val ta = context.obtainStyledAttributes(attrs, R.styleable.ChipsView, defStyleAttr, 0)
chipStyle = ta.getResourceId(R.styleable.ChipsView_chipStyle, R.style.Widget_Kotatsu_Chip)
ta.recycle()
if (isInEditMode) {
setChips(
List(5) {
@@ -103,14 +103,12 @@ class ChipsView @JvmOverloads constructor(
}
private fun addChip(): Chip {
val themedContext = MaterialThemeOverlay.wrap(context, null, 0, chipStyle)
val chip = Chip(themedContext, null)
val drawable = ChipDrawable.createFromAttributes(themedContext, null, 0, chipStyle)
val chip = Chip(context)
val drawable = ChipDrawable.createFromAttributes(context, null, 0, chipStyle)
chip.setChipDrawable(drawable)
chip.isCheckedIconVisible = true
chip.isChipIconVisible = false
chip.setCheckedIconResource(R.drawable.ic_check)
// chip.checkedIconTint = chip.ic
chip.isCloseIconVisible = onChipCloseClickListener != null
chip.setOnCloseIconClickListener(chipOnCloseListener)
chip.setEnsureMinTouchTargetSize(false)

View File

@@ -50,7 +50,7 @@ private fun getVolumePathBeforeAndroid11(volumeId: String, context: Context): St
val length = ArrayReflect.getLength(checkNotNull(result))
(0 until length).firstNotNullOfOrNull { i ->
val storageVolumeElement = ArrayReflect.get(result, i)
val uuid = getUuid.invoke(storageVolumeElement) as String
val uuid = getUuid.invoke(storageVolumeElement) as String?
val primary = isPrimary.invoke(storageVolumeElement) as Boolean
when {
primary && volumeId == PRIMARY_VOLUME_NAME -> getPath.invoke(storageVolumeElement) as String

View File

@@ -13,6 +13,7 @@ import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.model.MangaPage
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.parsers.util.toFileNameSafe
import java.io.File
@@ -54,7 +55,8 @@ sealed class LocalMangaInput(
zip.isFile -> LocalMangaZipInput(zip)
else -> null
}
if (input?.getMangaInfo()?.id == manga.id) {
val info = runCatchingCancellable { input?.getMangaInfo() }.getOrNull()
if (info?.id == manga.id) {
send(input)
}
}

View File

@@ -5,9 +5,11 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import okio.Closeable
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import org.koitharu.kotatsu.local.data.input.LocalMangaInput
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaChapter
import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
import org.koitharu.kotatsu.parsers.util.toFileNameSafe
import java.io.File
@@ -86,7 +88,11 @@ sealed class LocalMangaOutput(
}
private suspend fun canWriteTo(file: File, manga: Manga): Boolean {
val info = LocalMangaInput.of(file).getMangaInfo() ?: return false
val info = runCatchingCancellable {
LocalMangaInput.of(file).getMangaInfo()
}.onFailure {
it.printStackTraceDebug()
}.getOrNull() ?: return false
return info.id == manga.id
}
}

View File

@@ -55,6 +55,7 @@ import org.koitharu.kotatsu.history.ui.HistoryListFragment
import org.koitharu.kotatsu.local.ui.LocalStorageCleanupWorker
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
import org.koitharu.kotatsu.main.ui.owners.BottomNavOwner
import org.koitharu.kotatsu.main.ui.welcome.WelcomeSheet
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.model.MangaTag
@@ -66,7 +67,6 @@ import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionListener
import org.koitharu.kotatsu.search.ui.suggestion.SearchSuggestionViewModel
import org.koitharu.kotatsu.settings.SettingsActivity
import org.koitharu.kotatsu.settings.about.AppUpdateDialog
import org.koitharu.kotatsu.settings.onboard.OnboardDialogFragment
import javax.inject.Inject
import com.google.android.material.R as materialR
@@ -142,7 +142,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
viewModel.counters.observe(this, ::onCountersChanged)
viewModel.appUpdate.observe(this, MenuInvalidator(this))
viewModel.onFirstStart.observeEvent(this) {
OnboardDialogFragment.show(supportFragmentManager)
WelcomeSheet.show(supportFragmentManager)
}
viewModel.isIncognitoMode.observe(this) {
adjustSearchUI(isSearchOpened(), false)

View File

@@ -0,0 +1,127 @@
package org.koitharu.kotatsu.main.ui.welcome
import android.accounts.AccountManager
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.ActivityResultCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.isGone
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import com.google.android.material.chip.Chip
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.titleResId
import org.koitharu.kotatsu.core.ui.sheet.BaseAdaptiveSheet
import org.koitharu.kotatsu.core.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.showDistinct
import org.koitharu.kotatsu.core.util.ext.tryLaunch
import org.koitharu.kotatsu.databinding.SheetWelcomeBinding
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.settings.backup.RestoreDialogFragment
import java.util.Locale
@AndroidEntryPoint
class WelcomeSheet : BaseAdaptiveSheet<SheetWelcomeBinding>(), ChipsView.OnChipClickListener, View.OnClickListener,
ActivityResultCallback<Uri?> {
private val viewModel by viewModels<WelcomeViewModel>()
private val backupSelectCall = registerForActivityResult(
ActivityResultContracts.OpenDocument(),
this,
)
override fun onCreateViewBinding(inflater: LayoutInflater, container: ViewGroup?): SheetWelcomeBinding {
return SheetWelcomeBinding.inflate(inflater, container, false)
}
override fun onViewBindingCreated(binding: SheetWelcomeBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
binding.textViewWelcomeTitle.isGone = resources.getBoolean(R.bool.is_tablet)
binding.chipsLocales.onChipClickListener = this
binding.chipsType.onChipClickListener = this
binding.chipBackup.setOnClickListener(this)
binding.chipSync.setOnClickListener(this)
viewModel.locales.observe(viewLifecycleOwner, ::onLocalesChanged)
viewModel.types.observe(viewLifecycleOwner, ::onTypesChanged)
}
override fun onChipClick(chip: Chip, data: Any?) {
when (data) {
is ContentType -> viewModel.setTypeChecked(data, chip.isChecked)
is Locale? -> viewModel.setLocaleChecked(data, chip.isChecked)
}
}
override fun onClick(v: View) {
when (v.id) {
R.id.chip_backup -> {
if (!backupSelectCall.tryLaunch(arrayOf("*/*"))) {
Snackbar.make(
v, R.string.operation_not_supported, Snackbar.LENGTH_SHORT,
).show()
}
}
R.id.chip_sync -> {
val am = AccountManager.get(v.context)
val accountType = getString(R.string.account_type_sync)
am.addAccount(accountType, accountType, null, null, requireActivity(), null, null)
}
}
}
override fun onActivityResult(result: Uri?) {
if (result != null) {
RestoreDialogFragment.show(parentFragmentManager, result)
}
}
private fun onLocalesChanged(value: FilterProperty<Locale?>) {
val chips = viewBinding?.chipsLocales ?: return
chips.setChips(
value.availableItems.map {
ChipsView.ChipModel(
tint = 0,
title = it?.getDisplayLanguage(it)?.toTitleCase(it) ?: getString(R.string.various_languages),
icon = 0,
isCheckable = true,
isChecked = it in value.selectedItems,
data = it,
)
},
)
}
private fun onTypesChanged(value: FilterProperty<ContentType>) {
val chips = viewBinding?.chipsType ?: return
chips.setChips(
value.availableItems.map {
ChipsView.ChipModel(
tint = 0,
title = getString(it.titleResId),
icon = 0,
isCheckable = true,
isChecked = it in value.selectedItems,
data = it,
)
},
)
}
companion object {
private const val TAG = "WelcomeSheet"
fun show(fm: FragmentManager) = WelcomeSheet().showDistinct(fm, TAG)
}
}

View File

@@ -0,0 +1,107 @@
package org.koitharu.kotatsu.main.ui.welcome
import android.content.Context
import androidx.core.os.ConfigurationCompat
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.LocaleComparator
import org.koitharu.kotatsu.core.util.ext.sortedWithSafe
import org.koitharu.kotatsu.core.util.ext.toList
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
import org.koitharu.kotatsu.filter.ui.model.FilterProperty
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mapToSet
import java.util.EnumSet
import java.util.Locale
import javax.inject.Inject
@HiltViewModel
class WelcomeViewModel @Inject constructor(
private val repository: MangaSourcesRepository,
@ApplicationContext context: Context,
) : BaseViewModel() {
private val allSources = repository.allMangaSources
private val localesGroups by lazy { allSources.groupBy { it.locale?.let { x -> Locale(x) } } }
private var updateJob: Job
val locales = MutableStateFlow(
FilterProperty<Locale?>(
availableItems = listOf(null),
selectedItems = setOf(null),
isLoading = true,
error = null,
),
)
val types = MutableStateFlow(
FilterProperty(
availableItems = ContentType.entries.toList(),
selectedItems = setOf(ContentType.MANGA),
isLoading = false,
error = null,
),
)
init {
updateJob = launchJob(Dispatchers.Default) {
val languages = localesGroups.keys.associateBy { x -> x?.language }
val selectedLocales = HashSet<Locale?>(2)
selectedLocales += ConfigurationCompat.getLocales(context.resources.configuration).toList()
.firstNotNullOfOrNull { lc -> languages[lc.language] }
selectedLocales += null
locales.value = locales.value.copy(
availableItems = localesGroups.keys.sortedWithSafe(nullsFirst(LocaleComparator())),
selectedItems = selectedLocales,
isLoading = false,
)
}
}
fun setLocaleChecked(locale: Locale?, isChecked: Boolean) {
val snapshot = locales.value
locales.value = snapshot.copy(
selectedItems = if (isChecked) {
snapshot.selectedItems + locale
} else {
snapshot.selectedItems - locale
},
)
val prevJob = updateJob
updateJob = launchJob(Dispatchers.Default) {
prevJob.join()
commit()
}
}
fun setTypeChecked(type: ContentType, isChecked: Boolean) {
val snapshot = types.value
types.value = snapshot.copy(
selectedItems = if (isChecked) {
snapshot.selectedItems + type
} else {
snapshot.selectedItems - type
},
)
val prevJob = updateJob
updateJob = launchJob(Dispatchers.Default) {
prevJob.join()
commit()
}
}
private suspend fun commit() {
val languages = locales.value.selectedItems.mapToSet { it?.language }
val types = types.value.selectedItems
val enabledSources = allSources.filterTo(EnumSet.noneOf(MangaSource::class.java)) { x ->
x.contentType in types && x.locale in languages
}
repository.setSourcesEnabledExclusive(enabledSources)
}
}

View File

@@ -1,64 +0,0 @@
package org.koitharu.kotatsu.settings.onboard
import android.content.DialogInterface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.viewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.ui.AlertDialogFragment
import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.core.util.ext.showAllowStateLoss
import org.koitharu.kotatsu.databinding.DialogOnboardBinding
import org.koitharu.kotatsu.settings.onboard.adapter.SourceLocaleListener
import org.koitharu.kotatsu.settings.onboard.adapter.SourceLocalesAdapter
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
@AndroidEntryPoint
class OnboardDialogFragment :
AlertDialogFragment<DialogOnboardBinding>(),
DialogInterface.OnClickListener, SourceLocaleListener {
private val viewModel by viewModels<OnboardViewModel>()
override fun onCreateViewBinding(
inflater: LayoutInflater,
container: ViewGroup?,
) = DialogOnboardBinding.inflate(inflater, container, false)
override fun onBuildDialog(builder: MaterialAlertDialogBuilder): MaterialAlertDialogBuilder {
super.onBuildDialog(builder)
.setPositiveButton(R.string.done, this)
.setCancelable(false)
builder.setTitle(R.string.welcome)
return builder
}
override fun onViewBindingCreated(binding: DialogOnboardBinding, savedInstanceState: Bundle?) {
super.onViewBindingCreated(binding, savedInstanceState)
val adapter = SourceLocalesAdapter(this)
binding.recyclerView.adapter = adapter
binding.textViewTitle.setText(R.string.onboard_text)
viewModel.list.observe(viewLifecycleOwner, adapter)
}
override fun onItemCheckedChanged(item: SourceLocale, isChecked: Boolean) {
viewModel.setItemChecked(item.key, isChecked)
}
override fun onClick(dialog: DialogInterface, which: Int) {
when (which) {
DialogInterface.BUTTON_POSITIVE -> dialog.dismiss()
}
}
companion object {
private const val TAG = "OnboardDialog"
fun show(fm: FragmentManager) = OnboardDialogFragment().showAllowStateLoss(fm, TAG)
}
}

View File

@@ -1,91 +0,0 @@
package org.koitharu.kotatsu.settings.onboard
import android.content.Context
import androidx.core.os.ConfigurationCompat
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableStateFlow
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.LocaleComparator
import org.koitharu.kotatsu.core.util.ext.sortedWithSafe
import org.koitharu.kotatsu.core.util.ext.toList
import org.koitharu.kotatsu.explore.data.MangaSourcesRepository
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
import org.koitharu.kotatsu.parsers.util.toTitleCase
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
import java.util.EnumSet
import java.util.Locale
import javax.inject.Inject
@HiltViewModel
class OnboardViewModel @Inject constructor(
private val repository: MangaSourcesRepository,
@ApplicationContext context: Context,
) : BaseViewModel() {
private val allSources = repository.allMangaSources
private val locales = allSources.groupBy { it.locale }
private val selectedLocales = HashSet<String?>()
val list = MutableStateFlow<List<SourceLocale>?>(null)
private var updateJob: Job
init {
updateJob = launchJob(Dispatchers.Default) {
if (repository.isSetupRequired()) {
selectedLocales += ConfigurationCompat.getLocales(context.resources.configuration).toList()
.firstOrNull { lc -> lc.language in locales.keys }?.language
selectedLocales += null
} else {
selectedLocales.addAll(
repository.getEnabledSources().mapNotNullToSet { x -> x.locale },
)
}
commit()
}
}
fun setItemChecked(key: String?, isChecked: Boolean) {
val isModified = if (isChecked) {
selectedLocales.add(key)
} else {
selectedLocales.remove(key)
}
if (isModified) {
val prevJob = updateJob
updateJob = launchJob(Dispatchers.Default) {
prevJob.join()
commit()
}
}
}
private suspend fun commit() {
val enabledSources = allSources.filterTo(EnumSet.noneOf(MangaSource::class.java)) { x ->
x.locale in selectedLocales
}
repository.setSourcesEnabledExclusive(enabledSources)
list.value = locales.map { (key, srcs) ->
val locale = key?.let { Locale(it) }
SourceLocale(
key = key,
title = locale?.getDisplayLanguage(locale)?.toTitleCase(locale),
summary = srcs.joinToString { it.title },
isChecked = key in selectedLocales,
)
}.sortedWithSafe(SourceLocaleComparator())
}
private class SourceLocaleComparator : Comparator<SourceLocale> {
private val delegate = nullsFirst(LocaleComparator())
override fun compare(a: SourceLocale, b: SourceLocale): Int {
val localeA = a.key?.let { Locale(it) }
val localeB = b.key?.let { Locale(it) }
return delegate.compare(localeA, localeB)
}
}
}

View File

@@ -1,29 +0,0 @@
package org.koitharu.kotatsu.settings.onboard.adapter
import android.widget.CompoundButton
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.util.ext.setChecked
import org.koitharu.kotatsu.core.util.ext.textAndVisible
import org.koitharu.kotatsu.databinding.ItemSourceLocaleBinding
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
fun sourceLocaleAD(
listener: SourceLocaleListener,
) = adapterDelegateViewBinding<SourceLocale, SourceLocale, ItemSourceLocaleBinding>(
{ inflater, parent -> ItemSourceLocaleBinding.inflate(inflater, parent, false) },
) {
val checkedChangeListener = CompoundButton.OnCheckedChangeListener { _, isChecked ->
listener.onItemCheckedChanged(item, isChecked)
}
binding.switchToggle.setOnCheckedChangeListener(checkedChangeListener)
bind { payloads ->
binding.textViewTitle.text = item.title ?: getString(R.string.different_languages)
binding.textViewDescription.textAndVisible = item.summary
binding.switchToggle.setOnCheckedChangeListener(null)
binding.switchToggle.setChecked(item.isChecked, payloads.isNotEmpty())
binding.switchToggle.setOnCheckedChangeListener(checkedChangeListener)
}
}

View File

@@ -1,8 +0,0 @@
package org.koitharu.kotatsu.settings.onboard.adapter
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
interface SourceLocaleListener {
fun onItemCheckedChanged(item: SourceLocale, isChecked: Boolean)
}

View File

@@ -1,13 +0,0 @@
package org.koitharu.kotatsu.settings.onboard.adapter
import org.koitharu.kotatsu.core.ui.BaseListAdapter
import org.koitharu.kotatsu.settings.onboard.model.SourceLocale
class SourceLocalesAdapter(
listener: SourceLocaleListener,
) : BaseListAdapter<SourceLocale>() {
init {
delegatesManager.addDelegate(sourceLocaleAD(listener))
}
}

View File

@@ -1,35 +0,0 @@
package org.koitharu.kotatsu.settings.onboard.model
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
import org.koitharu.kotatsu.list.ui.model.ListModel
import java.util.Locale
data class SourceLocale(
val key: String?,
val title: String?,
val summary: String?,
val isChecked: Boolean,
) : ListModel, Comparable<SourceLocale> {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is SourceLocale && key == other.key
}
override fun getChangePayload(previousState: ListModel): Any? {
return if (previousState is SourceLocale && previousState.isChecked != isChecked) {
ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED
} else {
super.getChangePayload(previousState)
}
}
override fun compareTo(other: SourceLocale): Int {
return when {
this === other -> 0
key == Locale.getDefault().language -> -2
key == null -> 1
other.key == null -> -1
else -> compareValues(title, other.title)
}
}
}

View File

@@ -0,0 +1,11 @@
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="@android:color/white"
android:pathData="M12 2C13.1 2 14 2.9 14 4S13.1 6 12 6 10 5.1 10 4 10.9 2 12 2M15.9 8.1C15.5 7.7 14.8 7 13.5 7H11C8.2 7 6 4.8 6 2H4C4 5.2 6.1 7.8 9 8.7V22H11V16H13V22H15V10.1L19 14L20.4 12.6L15.9 8.1Z" />
</vector>

View File

@@ -114,10 +114,10 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingVertical="8dp"
android:drawablePadding="16dp"
android:paddingHorizontal="@dimen/margin_normal"
android:gravity="center_vertical"
android:paddingHorizontal="@dimen/margin_normal"
android:paddingVertical="8dp"
android:textAppearance="?textAppearanceBodySmall"
android:visibility="gone"
app:drawableStartCompat="@drawable/ic_error_small"

View File

@@ -0,0 +1,117 @@
<?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="wrap_content"
android:orientation="vertical">
<org.koitharu.kotatsu.core.ui.sheet.AdaptiveSheetHeaderBar
android:id="@+id/headerBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/welcome" />
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollIndicators="top">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/margin_normal">
<TextView
android:id="@+id/textView_welcome_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/welcome"
android:textAppearance="?textAppearanceTitleLarge"
android:drawablePadding="16dp"
app:drawableStartCompat="@drawable/ic_welcome" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:scrollbars="none">
<com.google.android.material.chip.ChipGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/margin_normal"
app:singleLine="true">
<com.google.android.material.chip.Chip
android:id="@+id/chip_backup"
style="@style/Widget.Kotatsu.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/restore_backup"
app:chipIcon="@drawable/ic_backup_restore" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_sync"
style="@style/Widget.Kotatsu.Chip.Assist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sync_auth"
app:chipIcon="@drawable/ic_sync" />
</com.google.android.material.chip.ChipGroup>
</HorizontalScrollView>
<TextView
android:id="@+id/textView_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:paddingHorizontal="@dimen/margin_normal"
android:text="@string/welcome_text"
android:textAppearance="?textAppearanceBodyMedium" />
<TextView
android:id="@+id/textView_locales_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/languages"
android:textAppearance="?textAppearanceTitleSmall" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_locales"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:paddingHorizontal="@dimen/margin_normal"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
<TextView
android:id="@+id/textView_type_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/margin_normal"
android:layout_marginTop="@dimen/margin_normal"
android:singleLine="true"
android:text="@string/type"
android:textAppearance="?textAppearanceTitleSmall" />
<org.koitharu.kotatsu.core.ui.widgets.ChipsView
android:id="@+id/chips_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_normal"
android:paddingHorizontal="@dimen/margin_normal"
app:chipStyle="@style/Widget.Kotatsu.Chip.Filter" />
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</LinearLayout>

View File

@@ -178,4 +178,7 @@
<string name="system_default">الافتراضي</string>
<string name="sign_in">تسجبل الدخول</string>
<string name="domain">المجال</string>
<string name="text_history_holder_primary">كل ما تقرأه سيعرض هنا</string>
<string name="text_search_holder_secondary">حاول إعادة صياغة الكلمات.</string>
<string name="text_empty_holder_primary">يبدو أنه فارغ…</string>
</resources>

View File

@@ -538,4 +538,5 @@
<string name="skip">Прапусціць</string>
<string name="color_correction_apply_text">Гэтыя налады могуць прымяняцца глабальна або толькі да бягучай мангі. Пры глабальным прымяненні індывідуальныя налады не будуць перавызначаны.</string>
<string name="grayscale">Адценні шэрага</string>
<string name="disable_battery_optimization_summary_downloads">Можа дапамагчы з пачаткам загрузкі, калі ў вас узнікаюць з ёй праблемы</string>
</resources>

View File

@@ -538,4 +538,5 @@
<string name="error_filter_states_genre_not_supported">Esta fuente no permite filtrar por géneros ni por estados</string>
<string name="error_filter_locale_genre_not_supported">Esta fuente no permite filtrar por géneros ni por configuración regional</string>
<string name="genres_search_hint">Empieza a escribir el nombre del género</string>
<string name="disable_battery_optimization_summary_downloads">Puede que te ayude a iniciar la descarga si tienes algún problema</string>
</resources>

View File

@@ -538,4 +538,6 @@
<string name="skip">Пропустить</string>
<string name="color_correction_apply_text">Эти настройки можно применить глобально или только к текущей манге. Если применяется глобально, отдельные настройки не будут переопределены.</string>
<string name="grayscale">Оттенки серого</string>
<string name="disable_battery_optimization_summary_downloads">Может помочь с запуском загрузки, если у вас с этим возникают проблемы</string>
<string name="sync_auth">Войти в аккаунт синхронизации</string>
</resources>

View File

@@ -121,7 +121,7 @@
<string name="text_shelf_holder_secondary">Пронађите шта да читате у одељку „Преглед“</string>
<string name="manga_shelf">Полица</string>
<string name="check_for_updates">Провери ажурирања</string>
<string name="feed">Фид</string>
<string name="feed">Новости</string>
<string name="services">Услуге</string>
<string name="show_on_shelf">Прикажи на полици</string>
<string name="explore">Преглед</string>
@@ -159,7 +159,7 @@
<string name="frequency_every_day">Сваки дан</string>
<string name="download_started">Преузимање је почело</string>
<string name="categories">Категорије</string>
<string name="tracking">Праћење</string>
<string name="tracking">Пратим</string>
<string name="progress">Напредак</string>
<string name="cancel_all">Откажи све</string>
<string name="sync_host_description">Можеш да користиш сервер за синхронизацију који се самостално хостује или подразумевани. Не мењај ово ако ниси сигуран шта радиш.</string>
@@ -183,7 +183,7 @@
<string name="error_multiple_states_not_supported">Овај извор манга не подржава филтрирање према више стања</string>
<string name="order_added">Додато</string>
<string name="source_summary_pattern">%1$s, %2$s</string>
<string name="enable_logging">Омогући пријаву</string>
<string name="enable_logging">Омогући дијагностику</string>
<string name="on_device">На уређају</string>
<string name="download_option_whole_manga">Цела манга</string>
<string name="settings_apply_restart_required">Поново покрени апликацију да би применио ове промене</string>
@@ -528,4 +528,15 @@
<string name="done">Готово</string>
<string name="error_no_space_left">Недовољно меморије на уређају</string>
<string name="sign_in">Пријави се</string>
<string name="error_filter_states_genre_not_supported">Овај извор не подржава филтрирање по жанровима и стањима у исто време</string>
<string name="error_filter_locale_genre_not_supported">Овај извор не подржава филтрирање по жанровима и локалним стандардима у исто време</string>
<string name="apply">Примени</string>
<string name="genres_search_hint">Унеси назив жанра</string>
<string name="globally">Глобално</string>
<string name="downloads_settings_info">Можеш да омогућиш успоравање преузимања за сваки извор манге појединачно у поставкама извора ако имаш проблеме са блокирањем од стране сервера</string>
<string name="disable_battery_optimization_summary_downloads">Можда ће вам помоћи да започнете преузимање ако имате проблем са преузимањем</string>
<string name="this_manga">Ова манга</string>
<string name="skip">Прескочи</string>
<string name="color_correction_apply_text">Ова подешавања се могу применити глобално или само на тренутну мангу. Ако се примењују глобално, појединачна подешавања неће бити замењена.</string>
<string name="grayscale">Сиви тон</string>
</resources>

View File

@@ -369,4 +369,13 @@
<string name="zoom_out">ซูมออก</string>
<string name="text_search_holder_secondary">โปรดลองเรียบเรียงคำค้นหา</string>
<string name="last_successful_backup">สำรองข้อมูลสำเร็จล่าสุด: %s</string>
<string name="content_type_manga">มังงะ</string>
<string name="source_summary_pattern">%1$s, %2$s</string>
<string name="reader_optimize">ลดการใช้แรม (เบต้า)</string>
<string name="disable_battery_optimization_summary_downloads">อาจจะช่วยในการเริ่มต้นการดาวน์โหลดถ้าคุณมีปัญหา</string>
<string name="this_manga">มังงะเรื่องนี้</string>
<string name="lock_screen_rotation">ล็อคการหมุนหน้าจอ</string>
<string name="skip">ข้าม</string>
<string name="state_paused">หยุดชั่วคราว</string>
<string name="content_type_other">อื่น</string>
</resources>

View File

@@ -123,7 +123,7 @@
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d / %2$d açık</string>
<string name="text_search_holder_secondary">Sorguyu yeniden biçimlendirmeyi deneyin.</string>
<string name="text_history_holder_primary">Okuduklarınız burada görüntülenecek</string>
<string name="text_history_holder_secondary">Keşfet menüsünde ne okuyacağınızı bulun.</string>
<string name="text_history_holder_secondary">«Keşfet» kısmında neler okuyacağınızı bulun</string>
<string name="text_local_holder_primary">Önce bir şey kaydedin</string>
<string name="text_local_holder_secondary">Çevrim içi kaynaklardan kaydedin veya dosyaları içe aktarın.</string>
<string name="manga_shelf">Raf</string>
@@ -282,8 +282,8 @@
<string name="canceled">İptal edilmiş</string>
<string name="account_already_exists">Hesap zaten var</string>
<string name="back">Geri</string>
<string name="sync">Senkronizasyon</string>
<string name="sync_title">Verini yedekle</string>
<string name="sync">Eşitleme</string>
<string name="sync_title">Verinizi eşitleyin</string>
<string name="email_enter_hint">Devam etmek için E-Postanızı girin</string>
<string name="clear_all_history">Tüm gecmişi temizle</string>
<string name="last_2_hours">Son 2 saat</string>
@@ -377,7 +377,7 @@
<string name="enable">Etkinleştir</string>
<string name="no_thanks">Hayır teşekkürler</string>
<string name="clear_network_cache">İnternet geçmişini temizle</string>
<string name="sync_settings">Eşitleme seçenekleri</string>
<string name="sync_settings">Eşitleme ayarları</string>
<string name="server_address">Sunucu adresi</string>
<string name="sync_host_description">Şirket içinde barındırılan bir eşitleme sunucusu veya varsayılan bir sunucu kullanabilirsiniz. Ne yaptığınızdan emin değilseniz bunu değiştirmeyin.</string>
<string name="mirror_switching_summary">Aynalar mevcutsa hatalarda manga kaynakları için etki alanlarını otomatik olarak değiştir</string>
@@ -528,4 +528,17 @@
<string name="disable_nsfw_summary">Eğer mümkünse yetişkin içerik ve NSFW kaynaklarını arama listesinden kaldır</string>
<string name="available_d">Mevcut:%1$d</string>
<string name="state">Durum</string>
<string name="error_filter_states_genre_not_supported">Hem türlere hem de duruma göre filtreleme bu kaynak tarafından desteklenmiyor</string>
<string name="error_filter_locale_genre_not_supported">Hem türlere hem de yerel ayara göre filtreleme bu kaynak tarafından desteklenmiyor</string>
<string name="apply">Uygula</string>
<string name="genres_search_hint">Tür adını yazmaya başlayın</string>
<string name="globally">Genel</string>
<string name="downloads_settings_info">Sunucu taraflı engelleme ile ilgili sorun yaşıyorsanız, kaynak ayarlarında her manga kaynağı için ayrı ayrı indirme yavaşlatmayı etkinleştirebilirsiniz</string>
<string name="disable_battery_optimization_summary_downloads">Herhangi bir sorun yaşarsanız indirmeyi başlatmanıza yardımcı olabilir</string>
<string name="this_manga">Bu manga</string>
<string name="skip">Atla</string>
<string name="color_correction_apply_text">Bu ayarlar genel olarak veya yalnızca geçerli mangaya uygulanabilir. Genel olarak uygulanırsa, bireysel ayarlar geçersiz kılınmaz.</string>
<string name="grayscale">Gri tonlamalı</string>
<string name="welcome_text">Lütfen hangi içerik kaynaklarını etkinleştirmek istediğinizi seçin. Bu daha sonra ayarlardan da yapılandırılabilir</string>
<string name="sync_auth">Eşitleme hesabında oturum aç</string>
</resources>

View File

@@ -538,4 +538,5 @@
<string name="skip">Пропустити</string>
<string name="color_correction_apply_text">Ці налаштування можна застосувати глобально або лише до поточної манґи. Користувацькі налаштування не будуть замінені, якщо застосовувати їх глобально.</string>
<string name="grayscale">Відтінки сірого</string>
<string name="disable_battery_optimization_summary_downloads">Може допомогти з початком завантаження, якщо у вас виникають проблеми з нею</string>
</resources>

View File

@@ -544,4 +544,6 @@
<string name="error_filter_states_genre_not_supported">Filtering by both genres and states is not supported by this source</string>
<string name="genres_search_hint">Start typing the genre name</string>
<string name="disable_battery_optimization_summary_downloads">Might help with getting the download started if you have any issues with it</string>
<string name="welcome_text">Please select which content sources you would like to enable. This can also be configured later in settings</string>
<string name="sync_auth">Login to sync account</string>
</resources>

View File

@@ -120,13 +120,11 @@
<item name="trackCornerRadius">@dimen/mtrl_progress_indicator_full_rounded_corner_radius</item>
</style>
<style name="Widget.Kotatsu.Chip" parent="Widget.Material3.Chip.Suggestion">
<style name="Widget.Kotatsu.Chip" parent="Widget.Material3.Chip.Suggestion" />
</style>
<style name="Widget.Kotatsu.Chip.Filter" parent="Widget.Material3.Chip.Filter" />
<style name="Widget.Kotatsu.Chip.Filter" parent="Widget.Material3.Chip.Filter">
</style>
<style name="Widget.Kotatsu.Chip.Assist" parent="Widget.Material3.Chip.Assist" />
<style name="Widget.Kotatsu.Button.More" parent="Widget.Material3.Button.TextButton">
<item name="android:minWidth">48dp</item>

View File

@@ -72,7 +72,6 @@
<item name="actionModeCloseDrawable">@drawable/abc_ic_clear_material</item>
<item name="actionModeWebSearchDrawable">@drawable/abc_ic_search_api_material</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Kotatsu</item>
<item name="chipStyle">@style/Widget.Kotatsu.Chip</item>
<item name="textInputStyle">@style/Widget.Material3.TextInputLayout.OutlinedBox</item>
<item name="toolbarStyle">@style/Widget.Material3.Toolbar</item>
<item name="appBarLayoutStyle">@style/Widget.Kotatsu.AppBarLayout</item>