Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
c7a1312cd6
|
|||
|
b2927854d4
|
|||
|
cfda150630
|
|||
|
4fa1382ce9
|
|||
|
43075c52d1
|
|||
|
87942747fc
|
|||
|
bb6cd73acd
|
|||
|
|
6790e5b0d4 | ||
|
|
845c356a73 | ||
|
|
34499ea77d | ||
|
|
6210864280 | ||
|
|
19084419c7 |
@@ -16,8 +16,8 @@ android {
|
||||
applicationId 'org.koitharu.kotatsu'
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 581
|
||||
versionName = '6.1.3'
|
||||
versionCode = 583
|
||||
versionName = '6.1.5'
|
||||
generatedDensities = []
|
||||
testInstrumentationRunner "org.koitharu.kotatsu.HiltTestRunner"
|
||||
ksp {
|
||||
@@ -81,7 +81,7 @@ afterEvaluate {
|
||||
}
|
||||
dependencies {
|
||||
//noinspection GradleDependency
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:0004be15ba') {
|
||||
implementation('com.github.KotatsuApp:kotatsu-parsers:931f126119') {
|
||||
exclude group: 'org.json', module: 'json'
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ dependencies {
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.core:core-ktx:1.12.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.7.2'
|
||||
implementation 'androidx.activity:activity-ktx:1.8.0-rc01'
|
||||
implementation 'androidx.fragment:fragment-ktx:1.6.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
|
||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.6.2'
|
||||
@@ -120,7 +120,7 @@ dependencies {
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
|
||||
implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.11.0'
|
||||
implementation 'com.squareup.okio:okio:3.5.0'
|
||||
implementation 'com.squareup.okio:okio:3.6.0'
|
||||
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl:4.3.2'
|
||||
implementation 'com.hannesdorfmann:adapterdelegates4-kotlin-dsl-viewbinding:4.3.2'
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.koitharu.kotatsu.core.network
|
||||
|
||||
import androidx.collection.ArraySet
|
||||
import dagger.Lazy
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
@@ -13,6 +16,7 @@ import org.koitharu.kotatsu.core.parser.MangaRepository
|
||||
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import java.util.EnumMap
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -22,9 +26,15 @@ class MirrorSwitchInterceptor @Inject constructor(
|
||||
private val settings: AppSettings,
|
||||
) : Interceptor {
|
||||
|
||||
private val locks = EnumMap<MangaSource, Any>(MangaSource::class.java)
|
||||
private val blacklist = EnumMap<MangaSource, MutableSet<String>>(MangaSource::class.java)
|
||||
|
||||
val isEnabled: Boolean
|
||||
get() = settings.isMirrorSwitchingAvailable
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request = chain.request()
|
||||
if (!settings.isMirrorSwitchingAvailable) {
|
||||
if (!isEnabled) {
|
||||
return chain.proceed(request)
|
||||
}
|
||||
return try {
|
||||
@@ -43,6 +53,30 @@ class MirrorSwitchInterceptor @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun trySwitchMirror(repository: RemoteMangaRepository): Boolean = runInterruptible(Dispatchers.Default) {
|
||||
if (!isEnabled) {
|
||||
return@runInterruptible false
|
||||
}
|
||||
val mirrors = repository.getAvailableMirrors()
|
||||
if (mirrors.size <= 1) {
|
||||
return@runInterruptible false
|
||||
}
|
||||
synchronized(obtainLock(repository.source)) {
|
||||
val currentMirror = repository.domain
|
||||
addToBlacklist(repository.source, currentMirror)
|
||||
val newMirror = mirrors.firstOrNull { x ->
|
||||
x != currentMirror && !isBlacklisted(repository.source, x)
|
||||
} ?: return@synchronized false
|
||||
repository.domain = newMirror
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
fun rollback(repository: RemoteMangaRepository, oldMirror: String) = synchronized(obtainLock(repository.source)) {
|
||||
blacklist[repository.source]?.remove(oldMirror)
|
||||
repository.domain = oldMirror
|
||||
}
|
||||
|
||||
private fun trySwitchMirror(request: Request, chain: Interceptor.Chain): Response? {
|
||||
val source = request.tag(MangaSource::class.java) ?: return null
|
||||
val repository = mangaRepositoryFactoryLazy.get().create(source) as? RemoteMangaRepository ?: return null
|
||||
@@ -50,7 +84,9 @@ class MirrorSwitchInterceptor @Inject constructor(
|
||||
if (mirrors.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
return tryMirrors(repository, mirrors, chain, request)
|
||||
return synchronized(obtainLock(repository.source)) {
|
||||
tryMirrors(repository, mirrors, chain, request)
|
||||
}
|
||||
}
|
||||
|
||||
private fun tryMirrors(
|
||||
@@ -66,7 +102,7 @@ class MirrorSwitchInterceptor @Inject constructor(
|
||||
}
|
||||
val urlBuilder = url.newBuilder()
|
||||
for (mirror in mirrors) {
|
||||
if (mirror == currentDomain) {
|
||||
if (mirror == currentDomain || isBlacklisted(repository.source, mirror)) {
|
||||
continue
|
||||
}
|
||||
val newHost = hostOf(url.host, mirror) ?: continue
|
||||
@@ -75,6 +111,7 @@ class MirrorSwitchInterceptor @Inject constructor(
|
||||
.build()
|
||||
val response = chain.proceed(newRequest)
|
||||
if (response.isFailed) {
|
||||
addToBlacklist(repository.source, mirror)
|
||||
response.closeQuietly()
|
||||
} else {
|
||||
repository.domain = mirror
|
||||
@@ -104,4 +141,18 @@ class MirrorSwitchInterceptor @Inject constructor(
|
||||
private fun ResponseBody.copy(): ResponseBody {
|
||||
return source().readByteArray().toResponseBody(contentType())
|
||||
}
|
||||
|
||||
private fun obtainLock(source: MangaSource): Any = locks.getOrPut(source) {
|
||||
Any()
|
||||
}
|
||||
|
||||
private fun isBlacklisted(source: MangaSource, domain: String): Boolean {
|
||||
return blacklist[source]?.contains(domain) == true
|
||||
}
|
||||
|
||||
private fun addToBlacklist(source: MangaSource, domain: String) {
|
||||
blacklist.getOrPut(source) {
|
||||
ArraySet(2)
|
||||
}.add(domain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +76,18 @@ class AppShortcutManager @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun requestPinShortcut(manga: Manga): Boolean {
|
||||
return ShortcutManagerCompat.requestPinShortcut(context, buildShortcutInfo(manga), null)
|
||||
suspend fun requestPinShortcut(manga: Manga): Boolean = try {
|
||||
ShortcutManagerCompat.requestPinShortcut(context, buildShortcutInfo(manga), null)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.printStackTraceDebug()
|
||||
false
|
||||
}
|
||||
|
||||
suspend fun requestPinShortcut(source: MangaSource): Boolean {
|
||||
return ShortcutManagerCompat.requestPinShortcut(context, buildShortcutInfo(source), null)
|
||||
suspend fun requestPinShortcut(source: MangaSource): Boolean = try {
|
||||
ShortcutManagerCompat.requestPinShortcut(context, buildShortcutInfo(source), null)
|
||||
} catch (e: IllegalStateException) {
|
||||
e.printStackTraceDebug()
|
||||
false
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.parser
|
||||
|
||||
import androidx.annotation.AnyThread
|
||||
import org.koitharu.kotatsu.core.cache.ContentCache
|
||||
import org.koitharu.kotatsu.core.network.MirrorSwitchInterceptor
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.MangaLoaderContext
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
@@ -43,6 +44,7 @@ interface MangaRepository {
|
||||
private val localMangaRepository: LocalMangaRepository,
|
||||
private val loaderContext: MangaLoaderContext,
|
||||
private val contentCache: ContentCache,
|
||||
private val mirrorSwitchInterceptor: MirrorSwitchInterceptor,
|
||||
) {
|
||||
|
||||
private val cache = EnumMap<MangaSource, WeakReference<RemoteMangaRepository>>(MangaSource::class.java)
|
||||
@@ -55,7 +57,11 @@ interface MangaRepository {
|
||||
cache[source]?.get()?.let { return it }
|
||||
return synchronized(cache) {
|
||||
cache[source]?.get()?.let { return it }
|
||||
val repository = RemoteMangaRepository(MangaParser(source, loaderContext), contentCache)
|
||||
val repository = RemoteMangaRepository(
|
||||
parser = MangaParser(source, loaderContext),
|
||||
cache = contentCache,
|
||||
mirrorSwitchInterceptor = mirrorSwitchInterceptor,
|
||||
)
|
||||
cache[source] = WeakReference(repository)
|
||||
repository
|
||||
}
|
||||
|
||||
@@ -13,11 +13,13 @@ import okhttp3.Response
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
import org.koitharu.kotatsu.core.cache.ContentCache
|
||||
import org.koitharu.kotatsu.core.cache.SafeDeferred
|
||||
import org.koitharu.kotatsu.core.network.MirrorSwitchInterceptor
|
||||
import org.koitharu.kotatsu.core.prefs.SourceSettings
|
||||
import org.koitharu.kotatsu.core.util.ext.processLifecycleScope
|
||||
import org.koitharu.kotatsu.parsers.MangaParser
|
||||
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
|
||||
import org.koitharu.kotatsu.parsers.config.ConfigKey
|
||||
import org.koitharu.kotatsu.parsers.exception.ParseException
|
||||
import org.koitharu.kotatsu.parsers.model.Favicons
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaChapter
|
||||
@@ -31,6 +33,7 @@ import org.koitharu.kotatsu.parsers.util.runCatchingCancellable
|
||||
class RemoteMangaRepository(
|
||||
private val parser: MangaParser,
|
||||
private val cache: ContentCache,
|
||||
private val mirrorSwitchInterceptor: MirrorSwitchInterceptor,
|
||||
) : MangaRepository, Interceptor {
|
||||
|
||||
override val source: MangaSource
|
||||
@@ -66,11 +69,15 @@ class RemoteMangaRepository(
|
||||
}
|
||||
|
||||
override suspend fun getList(offset: Int, query: String): List<Manga> {
|
||||
return parser.getList(offset, query)
|
||||
return mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getList(offset, query)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getList(offset: Int, tags: Set<MangaTag>?, sortOrder: SortOrder?): List<Manga> {
|
||||
return parser.getList(offset, tags, sortOrder)
|
||||
return mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getList(offset, tags, sortOrder)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDetails(manga: Manga): Manga = getDetails(manga, withCache = true)
|
||||
@@ -78,17 +85,25 @@ class RemoteMangaRepository(
|
||||
override suspend fun getPages(chapter: MangaChapter): List<MangaPage> {
|
||||
cache.getPages(source, chapter.url)?.let { return it }
|
||||
val pages = asyncSafe {
|
||||
parser.getPages(chapter).distinctById()
|
||||
mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getPages(chapter).distinctById()
|
||||
}
|
||||
}
|
||||
cache.putPages(source, chapter.url, pages)
|
||||
return pages.await()
|
||||
}
|
||||
|
||||
override suspend fun getPageUrl(page: MangaPage): String = parser.getPageUrl(page)
|
||||
override suspend fun getPageUrl(page: MangaPage): String = mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getPageUrl(page)
|
||||
}
|
||||
|
||||
override suspend fun getTags(): Set<MangaTag> = parser.getTags()
|
||||
override suspend fun getTags(): Set<MangaTag> = mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getTags()
|
||||
}
|
||||
|
||||
suspend fun getFavicons(): Favicons = parser.getFavicons()
|
||||
suspend fun getFavicons(): Favicons = mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getFavicons()
|
||||
}
|
||||
|
||||
override suspend fun getRelated(seed: Manga): List<Manga> {
|
||||
cache.getRelatedManga(source, seed.url)?.let { return it }
|
||||
@@ -105,7 +120,9 @@ class RemoteMangaRepository(
|
||||
}
|
||||
cache.getDetails(source, manga.url)?.let { return it }
|
||||
val details = asyncSafe {
|
||||
parser.getDetails(manga)
|
||||
mirrorSwitchInterceptor.withMirrorSwitching {
|
||||
parser.getDetails(manga)
|
||||
}
|
||||
}
|
||||
cache.putDetails(source, manga.url, details)
|
||||
return details.await()
|
||||
@@ -155,4 +172,33 @@ class RemoteMangaRepository(
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private suspend fun <R> MirrorSwitchInterceptor.withMirrorSwitching(block: suspend () -> R): R {
|
||||
if (!isEnabled) {
|
||||
return block()
|
||||
}
|
||||
val initialMirror = domain
|
||||
val result = runCatchingCancellable {
|
||||
block()
|
||||
}
|
||||
if (result.isValidResult()) {
|
||||
return result.getOrThrow()
|
||||
}
|
||||
return if (trySwitchMirror(this@RemoteMangaRepository)) {
|
||||
val newResult = runCatchingCancellable {
|
||||
block()
|
||||
}
|
||||
if (newResult.isValidResult()) {
|
||||
return newResult.getOrThrow()
|
||||
} else {
|
||||
rollback(this@RemoteMangaRepository, initialMirror)
|
||||
return result.getOrThrow()
|
||||
}
|
||||
} else {
|
||||
result.getOrThrow()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Result<*>.isValidResult() = exceptionOrNull() !is ParseException
|
||||
&& (getOrNull() as? Collection<*>)?.isEmpty() != true
|
||||
}
|
||||
|
||||
@@ -270,6 +270,9 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
val isReaderSliderEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_SLIDER, true)
|
||||
|
||||
val isReaderKeepScreenOn: Boolean
|
||||
get() = prefs.getBoolean(KEY_READER_SCREEN_ON, true)
|
||||
|
||||
val isImagesProxyEnabled: Boolean
|
||||
get() = prefs.getBoolean(KEY_IMAGES_PROXY, false)
|
||||
|
||||
@@ -460,6 +463,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
||||
const val KEY_READER_BAR = "reader_bar"
|
||||
const val KEY_READER_SLIDER = "reader_slider"
|
||||
const val KEY_READER_BACKGROUND = "reader_background"
|
||||
const val KEY_READER_SCREEN_ON = "reader_screen_on"
|
||||
const val KEY_SHORTCUTS = "dynamic_shortcuts"
|
||||
const val KEY_READER_TAPS_LTR = "reader_taps_ltr"
|
||||
const val KEY_LOCAL_LIST_ORDER = "local_order"
|
||||
|
||||
@@ -83,6 +83,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
private val viewModel by viewModels<MainViewModel>()
|
||||
private val searchSuggestionViewModel by viewModels<SearchSuggestionViewModel>()
|
||||
private val closeSearchCallback = CloseSearchCallback()
|
||||
private val appUpdateDialog = AppUpdateDialog(this)
|
||||
private lateinit var navigationDelegate: MainNavigationDelegate
|
||||
private lateinit var appUpdateBadge: OptionsMenuBadgeHelper
|
||||
|
||||
@@ -198,8 +199,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>(), AppBarOwner, BottomNav
|
||||
|
||||
R.id.action_app_update -> {
|
||||
viewModel.appUpdate.value?.also {
|
||||
AppUpdateDialog(this)
|
||||
.show(it)
|
||||
appUpdateDialog.show(it)
|
||||
} != null
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ class ReaderActivity :
|
||||
onLoadingStateChanged(viewModel.isLoading.value)
|
||||
}
|
||||
viewModel.isScreenshotsBlockEnabled.observe(this, this::setWindowSecure)
|
||||
viewModel.isKeepScreenOnEnabled.observe(this, this::setKeepScreenOn)
|
||||
viewModel.isInfoBarEnabled.observe(this, ::onReaderBarChanged)
|
||||
viewModel.isBookmarkAdded.observe(this, this::onBookmarkStateChanged)
|
||||
viewModel.onShowToast.observeEvent(this) { msgId ->
|
||||
@@ -304,6 +305,14 @@ class ReaderActivity :
|
||||
}
|
||||
}
|
||||
|
||||
private fun setKeepScreenOn(isKeep: Boolean) {
|
||||
if (isKeep) {
|
||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} else {
|
||||
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUiIsVisible(isUiVisible: Boolean) {
|
||||
if (viewBinding.appbarTop.isVisible != isUiVisible) {
|
||||
if (isAnimationsEnabled) {
|
||||
|
||||
@@ -113,6 +113,12 @@ class ReaderViewModel @Inject constructor(
|
||||
valueProducer = { isReaderBarEnabled },
|
||||
)
|
||||
|
||||
val isKeepScreenOnEnabled = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_READER_SCREEN_ON,
|
||||
valueProducer = { isReaderKeepScreenOn },
|
||||
)
|
||||
|
||||
val isWebtoonZoomEnabled = settings.observeAsStateFlow(
|
||||
scope = viewModelScope + Dispatchers.Default,
|
||||
key = AppSettings.KEY_WEBTOON_ZOOM,
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.koitharu.kotatsu.databinding.ActivitySettingsBinding
|
||||
import org.koitharu.kotatsu.main.ui.owners.AppBarOwner
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.settings.about.AboutSettingsFragment
|
||||
import org.koitharu.kotatsu.settings.about.AppUpdateDialog
|
||||
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment
|
||||
import org.koitharu.kotatsu.settings.sources.SourcesManageFragment
|
||||
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
|
||||
@@ -41,6 +42,8 @@ class SettingsActivity :
|
||||
AppBarOwner,
|
||||
FragmentManager.OnBackStackChangedListener {
|
||||
|
||||
val appUpdateDialog = AppUpdateDialog(this)
|
||||
|
||||
override val appBar: AppBarLayout
|
||||
get() = viewBinding.appbar
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.koitharu.kotatsu.core.ui.BasePreferenceFragment
|
||||
import org.koitharu.kotatsu.core.util.ShareHelper
|
||||
import org.koitharu.kotatsu.core.util.ext.observe
|
||||
import org.koitharu.kotatsu.core.util.ext.observeEvent
|
||||
import org.koitharu.kotatsu.settings.SettingsActivity
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -76,7 +77,7 @@ class AboutSettingsFragment : BasePreferenceFragment(R.string.about) {
|
||||
Snackbar.make(listView, R.string.no_update_available, Snackbar.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
AppUpdateDialog(context ?: return).show(version)
|
||||
(activity as SettingsActivity).appUpdateDialog.show(version)
|
||||
}
|
||||
|
||||
private fun openLink(url: String, title: CharSequence?) {
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package org.koitharu.kotatsu.settings.about
|
||||
|
||||
import android.Manifest
|
||||
import android.app.DownloadManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.text.buildSpannedString
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
@@ -14,19 +18,32 @@ import org.koitharu.kotatsu.core.github.AppVersion
|
||||
import org.koitharu.kotatsu.core.util.FileSize
|
||||
import com.google.android.material.R as materialR
|
||||
|
||||
class AppUpdateDialog(private val context: Context) {
|
||||
class AppUpdateDialog(private val activity: AppCompatActivity) {
|
||||
|
||||
private lateinit var latestVersion: AppVersion
|
||||
|
||||
private val permissionRequest = activity.registerForActivityResult(
|
||||
ActivityResultContracts.RequestPermission(),
|
||||
) {
|
||||
if (it) {
|
||||
downloadUpdateImpl()
|
||||
} else {
|
||||
openInBrowser()
|
||||
}
|
||||
}
|
||||
|
||||
fun show(version: AppVersion) {
|
||||
latestVersion = version
|
||||
val message = buildSpannedString {
|
||||
append(context.getString(R.string.new_version_s, version.name))
|
||||
append(activity.getString(R.string.new_version_s, version.name))
|
||||
appendLine()
|
||||
append(context.getString(R.string.size_s, FileSize.BYTES.format(context, version.apkSize)))
|
||||
append(activity.getString(R.string.size_s, FileSize.BYTES.format(activity, version.apkSize)))
|
||||
appendLine()
|
||||
appendLine()
|
||||
append(Markwon.create(context).toMarkdown(version.description))
|
||||
append(Markwon.create(activity).toMarkdown(version.description))
|
||||
}
|
||||
MaterialAlertDialogBuilder(
|
||||
context,
|
||||
activity,
|
||||
materialR.style.ThemeOverlay_Material3_MaterialAlertDialog_Centered,
|
||||
)
|
||||
.setTitle(R.string.app_update_available)
|
||||
@@ -34,24 +51,38 @@ class AppUpdateDialog(private val context: Context) {
|
||||
.setIcon(R.drawable.ic_app_update)
|
||||
.setNeutralButton(R.string.open_in_browser) { _, _ ->
|
||||
val intent = Intent(Intent.ACTION_VIEW, version.url.toUri())
|
||||
context.startActivity(Intent.createChooser(intent, context.getString(R.string.open_in_browser)))
|
||||
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.open_in_browser)))
|
||||
}.setPositiveButton(R.string.update) { _, _ ->
|
||||
downloadUpdate(version)
|
||||
downloadUpdate()
|
||||
}.setNegativeButton(android.R.string.cancel, null)
|
||||
.setCancelable(false)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun downloadUpdate(version: AppVersion) {
|
||||
private fun downloadUpdate() {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||
permissionRequest.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
} else {
|
||||
downloadUpdateImpl()
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadUpdateImpl() {
|
||||
val version = latestVersion
|
||||
val url = version.apkUrl.toUri()
|
||||
val dm = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val dm = activity.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
val request = DownloadManager.Request(url)
|
||||
.setTitle("${context.getString(R.string.app_name)} v${version.name}")
|
||||
.setTitle("${activity.getString(R.string.app_name)} v${version.name}")
|
||||
.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, url.lastPathSegment)
|
||||
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
.setMimeType("application/vnd.android.package-archive")
|
||||
dm.enqueue(request)
|
||||
Toast.makeText(context, R.string.download_started, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(activity, R.string.download_started, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun openInBrowser() {
|
||||
val intent = Intent(Intent.ACTION_VIEW, latestVersion.url.toUri())
|
||||
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.open_in_browser)))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ import org.koitharu.kotatsu.core.db.MangaDatabase
|
||||
import org.koitharu.kotatsu.core.db.entity.MangaEntity
|
||||
import org.koitharu.kotatsu.core.db.entity.toManga
|
||||
import org.koitharu.kotatsu.core.model.FavouriteCategory
|
||||
import org.koitharu.kotatsu.core.model.isLocal
|
||||
import org.koitharu.kotatsu.core.util.ext.mapItems
|
||||
import org.koitharu.kotatsu.favourites.data.toFavouriteCategory
|
||||
import org.koitharu.kotatsu.local.data.LocalMangaRepository
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.koitharu.kotatsu.parsers.model.MangaSource
|
||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||
import org.koitharu.kotatsu.tracker.data.TrackEntity
|
||||
import org.koitharu.kotatsu.tracker.data.TrackLogEntity
|
||||
@@ -27,12 +28,14 @@ import org.koitharu.kotatsu.tracker.domain.model.TrackingLogItem
|
||||
import java.util.Date
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
private const val NO_ID = 0L
|
||||
|
||||
@Reusable
|
||||
class TrackingRepository @Inject constructor(
|
||||
private val db: MangaDatabase,
|
||||
private val localMangaRepositoryProvider: Provider<LocalMangaRepository>,
|
||||
) {
|
||||
|
||||
private var isGcCalled = AtomicBoolean(false)
|
||||
@@ -66,12 +69,17 @@ class TrackingRepository @Inject constructor(
|
||||
val idSet = HashSet<Long>()
|
||||
val result = ArrayList<MangaTracking>(mangaList.size)
|
||||
for (item in mangaList) {
|
||||
if (item.source == MangaSource.LOCAL || !idSet.add(item.id)) {
|
||||
val manga = if (item.isLocal) {
|
||||
localMangaRepositoryProvider.get().getRemoteManga(item) ?: continue
|
||||
} else {
|
||||
item
|
||||
}
|
||||
if (!idSet.add(manga.id)) {
|
||||
continue
|
||||
}
|
||||
val track = tracks[item.id]?.lastOrNull()
|
||||
val track = tracks[manga.id]?.lastOrNull()
|
||||
result += MangaTracking(
|
||||
manga = item,
|
||||
manga = manga,
|
||||
lastChapterId = track?.lastChapterId ?: NO_ID,
|
||||
lastCheck = track?.lastCheck?.takeUnless { it == 0L }?.let(::Date),
|
||||
)
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:keepScreenOn="true">
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/container"
|
||||
|
||||
@@ -479,4 +479,8 @@
|
||||
<string name="directories">Каталогі</string>
|
||||
<string name="main_screen_sections">Раздзелы галоўнага экрана</string>
|
||||
<string name="to_top">Уверх</string>
|
||||
<string name="zoom_in">Павялічыць</string>
|
||||
<string name="reader_zoom_buttons_summary">Ці паказваць кнопкі кіравання маштабаваннем у правым ніжнім куце</string>
|
||||
<string name="reader_zoom_buttons">Паказаць кнопкі маштабавання</string>
|
||||
<string name="zoom_out">Зменшыць</string>
|
||||
</resources>
|
||||
@@ -473,4 +473,14 @@
|
||||
<string name="too_many_requests_message">Masyadong maraming request. Subukang ulit mamaya</string>
|
||||
<string name="default_section">Default na seksyon</string>
|
||||
<string name="manga_list">Listahan ng Manga</string>
|
||||
<string name="zoom_in">Mag-zoom paloob</string>
|
||||
<string name="reader_zoom_buttons_summary">Kung magpapakita ng mga button ng kontrol ng zoom sa kanang sulok sa ibaba</string>
|
||||
<string name="on_device">Sa device</string>
|
||||
<string name="moved_to_top">Nailipat sa itaas</string>
|
||||
<string name="items_limit_exceeded">Wala nang mga aytem na pwedeng idagdag</string>
|
||||
<string name="directories">Mga Directory</string>
|
||||
<string name="reader_zoom_buttons">Ipakita ang mga button ng pag-zoom</string>
|
||||
<string name="main_screen_sections">Mga pangunahing seksyon ng screen</string>
|
||||
<string name="zoom_out">Mag-zoom palabas</string>
|
||||
<string name="to_top">Sa taas</string>
|
||||
</resources>
|
||||
@@ -479,4 +479,8 @@
|
||||
<string name="main_screen_sections">メイン画面のセクション</string>
|
||||
<string name="moved_to_top">上部に移動しました</string>
|
||||
<string name="to_top">最上部へ移動</string>
|
||||
<string name="zoom_in">ズームイン</string>
|
||||
<string name="reader_zoom_buttons_summary">右下にズームコントロールボタンを表示するかどうか</string>
|
||||
<string name="reader_zoom_buttons">ズームボタンを表示</string>
|
||||
<string name="zoom_out">ズームアウト</string>
|
||||
</resources>
|
||||
@@ -1,27 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d item</item>
|
||||
<item quantity="other">%1$d itens</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d capítulo</item>
|
||||
<item quantity="other">%1$d capítulos</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d novo capítulo</item>
|
||||
<item quantity="other">%1$d novos capítulos</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d minuto atrás</item>
|
||||
<item quantity="other">%1$d minutos atrás</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d hora atrás</item>
|
||||
<item quantity="other">%1$d horas atrás</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d dia atrás</item>
|
||||
<item quantity="other">%1$d dias atrás</item>
|
||||
</plurals>
|
||||
</resources>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d item</item>
|
||||
<item quantity="other">%1$d itens</item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d capítulo</item>
|
||||
<item quantity="other">%1$d capítulos</item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d novo capítulo</item>
|
||||
<item quantity="other">%1$d novos capítulos</item>
|
||||
</plurals>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d minuto atrás</item>
|
||||
<item quantity="other">%1$d minutos atrás</item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d hora atrás</item>
|
||||
<item quantity="other">%1$d horas atrás</item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d dia atrás</item>
|
||||
<item quantity="other">%1$d dias atrás</item>
|
||||
</plurals>
|
||||
<plurals name="months_ago">
|
||||
<item quantity="one">%1$d mês atrás</item>
|
||||
<item quantity="many">%1$d meses atrás</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -90,7 +90,7 @@
|
||||
<string name="text_empty_holder_primary">Tá meio vazio aqui…</string>
|
||||
<string name="text_search_holder_secondary">Tente reformular a consulta.</string>
|
||||
<string name="text_history_holder_primary">O que você ler será exibido aqui</string>
|
||||
<string name="text_history_holder_secondary">Encontre o que ler no menu lateral.</string>
|
||||
<string name="text_history_holder_secondary">Encontre o que ler na aba «Explorar»</string>
|
||||
<string name="text_local_holder_primary">Salve algo primeiro</string>
|
||||
<string name="manga_shelf">Prateleira</string>
|
||||
<string name="recent_manga">Recente</string>
|
||||
@@ -281,7 +281,7 @@
|
||||
<string name="show_reading_indicators">Mostrar indicadores de progresso de leitura</string>
|
||||
<string name="data_deletion">Exclusão de dados</string>
|
||||
<string name="show_reading_indicators_summary">Mostrar percentual de leitura no histórico e nos favoritos</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Mangás marcados como +18 nunca serão adicionados ao histórico e seu progresso não sera salvo</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Mangás marcados como +18 não serão adicionados ao histórico e o progresso não será salvo</string>
|
||||
<string name="show_all">Mostrar tudo</string>
|
||||
<string name="clear_all_history">Limpar todo o histórico</string>
|
||||
<string name="last_2_hours">Ultimas 2 horas</string>
|
||||
@@ -361,14 +361,14 @@
|
||||
<string name="theme_name_rikka">Rikka</string>
|
||||
<string name="theme_name_sakura">Sakura</string>
|
||||
<string name="source_disabled">Fonte desativada</string>
|
||||
<string name="enable_logging_summary">Gravar algumas ações para propósitos de depuração</string>
|
||||
<string name="enable_logging_summary">Gravar algumas ações para propósitos de depuração. Não ative se você não sabe o que está fazendo</string>
|
||||
<string name="theme_name_mamimi">Mamimi</string>
|
||||
<string name="theme_name_miku">Miku</string>
|
||||
<string name="theme_name_kanade">Kanade</string>
|
||||
<string name="scrobbling_empty_hint">Para acompanhar o progresso da leitura, selecione Menu → Rastrear na tela de detalhes do mangá.</string>
|
||||
<string name="clear_new_chapters_counters">Também informações claras sobre novos capítulos</string>
|
||||
<string name="sync_auth_hint">Você pode entrar em uma conta existente ou criar uma nova</string>
|
||||
<string name="allow_unstable_updates_summary">Propor atualizações para versões beta do aplicativo</string>
|
||||
<string name="allow_unstable_updates_summary">Receber notificações sobre versões beta</string>
|
||||
<string name="comics_archive_import_description">Você pode selecionar um ou mais arquivos .cbz ou .zip, cada arquivo será reconhecido como um mangá separado.</string>
|
||||
<string name="folder_with_images_import_description">Você pode selecionar um diretório com arquivos ou imagens. Cada arquivo (ou subdiretório) será reconhecido como um capítulo.</string>
|
||||
<string name="speed">Velocidade</string>
|
||||
@@ -429,7 +429,7 @@
|
||||
<string name="sync_host_description">Você pode usar um servidor de sincronização auto-hospedado ou um padrão. Não mude isso se não tiver certeza do que está fazendo.</string>
|
||||
<string name="ignore_ssl_errors">Ignorar erros de SSL</string>
|
||||
<string name="mirror_switching">Escolher espelho automaticamente</string>
|
||||
<string name="mirror_switching_summary">Alternar domínios automaticamente para fontes remotas em caso de erros, se espelhos estiverem disponíveis</string>
|
||||
<string name="mirror_switching_summary">Troca automática de domínios para fontes de mangá em caso de erros, se houver espelhos disponíveis</string>
|
||||
<string name="pause">Pausar</string>
|
||||
<string name="suggestions_enable_prompt">Quer receber sugestões personalizadas de mangás\?</string>
|
||||
<string name="suggestion_manga">Sugestão: %s</string>
|
||||
@@ -443,4 +443,44 @@
|
||||
<string name="pick_custom_directory">Escolha um diretório personalizado</string>
|
||||
<string name="no_access_to_file">Você não tem acesso a esse arquivo ou diretório</string>
|
||||
<string name="local_manga_directories">Diretórios locais de mangá</string>
|
||||
<string name="languages">Idiomas</string>
|
||||
<string name="zoom_in">Aumentar o zoom</string>
|
||||
<string name="captcha_required_summary">%s requer que um captcha seja resolvido para funcionar</string>
|
||||
<string name="progress">Progresso</string>
|
||||
<string name="error_corrupted_file">Dados inválidos ou o arquivo está corrompido</string>
|
||||
<string name="related_manga_summary">Mostra uma lista de mangás relacionados. Em alguns casos, ela pode estar incorreta ou ausente</string>
|
||||
<string name="reader_zoom_buttons_summary">Se deseja mostrar os botões de controle de zoom no canto inferior direito</string>
|
||||
<string name="tracker_wifi_only_summary">Não procurar por novos capítulos usando dados móveis</string>
|
||||
<string name="order_added">Adicionado</string>
|
||||
<string name="on_device">No dispositivo</string>
|
||||
<string name="moved_to_top">Movido pra cima</string>
|
||||
<string name="data_not_restored_text">Certifique-se de ter selecionado o arquivo de backup correto</string>
|
||||
<string name="view_list">Ver lista</string>
|
||||
<string name="unknown">Desconhecido</string>
|
||||
<string name="in_progress">Em andamento</string>
|
||||
<string name="items_limit_exceeded">Não é possível adicionar mais itens</string>
|
||||
<string name="data_not_restored">Os dados não foram restaurados</string>
|
||||
<string name="directories">Diretórios</string>
|
||||
<string name="manage_categories">Gerenciar categorias</string>
|
||||
<string name="color_light">Claro</string>
|
||||
<string name="search_hint">Coloque o nome do mangá, gênero ou fonte</string>
|
||||
<string name="description">Descrição</string>
|
||||
<string name="reader_zoom_buttons">Mostrar botões de zoom</string>
|
||||
<string name="main_screen_sections">Seções da tela principal</string>
|
||||
<string name="advanced">Avançado</string>
|
||||
<string name="color_dark">Escuro</string>
|
||||
<string name="too_many_requests_message">Muitas tentativas. Tente de novo mais tarde</string>
|
||||
<string name="related_manga">Mangá relacionado</string>
|
||||
<string name="suggestions_wifi_only_summary">Não atualize sugestões usando dados móveis</string>
|
||||
<string name="default_section">Seção padrão</string>
|
||||
<string name="background">Fundo</string>
|
||||
<string name="zoom_out">Diminuir o zoom</string>
|
||||
<string name="voice_search">Pesquisa por voz</string>
|
||||
<string name="manga_list">Lista de mangás</string>
|
||||
<string name="disable_nsfw">Desativar NSFW (+18)</string>
|
||||
<string name="color_white">Branco</string>
|
||||
<string name="to_top">Para cima</string>
|
||||
<string name="show">Mostrar</string>
|
||||
<string name="color_black">Preto</string>
|
||||
<string name="this_month">Esse mês</string>
|
||||
</resources>
|
||||
38
app/src/main/res/values-pt/plurals.xml
Normal file
38
app/src/main/res/values-pt/plurals.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<plurals name="minutes_ago">
|
||||
<item quantity="one">%1$d minuto atrás</item>
|
||||
<item quantity="many">%1$d minutos atrás</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="items">
|
||||
<item quantity="one">%1$d item</item>
|
||||
<item quantity="many">%1$d itens</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="chapters">
|
||||
<item quantity="one">%1$d capítulo</item>
|
||||
<item quantity="many">%1$d capítulos</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="new_chapters">
|
||||
<item quantity="one">%1$d novo capítulo</item>
|
||||
<item quantity="many">%1$d novos capítulos</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="months_ago">
|
||||
<item quantity="one">%1$d mês atrás</item>
|
||||
<item quantity="many">%1$d meses atrás</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="days_ago">
|
||||
<item quantity="one">%1$d dia atrás</item>
|
||||
<item quantity="many">%1$d dias atrás</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
<plurals name="hours_ago">
|
||||
<item quantity="one">%1$d hora atrás</item>
|
||||
<item quantity="many">%1$d horas atrás</item>
|
||||
<item quantity="other"></item>
|
||||
</plurals>
|
||||
</resources>
|
||||
@@ -164,7 +164,7 @@
|
||||
<string name="vibration">Vibração</string>
|
||||
<string name="cannot_find_available_storage">Sem armazenamento disponível</string>
|
||||
<string name="favourites_categories">Categorias favoritas</string>
|
||||
<string name="text_history_holder_secondary">Encontre o que ler no menu lateral.</string>
|
||||
<string name="text_history_holder_secondary">Encontre o que ler na aba «Explorar»</string>
|
||||
<string name="text_local_holder_secondary">Salve-o de fontes online ou importe fiheiros.</string>
|
||||
<string name="recent_manga">Recente</string>
|
||||
<string name="other_storage">Outro armazenamento</string>
|
||||
@@ -313,7 +313,7 @@
|
||||
<string name="disable_all">Desativar tudo</string>
|
||||
<string name="report">Relatório</string>
|
||||
<string name="show_reading_indicators_summary">Mostrar porcentagem lida no histórico e favoritos</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Mangás marcados como NSFW nunca serão adicionados ao seu histórico e seu progresso não será salvo</string>
|
||||
<string name="exclude_nsfw_from_history_summary">Os mangás marcados como NSFW nunca serão adicionados ao histórico e o seu progresso não será guardado</string>
|
||||
<string name="history_cleared">Histórico deletado</string>
|
||||
<string name="manage">Gerenciar</string>
|
||||
<string name="no_bookmarks_yet">Ainda não há marcadores</string>
|
||||
@@ -351,7 +351,7 @@
|
||||
<string name="theme_name_mamimi">Mamimi</string>
|
||||
<string name="nothing_here">Não há nada aqui</string>
|
||||
<string name="services">Serviços</string>
|
||||
<string name="enable_logging_summary">Gravar algumas ações para propósitos de depuração</string>
|
||||
<string name="enable_logging_summary">Gravar algumas ações para propósitos de depuração. Não ative se você não sabe o que está fazendo</string>
|
||||
<string name="allow_unstable_updates">Permitir atualizações instáveis</string>
|
||||
<string name="download_started">Descarga iniciada</string>
|
||||
<string name="share_logs">Compartilhar registos</string>
|
||||
@@ -370,7 +370,7 @@
|
||||
<string name="scrobbling_empty_hint">Para acompanhar o progresso da leitura, selecione Menu → Vá até a tela de detalhes do mangá.</string>
|
||||
<string name="details_button_tip">Pressione e segure o botão Ler para ver mais opções</string>
|
||||
<string name="webtoon_zoom">Zoom Webtoon</string>
|
||||
<string name="allow_unstable_updates_summary">Propor atualizações para versões beta do aplicativo</string>
|
||||
<string name="allow_unstable_updates_summary">Receber notificações sobre versões instáveis</string>
|
||||
<string name="got_it">Entendi</string>
|
||||
<string name="sources_reorder_tip">Toque e segure em um item para reordená-lo</string>
|
||||
<string name="comics_archive_import_description">Você pode selecionar um ou mais arquivos .cbz ou .zip, cada arquivo será reconhecido como um mangá separado.</string>
|
||||
@@ -392,7 +392,7 @@
|
||||
<string name="server_address">Endereço do servidor</string>
|
||||
<string name="ignore_ssl_errors">Ignorar erros de SSL</string>
|
||||
<string name="mirror_switching">Escolher espelho automaticamente</string>
|
||||
<string name="mirror_switching_summary">Alternar domínios automaticamente para fontes remotas em caso de erros, se espelhos estiverem disponíveis</string>
|
||||
<string name="mirror_switching_summary">Troca automática de domínios para fontes de mangá em caso de erro, se houver espelhos disponíveis</string>
|
||||
<string name="pause">Pausa</string>
|
||||
<string name="downloads_wifi_only">Baixe apenas via Wi-Fi</string>
|
||||
<string name="downloads_wifi_only_summary">Interrompa o download ao mudar para uma rede móvel</string>
|
||||
@@ -431,4 +431,56 @@
|
||||
<string name="enable">Activar</string>
|
||||
<string name="no_thanks">Não obrigado</string>
|
||||
<string name="remove_completed">Remoção concluída</string>
|
||||
<string name="download_option_all_unread">Todos os capítulos não lidos</string>
|
||||
<string name="clear_source_cookies_summary">Limpa os cookies apenas para o domínio especificado. Na maioria dos casos, a autorização será invalidada</string>
|
||||
<string name="download_option_manual_selection">Selecionar os capítulos manualmente</string>
|
||||
<string name="download_option_all_unread_b">Todos os capítulos não lidos (%s)</string>
|
||||
<string name="download_option_first_n_chapters">Primeiro %s</string>
|
||||
<string name="custom_directory">Diretório personalizado</string>
|
||||
<string name="download_option_next_unread_n_chapters">Próximo não lido %s</string>
|
||||
<string name="download_option_all_chapters">Todos os capítulos com tradução %s</string>
|
||||
<string name="languages">Línguas</string>
|
||||
<string name="zoom_in">Aumentar o zoom</string>
|
||||
<string name="captcha_required_summary">%s requer que um captcha seja resolvido para funcionar corretamente</string>
|
||||
<string name="progress">Progresso</string>
|
||||
<string name="error_corrupted_file">São devolvidos dados inválidos ou o ficheiro está corrompido</string>
|
||||
<string name="pick_custom_directory">Escolher diretório personalizado</string>
|
||||
<string name="related_manga_summary">Mostrar uma lista de mangas relacionadas. Em alguns casos, pode estar incorrecta ou em falta</string>
|
||||
<string name="reader_zoom_buttons_summary">Mostrar ou não os botões de controlo do zoom no canto inferior direito</string>
|
||||
<string name="tracker_wifi_only_summary">Não verificar a existência de novos capítulos utilizando ligações de rede com contador</string>
|
||||
<string name="order_added">Adicionado</string>
|
||||
<string name="on_device">No dispositivo</string>
|
||||
<string name="download_option_whole_manga">Todo o mangá</string>
|
||||
<string name="moved_to_top">Movido para o topo</string>
|
||||
<string name="data_not_restored_text">Certifique-se de que seleccionou o ficheiro de cópia de segurança correto</string>
|
||||
<string name="view_list">Ver lista</string>
|
||||
<string name="unknown">Desconhecido</string>
|
||||
<string name="in_progress">Em curso</string>
|
||||
<string name="items_limit_exceeded">Não podem ser adicionados mais itens</string>
|
||||
<string name="data_not_restored">Os dados não foram restaurados</string>
|
||||
<string name="directories">Directórios</string>
|
||||
<string name="local_manga_directories">Directórios locais de manga</string>
|
||||
<string name="manage_categories">Gerir categorias</string>
|
||||
<string name="color_light">Claro</string>
|
||||
<string name="search_hint">Introduzir o título da manga, o género ou o nome da fonte</string>
|
||||
<string name="description">Descrição</string>
|
||||
<string name="reader_zoom_buttons">Mostrar botões de zoom</string>
|
||||
<string name="main_screen_sections">Secções do ecrã principal</string>
|
||||
<string name="advanced">Avançado</string>
|
||||
<string name="color_dark">Escuro</string>
|
||||
<string name="too_many_requests_message">Demasiados pedidos. Tentar novamente mais tarde</string>
|
||||
<string name="related_manga">Mangá relacionado</string>
|
||||
<string name="suggestions_wifi_only_summary">Não atualizar sugestões utilizando ligações de rede com contador</string>
|
||||
<string name="default_section">Secção predefinida</string>
|
||||
<string name="background">Fundo</string>
|
||||
<string name="no_access_to_file">Não tem acesso a este ficheiro ou diretório</string>
|
||||
<string name="zoom_out">Reduzir o zoom</string>
|
||||
<string name="voice_search">Pesquisa por voz</string>
|
||||
<string name="manga_list">Lista de mangás</string>
|
||||
<string name="disable_nsfw">Desativar NSFW</string>
|
||||
<string name="color_white">Branco</string>
|
||||
<string name="to_top">Para cima</string>
|
||||
<string name="show">Mostrar</string>
|
||||
<string name="color_black">Preto</string>
|
||||
<string name="this_month">Este mês</string>
|
||||
</resources>
|
||||
@@ -479,4 +479,8 @@
|
||||
<string name="directories">Каталоги</string>
|
||||
<string name="main_screen_sections">Розділи головного екрана</string>
|
||||
<string name="to_top">Вгору</string>
|
||||
<string name="zoom_in">Збільшити</string>
|
||||
<string name="reader_zoom_buttons_summary">Чи показувати елементи керування масштабуванням у нижньому правому куті</string>
|
||||
<string name="reader_zoom_buttons">Показати кнопки масштабування</string>
|
||||
<string name="zoom_out">Зменшити</string>
|
||||
</resources>
|
||||
@@ -477,4 +477,8 @@
|
||||
<string name="to_top">置顶</string>
|
||||
<string name="show">显示</string>
|
||||
<string name="color_black">黑色</string>
|
||||
<string name="zoom_in">放大</string>
|
||||
<string name="reader_zoom_buttons_summary">是否在右下角显示缩放按钮</string>
|
||||
<string name="reader_zoom_buttons">显示缩放按钮</string>
|
||||
<string name="zoom_out">缩小</string>
|
||||
</resources>
|
||||
@@ -489,4 +489,6 @@
|
||||
<string name="zoom_in">Zoom in</string>
|
||||
<string name="reader_zoom_buttons">Show zoom buttons</string>
|
||||
<string name="reader_zoom_buttons_summary">Whether to show zoom control buttons in the bottom right corner</string>
|
||||
<string name="keep_screen_on">Keep screen on</string>
|
||||
<string name="keep_screen_on_summary">Do not turn the screen off while you\'re reading manga</string>
|
||||
</resources>
|
||||
|
||||
@@ -78,13 +78,19 @@
|
||||
android:summary="@string/show_pages_numbers_summary"
|
||||
android:title="@string/show_pages_numbers" />
|
||||
|
||||
<SwitchPreferenceCompat
|
||||
android:defaultValue="true"
|
||||
android:key="reader_screen_on"
|
||||
android:summary="@string/keep_screen_on_summary"
|
||||
android:title="@string/keep_screen_on"
|
||||
app:allowDividerAbove="true" />
|
||||
|
||||
<ListPreference
|
||||
android:defaultValue="allow"
|
||||
android:entries="@array/screenshots_policy"
|
||||
android:entryValues="@array/values_screenshots_policy"
|
||||
android:key="screenshots_policy"
|
||||
android:title="@string/screenshots_policy"
|
||||
app:allowDividerAbove="true"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<ListPreference
|
||||
|
||||
Reference in New Issue
Block a user