Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7135902100 | ||
|
|
969947ef71 | ||
|
|
806e4eade6 | ||
|
|
063cfbe6b9 | ||
|
|
7cb94a3baa | ||
|
|
894c584c78 | ||
|
|
2f65e7776a | ||
|
|
76c56c9119 | ||
|
|
e0a803399c | ||
|
|
7803f42486 | ||
|
|
39713b3cf6 | ||
|
|
8ebf5cea62 | ||
|
|
663dabe218 | ||
|
|
3a5d0120bf | ||
|
|
a773f932d4 | ||
|
|
2a5812735f | ||
|
|
06ec145802 | ||
|
|
6624778f7f | ||
|
|
1af1f071ad | ||
|
|
f87db4e6d3 | ||
|
|
07bd66fb39 | ||
|
|
4bb0d52217 | ||
|
|
66de4bd49e | ||
|
|
ff12d63696 | ||
|
|
c168a841f3 | ||
|
|
8bfb676e6a | ||
|
|
d5c0ce280e | ||
|
|
b34627c361 | ||
|
|
cbc3be056a | ||
|
|
d9acc4ec18 | ||
|
|
577cc848ee | ||
|
|
8a64c88a07 | ||
|
|
1cd7745e38 | ||
|
|
395b3f7200 | ||
|
|
b8db4c81d8 | ||
|
|
98bd42f3ae | ||
|
|
db8835a7b8 | ||
|
|
afe50a9ed6 | ||
|
|
beba818f57 | ||
|
|
beb17ef442 | ||
|
|
24f1546019 | ||
|
|
1b0fed5c56 |
@@ -16,8 +16,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 590
|
||||
versionName = '6.2.3'
|
||||
versionCode = 595
|
||||
versionName = '6.2.7'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner "org.koitharu.kotatsu.HiltTestRunner"
|
||||
ksp {
|
||||
@@ -82,17 +82,17 @@ afterEvaluate {
|
||||
}
|
||||
dependencies {
|
||||
//noinspection GradleDependency
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:6bf0ae92e4') {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:02463e5833') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.9.10'
|
||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.9.20'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.8.0'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.6.1'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.6.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-service:2.6.2'
|
||||
@@ -119,8 +119,8 @@ dependencies {
|
||||
implementation 'androidx.room:room-ktx:2.6.0'
|
||||
ksp 'androidx.room:room-compiler:2.6.0'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.11.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.12.0'
|
||||
implementation 'com.squareup.okio:okio:3.6.0'
|
||||
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'
|
||||
@@ -128,22 +128,22 @@ dependencies {
|
||||
|
||||
implementation 'com.google.dagger:hilt-android:2.48.1'
|
||||
kapt 'com.google.dagger:hilt-compiler:2.48.1'
|
||||
implementation 'androidx.hilt:hilt-work:1.0.0'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.0.0'
|
||||
implementation 'androidx.hilt:hilt-work:1.1.0'
|
||||
kapt 'androidx.hilt:hilt-compiler:1.1.0'
|
||||
|
||||
implementation 'io.coil-kt:coil-base:2.4.0'
|
||||
implementation 'io.coil-kt:coil-svg:2.4.0'
|
||||
implementation 'io.coil-kt:coil-base:2.5.0'
|
||||
implementation 'io.coil-kt:coil-svg:2.5.0'
|
||||
implementation 'com.github.KotatsuApp:subsampling-scale-image-view:cf089a264d'
|
||||
implementation 'com.github.solkin:disk-lru-cache:1.4'
|
||||
implementation 'io.noties.markwon:core:4.6.2'
|
||||
|
||||
implementation 'ch.acra:acra-http:5.11.2'
|
||||
implementation 'ch.acra:acra-dialog:5.11.2'
|
||||
implementation 'ch.acra:acra-http:5.11.3'
|
||||
implementation 'ch.acra:acra-dialog:5.11.3'
|
||||
|
||||
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
testImplementation 'org.json:json:20230618'
|
||||
testImplementation 'org.json:json:20231013'
|
||||
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3'
|
||||
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
|
||||
1
app/proguard-rules.pro
vendored
1
app/proguard-rules.pro
vendored
@@ -18,3 +18,4 @@
|
||||
-keep class org.koitharu.kotatsu.core.exceptions.* { *; }
|
||||
-keep class org.koitharu.kotatsu.settings.NotificationSettingsLegacyFragment
|
||||
-keep class org.koitharu.kotatsu.core.prefs.ScreenshotsPolicy { *; }
|
||||
-keep class org.koitharu.kotatsu.settings.backup.PeriodicalBackupSettingsFragment { *; }
|
||||
|
||||
@@ -48,8 +48,8 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
||||
if (!catchingWebViewUnavailability {
|
||||
setContentView(
|
||||
ActivityBrowserBinding.inflate(
|
||||
layoutInflater
|
||||
)
|
||||
layoutInflater,
|
||||
),
|
||||
)
|
||||
}) {
|
||||
return
|
||||
@@ -82,9 +82,11 @@ class CloudFlareActivity : BaseActivity<ActivityBrowserBinding>(), CloudFlareCal
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
viewBinding.webView.run {
|
||||
stopLoading()
|
||||
destroy()
|
||||
runCatching {
|
||||
viewBinding.webView
|
||||
}.onSuccess {
|
||||
it.stopLoading()
|
||||
it.destroy()
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class BackupZipOutput(val file: File) : Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
private const val DIR_BACKUPS = "backups"
|
||||
const val DIR_BACKUPS = "backups"
|
||||
|
||||
suspend fun BackupZipOutput(context: Context): BackupZipOutput = runInterruptible(Dispatchers.IO) {
|
||||
val dir = context.run {
|
||||
|
||||
@@ -354,6 +354,16 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
val is32BitColorsEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_32BIT_COLOR, false)
|
||||
|
||||
val isPeriodicalBackupEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_BACKUP_PERIODICAL_ENABLED, false)
|
||||
|
||||
val periodicalBackupFrequency: Long
|
||||
get() = prefs.getString(KEY_BACKUP_PERIODICAL_FREQUENCY, null)?.toLongOrNull() ?: 7L
|
||||
|
||||
var periodicalBackupOutput: Uri?
|
||||
get() = prefs.getString(KEY_BACKUP_PERIODICAL_OUTPUT, null)?.toUriOrNull()
|
||||
set(value) = prefs.edit { putString(KEY_BACKUP_PERIODICAL_OUTPUT, value?.toString()) }
|
||||
|
||||
fun isTipEnabled(tip: String): Boolean {
|
||||
return prefs.getStringSet(KEY_TIPS_CLOSED, emptySet())?.contains(tip) != true
|
||||
}
|
||||
@@ -458,6 +468,10 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_ZOOM_MODE = "zoom_mode"
|
||||
const val KEY_BACKUP = "backup"
|
||||
const val KEY_RESTORE = "restore"
|
||||
const val KEY_BACKUP_PERIODICAL_ENABLED = "backup_periodic"
|
||||
const val KEY_BACKUP_PERIODICAL_FREQUENCY = "backup_periodic_freq"
|
||||
const val KEY_BACKUP_PERIODICAL_OUTPUT = "backup_periodic_output"
|
||||
const val KEY_BACKUP_PERIODICAL_LAST = "backup_periodic_last"
|
||||
const val KEY_HISTORY_GROUPING = "history_grouping"
|
||||
const val KEY_READING_INDICATORS = "reading_indicators"
|
||||
const val KEY_REVERSE_CHAPTERS = "reverse_chapters"
|
||||
|
||||
@@ -6,7 +6,6 @@ import android.graphics.Color
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.CallSuper
|
||||
@@ -96,11 +95,10 @@ abstract class BaseActivity<B : ViewBinding> :
|
||||
insetsDelegate.onViewCreated(binding.root)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem) = if (item.itemId == android.R.id.home) {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
// TODO: navigateUpTo
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
dispatchNavigateUp()
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||
if (BuildConfig.DEBUG && keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
|
||||
@@ -151,6 +149,17 @@ abstract class BaseActivity<B : ViewBinding> :
|
||||
window.statusBarColor = defaultStatusBarColor
|
||||
}
|
||||
|
||||
protected open fun dispatchNavigateUp() {
|
||||
val upIntent = parentActivityIntent
|
||||
if (upIntent != null) {
|
||||
if (!navigateUpTo(upIntent)) {
|
||||
startActivity(upIntent)
|
||||
}
|
||||
} else {
|
||||
finishAfterTransition()
|
||||
}
|
||||
}
|
||||
|
||||
private fun putDataToExtras(intent: Intent?) {
|
||||
intent?.putExtra(EXTRA_DATA, intent.data)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package org.koitharu.kotatsu.core.ui.list
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.AdapterDataObserver
|
||||
|
||||
class RecyclerScrollKeeper(
|
||||
private val rv: RecyclerView,
|
||||
) : AdapterDataObserver() {
|
||||
|
||||
private val scrollUpRunnable = Runnable {
|
||||
(rv.layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(0, 0)
|
||||
}
|
||||
|
||||
fun attach() {
|
||||
rv.adapter?.registerAdapterDataObserver(this)
|
||||
}
|
||||
|
||||
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
|
||||
super.onItemRangeInserted(positionStart, itemCount)
|
||||
if (positionStart == 0 && isScrolledToTop()) {
|
||||
postScrollUp()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onItemRangeMoved(fromPosition: Int, toPosition: Int, itemCount: Int) {
|
||||
super.onItemRangeMoved(fromPosition, toPosition, itemCount)
|
||||
if (toPosition == 0 && isScrolledToTop()) {
|
||||
postScrollUp()
|
||||
}
|
||||
}
|
||||
|
||||
private fun postScrollUp() {
|
||||
rv.postDelayed(scrollUpRunnable, 500L)
|
||||
}
|
||||
|
||||
private fun isScrolledToTop(): Boolean {
|
||||
return (rv.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition() == 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package org.koitharu.kotatsu.core.ui.widgets
|
||||
|
||||
import android.content.Context
|
||||
import android.util.ArrayMap
|
||||
import android.util.AttributeSet
|
||||
import com.google.android.material.slider.Slider
|
||||
import kotlin.math.cbrt
|
||||
import kotlin.math.pow
|
||||
|
||||
class CubicSlider @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : Slider(context, attrs) {
|
||||
|
||||
private val changeListeners = ArrayMap<OnChangeListener, OnChangeListenerMapper>(1)
|
||||
|
||||
override fun setValue(value: Float) {
|
||||
super.setValue(value.unmap())
|
||||
}
|
||||
|
||||
override fun getValue(): Float {
|
||||
return super.getValue().map()
|
||||
}
|
||||
|
||||
override fun getValueFrom(): Float {
|
||||
return super.getValueFrom().map()
|
||||
}
|
||||
|
||||
override fun setValueFrom(valueFrom: Float) {
|
||||
super.setValueFrom(valueFrom.unmap())
|
||||
}
|
||||
|
||||
override fun getValueTo(): Float {
|
||||
return super.getValueTo().map()
|
||||
}
|
||||
|
||||
override fun setValueTo(valueTo: Float) {
|
||||
super.setValueTo(valueTo.unmap())
|
||||
}
|
||||
|
||||
override fun addOnChangeListener(listener: OnChangeListener) {
|
||||
val mapper = OnChangeListenerMapper(listener)
|
||||
super.addOnChangeListener(mapper)
|
||||
changeListeners[listener] = mapper
|
||||
}
|
||||
|
||||
override fun removeOnChangeListener(listener: OnChangeListener) {
|
||||
changeListeners.remove(listener)?.let {
|
||||
super.removeOnChangeListener(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearOnChangeListeners() {
|
||||
super.clearOnChangeListeners()
|
||||
changeListeners.clear()
|
||||
}
|
||||
|
||||
private fun Float.map(): Float {
|
||||
return this.pow(3)
|
||||
}
|
||||
|
||||
private fun Float.unmap(): Float {
|
||||
return cbrt(this)
|
||||
}
|
||||
|
||||
private inner class OnChangeListenerMapper(
|
||||
private val delegate: OnChangeListener,
|
||||
) : OnChangeListener {
|
||||
|
||||
override fun onValueChange(slider: Slider, value: Float, fromUser: Boolean) {
|
||||
delegate.onValueChange(slider, value.map(), fromUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class CompositeMutex<T : Any> : Set<T> {
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return state.isEmpty
|
||||
return state.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
|
||||
@@ -19,7 +19,7 @@ class CompositeMutex2<T : Any> : Set<T> {
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return delegates.isEmpty
|
||||
return delegates.isEmpty()
|
||||
}
|
||||
|
||||
override fun iterator(): Iterator<T> {
|
||||
|
||||
@@ -83,7 +83,7 @@ fun <I> ActivityResultLauncher<I>.tryLaunch(
|
||||
e.printStackTraceDebug()
|
||||
}.isSuccess
|
||||
|
||||
fun SharedPreferences.observe() = callbackFlow<String?> {
|
||||
fun SharedPreferences.observe(): Flow<String?> = callbackFlow {
|
||||
val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
|
||||
trySendBlocking(key)
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ fun ImageView.newImageRequest(lifecycleOwner: LifecycleOwner, data: Any?): Image
|
||||
return null
|
||||
}
|
||||
}
|
||||
disposeImageRequest()
|
||||
// disposeImageRequest()
|
||||
return ImageRequest.Builder(context)
|
||||
.data(data)
|
||||
.lifecycle(lifecycleOwner)
|
||||
|
||||
@@ -87,5 +87,5 @@ class DetailsInteractor @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findLocal(seed: Manga) = localMangaRepository.getRemoteManga(seed)
|
||||
suspend fun findRemote(seed: Manga) = localMangaRepository.getRemoteManga(seed)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.koitharu.kotatsu.details.domain
|
||||
import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.model.findChapter
|
||||
import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.core.os.NetworkState
|
||||
import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.history.data.PROGRESS_NONE
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
@@ -13,6 +14,7 @@ class ProgressUpdateUseCase @Inject constructor(
|
||||
private val mangaRepositoryFactory: MangaRepository.Factory,
|
||||
private val database: MangaDatabase,
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val networkState: NetworkState,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(manga: Manga): Float {
|
||||
@@ -22,6 +24,9 @@ class ProgressUpdateUseCase @Inject constructor(
|
||||
} else {
|
||||
manga
|
||||
}
|
||||
if (!seed.isLocal && !networkState.value) {
|
||||
return PROGRESS_NONE
|
||||
}
|
||||
val repo = mangaRepositoryFactory.create(seed.source)
|
||||
val details = if (manga.source != seed.source || seed.chapters.isNullOrEmpty()) {
|
||||
repo.getDetails(seed)
|
||||
|
||||
@@ -149,15 +149,13 @@ class DetailsViewModel @Inject constructor(
|
||||
val scrobblingInfo: StateFlow<List<ScrobblingInfo>> = interactor.observeScrobblingInfo(mangaId)
|
||||
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
|
||||
|
||||
val relatedManga: StateFlow<List<MangaItemModel>> = manga
|
||||
.mapLatest {
|
||||
if (it != null && settings.isRelatedMangaEnabled) {
|
||||
relatedMangaUseCase.invoke(it)?.toUi(ListMode.GRID, extraProvider).orEmpty()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
val relatedManga: StateFlow<List<MangaItemModel>> = manga.mapLatest {
|
||||
if (it != null && settings.isRelatedMangaEnabled) {
|
||||
relatedMangaUseCase.invoke(it)?.toUi(ListMode.GRID, extraProvider).orEmpty()
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
|
||||
}.stateIn(viewModelScope, SharingStarted.Lazily, emptyList())
|
||||
|
||||
val branches: StateFlow<List<MangaBranch>> = combine(
|
||||
details,
|
||||
@@ -217,7 +215,7 @@ class DetailsViewModel @Inject constructor(
|
||||
}
|
||||
launchJob(Dispatchers.Default) {
|
||||
val manga = details.firstOrNull { it != null && it.isLocal } ?: return@launchJob
|
||||
remoteManga.value = interactor.findLocal(manga.toManga())
|
||||
remoteManga.value = interactor.findRemote(manga.toManga())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,8 +18,7 @@ data class DownloadState(
|
||||
val currentPage: Int = 0,
|
||||
val eta: Long = -1L,
|
||||
val localManga: LocalManga? = null,
|
||||
val downloadedChapters: LongArray = LongArray(0),
|
||||
val scheduledChapters: LongArray = LongArray(0),
|
||||
val downloadedChapters: Int = 0,
|
||||
val timestamp: Long = System.currentTimeMillis(),
|
||||
) {
|
||||
|
||||
@@ -42,68 +41,17 @@ data class DownloadState(
|
||||
.putLong(DATA_ETA, eta)
|
||||
.putLong(DATA_TIMESTAMP, timestamp)
|
||||
.putString(DATA_ERROR, error)
|
||||
.putLongArray(DATA_CHAPTERS, downloadedChapters)
|
||||
.putLongArray(DATA_CHAPTERS_SRC, scheduledChapters)
|
||||
.putInt(DATA_CHAPTERS, downloadedChapters)
|
||||
.putBoolean(DATA_INDETERMINATE, isIndeterminate)
|
||||
.putBoolean(DATA_PAUSED, isPaused)
|
||||
.build()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as DownloadState
|
||||
|
||||
if (manga != other.manga) return false
|
||||
if (isIndeterminate != other.isIndeterminate) return false
|
||||
if (isPaused != other.isPaused) return false
|
||||
if (isStopped != other.isStopped) return false
|
||||
if (error != other.error) return false
|
||||
if (totalChapters != other.totalChapters) return false
|
||||
if (currentChapter != other.currentChapter) return false
|
||||
if (totalPages != other.totalPages) return false
|
||||
if (currentPage != other.currentPage) return false
|
||||
if (eta != other.eta) return false
|
||||
if (localManga != other.localManga) return false
|
||||
if (!downloadedChapters.contentEquals(other.downloadedChapters)) return false
|
||||
if (!scheduledChapters.contentEquals(other.scheduledChapters)) return false
|
||||
if (timestamp != other.timestamp) return false
|
||||
if (max != other.max) return false
|
||||
if (progress != other.progress) return false
|
||||
if (percent != other.percent) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = manga.hashCode()
|
||||
result = 31 * result + isIndeterminate.hashCode()
|
||||
result = 31 * result + isPaused.hashCode()
|
||||
result = 31 * result + isStopped.hashCode()
|
||||
result = 31 * result + (error?.hashCode() ?: 0)
|
||||
result = 31 * result + totalChapters
|
||||
result = 31 * result + currentChapter
|
||||
result = 31 * result + totalPages
|
||||
result = 31 * result + currentPage
|
||||
result = 31 * result + eta.hashCode()
|
||||
result = 31 * result + (localManga?.hashCode() ?: 0)
|
||||
result = 31 * result + downloadedChapters.contentHashCode()
|
||||
result = 31 * result + scheduledChapters.contentHashCode()
|
||||
result = 31 * result + timestamp.hashCode()
|
||||
result = 31 * result + max
|
||||
result = 31 * result + progress
|
||||
result = 31 * result + percent.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private const val DATA_MANGA_ID = "manga_id"
|
||||
private const val DATA_MAX = "max"
|
||||
private const val DATA_PROGRESS = "progress"
|
||||
private const val DATA_CHAPTERS = "chapter"
|
||||
private const val DATA_CHAPTERS_SRC = "chapters_src"
|
||||
private const val DATA_CHAPTERS = "chapter_cnt"
|
||||
private const val DATA_ETA = "eta"
|
||||
private const val DATA_TIMESTAMP = "timestamp"
|
||||
private const val DATA_ERROR = "error"
|
||||
@@ -126,8 +74,6 @@ data class DownloadState(
|
||||
|
||||
fun getTimestamp(data: Data): Date = Date(data.getLong(DATA_TIMESTAMP, 0L))
|
||||
|
||||
fun getDownloadedChapters(data: Data): LongArray = data.getLongArray(DATA_CHAPTERS) ?: LongArray(0)
|
||||
|
||||
fun getScheduledChapters(data: Data): LongArray = data.getLongArray(DATA_CHAPTERS_SRC) ?: LongArray(0)
|
||||
fun getDownloadedChapters(data: Data): Int = data.getInt(DATA_CHAPTERS, 0)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,28 +2,22 @@ package org.koitharu.kotatsu.download.ui.list
|
||||
|
||||
import android.transition.TransitionManager
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.work.WorkInfo
|
||||
import coil.ImageLoader
|
||||
import coil.request.SuccessResult
|
||||
import coil.util.CoilUtils
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseListAdapter
|
||||
import org.koitharu.kotatsu.core.ui.image.TrimTransformation
|
||||
import org.koitharu.kotatsu.core.util.ext.drawableEnd
|
||||
import org.koitharu.kotatsu.core.util.ext.enqueueWith
|
||||
import org.koitharu.kotatsu.core.util.ext.isAnimationsEnabled
|
||||
import org.koitharu.kotatsu.core.util.ext.newImageRequest
|
||||
import org.koitharu.kotatsu.core.util.ext.source
|
||||
import org.koitharu.kotatsu.core.util.ext.textAndVisible
|
||||
import org.koitharu.kotatsu.databinding.ItemDownloadBinding
|
||||
import org.koitharu.kotatsu.download.ui.list.chapters.DownloadChapter
|
||||
import org.koitharu.kotatsu.download.ui.list.chapters.downloadChapterAD
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||
import org.koitharu.kotatsu.list.ui.adapter.ListItemType
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.util.format
|
||||
|
||||
@@ -36,9 +30,7 @@ fun downloadItemAD(
|
||||
) {
|
||||
|
||||
val percentPattern = context.resources.getString(R.string.percent_string_pattern)
|
||||
val expandIcon = ContextCompat.getDrawable(context, R.drawable.ic_expand_collapse)
|
||||
val chaptersAdapter = BaseListAdapter<DownloadChapter>()
|
||||
.addDelegate(ListItemType.CHAPTER, downloadChapterAD())
|
||||
// val expandIcon = ContextCompat.getDrawable(context, R.drawable.ic_expand_collapse)
|
||||
|
||||
val clickListener = object : View.OnClickListener, View.OnLongClickListener {
|
||||
override fun onClick(v: View) {
|
||||
@@ -59,27 +51,26 @@ fun downloadItemAD(
|
||||
binding.buttonResume.setOnClickListener(clickListener)
|
||||
itemView.setOnClickListener(clickListener)
|
||||
itemView.setOnLongClickListener(clickListener)
|
||||
binding.recyclerViewChapters.addItemDecoration(DividerItemDecoration(context, RecyclerView.VERTICAL))
|
||||
binding.recyclerViewChapters.adapter = chaptersAdapter
|
||||
|
||||
bind { payloads ->
|
||||
if (ListModelDiffCallback.PAYLOAD_CHECKED_CHANGED in payloads && context.isAnimationsEnabled) {
|
||||
TransitionManager.beginDelayedTransition(binding.constraintLayout)
|
||||
}
|
||||
binding.textViewTitle.text = item.manga.title
|
||||
binding.imageViewCover.newImageRequest(lifecycleOwner, item.manga.coverUrl)?.apply {
|
||||
placeholder(R.drawable.ic_placeholder)
|
||||
fallback(R.drawable.ic_placeholder)
|
||||
error(R.drawable.ic_error_placeholder)
|
||||
allowRgb565(true)
|
||||
transformations(TrimTransformation())
|
||||
source(item.manga.source)
|
||||
enqueueWith(coil)
|
||||
if ((CoilUtils.result(binding.imageViewCover) as? SuccessResult)?.memoryCacheKey != item.coverCacheKey) {
|
||||
binding.imageViewCover.newImageRequest(lifecycleOwner, item.manga.coverUrl)?.apply {
|
||||
placeholder(R.drawable.ic_placeholder)
|
||||
fallback(R.drawable.ic_placeholder)
|
||||
error(R.drawable.ic_error_placeholder)
|
||||
allowRgb565(true)
|
||||
transformations(TrimTransformation())
|
||||
memoryCacheKey(item.coverCacheKey)
|
||||
source(item.manga.source)
|
||||
enqueueWith(coil)
|
||||
}
|
||||
}
|
||||
binding.textViewTitle.isChecked = item.isExpanded
|
||||
binding.textViewTitle.drawableEnd = if (item.isExpandable) expandIcon else null
|
||||
binding.cardDetails.isVisible = item.isExpanded
|
||||
chaptersAdapter.items = item.chapters
|
||||
// binding.textViewTitle.isChecked = item.isExpanded
|
||||
// binding.textViewTitle.drawableEnd = if (item.isExpandable) expandIcon else null
|
||||
when (item.workState) {
|
||||
WorkInfo.State.ENQUEUED,
|
||||
WorkInfo.State.BLOCKED -> {
|
||||
@@ -117,11 +108,11 @@ fun downloadItemAD(
|
||||
binding.progressBar.isVisible = false
|
||||
binding.progressBar.isEnabled = true
|
||||
binding.textViewPercent.isVisible = false
|
||||
if (item.totalChapters > 0) {
|
||||
if (item.chaptersDownloaded > 0) {
|
||||
binding.textViewDetails.text = context.resources.getQuantityString(
|
||||
R.plurals.chapters,
|
||||
item.totalChapters,
|
||||
item.totalChapters,
|
||||
item.chaptersDownloaded,
|
||||
item.chaptersDownloaded,
|
||||
)
|
||||
binding.textViewDetails.isVisible = true
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.koitharu.kotatsu.download.ui.list
|
||||
|
||||
import android.text.format.DateUtils
|
||||
import androidx.work.WorkInfo
|
||||
import org.koitharu.kotatsu.download.ui.list.chapters.DownloadChapter
|
||||
import coil.memory.MemoryCache
|
||||
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
|
||||
import org.koitharu.kotatsu.list.ui.model.ListModel
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -17,14 +17,15 @@ data class DownloadItemModel(
|
||||
val manga: Manga,
|
||||
val error: String?,
|
||||
val max: Int,
|
||||
val totalChapters: Int,
|
||||
val progress: Int,
|
||||
val eta: Long,
|
||||
val timestamp: Date,
|
||||
val chapters: List<DownloadChapter>,
|
||||
val chaptersDownloaded: Int,
|
||||
val isExpanded: Boolean,
|
||||
) : ListModel, Comparable<DownloadItemModel> {
|
||||
|
||||
val coverCacheKey = MemoryCache.Key(manga.coverUrl, mapOf("dl" to "1"))
|
||||
|
||||
val percent: Float
|
||||
get() = if (max > 0) progress / max.toFloat() else 0f
|
||||
|
||||
@@ -38,7 +39,7 @@ data class DownloadItemModel(
|
||||
get() = workState == WorkInfo.State.RUNNING && isPaused
|
||||
|
||||
val isExpandable: Boolean
|
||||
get() = chapters.isNotEmpty()
|
||||
get() = false // TODO
|
||||
|
||||
fun getEtaString(): CharSequence? = if (hasEta) {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
|
||||
@@ -15,6 +15,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper
|
||||
import org.koitharu.kotatsu.core.ui.util.MenuInvalidator
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleActionObserver
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
@@ -53,6 +54,7 @@ class DownloadsActivity : BaseActivity<ActivityDownloadsBinding>(),
|
||||
addItemDecoration(decoration)
|
||||
adapter = downloadsAdapter
|
||||
selectionController.attachToRecyclerView(this)
|
||||
RecyclerScrollKeeper(this).attach()
|
||||
}
|
||||
addMenuProvider(DownloadsMenuProvider(this, viewModel))
|
||||
viewModel.items.observe(this) {
|
||||
|
||||
@@ -28,7 +28,6 @@ import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
import org.koitharu.kotatsu.core.util.ext.call
|
||||
import org.koitharu.kotatsu.core.util.ext.daysDiff
|
||||
import org.koitharu.kotatsu.download.domain.DownloadState
|
||||
import org.koitharu.kotatsu.download.ui.list.chapters.DownloadChapter
|
||||
import org.koitharu.kotatsu.download.ui.worker.DownloadWorker
|
||||
import org.koitharu.kotatsu.list.ui.model.EmptyState
|
||||
import org.koitharu.kotatsu.list.ui.model.ListHeader
|
||||
@@ -239,8 +238,6 @@ class DownloadsViewModel @Inject constructor(
|
||||
val mangaId = DownloadState.getMangaId(workData)
|
||||
if (mangaId == 0L) return null
|
||||
val manga = getManga(mangaId) ?: return null
|
||||
val downloadedChapters = DownloadState.getDownloadedChapters(workData)
|
||||
val scheduledChapters = DownloadState.getScheduledChapters(workData).toSet()
|
||||
return DownloadItemModel(
|
||||
id = id,
|
||||
workState = state,
|
||||
@@ -252,19 +249,8 @@ class DownloadsViewModel @Inject constructor(
|
||||
progress = DownloadState.getProgress(workData),
|
||||
eta = DownloadState.getEta(workData),
|
||||
timestamp = DownloadState.getTimestamp(workData),
|
||||
totalChapters = downloadedChapters.size,
|
||||
chaptersDownloaded = DownloadState.getDownloadedChapters(workData),
|
||||
isExpanded = isExpanded,
|
||||
chapters = manga.chapters?.mapNotNull {
|
||||
if (it.id in scheduledChapters) {
|
||||
DownloadChapter(
|
||||
number = it.number,
|
||||
name = it.name,
|
||||
isDownloaded = it.id in downloadedChapters,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.orEmpty(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ import okio.buffer
|
||||
import okio.sink
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.exceptions.TooManyRequestExceptions
|
||||
import org.koitharu.kotatsu.core.model.ids
|
||||
import org.koitharu.kotatsu.core.network.CommonHeaders
|
||||
import org.koitharu.kotatsu.core.network.MangaHttpClient
|
||||
import org.koitharu.kotatsu.core.parser.MangaDataRepository
|
||||
@@ -46,7 +47,6 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.Throttler
|
||||
import org.koitharu.kotatsu.core.util.ext.awaitFinishedWorkInfosByTag
|
||||
import org.koitharu.kotatsu.core.util.ext.awaitUpdateWork
|
||||
import org.koitharu.kotatsu.core.util.ext.awaitWorkInfoById
|
||||
import org.koitharu.kotatsu.core.util.ext.awaitWorkInfosByTag
|
||||
import org.koitharu.kotatsu.core.util.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.core.util.ext.deleteWork
|
||||
@@ -105,11 +105,12 @@ class DownloadWorker @AssistedInject constructor(
|
||||
setForeground(getForegroundInfo())
|
||||
val mangaId = inputData.getLong(MANGA_ID, 0L)
|
||||
val manga = mangaDataRepository.findMangaById(mangaId) ?: return Result.failure()
|
||||
val chaptersIds = inputData.getLongArray(CHAPTERS_IDS)?.takeUnless { it.isEmpty() }
|
||||
val downloadedIds = getDoneChapters()
|
||||
lastPublishedState = DownloadState(manga, isIndeterminate = true)
|
||||
publishState(DownloadState(manga, isIndeterminate = true))
|
||||
val chaptersIds = inputData.getLongArray(CHAPTERS_IDS)?.takeUnless { it.isEmpty() }
|
||||
val downloadedIds = getDoneChapters(manga)
|
||||
return try {
|
||||
downloadMangaImpl(chaptersIds, downloadedIds)
|
||||
downloadMangaImpl(manga, chaptersIds, downloadedIds)
|
||||
Result.success(currentState.toWorkData())
|
||||
} catch (e: CancellationException) {
|
||||
withContext(NonCancellable) {
|
||||
@@ -147,10 +148,11 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private suspend fun downloadMangaImpl(
|
||||
subject: Manga,
|
||||
includedIds: LongArray?,
|
||||
excludedIds: LongArray,
|
||||
excludedIds: Set<Long>,
|
||||
) {
|
||||
var manga = currentState.manga
|
||||
var manga = subject
|
||||
val chaptersToSkip = excludedIds.toMutableSet()
|
||||
withMangaLock(manga) {
|
||||
ContextCompat.registerReceiver(
|
||||
@@ -178,16 +180,9 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
val chapters = getChapters(mangaDetails, includedIds)
|
||||
publishState(
|
||||
currentState.copy(scheduledChapters = LongArray(chapters.size) { i -> chapters[i].id }),
|
||||
)
|
||||
for ((chapterIndex, chapter) in chapters.withIndex()) {
|
||||
if (chaptersToSkip.remove(chapter.id)) {
|
||||
publishState(
|
||||
currentState.copy(
|
||||
downloadedChapters = currentState.downloadedChapters + chapter.id,
|
||||
),
|
||||
)
|
||||
publishState(currentState.copy(downloadedChapters = currentState.downloadedChapters + 1))
|
||||
continue
|
||||
}
|
||||
val pages = runFailsafe(pausingHandle) {
|
||||
@@ -225,11 +220,7 @@ class DownloadWorker @AssistedInject constructor(
|
||||
localStorageChanges.emit(LocalMangaInput.of(output.rootFile).getManga())
|
||||
}.onFailure(Throwable::printStackTraceDebug)
|
||||
}
|
||||
publishState(
|
||||
currentState.copy(
|
||||
downloadedChapters = currentState.downloadedChapters + chapter.id,
|
||||
),
|
||||
)
|
||||
publishState(currentState.copy(downloadedChapters = currentState.downloadedChapters + 1))
|
||||
}
|
||||
publishState(currentState.copy(isIndeterminate = true, eta = -1L))
|
||||
output.mergeWithExisting()
|
||||
@@ -336,11 +327,9 @@ class DownloadWorker @AssistedInject constructor(
|
||||
setProgress(state.toWorkData())
|
||||
}
|
||||
|
||||
private suspend fun getDoneChapters(): LongArray {
|
||||
val work = WorkManager.getInstance(applicationContext).awaitWorkInfoById(id)
|
||||
?: return LongArray(0)
|
||||
return DownloadState.getDownloadedChapters(work.progress)
|
||||
}
|
||||
private suspend fun getDoneChapters(manga: Manga) = runCatchingCancellable {
|
||||
localMangaRepository.getDetails(manga).chapters?.ids()
|
||||
}.getOrNull().orEmpty()
|
||||
|
||||
private fun getChapters(
|
||||
manga: Manga,
|
||||
|
||||
@@ -169,6 +169,7 @@ abstract class FavouritesDao {
|
||||
ListSortOrder.NEWEST -> "favourites.created_at DESC"
|
||||
ListSortOrder.ALPHABETIC -> "manga.title ASC"
|
||||
ListSortOrder.NEW_CHAPTERS -> "(SELECT chapters_new FROM tracks WHERE tracks.manga_id = manga.manga_id) DESC"
|
||||
ListSortOrder.UPDATED, // for legacy support
|
||||
ListSortOrder.PROGRESS -> "(SELECT percent FROM history WHERE history.manga_id = manga.manga_id) DESC"
|
||||
else -> throw IllegalArgumentException("Sort order $sortOrder is not supported")
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.koitharu.kotatsu.core.db.entity.toMangaTag
|
||||
import org.koitharu.kotatsu.core.db.entity.toMangaTags
|
||||
import org.koitharu.kotatsu.core.model.MangaHistory
|
||||
import org.koitharu.kotatsu.core.model.findById
|
||||
import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleHandle
|
||||
import org.koitharu.kotatsu.core.util.ext.mapItems
|
||||
@@ -185,7 +186,7 @@ class HistoryRepository @Inject constructor(
|
||||
|
||||
private suspend fun HistoryEntity.recoverIfNeeded(manga: Manga): HistoryEntity {
|
||||
val chapters = manga.chapters
|
||||
if (chapters.isNullOrEmpty() || chapters.findById(chapterId) != null) {
|
||||
if (manga.isLocal || chapters.isNullOrEmpty() || chapters.findById(chapterId) != null) {
|
||||
return this
|
||||
}
|
||||
val newChapterId = chapters.getOrNull(
|
||||
|
||||
@@ -9,6 +9,7 @@ import dagger.hilt.android.AndroidEntryPoint
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.os.NetworkManageIntent
|
||||
import org.koitharu.kotatsu.core.ui.list.ListSelectionController
|
||||
import org.koitharu.kotatsu.core.ui.list.RecyclerScrollKeeper
|
||||
import org.koitharu.kotatsu.core.util.ext.addMenuProvider
|
||||
import org.koitharu.kotatsu.databinding.FragmentListBinding
|
||||
import org.koitharu.kotatsu.list.ui.MangaListFragment
|
||||
@@ -23,6 +24,7 @@ class HistoryListFragment : MangaListFragment() {
|
||||
|
||||
override fun onViewBindingCreated(binding: FragmentListBinding, savedInstanceState: Bundle?) {
|
||||
super.onViewBindingCreated(binding, savedInstanceState)
|
||||
RecyclerScrollKeeper(binding.recyclerView).attach()
|
||||
addMenuProvider(HistoryListMenuProvider(binding.root.context, viewModel))
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,8 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) {
|
||||
android.R.id.home -> if (isSearchOpened()) {
|
||||
super.onOptionsItemSelected(item)
|
||||
closeSearchCallback.handleOnBackPressed()
|
||||
true
|
||||
} else {
|
||||
viewBinding.searchView.requestFocus()
|
||||
true
|
||||
|
||||
@@ -52,6 +52,7 @@ import org.koitharu.kotatsu.core.util.ext.postDelayed
|
||||
import org.koitharu.kotatsu.core.util.ext.setValueRounded
|
||||
import org.koitharu.kotatsu.core.util.ext.zipWithPrevious
|
||||
import org.koitharu.kotatsu.databinding.ActivityReaderBinding
|
||||
import org.koitharu.kotatsu.details.ui.DetailsActivity
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
import org.koitharu.kotatsu.reader.ui.config.ReaderConfigSheet
|
||||
@@ -147,6 +148,11 @@ class ReaderActivity :
|
||||
}
|
||||
}
|
||||
|
||||
override fun getParentActivityIntent(): Intent? {
|
||||
val manga = viewModel.manga?.toManga() ?: return null
|
||||
return DetailsActivity.newIntent(this, manga)
|
||||
}
|
||||
|
||||
override fun onUserInteraction() {
|
||||
super.onUserInteraction()
|
||||
scrollTimer.onUserInteraction()
|
||||
@@ -249,6 +255,7 @@ class ReaderActivity :
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
touchHelper.dispatchTouchEvent(ev)
|
||||
scrollTimer.onTouchEvent(ev)
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package org.koitharu.kotatsu.reader.ui
|
||||
|
||||
import android.view.MotionEvent
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.assisted.Assisted
|
||||
@@ -8,11 +9,14 @@ import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.yield
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import kotlin.math.roundToLong
|
||||
@@ -33,6 +37,7 @@ class ScrollTimer @AssistedInject constructor(
|
||||
private var delayMs: Long = 10L
|
||||
private var pageSwitchDelay: Long = 100L
|
||||
private var resumeAt = 0L
|
||||
private var isTouchDown = MutableStateFlow(false)
|
||||
|
||||
var isEnabled: Boolean = false
|
||||
set(value) {
|
||||
@@ -55,6 +60,19 @@ class ScrollTimer @AssistedInject constructor(
|
||||
resumeAt = System.currentTimeMillis() + INTERACTION_SKIP_MS
|
||||
}
|
||||
|
||||
fun onTouchEvent(event: MotionEvent) {
|
||||
when (event.actionMasked) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
isTouchDown.value = true
|
||||
}
|
||||
|
||||
MotionEvent.ACTION_UP,
|
||||
MotionEvent.ACTION_CANCEL -> {
|
||||
isTouchDown.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onSpeedChanged(speed: Float) {
|
||||
if (speed <= 0f) {
|
||||
delayMs = 0L
|
||||
@@ -108,12 +126,18 @@ class ScrollTimer @AssistedInject constructor(
|
||||
}
|
||||
|
||||
private fun isPaused(): Boolean {
|
||||
return resumeAt > System.currentTimeMillis()
|
||||
return isTouchDown.value || resumeAt > System.currentTimeMillis()
|
||||
}
|
||||
|
||||
private suspend fun delayUntilResumed() {
|
||||
while (isPaused()) {
|
||||
delay(resumeAt - System.currentTimeMillis())
|
||||
val delayTime = resumeAt - System.currentTimeMillis()
|
||||
if (delayTime > 0) {
|
||||
delay(delayTime)
|
||||
} else {
|
||||
yield()
|
||||
}
|
||||
isTouchDown.first { !it }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -128,7 +128,7 @@ class ReaderConfigSheet :
|
||||
when (buttonView.id) {
|
||||
R.id.switch_scroll_timer -> {
|
||||
findCallback()?.isAutoScrollEnabled = isChecked
|
||||
requireViewBinding().labelTimer.isVisible = isChecked
|
||||
requireViewBinding().layoutTimer.isVisible = isChecked
|
||||
requireViewBinding().sliderTimer.isVisible = isChecked
|
||||
}
|
||||
}
|
||||
@@ -159,6 +159,7 @@ class ReaderConfigSheet :
|
||||
if (fromUser) {
|
||||
settings.readerAutoscrollSpeed = value
|
||||
}
|
||||
(viewBinding ?: return).labelTimerValue.text = getString(R.string.speed_value, value * 10f)
|
||||
}
|
||||
|
||||
override fun onActivityResult(result: Uri?) {
|
||||
|
||||
@@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.getAndUpdate
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.plus
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.model.distinctById
|
||||
@@ -134,15 +134,17 @@ open class RemoteListViewModel @Inject constructor(
|
||||
sortOrder = filterState.sortOrder,
|
||||
tags = filterState.tags,
|
||||
)
|
||||
mangaList.update { oldList ->
|
||||
val oldList = mangaList.getAndUpdate { oldList ->
|
||||
if (!append || oldList.isNullOrEmpty()) {
|
||||
list
|
||||
} else {
|
||||
oldList + list
|
||||
}
|
||||
}
|
||||
if (append) {
|
||||
hasNextPage.value = list.isNotEmpty()
|
||||
}.orEmpty()
|
||||
hasNextPage.value = if (append) {
|
||||
list.isNotEmpty()
|
||||
} else {
|
||||
list.size > oldList.size || hasNextPage.value
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
throw e
|
||||
@@ -152,6 +154,7 @@ open class RemoteListViewModel @Inject constructor(
|
||||
if (!mangaList.value.isNullOrEmpty()) {
|
||||
errorEvent.call(e)
|
||||
}
|
||||
hasNextPage.value = false
|
||||
}
|
||||
}.also { loadingJob = it }
|
||||
}
|
||||
|
||||
@@ -139,6 +139,7 @@ class AppearanceSettingsFragment :
|
||||
|
||||
private val deviceLocales = LocaleManagerCompat.getSystemLocales(context)
|
||||
.map { it.language }
|
||||
.distinct()
|
||||
|
||||
override fun compare(a: Locale, b: Locale): Int {
|
||||
return if (a === b) {
|
||||
|
||||
@@ -16,6 +16,7 @@ import org.koitharu.kotatsu.core.ui.AlertDialogFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.core.util.ext.tryLaunch
|
||||
import org.koitharu.kotatsu.databinding.DialogProgressBinding
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
@@ -28,7 +29,7 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
|
||||
|
||||
private var backup: File? = null
|
||||
private val saveFileContract = registerForActivityResult(
|
||||
ActivityResultContracts.CreateDocument("*/*"),
|
||||
ActivityResultContracts.CreateDocument("application/zip"),
|
||||
) { uri ->
|
||||
val file = backup
|
||||
if (uri != null && file != null) {
|
||||
@@ -81,7 +82,10 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
|
||||
|
||||
private fun onBackupDone(file: File) {
|
||||
this.backup = file
|
||||
saveFileContract.launch(file.name)
|
||||
if (!saveFileContract.tryLaunch(file.name)) {
|
||||
Toast.makeText(requireContext(), R.string.operation_not_supported, Toast.LENGTH_SHORT).show()
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveBackup(file: File, output: Uri) {
|
||||
@@ -91,7 +95,7 @@ class BackupDialogFragment : AlertDialogFragment<DialogProgressBinding>() {
|
||||
it.write(file.readBytes())
|
||||
}
|
||||
}
|
||||
Toast.makeText(requireContext(), R.string.backup_saved, Toast.LENGTH_LONG).show()
|
||||
Toast.makeText(requireContext(), R.string.backup_saved, Toast.LENGTH_SHORT).show()
|
||||
dismiss()
|
||||
} catch (e: InterruptedException) {
|
||||
throw e
|
||||
|
||||
@@ -43,7 +43,6 @@ class BackupViewModel @Inject constructor(
|
||||
|
||||
backup.finish()
|
||||
progress.value = 1f
|
||||
backup.close()
|
||||
backup.file
|
||||
}
|
||||
onBackupDone.call(file)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.koitharu.kotatsu.settings.backup
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.activity.result.ActivityResultCallback
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.preference.Preference
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.backup.DIR_BACKUPS
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.util.ext.resolveFile
|
||||
import org.koitharu.kotatsu.core.util.ext.tryLaunch
|
||||
import org.koitharu.kotatsu.core.util.ext.viewLifecycleScope
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class PeriodicalBackupSettingsFragment : BasePreferenceFragment(R.string.periodic_backups),
|
||||
ActivityResultCallback<Uri?> {
|
||||
|
||||
@Inject
|
||||
lateinit var scheduler: PeriodicalBackupWorker.Scheduler
|
||||
|
||||
private val outputSelectCall = registerForActivityResult(
|
||||
ActivityResultContracts.OpenDocumentTree(),
|
||||
this,
|
||||
)
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.pref_backup_periodic)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
bindOutputSummary()
|
||||
bindLastBackupInfo()
|
||||
}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
return when (preference.key) {
|
||||
AppSettings.KEY_BACKUP_PERIODICAL_OUTPUT -> outputSelectCall.tryLaunch(null)
|
||||
else -> super.onPreferenceTreeClick(preference)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(result: Uri?) {
|
||||
if (result != null) {
|
||||
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
context?.contentResolver?.takePersistableUriPermission(result, takeFlags)
|
||||
settings.periodicalBackupOutput = result
|
||||
bindOutputSummary()
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindOutputSummary() {
|
||||
val preference = findPreference<Preference>(AppSettings.KEY_BACKUP_PERIODICAL_OUTPUT) ?: return
|
||||
viewLifecycleScope.launch {
|
||||
preference.summary = withContext(Dispatchers.Default) {
|
||||
val value = settings.periodicalBackupOutput
|
||||
value?.toUserFriendlyString(preference.context) ?: preference.context.run {
|
||||
getExternalFilesDir(DIR_BACKUPS) ?: File(filesDir, DIR_BACKUPS)
|
||||
}.path
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindLastBackupInfo() {
|
||||
val preference = findPreference<Preference>(AppSettings.KEY_BACKUP_PERIODICAL_LAST) ?: return
|
||||
viewLifecycleScope.launch {
|
||||
val lastDate = withContext(Dispatchers.Default) {
|
||||
scheduler.getLastSuccessfulBackup()
|
||||
}
|
||||
preference.summary = lastDate?.let {
|
||||
val format = SimpleDateFormat.getDateTimeInstance(SimpleDateFormat.MEDIUM, SimpleDateFormat.SHORT)
|
||||
preference.context.getString(R.string.last_successful_backup, format.format(it))
|
||||
}
|
||||
preference.isVisible = lastDate != null
|
||||
}
|
||||
}
|
||||
|
||||
private fun Uri.toUserFriendlyString(context: Context): String {
|
||||
val df = DocumentFile.fromTreeUri(context, this)
|
||||
if (df?.canWrite() != true) {
|
||||
return context.getString(R.string.invalid_value_message)
|
||||
}
|
||||
return resolveFile(context)?.path ?: toString()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package org.koitharu.kotatsu.settings.backup
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import androidx.hilt.work.HiltWorker
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkInfo
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.WorkerParameters
|
||||
import androidx.work.await
|
||||
import androidx.work.workDataOf
|
||||
import dagger.Reusable
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import org.koitharu.kotatsu.core.backup.BackupRepository
|
||||
import org.koitharu.kotatsu.core.backup.BackupZipOutput
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.ext.awaitUniqueWorkInfoByName
|
||||
import org.koitharu.kotatsu.core.util.ext.deleteAwait
|
||||
import org.koitharu.kotatsu.settings.work.PeriodicWorkScheduler
|
||||
import java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltWorker
|
||||
class PeriodicalBackupWorker @AssistedInject constructor(
|
||||
@Assisted appContext: Context,
|
||||
@Assisted params: WorkerParameters,
|
||||
private val repository: BackupRepository,
|
||||
private val settings: AppSettings,
|
||||
) : CoroutineWorker(appContext, params) {
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
val resultData = workDataOf(DATA_TIMESTAMP to Date().time)
|
||||
val file = BackupZipOutput(applicationContext).use { backup ->
|
||||
backup.put(repository.createIndex())
|
||||
backup.put(repository.dumpHistory())
|
||||
backup.put(repository.dumpCategories())
|
||||
backup.put(repository.dumpFavourites())
|
||||
backup.put(repository.dumpBookmarks())
|
||||
backup.put(repository.dumpSettings())
|
||||
backup.finish()
|
||||
backup.file
|
||||
}
|
||||
val dirUri = settings.periodicalBackupOutput ?: return Result.success(resultData)
|
||||
val target = DocumentFile.fromTreeUri(applicationContext, dirUri)
|
||||
?.createFile("application/zip", file.nameWithoutExtension)
|
||||
?.uri ?: return Result.failure()
|
||||
applicationContext.contentResolver.openOutputStream(target, "wt")?.use { output ->
|
||||
file.inputStream().copyTo(output)
|
||||
} ?: return Result.failure()
|
||||
file.deleteAwait()
|
||||
return Result.success(resultData)
|
||||
}
|
||||
|
||||
@Reusable
|
||||
class Scheduler @Inject constructor(
|
||||
private val workManager: WorkManager,
|
||||
private val settings: AppSettings,
|
||||
) : PeriodicWorkScheduler {
|
||||
|
||||
override suspend fun schedule() {
|
||||
val constraints = Constraints.Builder()
|
||||
.setRequiresStorageNotLow(true)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
constraints.setRequiresDeviceIdle(true)
|
||||
}
|
||||
val request = PeriodicWorkRequestBuilder<PeriodicalBackupWorker>(
|
||||
settings.periodicalBackupFrequency,
|
||||
TimeUnit.DAYS,
|
||||
).setConstraints(constraints.build())
|
||||
.keepResultsForAtLeast(20, TimeUnit.DAYS)
|
||||
.addTag(TAG)
|
||||
.build()
|
||||
workManager
|
||||
.enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.UPDATE, request)
|
||||
.await()
|
||||
}
|
||||
|
||||
override suspend fun unschedule() {
|
||||
workManager
|
||||
.cancelUniqueWork(TAG)
|
||||
.await()
|
||||
}
|
||||
|
||||
override suspend fun isScheduled(): Boolean {
|
||||
return workManager
|
||||
.awaitUniqueWorkInfoByName(TAG)
|
||||
.any { !it.state.isFinished }
|
||||
}
|
||||
|
||||
suspend fun getLastSuccessfulBackup(): Date? {
|
||||
return workManager
|
||||
.awaitUniqueWorkInfoByName(TAG)
|
||||
.lastOrNull { x -> x.state == WorkInfo.State.SUCCEEDED }
|
||||
?.outputData
|
||||
?.getLong(DATA_TIMESTAMP, 0)
|
||||
?.let { if (it != 0L) Date(it) else null }
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
||||
const val TAG = "backups"
|
||||
const val DATA_TIMESTAMP = "ts"
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac
|
||||
findPreference<Preference>(AppSettings.KEY_PAGES_CACHE_CLEAR)?.bindBytesSizeSummary(checkNotNull(viewModel.cacheSizes[CacheDir.PAGES]))
|
||||
findPreference<Preference>(AppSettings.KEY_THUMBS_CACHE_CLEAR)?.bindBytesSizeSummary(checkNotNull(viewModel.cacheSizes[CacheDir.THUMBS]))
|
||||
findPreference<Preference>(AppSettings.KEY_HTTP_CACHE_CLEAR)?.bindBytesSizeSummary(viewModel.httpCacheSize)
|
||||
bindPeriodicalBackupSummary()
|
||||
findPreference<Preference>(AppSettings.KEY_SEARCH_HISTORY_CLEAR)?.let { pref ->
|
||||
viewModel.searchHistoryCount.observe(viewLifecycleOwner) {
|
||||
pref.summary = if (it < 0) {
|
||||
@@ -200,6 +201,20 @@ class UserDataSettingsFragment : BasePreferenceFragment(R.string.data_and_privac
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindPeriodicalBackupSummary() {
|
||||
val preference = findPreference<Preference>(AppSettings.KEY_BACKUP_PERIODICAL_ENABLED) ?: return
|
||||
val entries = resources.getStringArray(R.array.backup_frequency)
|
||||
val entryValues = resources.getStringArray(R.array.values_backup_frequency)
|
||||
viewModel.periodicalBackupFrequency.observe(viewLifecycleOwner) { freq ->
|
||||
preference.summary = if (freq == 0L) {
|
||||
getString(R.string.disabled)
|
||||
} else {
|
||||
val index = entryValues.indexOf(freq.toString())
|
||||
entries.getOrNull(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun clearSearchHistory() {
|
||||
MaterialAlertDialogBuilder(context ?: return)
|
||||
.setTitle(R.string.clear_search_history)
|
||||
|
||||
@@ -5,12 +5,15 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import okhttp3.Cache
|
||||
import org.koitharu.kotatsu.R
|
||||
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.prefs.observeAsFlow
|
||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||
import org.koitharu.kotatsu.core.ui.util.ReversibleAction
|
||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||
@@ -29,6 +32,7 @@ class UserDataSettingsViewModel @Inject constructor(
|
||||
private val searchRepository: MangaSearchRepository,
|
||||
private val trackingRepository: TrackingRepository,
|
||||
private val cookieJar: MutableCookieJar,
|
||||
private val settings: AppSettings,
|
||||
) : BaseViewModel() {
|
||||
|
||||
val onActionDone = MutableEventFlow<ReversibleAction>()
|
||||
@@ -40,6 +44,20 @@ class UserDataSettingsViewModel @Inject constructor(
|
||||
val cacheSizes = EnumMap<CacheDir, MutableStateFlow<Long>>(CacheDir::class.java)
|
||||
val storageUsage = MutableStateFlow<StorageUsage?>(null)
|
||||
|
||||
val periodicalBackupFrequency = settings.observeAsFlow(
|
||||
key = AppSettings.KEY_BACKUP_PERIODICAL_ENABLED,
|
||||
valueProducer = { isPeriodicalBackupEnabled },
|
||||
).flatMapLatest { isEnabled ->
|
||||
if (isEnabled) {
|
||||
settings.observeAsFlow(
|
||||
key = AppSettings.KEY_BACKUP_PERIODICAL_FREQUENCY,
|
||||
valueProducer = { periodicalBackupFrequency },
|
||||
)
|
||||
} else {
|
||||
flowOf(0)
|
||||
}
|
||||
}
|
||||
|
||||
private var storageUsageJob: Job? = null
|
||||
|
||||
init {
|
||||
|
||||
@@ -5,6 +5,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
|
||||
import org.koitharu.kotatsu.settings.backup.PeriodicalBackupWorker
|
||||
import org.koitharu.kotatsu.suggestions.ui.SuggestionsWorker
|
||||
import org.koitharu.kotatsu.tracker.work.TrackWorker
|
||||
import javax.inject.Inject
|
||||
@@ -13,6 +14,7 @@ class WorkScheduleManager @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
private val suggestionScheduler: SuggestionsWorker.Scheduler,
|
||||
private val trackerScheduler: TrackWorker.Scheduler,
|
||||
private val periodicalBackupScheduler: PeriodicalBackupWorker.Scheduler,
|
||||
) : SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
|
||||
override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) {
|
||||
@@ -30,6 +32,13 @@ class WorkScheduleManager @Inject constructor(
|
||||
isEnabled = settings.isSuggestionsEnabled,
|
||||
force = key != AppSettings.KEY_SUGGESTIONS,
|
||||
)
|
||||
|
||||
AppSettings.KEY_BACKUP_PERIODICAL_ENABLED,
|
||||
AppSettings.KEY_BACKUP_PERIODICAL_FREQUENCY -> updateWorker(
|
||||
scheduler = periodicalBackupScheduler,
|
||||
isEnabled = settings.isPeriodicalBackupEnabled,
|
||||
force = key != AppSettings.KEY_BACKUP_PERIODICAL_ENABLED,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +47,7 @@ class WorkScheduleManager @Inject constructor(
|
||||
processLifecycleScope.launch(Dispatchers.Default) {
|
||||
updateWorkerImpl(trackerScheduler, settings.isTrackerEnabled, false)
|
||||
updateWorkerImpl(suggestionScheduler, settings.isSuggestionsEnabled, false)
|
||||
updateWorkerImpl(periodicalBackupScheduler, settings.isPeriodicalBackupEnabled, false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -130,9 +130,6 @@ class SyncHelper @AssistedInject constructor(
|
||||
private fun upsertHistory(json: JSONArray, timestamp: Long): Array<ContentProviderResult> {
|
||||
val uri = uri(authorityHistory, TABLE_HISTORY)
|
||||
val operations = ArrayList<ContentProviderOperation>()
|
||||
operations += ContentProviderOperation.newDelete(uri)
|
||||
.withSelection("updated_at < ?", arrayOf(timestamp.toString()))
|
||||
.build()
|
||||
json.mapJSONTo(operations) { jo ->
|
||||
operations.addAll(upsertManga(jo.removeJSONObject("manga"), authorityHistory))
|
||||
ContentProviderOperation.newInsert(uri)
|
||||
@@ -145,9 +142,6 @@ class SyncHelper @AssistedInject constructor(
|
||||
private fun upsertFavouriteCategories(json: JSONArray, timestamp: Long): Array<ContentProviderResult> {
|
||||
val uri = uri(authorityFavourites, TABLE_FAVOURITE_CATEGORIES)
|
||||
val operations = ArrayList<ContentProviderOperation>()
|
||||
operations += ContentProviderOperation.newDelete(uri)
|
||||
.withSelection("created_at < ?", arrayOf(timestamp.toString()))
|
||||
.build()
|
||||
json.mapJSONTo(operations) { jo ->
|
||||
ContentProviderOperation.newInsert(uri)
|
||||
.withValues(jo.toContentValues())
|
||||
@@ -159,9 +153,6 @@ class SyncHelper @AssistedInject constructor(
|
||||
private fun upsertFavourites(json: JSONArray, timestamp: Long): Array<ContentProviderResult> {
|
||||
val uri = uri(authorityFavourites, TABLE_FAVOURITES)
|
||||
val operations = ArrayList<ContentProviderOperation>()
|
||||
operations += ContentProviderOperation.newDelete(uri)
|
||||
.withSelection("created_at < ?", arrayOf(timestamp.toString()))
|
||||
.build()
|
||||
json.mapJSONTo(operations) { jo ->
|
||||
operations.addAll(upsertManga(jo.removeJSONObject("manga"), authorityFavourites))
|
||||
ContentProviderOperation.newInsert(uri)
|
||||
|
||||
@@ -104,31 +104,6 @@
|
||||
app:layout_constraintTop_toBottomOf="@id/textView_status"
|
||||
tools:text="@tools:sample/lorem[3]" />
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_details"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginHorizontal="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHeight_default="wrap"
|
||||
app:layout_constraintHeight_max="280dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/progressBar"
|
||||
app:shapeAppearance="?shapeAppearanceCornerMedium">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView_chapters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:scrollbars="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
tools:itemCount="200"
|
||||
tools:listitem="@layout/item_chapter_download" />
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_pause"
|
||||
style="?materialButtonOutlinedStyle"
|
||||
@@ -139,7 +114,7 @@
|
||||
android:text="@string/pause"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_resume"
|
||||
app:layout_constraintTop_toBottomOf="@id/card_details"
|
||||
app:layout_constraintTop_toBottomOf="@id/progressBar"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<Button
|
||||
@@ -152,7 +127,7 @@
|
||||
android:text="@string/resume"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toStartOf="@id/button_cancel"
|
||||
app:layout_constraintTop_toBottomOf="@id/card_details" />
|
||||
app:layout_constraintTop_toBottomOf="@id/progressBar" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/button_cancel"
|
||||
@@ -164,7 +139,7 @@
|
||||
android:text="@android:string/cancel"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/card_details"
|
||||
app:layout_constraintTop_toBottomOf="@id/progressBar"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -119,18 +119,35 @@
|
||||
android:textColor="?colorOnSurfaceVariant"
|
||||
app:drawableStartCompat="@drawable/ic_timer" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_timer"
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/margin_normal"
|
||||
android:layout_marginTop="@dimen/margin_small"
|
||||
android:text="@string/speed"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:layout_marginTop="@dimen/margin_normal"
|
||||
android:textAppearance="?textAppearanceTitleSmall"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.slider.Slider
|
||||
<TextView
|
||||
android:id="@+id/label_timer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/speed"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/label_timer_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_small"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
tools:text="x0.5" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<org.koitharu.kotatsu.core.ui.widgets.CubicSlider
|
||||
android:id="@+id/slider_timer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -2,18 +2,58 @@
|
||||
<resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="zero">%1$d فصل جديد</item>
|
||||
<item quantity="one"/>
|
||||
<item quantity="two"/>
|
||||
<item quantity="few"/>
|
||||
<item quantity="one">%1$d فصل جديد</item>
|
||||
<item quantity="two">%1$d فصول جديدة</item>
|
||||
<item quantity="few">%1$d فصول جديدة</item>
|
||||
<item quantity="many">%1$d فصول جديدة</item>
|
||||
<item quantity="other"/>
|
||||
<item quantity="other">%1$d فصول جديدة</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="zero">لا يوجد</item>
|
||||
<item quantity="zero">%1$d فصل</item>
|
||||
<item quantity="one">%1$d فصل</item>
|
||||
<item quantity="two">%1$d فصلين</item>
|
||||
<item quantity="few">%1$d بعض فصول</item>
|
||||
<item quantity="many">%1$d عدة فصول</item>
|
||||
<item quantity="other">أخرى</item>
|
||||
<item quantity="few">%1$d فصول</item>
|
||||
<item quantity="many">%1$d فصول</item>
|
||||
<item quantity="other">%1$d فصول</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="zero">%1$d دقيقة مضت</item>
|
||||
<item quantity="one">%1$d دقيقة مضت</item>
|
||||
<item quantity="two">%1$d دقائق مضت</item>
|
||||
<item quantity="few">%1$d دقائق مضت</item>
|
||||
<item quantity="many">%1$d دقائق مضت</item>
|
||||
<item quantity="other">%1$d دقائق مضت</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="zero">%1$d عنصر</item>
|
||||
<item quantity="one">%1$d عنصر</item>
|
||||
<item quantity="two">%1$d عناصر</item>
|
||||
<item quantity="few">%1$d عناصر</item>
|
||||
<item quantity="many">%1$d عناصر</item>
|
||||
<item quantity="other">%1$d عناصر</item>
|
||||
</plurals>
|
||||
<plurals name="months_ago">
|
||||
<item quantity="zero">%1$d شهر مضا</item>
|
||||
<item quantity="one">%1$d شهر مضا</item>
|
||||
<item quantity="two">%1$d شهرين مضت</item>
|
||||
<item quantity="few">%1$d أشهر مضت</item>
|
||||
<item quantity="many">%1$d أشهر مضت</item>
|
||||
<item quantity="other">%1$d أشهر مضت</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="zero">%1$d يوم مضا</item>
|
||||
<item quantity="one">%1$d يوم مضا</item>
|
||||
<item quantity="two">%1$d يومين مضت</item>
|
||||
<item quantity="few">%1$d أيام مضت</item>
|
||||
<item quantity="many">%1$d أيام مضت</item>
|
||||
<item quantity="other">%1$d أيام مضت</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="zero">%1$d ساعة مضت</item>
|
||||
<item quantity="one">%1$d ساعة مضت</item>
|
||||
<item quantity="two">%1$d ساعات مضت</item>
|
||||
<item quantity="few">%1$d ساعات مضت</item>
|
||||
<item quantity="many">%1$d ساعات مضت</item>
|
||||
<item quantity="other">%1$d ساعات مضت</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -156,7 +156,7 @@
|
||||
<string name="long_ago">منذ فترة</string>
|
||||
<string name="notifications_settings">إعدادات الإشعارات</string>
|
||||
<string name="save_manga">حفظ</string>
|
||||
<string name="large_manga_save_confirm">تحتوي هذه المانجا على s%. حفظ الكل؟</string>
|
||||
<string name="large_manga_save_confirm">هذه المانجا فيها %s . حفظ الكل</string>
|
||||
<string name="read_more">اقرأ المزيد</string>
|
||||
<string name="search_results">نتائج البحث</string>
|
||||
<string name="file_not_found">الملف غير موجود</string>
|
||||
@@ -172,9 +172,10 @@
|
||||
<string name="data_restored_success">استعيدت جميع البيانات</string>
|
||||
<string name="backup_information">يمكنك إنشاء نسخة احتياطية من السجل الخاص بك والمفضلة واستعادتها</string>
|
||||
<string name="available_sources">المصادر المتاحة</string>
|
||||
<string name="external_storage">التخزين الخارجي</string>
|
||||
<string name="external_storage">تخزين خارجي</string>
|
||||
<string name="silent">صامت</string>
|
||||
<string name="today">اليوم</string>
|
||||
<string name="system_default">الافتراضي</string>
|
||||
<string name="sign_in">تسجبل الدخول</string>
|
||||
<string name="domain">المجال</string>
|
||||
</resources>
|
||||
@@ -494,4 +494,15 @@
|
||||
<string name="enhanced_colors">32-бітны каляровы рэжым</string>
|
||||
<string name="suggest_new_sources_summary">Прапаноўваць крыніцы мангі, дададзеныя ў апошнім абнаўленні праграмы</string>
|
||||
<string name="online_variant">Анлайн варыянт</string>
|
||||
<string name="frequency_every_day">Кожны дзень</string>
|
||||
<string name="backup_frequency">Частата стварэння рэзервовых копій</string>
|
||||
<string name="periodic_backups_enable">Уключыць перыядычнае рэзервовае капіраванне</string>
|
||||
<string name="frequency_every_2_days">Кожныя 2 дні</string>
|
||||
<string name="frequency_once_per_week">Раз на тыдзень</string>
|
||||
<string name="periodic_backups">Перыядычнае рэзервовае капіраванне</string>
|
||||
<string name="frequency_twice_per_month">Два разы на месяц</string>
|
||||
<string name="frequency_once_per_month">Адзін раз у месяц</string>
|
||||
<string name="last_successful_backup">Апошняе паспяховае рэзервовае капіраванне: %s</string>
|
||||
<string name="backups_output_directory">Вывадны каталог рэзервовых копій</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
</resources>
|
||||
@@ -494,4 +494,15 @@
|
||||
<string name="enhanced_colors_summary">Reduce el banding, pero puede afectar al rendimiento</string>
|
||||
<string name="by_relevance">Relevancia</string>
|
||||
<string name="online_variant">Variante en línea</string>
|
||||
<string name="frequency_every_day">Cada día</string>
|
||||
<string name="backup_frequency">Frecuencia de creación de las copias de seguridad</string>
|
||||
<string name="periodic_backups_enable">Activar las copias de seguridad periódicas</string>
|
||||
<string name="frequency_every_2_days">Cada 2 días</string>
|
||||
<string name="frequency_once_per_week">Una vez a la semana</string>
|
||||
<string name="periodic_backups">Copias de seguridad periódicas</string>
|
||||
<string name="frequency_twice_per_month">Dos veces al mes</string>
|
||||
<string name="frequency_once_per_month">Una vez al mes</string>
|
||||
<string name="backups_output_directory">Directorio para guardar la copia de seguridad</string>
|
||||
<string name="last_successful_backup">La última copia de seguridad correcta: %s</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
</resources>
|
||||
@@ -494,4 +494,15 @@
|
||||
<string name="list_options">Opsyon sa Listahan</string>
|
||||
<string name="by_relevance">Kaugnayan</string>
|
||||
<string name="online_variant">Online na baryante</string>
|
||||
<string name="frequency_every_day">Araw araw</string>
|
||||
<string name="backup_frequency">Dalas ng paglikha ng backup</string>
|
||||
<string name="periodic_backups_enable">Paganahin ang periodic na pag-backup</string>
|
||||
<string name="frequency_every_2_days">Kada 2 araw</string>
|
||||
<string name="frequency_once_per_week">Isang beses kada linggo</string>
|
||||
<string name="periodic_backups">Mga periodic na pag-backup</string>
|
||||
<string name="frequency_twice_per_month">Dalawang beses bawat buwan</string>
|
||||
<string name="frequency_once_per_month">Isang beses bawat buwan</string>
|
||||
<string name="backups_output_directory">Output directory ng mga backup</string>
|
||||
<string name="last_successful_backup">Huling matagumpay na pag-backup: %s</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
</resources>
|
||||
@@ -9,13 +9,13 @@
|
||||
<string name="grid">Kisi</string>
|
||||
<string name="list_mode">Mode daftar</string>
|
||||
<string name="settings">Pengaturan</string>
|
||||
<string name="remote_sources">Sumber jarak jauh</string>
|
||||
<string name="remote_sources">Sumber manga</string>
|
||||
<string name="loading_">Memuat…</string>
|
||||
<string name="computing_">Menghitung…</string>
|
||||
<string name="chapter_d_of_d">Bab %1$d dari %2$d</string>
|
||||
<string name="close">Tutup</string>
|
||||
<string name="try_again">Coba lagi</string>
|
||||
<string name="nothing_found">Nihil</string>
|
||||
<string name="nothing_found">Hasil tidak ditemukan</string>
|
||||
<string name="history_is_empty">Belum ada riwayat</string>
|
||||
<string name="read">Baca</string>
|
||||
<string name="you_have_not_favourites_yet">Belum ada favorit</string>
|
||||
@@ -37,8 +37,8 @@
|
||||
<string name="updated">Diperbarui</string>
|
||||
<string name="newest">Terbaru</string>
|
||||
<string name="by_rating">Peringkat</string>
|
||||
<string name="sort_order">Urutan penyortiran</string>
|
||||
<string name="filter">Saring</string>
|
||||
<string name="sort_order">Urutkan berdasarkan</string>
|
||||
<string name="filter">Filter</string>
|
||||
<string name="theme">Tema</string>
|
||||
<string name="light">Terang</string>
|
||||
<string name="dark">Gelap</string>
|
||||
@@ -478,4 +478,31 @@
|
||||
<string name="directories">Direktori</string>
|
||||
<string name="main_screen_sections">Bagian layar utama</string>
|
||||
<string name="to_top">Ke atas</string>
|
||||
<string name="zoom_in">Perbesar</string>
|
||||
<string name="frequency_every_day">Setiap Hari</string>
|
||||
<string name="categories">Kategori</string>
|
||||
<string name="frequency_every_2_days">Setiap 2 Hari</string>
|
||||
<string name="online_variant">Variasi Online</string>
|
||||
<string name="keep_screen_on">Biarkan Layar Menyala</string>
|
||||
<string name="zoom_out">Perkecil</string>
|
||||
<string name="keep_screen_on_summary">Jangan Matikan Layar Saat Membaca Komik</string>
|
||||
<string name="list_options">Opsi daftar</string>
|
||||
<string name="reader_zoom_buttons_summary">Apakah menampilkan tombol kontrol zoom di sudut kanan bawah</string>
|
||||
<string name="backup_frequency">Frekuensi pembuatan cadangan</string>
|
||||
<string name="suggest_new_sources">Sumber baru yang disarankan setelah pembaruan aplikasi</string>
|
||||
<string name="periodic_backups_enable">Aktifkan pencadangan berkala</string>
|
||||
<string name="moved_to_top">Bergerak ke atas</string>
|
||||
<string name="enhanced_colors_summary">Mengurangi banding, tetapi dapat mempengaruhi kinerja</string>
|
||||
<string name="frequency_once_per_week">Sekali dalam seminggu</string>
|
||||
<string name="periodic_backups">Pencadangan berkala</string>
|
||||
<string name="reader_zoom_buttons">Tampilkan tombol zoom</string>
|
||||
<string name="frequency_twice_per_month">Dua kali sebulan</string>
|
||||
<string name="by_relevance">Relevansi</string>
|
||||
<string name="state_abandoned">Istirahat</string>
|
||||
<string name="frequency_once_per_month">Sebulan sekali</string>
|
||||
<string name="enhanced_colors">mode warna 32-bit</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
<string name="last_successful_backup">Cadangan sukses terakhir: %s</string>
|
||||
<string name="backups_output_directory">Direktori keluaran cadangan</string>
|
||||
<string name="suggest_new_sources_summary">Prompt untuk mengaktifkan sumber baru yang ditambahkan setelah memperbarui aplikasi</string>
|
||||
</resources>
|
||||
31
app/src/main/res/values-kk/plurals.xml
Normal file
31
app/src/main/res/values-kk/plurals.xml
Normal file
@@ -0,0 +1,31 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d минут бұрын</item>
|
||||
<item quantity="other">%1$d минут бұрын</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d елемент</item>
|
||||
<item quantity="other">%1$d елемент</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d тарау</item>
|
||||
<item quantity="other">%1$d тарау</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d жаңа тарау</item>
|
||||
<item quantity="other">%1$d жаңа тарау</item>
|
||||
</plurals>
|
||||
<plurals name="months_ago">
|
||||
<item quantity="one">%1$d ай бұрын</item>
|
||||
<item quantity="other">%1$d ай бұрын</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d күн бұрын</item>
|
||||
<item quantity="other">%1$d күн бұрын</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d сағат бұрын</item>
|
||||
<item quantity="other">%1$d сағат бұрын</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="text_search_holder_secondary">Сұрауыңызды қайталап көріңіз.</string>
|
||||
<string name="text_history_holder_secondary">Шеткі мәзірден оқуға болатынды табыңыз.</string>
|
||||
<string name="text_history_holder_secondary">«Шолу» бөлімінен не оқуға болатынын табыңыз.</string>
|
||||
<string name="text_local_holder_secondary">Файл импорттаңыз не онлайн каталогтан бірдеңе сақтаңыз.</string>
|
||||
<string name="other_storage">Басқа бума</string>
|
||||
<string name="new_version_s">Жаңа нұсқа: %s</string>
|
||||
@@ -65,14 +65,14 @@
|
||||
<string name="search_on_s">%s-те іздеу</string>
|
||||
<string name="delete_manga">Маңганы жою</string>
|
||||
<string name="reader_settings">Оқыманы баптау</string>
|
||||
<string name="switch_pages">Бет ауыстыру</string>
|
||||
<string name="switch_pages">Парақтау</string>
|
||||
<string name="taps_on_edges">Шет жақты түру</string>
|
||||
<string name="clear_thumbs_cache">Нобай кәшін тазалау</string>
|
||||
<string name="search_history_cleared">Тазаланды</string>
|
||||
<string name="gestures_only">Ым ғана</string>
|
||||
<string name="internal_storage">Ішкі жады</string>
|
||||
<string name="external_storage">Сыртқы жады</string>
|
||||
<string name="domain">Домен</string>
|
||||
<string name="domain">Дәмейін</string>
|
||||
<string name="app_update_available">Жаңа нұсқа қолжетімді</string>
|
||||
<string name="open_in_browser">Уеб браузер арқылы ашу</string>
|
||||
<string name="save_manga">Сақтау</string>
|
||||
@@ -92,7 +92,7 @@
|
||||
<string name="text_local_holder_primary">Алдымен бірдеңе сақтаңыз</string>
|
||||
<string name="manga_shelf">Сөре</string>
|
||||
<string name="recent_manga">Соңғы</string>
|
||||
<string name="pages_animation">Бет анимациясы</string>
|
||||
<string name="pages_animation">Бет анимасасы</string>
|
||||
<string name="manga_save_location">Жүктеу бумасы</string>
|
||||
<string name="not_available">Қолжетімсіз</string>
|
||||
<string name="cannot_find_available_storage">Қолжетімді бума жоқ</string>
|
||||
@@ -208,4 +208,301 @@
|
||||
<string name="protect_application_subtitle">Қолданбаға кіру үшін құпиясөз енгізіңіз</string>
|
||||
<string name="confirm">Растау</string>
|
||||
<string name="password_length_hint">Құпиясөзде 4, не одан көп таңба болу керек</string>
|
||||
<string name="status_re_reading">Қайталап оқып жатырмын</string>
|
||||
<string name="detect_reader_mode">Оқу режімін өздігінен анықтау</string>
|
||||
<string name="tracking">Бақылау</string>
|
||||
<string name="email_enter_hint">Жалғастыру үшін email поштаңызды жазыңыз</string>
|
||||
<string name="disable_all">Бәрін өшіру</string>
|
||||
<string name="clear_feed">Лекті тазалау</string>
|
||||
<string name="chapters_empty">Бұл маңгада тарау жоқ</string>
|
||||
<string name="clear_all_history">Түгел тарихты тазалау</string>
|
||||
<string name="preload_pages">Беттерді алдынала жүктей беру</string>
|
||||
<string name="data_deletion">Деректі жою</string>
|
||||
<string name="show_reading_indicators">Оқу прогрессін көрсету</string>
|
||||
<string name="local_manga_processing">Сақталған маңгаңыз үдерісте</string>
|
||||
<string name="show_notification_new_chapters_off">Мәлімдеме алмайсыз, бірақ жаңа тараулар тізімде көрсетіліп тұрады</string>
|
||||
<string name="show_notification_new_chapters_on">Оқып жүрген маңгаңыздың жаңаруы туралы мәлімдеме алып тұрасыз</string>
|
||||
<string name="status_reading">Оқып жүрмін</string>
|
||||
<string name="various_languages">Түрлі тіл</string>
|
||||
<string name="removal_completed">Жойылды</string>
|
||||
<string name="edit">Өңдеу</string>
|
||||
<string name="filter_load_error">Жанр тізімі жүктеліне алмады</string>
|
||||
<string name="removed_from_history">Тарихтан жойылды</string>
|
||||
<string name="crash_text">Ақау пайда болды. Жөндеу үшін әзірлеушіге шағым жіберіңізші.</string>
|
||||
<string name="detect_reader_mode_summary">Маңга уебтүн бе екенін өздігінен анықтап береді</string>
|
||||
<string name="appwidget_recent_description">Соңғы оқыған маңгаңыз</string>
|
||||
<string name="appearance">Кейіп</string>
|
||||
<string name="bookmark_remove">Бетбелгіні алып тастау</string>
|
||||
<string name="disable_battery_optimization_summary">Аяда жаңарту іздеуді көмектеседі</string>
|
||||
<string name="status_on_hold">Кейінге қалған</string>
|
||||
<string name="last_2_hours">Соңғы 2 сағат</string>
|
||||
<string name="name">Атау</string>
|
||||
<string name="edit_category">Санатты өңдеу</string>
|
||||
<string name="bookmark_removed">Бетбелгі жойылды</string>
|
||||
<string name="select_range">Ауқымын таңдау</string>
|
||||
<string name="suggestions_excluded_genres_summary">Көргіңіз келмейтін жанрды таңдаңыз</string>
|
||||
<string name="only_using_wifi">Тек Wi-Fi арқылы</string>
|
||||
<string name="back">Кері</string>
|
||||
<string name="dns_over_https">HTTPS үстінен DNS</string>
|
||||
<string name="sync_title">Дерегіңізді үйлестіріңіз</string>
|
||||
<string name="appwidget_shelf_description">Таңдаулы маңгаңыз</string>
|
||||
<string name="send">Жіберу</string>
|
||||
<string name="bookmark_add">Бетбелгілеу</string>
|
||||
<string name="new_sources_text">Жаңа маңга дереккөзі қолжетімді</string>
|
||||
<string name="check_new_chapters_title">Жаңа тарау барын тексеріп, сол туралы мәлімдеу</string>
|
||||
<string name="logged_in_as">%s деп тіркелгенсіз</string>
|
||||
<string name="suggestions_info">Деректің бәрі тек осы құрылғы аясында қаралып, ешқайда жіберілмейді.</string>
|
||||
<string name="history_cleared">Тарих тазарды</string>
|
||||
<string name="undo">Қайтару</string>
|
||||
<string name="download_slowdown_summary">IP мекенжайыңыздың бұғатқа түспеуіне көмектеседі</string>
|
||||
<string name="text_delete_local_manga_batch">Таңдалғанды құрылғыдан жоямысыз\?</string>
|
||||
<string name="report">Шағым</string>
|
||||
<string name="download_slowdown">Жүктеп алуды баяулату</string>
|
||||
<string name="bookmark_added">Бетбелгі қойылды</string>
|
||||
<string name="sync">Үйлестіру</string>
|
||||
<string name="search_chapters">Тарау табу</string>
|
||||
<string name="always">Әрқашан</string>
|
||||
<string name="suggestions_excluded_genres">Жанрды шектеу</string>
|
||||
<string name="canceled">Доғарылды</string>
|
||||
<string name="account_already_exists">Бұндай тіркелгі бос емес</string>
|
||||
<string name="hide">Жасыру</string>
|
||||
<string name="exclude_nsfw_from_history_summary">ҰЯТСЫЗ деген маңганы оқығаныңыз тарихыңызда сақталмайды</string>
|
||||
<string name="show_reading_indicators_summary">Таңдаулы мен тарихта оқылған туынды пайызын көрсету</string>
|
||||
<string name="use_fingerprint">Қолжетімді болса саусақ ізін қолдану</string>
|
||||
<string name="onboard_text">Маңганы қай тілде оқығыңыз келетінін таңдаңыз. Кейінірек баптауда өзгертіп ала аласыз.</string>
|
||||
<string name="suggestions_updating">Ұсынысты жаңарту</string>
|
||||
<string name="percent_string_pattern">%%%1$s</string>
|
||||
<string name="chapters_will_removed_background">Тараулар аяда жойылады</string>
|
||||
<string name="default_mode">Әдепкі режім</string>
|
||||
<string name="logout">Шығу</string>
|
||||
<string name="status_completed">Аяқталған</string>
|
||||
<string name="reset_filter">Сүзгіні тазарту</string>
|
||||
<string name="status_dropped">Тастап кеткен</string>
|
||||
<string name="nsfw">18+</string>
|
||||
<string name="notifications_enable">Мәлімдеме қосу</string>
|
||||
<string name="never">Ешқашан</string>
|
||||
<string name="disable_battery_optimization">Қуат оңтайлығын өшіру</string>
|
||||
<string name="status_planned">Жоспарланған</string>
|
||||
<string name="bookmarks">Бетбелгілер</string>
|
||||
<string name="show_all">Бәрін көрсету</string>
|
||||
<string name="empty_favourite_categories">Таңдаулы санатыңыз жоқ</string>
|
||||
<string name="invalid_domain_message">Қате дәмейін</string>
|
||||
<string name="languages">Тілдер</string>
|
||||
<string name="zoom_in">Ұлғайту</string>
|
||||
<string name="captcha_required_summary">%s дұрыс істеуі үшін captcha өтіңіз</string>
|
||||
<string name="download_option_all_unread">Түгел оқылмаған тарау</string>
|
||||
<string name="restore_backup_description">Алдында жасалған жеке деректің сақтық көшірмесін импорттау</string>
|
||||
<string name="frequency_every_day">Күнде</string>
|
||||
<string name="download_started">Жүктеп алу басталды</string>
|
||||
<string name="categories">Санат</string>
|
||||
<string name="progress">Прогресс</string>
|
||||
<string name="cancel_all">Бәрін доғару</string>
|
||||
<string name="sync_host_description">Өзіңіздің үйлестіру сербірін я әдепкісін таңдай аласыз. Не істеп жатқаныңызды түсінбеңіз баспаңыз.</string>
|
||||
<string name="error_corrupted_file">Қайта жарамсыз дерек қосылды яки файыл сынған</string>
|
||||
<string name="pick_custom_directory">Жеке каталогты таңдау</string>
|
||||
<string name="no_chapters">Тарау жоқ</string>
|
||||
<string name="list_options">Тізімді реттеу</string>
|
||||
<string name="related_manga_summary">Байланыс маңга тізімін көрсету. Кейде тізім қате я мүлде болмауы мүмкін</string>
|
||||
<string name="remove_completed_downloads_confirm">Жүктеу тарихыңыз тазарады</string>
|
||||
<string name="theme_name_dynamic">Динамикалық</string>
|
||||
<string name="reader_zoom_buttons_summary">Ұлғайту батырмасын астыңғы оң жақта көрсету я көрсетпеу</string>
|
||||
<string name="pages_cache">Бет кәші</string>
|
||||
<string name="reset">Арылту</string>
|
||||
<string name="tracker_wifi_only_summary">Шектеулі желі қосылымы болса жаңа тарау қолжетімдігін тексермеу</string>
|
||||
<string name="order_added">Қосылды</string>
|
||||
<string name="enable_logging">Логтауды қосу</string>
|
||||
<string name="on_device">Құрылғыда</string>
|
||||
<string name="password">Құпиясөз</string>
|
||||
<string name="download_option_whole_manga">Маңганы толықтай</string>
|
||||
<string name="settings_apply_restart_required">Өзгерту іске қосылуы үшін қолданбаны өшіріп қосыңыз</string>
|
||||
<string name="source_disabled">Дереккөз сөніп жатыр</string>
|
||||
<string name="backup_frequency">Сақтық көшірмесінің жиілігі</string>
|
||||
<string name="data_and_privacy">Дерек пен құпиялық</string>
|
||||
<string name="clear_cookies_summary">Қате болса көмектесе алады. Түгел тіркелгінің күші жойылады</string>
|
||||
<string name="enable_logging_summary">Кей әрекетті түзеуге сақтау қою. Не екенін білмесеңіз қоспаңыз</string>
|
||||
<string name="clear_source_cookies_summary">Осы дәмейіннің кукиін тазалау. Көп жағдайда тіркелгіден шығылып кетеді</string>
|
||||
<string name="history_shortcuts">Соңғы маңганың таңбашасын көрсету</string>
|
||||
<string name="downloads_wifi_only_summary">Ұялы желіге көшкенде жүктеп алуды тоқтату</string>
|
||||
<string name="suggest_new_sources">Қолданбаны жаңартқан соң жаңа дереккөз ұсыну</string>
|
||||
<string name="import_completed">Импорт аяқталды</string>
|
||||
<string name="different_languages">Әртүрлі тіл</string>
|
||||
<string name="user_agent">UserAgent басы</string>
|
||||
<string name="ignore_ssl_errors">SSL қатеге мән бермеу</string>
|
||||
<string name="reader_info_bar">Оқымада ақпар тақтасын көрсету</string>
|
||||
<string name="periodic_backups_enable">Сақтық көшірмесін кезең-кезеңімен жасауды қосу</string>
|
||||
<string name="server_address">Сербір адресі</string>
|
||||
<string name="moved_to_top">Үстіге жылжыды</string>
|
||||
<string name="explore">Қарау</string>
|
||||
<string name="find_similar">Ұқсасын табу</string>
|
||||
<string name="storage_usage">Жадты қолдану</string>
|
||||
<string name="data_not_restored_text">Дұрыс сақтық көшірме файылын таңдағаныңызды тексеріңіз</string>
|
||||
<string name="theme_name_sakura">Сакура</string>
|
||||
<string name="view_list">Тізімді көру</string>
|
||||
<string name="unknown">Белгісіз</string>
|
||||
<string name="in_progress">Оқылып жатыр</string>
|
||||
<string name="download_option_manual_selection">Тарауды таңдап шығу</string>
|
||||
<string name="enhanced_colors_summary">Түс ыдырауын азайтады, бірақ өнімділікке әсер ете алады</string>
|
||||
<string name="importing_manga">Маңга импорттау</string>
|
||||
<string name="pause">Үзіліс</string>
|
||||
<string name="clear_new_chapters_counters">Жаңа тарау ақпарын да жою</string>
|
||||
<string name="nothing_here">Мұнда түк жоқ</string>
|
||||
<string name="remove_completed">Дайынын жою</string>
|
||||
<string name="items_limit_exceeded">Елемент сыймайды</string>
|
||||
<string name="frequency_every_2_days">Екі күн сайын</string>
|
||||
<string name="suggestions_notifications_summary">Анда-санда мәлімдеме арқылы маңга ұсынып тұру</string>
|
||||
<string name="invalid_value_message">Жарамсыз өлшем</string>
|
||||
<string name="downloads_cancelled">Жүктеп алу доғарылды</string>
|
||||
<string name="webtoon_zoom">Уебтүнді ұлғайту</string>
|
||||
<string name="theme_name_miku">Мику</string>
|
||||
<string name="data_not_restored">Дерек қалыпқа келмеді</string>
|
||||
<string name="directories">Каталог</string>
|
||||
<string name="local_manga_directories">Жергілікті маңга тізімі</string>
|
||||
<string name="manage_categories">Санатты реттеу</string>
|
||||
<string name="scrobbling_empty_hint">Оқу прогрессін бақылау үшін маңга ақпар экранындағы «Мәзірге» өтіп, «Бақылау» батырмасын басыңыз.</string>
|
||||
<string name="color_light">Жарық</string>
|
||||
<string name="web_view_unavailable">WebView қолжетімсіз: WebView провайдері орнатылғанын тексеріп көріңіз</string>
|
||||
<string name="port">Порт</string>
|
||||
<string name="color_correction_hint">Таңдалған түс баптауы осы маңга үшін сақталып тұрады</string>
|
||||
<string name="not_found_404">Контент табылмады, жойылған-мыс</string>
|
||||
<string name="got_it">Ұқтым</string>
|
||||
<string name="type">Түрі</string>
|
||||
<string name="search_hint">Маңга атын, жанрын я дереккөз атауын жазыңыз</string>
|
||||
<string name="frequency_once_per_week">Апта сайын</string>
|
||||
<string name="description">Сипаттама</string>
|
||||
<string name="periodic_backups">Сақтық көшірмесін кезең-кезеңімен жасау</string>
|
||||
<string name="reader_zoom_buttons">Ұлғайту батырмасын көрсету</string>
|
||||
<string name="sources_reorder_tip">Ретін өзгерту үшін елементті басып тұрыңыз</string>
|
||||
<string name="resume">Жалғастыру</string>
|
||||
<string name="server_error">Сербір қуып тұр (%1$d). Сәлден соң қайталап көріңіз</string>
|
||||
<string name="images_proxy_title">Суретті оңтайлау проксиі</string>
|
||||
<string name="network_unavailable_hint">Маңганы онлайн оқу үшін Wi-Fi-ды я ұялы желіні қосыңыз</string>
|
||||
<string name="no_manga_sources">Маңға дереккөзі жоқ</string>
|
||||
<string name="username">Қолданушы аты</string>
|
||||
<string name="frequency_twice_per_month">Екі апта сайын</string>
|
||||
<string name="prefetch_content">Алдын-ала жүктей беру</string>
|
||||
<string name="main_screen_sections">Басты экран бөлімдері</string>
|
||||
<string name="confirm_exit">Шығу үшін тағы бір рет басыңыз</string>
|
||||
<string name="advanced">Қосымша баптау</string>
|
||||
<string name="sync_settings">Үйлестіру баптауы</string>
|
||||
<string name="online_variant">Онлайн нұсқа</string>
|
||||
<string name="download_option_all_unread_b">Түгел оқылмаған тарау (%s)</string>
|
||||
<string name="authorization_optional">Кіру (міндетті емес)</string>
|
||||
<string name="color_dark">Күңгірт</string>
|
||||
<string name="other_cache">Басқа кәш</string>
|
||||
<string name="show_suspicious_content">Күмәнді контентті көрсету</string>
|
||||
<string name="reader_info_bar_summary">Экранның үстінгі жағында уақыт пен оқу прогрессін көрсету</string>
|
||||
<string name="allow_unstable_updates_summary">Тұрақсыз нұсқа туралы мәлімдеме алу</string>
|
||||
<string name="translations">Аударма</string>
|
||||
<string name="comics_archive_import_description">Бір не одан көп .cbz я .zip файлын таңдай аласыз, әр файыл бөлек маңга болып анықталады.</string>
|
||||
<string name="downloads_paused">Жүктеп алу тоқтап қалды</string>
|
||||
<string name="too_many_requests_message">Тым көп сұрату. Біраздан соң қайталап көріңіз</string>
|
||||
<string name="downloads_wifi_only">Wi-Fi арқылы ғана жүктеу</string>
|
||||
<string name="cancel_all_downloads_confirm">Белсенді жүктеудің бәрі жойылып, жартылай жүктелгендер жоғалып кетеді</string>
|
||||
<string name="by_relevance">Өзектілігі</string>
|
||||
<string name="related_manga">Ұқсас маңга</string>
|
||||
<string name="discard">Сақтамау</string>
|
||||
<string name="saved_manga">Сақталған маңга</string>
|
||||
<string name="manga_error_description_pattern">Қате мәліметі:<br><tt>%1$s</tt><br><br>1. Дереккөзде бар ма екенін тексеру үшін <a href=%2$s>маңганы уеб браузер арқылы</a> ашып көріңіз<br>2. <a href=kotatsu://about>Kotatsu-ның ең соңғы нұсқасын</a><br>3 қолданып отырсыз ба, тексеріп көріңіз. Мүмкін болса: әзірлеушіге қате туралы шағып жіберіңіз.</string>
|
||||
<string name="state_abandoned">Тасталған</string>
|
||||
<string name="history_shortcuts_summary">Қолданба таңбасын ұзақ басып тұрғанда соңғы маңганы қолжетімді ету</string>
|
||||
<string name="automatic_scroll">Өздігінен парақтау</string>
|
||||
<string name="download_option_first_n_chapters">Бірінші %s</string>
|
||||
<string name="keep_screen_on">Экранды сөндірмеу</string>
|
||||
<string name="paused">Тоқтап тұр</string>
|
||||
<string name="text_downloads_list_holder">Жүктеп алғаныңыз жоқ</string>
|
||||
<string name="color_correction">Түс реттеу</string>
|
||||
<string name="invalid_port_number">Порттың нөмірі қате</string>
|
||||
<string name="suggestions_wifi_only_summary">Шектеулі желі қосылымы болса ұсынысты жаңартпау</string>
|
||||
<string name="webtoon_zoom_summary">Уебтүн режімінде ұлғайту ымына рұқсат ету</string>
|
||||
<string name="categories_delete_confirm">Таңдаулы санатты шынымен жойғыңыз келе ме\?
|
||||
\nІшіндегі бар маңга жойылады, сосын оны қайтарып ала алмайсыз.</string>
|
||||
<string name="frequency_once_per_month">Ай сайын</string>
|
||||
<string name="reader_info_pattern">%1$d/%2$d-тарау %3$d/%4$d-бет</string>
|
||||
<string name="contrast">Көреғарлық</string>
|
||||
<string name="network">Желі</string>
|
||||
<string name="reader_slider">Парақтау жүгірткісін көрсету</string>
|
||||
<string name="no_manga_sources_text">Онлайн оқу үшін маңга дереккөзін қосыңыз</string>
|
||||
<string name="downloaded">Жүктеп алынған</string>
|
||||
<string name="options">Басқа</string>
|
||||
<string name="services">Қызмет</string>
|
||||
<string name="suggestions_enable_prompt">Сізге арналған маңга ұсынысын алғыңыз келе ме\?</string>
|
||||
<string name="exit_confirmation">Шығуды растау</string>
|
||||
<string name="comics_archive">Комикс мұрағаты</string>
|
||||
<string name="custom_directory">Жеке каталог</string>
|
||||
<string name="more">Тағы</string>
|
||||
<string name="theme_name_asuka">Асука</string>
|
||||
<string name="address">Адресі</string>
|
||||
<string name="import_will_start_soon">Импорт қазір басталады</string>
|
||||
<string name="compact">Жинақы</string>
|
||||
<string name="enhanced_colors">32-биттік түс режімі</string>
|
||||
<string name="folder_with_images_import_description">Мұрағат я сурет каталогын таңдай аласыз. Әр мұрағат (яки ішкі каталог) бір тарау деп анықталады.</string>
|
||||
<string name="reorder">Ретін өзгерту</string>
|
||||
<string name="default_section">Әдепкі бөлім</string>
|
||||
<string name="background">Ая</string>
|
||||
<string name="feed">Лек</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
<string name="downloads_removed">Жүктеп алғаныңыз жойылды</string>
|
||||
<string name="pages_animation_summary">Парақтау анимасасы</string>
|
||||
<string name="no_access_to_file">Бұл файлға я каталогқа рұқсатыңыз жоқ</string>
|
||||
<string name="mark_as_current">Қазіргі деп белгілеу</string>
|
||||
<string name="random">Кездейсоқ</string>
|
||||
<string name="mirror_switching">Айнаны өздігінен таңдау</string>
|
||||
<string name="restore_summary">Бұған дейін жасалған сақтық көшірмесін қалпына келтіру</string>
|
||||
<string name="show_pages_numbers_summary">Бет нөмірін төменгі жақта көрсету</string>
|
||||
<string name="show_in_grid_view">Кесте қып көрсету</string>
|
||||
<string name="zoom_out">Кішірейту</string>
|
||||
<string name="keep_screen_on_summary">Маңга оқып отырғанда экранды сөндірмеу</string>
|
||||
<string name="download_option_next_unread_n_chapters">Келесі оқылмаған %s</string>
|
||||
<string name="clear_network_cache">Желі кәшін тазалау</string>
|
||||
<string name="voice_search">Дауыс іздеуі</string>
|
||||
<string name="enable">Қосу</string>
|
||||
<string name="import_completed_hint">Орын үнемдеу үшін түпнұсқа файлды құрылғыдан жойып тастай аласыз</string>
|
||||
<string name="theme_name_rikka">Рикка</string>
|
||||
<string name="reader_control_ltr_summary">Оң жақты я оң жақ батырманы басқан сайын бет ауысады</string>
|
||||
<string name="language">Тіл</string>
|
||||
<string name="incognito_mode">Инкогнито режімі</string>
|
||||
<string name="no_bookmarks_summary">Оқып жатқан маңгаға бетбелгі жасай аласыз</string>
|
||||
<string name="images_procy_description">Трафик қолдануды азайтып, сурет жүктеп алуды тездету үшін wsrv.nl қызметін қолданыңыз</string>
|
||||
<string name="theme_name_mamimi">Мамими</string>
|
||||
<string name="manga_branch_title_template">%1$s (%2$s)</string>
|
||||
<string name="manage">Реттеу</string>
|
||||
<string name="manga_list">Маңга тізімі</string>
|
||||
<string name="reader_control_ltr">Оқыманы эргономді басқару</string>
|
||||
<string name="disable_nsfw">ҰЯТСЫЗ-ды өшіру</string>
|
||||
<string name="last_successful_backup">Соңғы сақтық көшірме: %s</string>
|
||||
<string name="available">Қолжетімді</string>
|
||||
<string name="color_white">Ақ</string>
|
||||
<string name="network_unavailable">Желі қолжетімсіз</string>
|
||||
<string name="empty">Бос</string>
|
||||
<string name="downloads_resumed">Жүктеп алу жалғасты</string>
|
||||
<string name="details_button_tip">Қосымша реттеу көру үшін «Оқу» батырмасын басып тұрыңыз</string>
|
||||
<string name="text_unsaved_changes_prompt">Өзгерісті сақтайсыз ба\?</string>
|
||||
<string name="folder_with_images">Суреті бар бума</string>
|
||||
<string name="to_top">Үстіге</string>
|
||||
<string name="show">Көрсету</string>
|
||||
<string name="show_on_shelf">Сөреде көрсету</string>
|
||||
<string name="allow_unstable_updates">Тұрақсыз жаңартуды орнатуға рұқсат беру</string>
|
||||
<string name="sync_auth_hint">Жаңа тіркелгі аша аласыз я жаңасын жасай аласыз</string>
|
||||
<string name="theme_name_kanade">Канаде</string>
|
||||
<string name="backups_output_directory">Сақтық көшірмесінің каталогы</string>
|
||||
<string name="invert_colors">Түстерді терістету</string>
|
||||
<string name="color_theme">Түс схемасы</string>
|
||||
<string name="brightness">Ашықтық</string>
|
||||
<string name="memory_usage_pattern">%s - %s</string>
|
||||
<string name="exit_confirmation_summary">Шығып кету үшін «Кері» батырмасын екі рет басыңыз</string>
|
||||
<string name="bookmarks_removed">Бетбелгілер жойылды</string>
|
||||
<string name="theme_name_mion">Мион</string>
|
||||
<string name="mirror_switching_summary">Дереккөз дәмейіннің қатесі пайда болып, айнасы қолжетімді болса, өздігінен соған ауыстыру</string>
|
||||
<string name="no_bookmarks_yet">Бетбелгі жоқ</string>
|
||||
<string name="no_thanks">Жоқ, рақмет</string>
|
||||
<string name="suggest_new_sources_summary">Қолданбаның жаңа нұсқасында пайда болған дереккөзді ұсыну</string>
|
||||
<string name="share_logs">Логты бөлісу</string>
|
||||
<string name="speed">Жылдамдық</string>
|
||||
<string name="download_option_all_chapters">%s аударған түгел тарау</string>
|
||||
<string name="suggestion_manga">Ұсыныс: %s</string>
|
||||
<string name="color_black">Қара</string>
|
||||
<string name="removed_from_favourites">Таңдаулыдан жойылды</string>
|
||||
<string name="this_month">Осы ай</string>
|
||||
<string name="proxy">Прокси</string>
|
||||
<string name="error_no_space_left">Жадта бос орын қалмады</string>
|
||||
</resources>
|
||||
@@ -483,4 +483,25 @@
|
||||
<string name="show">Mostrar</string>
|
||||
<string name="color_black">Preto</string>
|
||||
<string name="this_month">Esse mês</string>
|
||||
<string name="frequency_every_day">Diariamente</string>
|
||||
<string name="categories">Categorias</string>
|
||||
<string name="list_options">Opções da lista</string>
|
||||
<string name="backup_frequency">Frequência de criação de backup</string>
|
||||
<string name="suggest_new_sources">Sugerir novas fontes após a atualização do aplicativo</string>
|
||||
<string name="periodic_backups_enable">Ativar backups periódicos</string>
|
||||
<string name="enhanced_colors_summary">Reduz a formação de faixas, mas pode afetar o desempenho</string>
|
||||
<string name="frequency_every_2_days">A cada 2 dias</string>
|
||||
<string name="frequency_once_per_week">Semanalmente</string>
|
||||
<string name="periodic_backups">Backups periódicos</string>
|
||||
<string name="frequency_twice_per_month">Duas vezes ao mês</string>
|
||||
<string name="online_variant">Variante on-line</string>
|
||||
<string name="by_relevance">Relevância</string>
|
||||
<string name="state_abandoned">Abandonado</string>
|
||||
<string name="keep_screen_on">Manter a tela ligada</string>
|
||||
<string name="frequency_once_per_month">Uma vez por mês</string>
|
||||
<string name="enhanced_colors">Modo de cor de 32 bits</string>
|
||||
<string name="keep_screen_on_summary">Não desligue a tela enquanto estiver lendo mangá</string>
|
||||
<string name="last_successful_backup">Último backup bem-sucedido: %s</string>
|
||||
<string name="backups_output_directory">Local de saída de backups</string>
|
||||
<string name="suggest_new_sources_summary">Solicitação para ativar fontes recém-adicionadas após a atualização do aplicativo</string>
|
||||
</resources>
|
||||
@@ -41,11 +41,11 @@
|
||||
<string name="remove">Remover</string>
|
||||
<string name="_s_deleted_from_local_storage">«%s» deletado do armazenamento local</string>
|
||||
<string name="save_page">Salvar página</string>
|
||||
<string name="page_saved">Salvou</string>
|
||||
<string name="page_saved">Salvo</string>
|
||||
<string name="share_image">Compartilhar imagem</string>
|
||||
<string name="_import">Importar</string>
|
||||
<string name="updated">Atualizado</string>
|
||||
<string name="delete">Deletar</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="operation_not_supported">Essa operação não é suportada</string>
|
||||
<string name="clear_pages_cache">Limpar cache de página</string>
|
||||
<string name="text_file_sizes">B|kB|MB|GB|TB</string>
|
||||
@@ -66,10 +66,10 @@
|
||||
<string name="internal_storage">Armazenamento interno</string>
|
||||
<string name="external_storage">Armazenamento externo</string>
|
||||
<string name="domain">Domínio</string>
|
||||
<string name="app_update_available">Uma nova versão da app está disponível</string>
|
||||
<string name="app_update_available">Uma nova versão do app está disponível</string>
|
||||
<string name="open_in_browser">Abrir no navegador da web</string>
|
||||
<string name="large_manga_save_confirm">Este mangá tem %s. Salvar tudo isso\?</string>
|
||||
<string name="save_manga">Salve</string>
|
||||
<string name="save_manga">Salvar</string>
|
||||
<string name="notifications">Notificações</string>
|
||||
<string name="new_chapters">Novos capítulos</string>
|
||||
<string name="download">Download</string>
|
||||
@@ -483,4 +483,15 @@
|
||||
<string name="show">Mostrar</string>
|
||||
<string name="color_black">Preto</string>
|
||||
<string name="this_month">Este mês</string>
|
||||
<string name="categories">Categorias</string>
|
||||
<string name="list_options">Listar opções</string>
|
||||
<string name="suggest_new_sources">Sugira novas fontes após atualização do app</string>
|
||||
<string name="enhanced_colors_summary">Reduz faixas, mas pode afetar o desempenho</string>
|
||||
<string name="online_variant">Variante online</string>
|
||||
<string name="by_relevance">Relevância</string>
|
||||
<string name="enhanced_colors">Modo de cor 32-bit</string>
|
||||
<string name="suggest_new_sources_summary">Solicitar a ativação de fontes recém-adicionadas após atualizar o aplicativo</string>
|
||||
<string name="state_abandoned">Caiu</string>
|
||||
<string name="keep_screen_on">Manter a tela ligada</string>
|
||||
<string name="keep_screen_on_summary">Não desligue a tela enquanto estiver lendo mangá</string>
|
||||
</resources>
|
||||
@@ -494,4 +494,15 @@
|
||||
<string name="enhanced_colors">32-битный цветовой режим</string>
|
||||
<string name="suggest_new_sources_summary">Предлагать источники манги, добавленные в последнем обновлении приложения</string>
|
||||
<string name="online_variant">Онлайн вариант</string>
|
||||
<string name="frequency_every_day">Каждый день</string>
|
||||
<string name="backup_frequency">Частота создания резервных копий</string>
|
||||
<string name="periodic_backups_enable">Включить резервное копирование по расписанию</string>
|
||||
<string name="frequency_every_2_days">Каждые 2 дня</string>
|
||||
<string name="frequency_once_per_week">Раз в неделю</string>
|
||||
<string name="periodic_backups">Резервное копирование по расписанию</string>
|
||||
<string name="frequency_twice_per_month">Дважды в месяц</string>
|
||||
<string name="frequency_once_per_month">Один раз в месяц</string>
|
||||
<string name="backups_output_directory">Каталог для сохранения резервных копий</string>
|
||||
<string name="last_successful_backup">Последняя резервная копия: %s</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
</resources>
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="light">สว่าง</string>
|
||||
<string name="automatic">ตั้งค่าตามเครื่อง</string>
|
||||
<string name="text_clear_history_prompt">จะเคลียร์ประวัติการอ่านทั้งหมดแบบถาวรใช่ไหม\?</string>
|
||||
@@ -160,7 +160,7 @@
|
||||
<string name="zoom_mode_fit_center">พอดีตรงกลาง</string>
|
||||
<string name="zoom_mode_fit_height">พอดีกับความสูง</string>
|
||||
<string name="black_dark_theme">ดำ</string>
|
||||
<string name="just_now">เมื่อกี้</string>
|
||||
<string name="just_now">เมื่อเร็วนี้</string>
|
||||
<string name="clear_feed">เคลียร์ฟีด</string>
|
||||
<string name="backup_restore">สำรองและคืนค่า</string>
|
||||
<string name="create_backup">สร้างข้อมูลสำรอง</string>
|
||||
@@ -344,4 +344,29 @@
|
||||
<string name="exit_confirmation_summary">กดย้อนกลับสองครั้งเพื่อออกจากแอป</string>
|
||||
<string name="bookmarks_removed">ลบบุ๊คมาร์กแล้ว</string>
|
||||
<string name="error_no_space_left">ไม่มีพื้นที่เหลือบนอุปกรณ์แล้ว</string>
|
||||
<string name="zoom_in">ซูมเข้า</string>
|
||||
<string name="detect_reader_mode">โหมดตรวจจับเครื่องอ่านอัตโนมัติ</string>
|
||||
<string name="frequency_every_day">ทุกวัน</string>
|
||||
<string name="categories">หมวดหมู่</string>
|
||||
<string name="backup_frequency">ความถี่การสำรองข้อมูล</string>
|
||||
<string name="enabled_d_of_d" tools:ignore="PluralsCandidate">%1$d of %2$d on</string>
|
||||
<string name="unknown">ไม่ทราบ</string>
|
||||
<string name="frequency_every_2_days">ทุก 2 วัน</string>
|
||||
<string name="frequency_once_per_week">สัปดาห์ละครั้ง</string>
|
||||
<string name="reader_zoom_buttons">โชว์ปุ่มซูม</string>
|
||||
<string name="select_range">เลือกช่วง</string>
|
||||
<string name="no_manga_sources">ไม่พบแหล่งมังงะ</string>
|
||||
<string name="frequency_twice_per_month">สองครั้งต่อเดือน</string>
|
||||
<string name="keep_screen_on">เปิดหน้าจอไว้ตลอด</string>
|
||||
<string name="categories_delete_confirm">คุณแน่ใจหรือไม่ว่าต้องการลบหมวดหมู่รายการโปรดที่เลือก
|
||||
\nมังงะทั้งหมดในนั้นจะหายไปและไม่สามารถยกเลิกได้</string>
|
||||
<string name="frequency_once_per_month">เดือนละครั้ง</string>
|
||||
<string name="no_manga_sources_text">เปิดใช้งานแหล่งมังงะเพื่ออ่านมังงะออนไลน์</string>
|
||||
<string name="enhanced_colors">โหมดสี 32 บิต</string>
|
||||
<string name="background">พื้นหลัง</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
<string name="use_fingerprint">ใช้สแกนลายนิ้วมือหากมี</string>
|
||||
<string name="zoom_out">ซูมออก</string>
|
||||
<string name="text_search_holder_secondary">โปรดลองเรียบเรียงคำค้นหา</string>
|
||||
<string name="last_successful_backup">สำรองข้อมูลสำเร็จล่าสุด: %s</string>
|
||||
</resources>
|
||||
@@ -494,4 +494,15 @@
|
||||
<string name="list_options">Seçenekleri listele</string>
|
||||
<string name="online_variant">Çevrimiçi varyant</string>
|
||||
<string name="by_relevance">Alaka düzeyi</string>
|
||||
<string name="frequency_every_day">Her gün</string>
|
||||
<string name="backup_frequency">Yedek oluşturma sıklığı</string>
|
||||
<string name="periodic_backups_enable">Zamanlı yedeklemeleri etkinleştirin</string>
|
||||
<string name="frequency_every_2_days">2 günde 1</string>
|
||||
<string name="frequency_once_per_week">Haftada 1 kez</string>
|
||||
<string name="periodic_backups">Zamanlı yedekleme</string>
|
||||
<string name="frequency_twice_per_month">Ayda 2 kere</string>
|
||||
<string name="frequency_once_per_month">Ayda 1 kere</string>
|
||||
<string name="backups_output_directory">Yedekleme dizini</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
<string name="last_successful_backup">Son başarılı yedekleme: %s</string>
|
||||
</resources>
|
||||
@@ -494,4 +494,15 @@
|
||||
<string name="enhanced_colors">32-бітний колірний режим</string>
|
||||
<string name="suggest_new_sources_summary">Пропонує включити нові джерела манґи після оновлення застосунку</string>
|
||||
<string name="online_variant">Онлайн варіант</string>
|
||||
<string name="frequency_every_day">Кожен день</string>
|
||||
<string name="backup_frequency">Частота резервного копіювання</string>
|
||||
<string name="periodic_backups_enable">Увімкніть періодичне резервне копіювання</string>
|
||||
<string name="frequency_every_2_days">Кожні 2 дні</string>
|
||||
<string name="frequency_once_per_week">Раз на тиждень</string>
|
||||
<string name="periodic_backups">Періодичне резервне копіювання</string>
|
||||
<string name="frequency_twice_per_month">Двічі на місяць</string>
|
||||
<string name="frequency_once_per_month">Раз на місяць</string>
|
||||
<string name="last_successful_backup">Останнє успішне резервне копіювання: %s</string>
|
||||
<string name="backups_output_directory">Вихідний каталог резервних копій</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
</resources>
|
||||
@@ -1,18 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="other">%1$d chương mới</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="other">%1$d chương</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="other">%1$d phút trước</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="other">%1$d giờ trước</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="other">%1$d ngày trước</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="other">%1$d chương mới</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="other">%1$d chương</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="other">%1$d phút trước</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="other">%1$d giờ trước</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="other">%1$d ngày trước</item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="other">%1$d mục</item>
|
||||
</plurals>
|
||||
<plurals name="months_ago">
|
||||
<item quantity="other">%1$d tháng trước</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -407,7 +407,7 @@
|
||||
<string name="local_manga_processing">Xử lý các truyện được lưu</string>
|
||||
<string name="text_delete_local_manga_batch">Xoá vĩnh viễn những thứ được chọn khỏi thiết bị\?</string>
|
||||
<string name="percent_string_pattern">%1$s%%</string>
|
||||
<string name="disable_battery_optimization_summary">Giúp kiểm tra chương mới dưới nền dễ dàng hơn</string>
|
||||
<string name="disable_battery_optimization_summary">Giúp kiểm tra chương mới trong khi chạy dưới nền dễ dàng hơn</string>
|
||||
<string name="show_on_shelf">Hiển thị trên kệ sách</string>
|
||||
<string name="mirror_switching">Tự động chọn máy chủ dự phòng</string>
|
||||
<string name="mirror_switching_summary">Tự động chuyển tên miền sang máy chủ dự phòng (nếu có) nếu gặp lỗi trên tên miền chính</string>
|
||||
|
||||
@@ -17,16 +17,16 @@
|
||||
<string name="silent">无声</string>
|
||||
<string name="preparing_">准备…</string>
|
||||
<string name="file_not_found">未找到文件</string>
|
||||
<string name="yesterday">昨日</string>
|
||||
<string name="yesterday">昨天</string>
|
||||
<string name="backup_information">你可以创建你的历史和收藏的备份并恢复它</string>
|
||||
<string name="just_now">刚刚</string>
|
||||
<string name="long_ago">很久以前</string>
|
||||
<string name="group">分组</string>
|
||||
<string name="tap_to_try_again">轻击重试</string>
|
||||
<string name="tap_to_try_again">点击重试</string>
|
||||
<string name="reader_mode_hint">所选配置将被这部漫画记住</string>
|
||||
<string name="captcha_required">需要验证码</string>
|
||||
<string name="captcha_solve">解决</string>
|
||||
<string name="today">今日</string>
|
||||
<string name="today">今天</string>
|
||||
<string name="clear_cookies">清除cookies</string>
|
||||
<string name="new_sources_text">有新的漫画源可用</string>
|
||||
<string name="suggestions_summary">根据你的喜好推荐漫画</string>
|
||||
@@ -79,7 +79,7 @@
|
||||
<string name="clear">清除</string>
|
||||
<string name="text_clear_history_prompt">永久清除所有阅读历史\?</string>
|
||||
<string name="remove">删除</string>
|
||||
<string name="_s_deleted_from_local_storage">\"%s\"从本地存储中删除</string>
|
||||
<string name="_s_deleted_from_local_storage">已从本地存储中删除“%s”</string>
|
||||
<string name="save_page">保存页面</string>
|
||||
<string name="page_saved">保存</string>
|
||||
<string name="share_image">分享图片</string>
|
||||
@@ -110,8 +110,8 @@
|
||||
<string name="internal_storage">内部存储</string>
|
||||
<string name="external_storage">外部存储</string>
|
||||
<string name="domain">范围</string>
|
||||
<string name="app_update_available">新版本应用程序已经推出</string>
|
||||
<string name="open_in_browser">在网络浏览器中打开</string>
|
||||
<string name="app_update_available">发现新版本</string>
|
||||
<string name="open_in_browser">在浏览器中打开</string>
|
||||
<string name="large_manga_save_confirm">这部漫画有 %s 。全部保存?</string>
|
||||
<string name="save_manga">保存</string>
|
||||
<string name="notifications">通知</string>
|
||||
@@ -122,7 +122,7 @@
|
||||
<string name="light_indicator">LED指示器</string>
|
||||
<string name="vibration">振动</string>
|
||||
<string name="favourites_categories">收藏分类</string>
|
||||
<string name="remove_category">删除</string>
|
||||
<string name="remove_category">移除</string>
|
||||
<string name="text_empty_holder_primary">这里有点空…</string>
|
||||
<string name="text_search_holder_secondary">尝试重新表述查询。</string>
|
||||
<string name="text_history_holder_primary">你看过的内容将在这里显示</string>
|
||||
@@ -146,9 +146,9 @@
|
||||
<string name="new_version_s">新版本: %s</string>
|
||||
<string name="clear_updates_feed">清除订阅更新记录</string>
|
||||
<string name="updates_feed_cleared">已清除</string>
|
||||
<string name="rotate_screen">旋转屏幕</string>
|
||||
<string name="rotate_screen">屏幕旋转</string>
|
||||
<string name="update">更新</string>
|
||||
<string name="feed_will_update_soon">订阅更新即将开始</string>
|
||||
<string name="feed_will_update_soon">即将开始更新订阅</string>
|
||||
<string name="track_sources">查找更新</string>
|
||||
<string name="dont_check">不要检查</string>
|
||||
<string name="enter_password">输入密码</string>
|
||||
@@ -160,7 +160,7 @@
|
||||
<string name="about">关于</string>
|
||||
<string name="app_version">版本%s</string>
|
||||
<string name="check_for_updates">检查更新</string>
|
||||
<string name="no_update_available">没有更新</string>
|
||||
<string name="no_update_available">无可用更新</string>
|
||||
<string name="right_to_left">从右到左</string>
|
||||
<string name="create_category">新分类</string>
|
||||
<string name="scale_mode">缩放模式</string>
|
||||
@@ -245,7 +245,7 @@
|
||||
<string name="bookmark_added">书签已添加</string>
|
||||
<string name="undo">撤销</string>
|
||||
<string name="removed_from_history">从历史中删除</string>
|
||||
<string name="dns_over_https">DNS over HTTPS</string>
|
||||
<string name="dns_over_https">基于 HTTPS 的 DNS</string>
|
||||
<string name="default_mode">默认模式</string>
|
||||
<string name="detect_reader_mode">自动检测阅读器模式</string>
|
||||
<string name="detect_reader_mode_summary">自动检测漫画是否为条漫</string>
|
||||
@@ -369,7 +369,7 @@
|
||||
<string name="allow_unstable_updates">允许不稳定更新</string>
|
||||
<string name="allow_unstable_updates_summary">接收不稳定版本更新的通知</string>
|
||||
<string name="download_started">下载已开始</string>
|
||||
<string name="user_agent">UserAgent 标头</string>
|
||||
<string name="user_agent">UserAgent 标识</string>
|
||||
<string name="settings_apply_restart_required">要应用这些更改请重启程序</string>
|
||||
<string name="sources_reorder_tip">点击并长按项目排序</string>
|
||||
<string name="got_it">知道了</string>
|
||||
@@ -451,7 +451,7 @@
|
||||
<string name="progress">阅读进度</string>
|
||||
<string name="error_corrupted_file">无效数据回传或文件已损坏</string>
|
||||
<string name="related_manga_summary">显示相关漫画。可能并不准确或缺失</string>
|
||||
<string name="tracker_wifi_only_summary">使用计量网络时停止检查新章节</string>
|
||||
<string name="tracker_wifi_only_summary">使用按流量计费的网络时停止检查新章节</string>
|
||||
<string name="order_added">添加日期</string>
|
||||
<string name="on_device">本地</string>
|
||||
<string name="moved_to_top">移动到顶部</string>
|
||||
@@ -468,7 +468,7 @@
|
||||
<string name="advanced">高级</string>
|
||||
<string name="color_dark">深色</string>
|
||||
<string name="too_many_requests_message">请求次数过多,稍候再尝试</string>
|
||||
<string name="suggestions_wifi_only_summary">使用计量网络时停止推荐漫画</string>
|
||||
<string name="suggestions_wifi_only_summary">使用按流量计费的网络时停止推荐漫画</string>
|
||||
<string name="default_section">默认栏目</string>
|
||||
<string name="background">阅读背景色</string>
|
||||
<string name="manga_list">漫画列表</string>
|
||||
@@ -488,4 +488,17 @@
|
||||
<string name="enhanced_colors_summary">减少色带,但可能会影响性能</string>
|
||||
<string name="enhanced_colors">32位色彩模式</string>
|
||||
<string name="suggest_new_sources_summary">应用更新后快速启用新增的漫画源</string>
|
||||
<string name="frequency_every_day">每天一次</string>
|
||||
<string name="categories">分类</string>
|
||||
<string name="backup_frequency">备份频率</string>
|
||||
<string name="periodic_backups_enable">定期备份</string>
|
||||
<string name="view_list">查看列表</string>
|
||||
<string name="frequency_every_2_days">每两天一次</string>
|
||||
<string name="frequency_once_per_week">每周一次</string>
|
||||
<string name="periodic_backups">自动备份</string>
|
||||
<string name="frequency_twice_per_month">每月两次</string>
|
||||
<string name="frequency_once_per_month">每月一次</string>
|
||||
<string name="last_successful_backup">上次备份成功:%s</string>
|
||||
<string name="backups_output_directory">备份保存路径</string>
|
||||
<string name="download_option_all_chapters">所有已翻译的章节 %s</string>
|
||||
</resources>
|
||||
@@ -1,51 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string-array name="themes">
|
||||
<string-array name="themes" translatable="false">
|
||||
<item>@string/automatic</item>
|
||||
<item>@string/light</item>
|
||||
<item>@string/dark</item>
|
||||
</string-array>
|
||||
<string-array name="reader_switchers">
|
||||
<string-array name="reader_switchers" translatable="false">
|
||||
<item>@string/taps_on_edges</item>
|
||||
<item>@string/volume_buttons</item>
|
||||
</string-array>
|
||||
<string-array name="zoom_modes">
|
||||
<string-array name="zoom_modes" translatable="false">
|
||||
<item>@string/zoom_mode_fit_center</item>
|
||||
<item>@string/zoom_mode_fit_height</item>
|
||||
<item>@string/zoom_mode_fit_width</item>
|
||||
<item>@string/zoom_mode_keep_start</item>
|
||||
</string-array>
|
||||
<string-array name="track_sources">
|
||||
<string-array name="track_sources" translatable="false">
|
||||
<item>@string/favourites</item>
|
||||
<item>@string/history</item>
|
||||
</string-array>
|
||||
<string-array name="list_modes">
|
||||
<string-array name="list_modes" translatable="false">
|
||||
<item>@string/list</item>
|
||||
<item>@string/detailed_list</item>
|
||||
<item>@string/grid</item>
|
||||
</string-array>
|
||||
<string-array name="screenshots_policy">
|
||||
<string-array name="screenshots_policy" translatable="false">
|
||||
<item>@string/screenshots_allow</item>
|
||||
<item>@string/screenshots_block_nsfw</item>
|
||||
<item>@string/screenshots_block_all</item>
|
||||
</string-array>
|
||||
<string-array name="network_policy">
|
||||
<string-array name="network_policy" translatable="false">
|
||||
<item>@string/always</item>
|
||||
<item>@string/only_using_wifi</item>
|
||||
<item>@string/never</item>
|
||||
</string-array>
|
||||
<string-array name="doh_providers">
|
||||
<string-array name="doh_providers" translatable="false">
|
||||
<item>@string/disabled</item>
|
||||
<item>Google</item>
|
||||
<item>CloudFlare</item>
|
||||
<item>AdGuard</item>
|
||||
</string-array>
|
||||
<string-array name="reader_modes">
|
||||
<string-array name="reader_modes" translatable="false">
|
||||
<item>@string/standard</item>
|
||||
<item>@string/right_to_left</item>
|
||||
<item>@string/webtoon</item>
|
||||
</string-array>
|
||||
<string-array name="scrobbling_statuses">
|
||||
<string-array name="scrobbling_statuses" translatable="false">
|
||||
<item>@string/status_planned</item>
|
||||
<item>@string/status_reading</item>
|
||||
<item>@string/status_re_reading</item>
|
||||
@@ -53,25 +53,32 @@
|
||||
<item>@string/status_on_hold</item>
|
||||
<item>@string/status_dropped</item>
|
||||
</string-array>
|
||||
<string-array name="proxy_types">
|
||||
<string-array name="proxy_types" translatable="false">
|
||||
<item>@string/disabled</item>
|
||||
<item>HTTP</item>
|
||||
<item>SOCKS (v4/v5)</item>
|
||||
</string-array>
|
||||
<string-array name="reader_backgrounds">
|
||||
<string-array name="reader_backgrounds" translatable="false">
|
||||
<item>@string/system_default</item>
|
||||
<item>@string/color_light</item>
|
||||
<item>@string/color_dark</item>
|
||||
<item>@string/color_white</item>
|
||||
<item>@string/color_black</item>
|
||||
</string-array>
|
||||
<string-array name="reader_animation">
|
||||
<string-array name="reader_animation" translatable="false">
|
||||
<item>@string/disabled</item>
|
||||
<item>@string/system_default</item>
|
||||
<item>@string/advanced</item>
|
||||
</string-array>
|
||||
<string-array name="first_nav_item">
|
||||
<string-array name="first_nav_item" translatable="false">
|
||||
<item>@string/history</item>
|
||||
<item>@string/favourites</item>
|
||||
</string-array>
|
||||
<string-array name="backup_frequency" translatable="false">
|
||||
<item>@string/frequency_every_day</item>
|
||||
<item>@string/frequency_every_2_days</item>
|
||||
<item>@string/frequency_once_per_week</item>
|
||||
<item>@string/frequency_twice_per_month</item>
|
||||
<item>@string/frequency_once_per_month</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -64,4 +64,11 @@
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
</string-array>
|
||||
<string-array name="values_backup_frequency" translatable="false">
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
<item>7</item>
|
||||
<item>14</item>
|
||||
<item>30</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
||||
@@ -500,4 +500,15 @@
|
||||
<string name="by_relevance">Relevance</string>
|
||||
<string name="categories">Categories</string>
|
||||
<string name="online_variant">Online variant</string>
|
||||
<string name="periodic_backups">Periodic backups</string>
|
||||
<string name="backup_frequency">Backup creation frequency</string>
|
||||
<string name="frequency_every_day">Every day</string>
|
||||
<string name="frequency_every_2_days">Every 2 days</string>
|
||||
<string name="frequency_once_per_week">Once per week</string>
|
||||
<string name="frequency_twice_per_month">Twice per month</string>
|
||||
<string name="frequency_once_per_month">Once per month</string>
|
||||
<string name="periodic_backups_enable">Enable periodic backups</string>
|
||||
<string name="backups_output_directory">Backups output directory</string>
|
||||
<string name="last_successful_backup">Last successful backup: %s</string>
|
||||
<string name="speed_value">x%.1f</string>
|
||||
</resources>
|
||||
|
||||
35
app/src/main/res/xml/pref_backup_periodic.xml
Normal file
35
app/src/main/res/xml/pref_backup_periodic.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.preference.PreferenceScreen
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="false"
|
||||
android:key="backup_periodic"
|
||||
android:layout="@layout/preference_toggle_header"
|
||||
android:title="@string/periodic_backups_enable" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="7"
|
||||
android:dependency="backup_periodic"
|
||||
android:entries="@array/backup_frequency"
|
||||
android:entryValues="@array/values_backup_frequency"
|
||||
android:key="backup_periodic_freq"
|
||||
android:title="@string/backup_frequency"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<Preference
|
||||
android:dependency="backup_periodic"
|
||||
android:key="backup_periodic_output"
|
||||
android:title="@string/backups_output_directory" />
|
||||
|
||||
<Preference
|
||||
android:dependency="backup_periodic"
|
||||
android:icon="@drawable/ic_info_outline"
|
||||
android:key="backup_periodic_last"
|
||||
android:persistent="false"
|
||||
android:selectable="false"
|
||||
app:allowDividerAbove="true"
|
||||
app:isPreferenceVisible="false" />
|
||||
|
||||
</androidx.preference.PreferenceScreen>
|
||||
@@ -34,6 +34,12 @@
|
||||
android:summary="@string/restore_summary"
|
||||
android:title="@string/restore_backup" />
|
||||
|
||||
<Preference
|
||||
android:fragment="org.koitharu.kotatsu.settings.backup.PeriodicalBackupSettingsFragment"
|
||||
android:key="backup_periodic"
|
||||
android:persistent="false"
|
||||
android:title="@string/periodic_backups" />
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory android:title="@string/storage_usage">
|
||||
|
||||
@@ -5,9 +5,9 @@ buildscript {
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.2'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.10'
|
||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20'
|
||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.48.1'
|
||||
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.10-1.0.13'
|
||||
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.20-RC2-1.0.13'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user